首页
社区
课程
招聘
[原创]2019看雪CTF 晋级赛Q1 第9题
发表于: 2019-3-23 00:26 3145

[原创]2019看雪CTF 晋级赛Q1 第9题

2019-3-23 00:26
3145

一、使用checksec 看下程序状况: 可知got表可写、elf文件地址固定。

 Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    FORTIFY:  Enabled
二、main函数
 Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
    FORTIFY:  Enabled
二、main函数
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 v3; // rdx
  unsigned __int64 readCmd; // rax

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  __printf_chk(1LL, "Please input your name: ", v3);
  readData(g_inputName, 16LL);
  while ( 1 )
  {
    printMenu();
    readCmd = readInt();
LABEL_3:
    switch ( readCmd )
    {
      case 0uLL:
        continue;
      case 1uLL:                                // malloc
        mallocByFun((__int64 (__fastcall *)(unsigned __int64))mallocAndClearBuf);
        continue;
      case 2uLL:                                // free
        sub_401010((__int64 (__fastcall *)(unsigned __int64))freeByIndex);
        continue;
      case 3uLL:                                // new
        mallocByFun((__int64 (__fastcall *)(unsigned __int64))newClassFun);
        continue;
      case 4uLL:                                // delete
        sub_401010((__int64 (__fastcall *)(unsigned __int64))freeNewClass);
        printMenu();
        readCmd = readInt();
        if ( readCmd > 5 )
          return 0LL;
        goto LABEL_3;
      case 5uLL:                                // puts
        sub_401010((__int64 (__fastcall *)(unsigned __int64))PrintfByIndex);
        break;
      default:
        return 0LL;
    }
  }
}
一、malloc函数
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 v3; // rdx
  unsigned __int64 readCmd; // rax

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  __printf_chk(1LL, "Please input your name: ", v3);
  readData(g_inputName, 16LL);
  while ( 1 )
  {
    printMenu();
    readCmd = readInt();
LABEL_3:
    switch ( readCmd )
    {
      case 0uLL:
        continue;
      case 1uLL:                                // malloc
        mallocByFun((__int64 (__fastcall *)(unsigned __int64))mallocAndClearBuf);
        continue;
      case 2uLL:                                // free
        sub_401010((__int64 (__fastcall *)(unsigned __int64))freeByIndex);
        continue;
      case 3uLL:                                // new
        mallocByFun((__int64 (__fastcall *)(unsigned __int64))newClassFun);
        continue;
      case 4uLL:                                // delete
        sub_401010((__int64 (__fastcall *)(unsigned __int64))freeNewClass);
        printMenu();
        readCmd = readInt();
        if ( readCmd > 5 )
          return 0LL;
        goto LABEL_3;
      case 5uLL:                                // puts
        sub_401010((__int64 (__fastcall *)(unsigned __int64))PrintfByIndex);
        break;
      default:
        return 0LL;
    }
  }
}
一、malloc函数
char __fastcall mallocByFun(__int64 (__fastcall *mallocFun)(unsigned __int64))
{
  unsigned __int64 num; // rax
  __int64 findIndex; // rbx
  unsigned __int64 index; // rbp
  char result; // al
  signed __int64 v5; // r12
  __int64 v6; // rbp
  signed __int64 v7; // r12

  puts("Please input length of the string");
  num = readInt();                              // len <=0x400
  if ( num > 0x400 )
LABEL_10:
    exit(-1);
  findIndex = 0LL;
  index = (unsigned __int64)(0x8888888888888889LL * (unsigned __int128)(num + 14) >> 0x40) >> 3;
  while ( g_array_mallocBuf[findIndex] )
  {
    if ( ++findIndex == 0x20 )                  // g_array_mallocBuf[0x19]不为空,则findIndex = 0x20,
                                                // 此时会越界g_array_mallocBuf[findIndex]
      goto LABEL_10;
  }
  g_array_mallocBuf[findIndex] = (void *)mallocFun((unsigned __int64)(0x8888888888888889LL
                                                                    * (unsigned __int128)(num + 14) >> 0x40) >> 3);
  g_array_mallocBufSize[findIndex] = index;
  result = puts("Please input the string");
  if ( index )
  {
    v5 = 3 * index;
    v6 = 0LL;
    v7 = 8 * v5;
    do
    {
      result = readData((char *)g_array_mallocBuf[findIndex] + v6 + 8, 16LL);
      if ( !result )
        break;
      v6 += 24LL;
    }
    while ( v6 != v7 );
  }
  return result;
}

char *__fastcall mallocAndClearBuf(__int64 num)
{
  char *mallocBuf; // rax
  char *v2; // rdx
  __int64 v3; // rcx

  mallocBuf = (char *)malloc(24 * num);
  if ( num )
  {
    v2 = mallocBuf + 8;
    v3 = 0LL;
    do
    {
      ++v3;
      *(_QWORD *)v2 = 0LL;
      *((_QWORD *)v2 + 1) = 0LL;
      v2 += 24;
    }
    while ( num != v3 );
  }
  return mallocBuf;
}
1、维护一个存储malloc指针数组和存储每个malloc buf的size数组。数组个数为0x20。没有发现存在越界的问题。
char __fastcall mallocByFun(__int64 (__fastcall *mallocFun)(unsigned __int64))
{
  unsigned __int64 num; // rax
  __int64 findIndex; // rbx
  unsigned __int64 index; // rbp
  char result; // al
  signed __int64 v5; // r12
  __int64 v6; // rbp
  signed __int64 v7; // r12

  puts("Please input length of the string");
  num = readInt();                              // len <=0x400
  if ( num > 0x400 )
LABEL_10:
    exit(-1);
  findIndex = 0LL;
  index = (unsigned __int64)(0x8888888888888889LL * (unsigned __int128)(num + 14) >> 0x40) >> 3;
  while ( g_array_mallocBuf[findIndex] )
  {
    if ( ++findIndex == 0x20 )                  // g_array_mallocBuf[0x19]不为空,则findIndex = 0x20,
                                                // 此时会越界g_array_mallocBuf[findIndex]
      goto LABEL_10;
  }
  g_array_mallocBuf[findIndex] = (void *)mallocFun((unsigned __int64)(0x8888888888888889LL
                                                                    * (unsigned __int128)(num + 14) >> 0x40) >> 3);
  g_array_mallocBufSize[findIndex] = index;
  result = puts("Please input the string");
  if ( index )
  {
    v5 = 3 * index;
    v6 = 0LL;
    v7 = 8 * v5;
    do
    {
      result = readData((char *)g_array_mallocBuf[findIndex] + v6 + 8, 16LL);
      if ( !result )
        break;
      v6 += 24LL;
    }
    while ( v6 != v7 );
  }
  return result;
}

char *__fastcall mallocAndClearBuf(__int64 num)
{
  char *mallocBuf; // rax
  char *v2; // rdx
  __int64 v3; // rcx

  mallocBuf = (char *)malloc(24 * num);
  if ( num )
  {
    v2 = mallocBuf + 8;
    v3 = 0LL;
    do
    {
      ++v3;
      *(_QWORD *)v2 = 0LL;
      *((_QWORD *)v2 + 1) = 0LL;
      v2 += 24;
    }
    while ( num != v3 );
  }
  return mallocBuf;
}
1、维护一个存储malloc指针数组和存储每个malloc buf的size数组。数组个数为0x20。没有发现存在越界的问题。
2、相应的malloc函数也没有发现存在缓冲区溢出的问题。
3、程序根据用于输入数据通过计算后得到一个num 申请的buf大小为24,将后16个字节清零,然后从终端读取数据存入这16个字节中,只存储15个字符,最后一个字符清0。
4、malloc相关函数没发现漏洞。
二、free函数
int __fastcall sub_401010(__int64 (__fastcall *a1)(unsigned __int64))
{
  unsigned __int64 delteNum; // rax
  int result; // eax

  puts("Please input index of the string");
  delteNum = readInt();
  if ( delteNum > 0x1F )
    exit(-1);
  if ( g_array_mallocBuf[delteNum] )
    result = a1(delteNum);
  else
    result = puts("The string does not exit");
  return result;
}
void __fastcall freeByIndex(__int64 index)
{
  free(g_array_mallocBuf[index]);
  g_array_mallocBuf[index] = 0LL;
}
free相关函数没有发现啥漏洞。
int __fastcall sub_401010(__int64 (__fastcall *a1)(unsigned __int64))
{
  unsigned __int64 delteNum; // rax
  int result; // eax

  puts("Please input index of the string");
  delteNum = readInt();
  if ( delteNum > 0x1F )
    exit(-1);
  if ( g_array_mallocBuf[delteNum] )
    result = a1(delteNum);
  else
    result = puts("The string does not exit");
  return result;
}
void __fastcall freeByIndex(__int64 index)
{
  free(g_array_mallocBuf[index]);
  g_array_mallocBuf[index] = 0LL;
}
free相关函数没有发现啥漏洞。
三、new函数
struct newBuf *__fastcall newClassFun(unsigned __int64 num)
{
  __int64 num_1; // rbx
  bool v2; // cf
  bool v3; // zf
  unsigned __int64 mallocLen; // rdi
  struct NewClass *strClass; // rax
  signed __int64 realNum; // rcx
  char *v7; // rdx
  struct newBuf *result; // rax

  num_1 = num;
  v2 = num < 0x550000000000000LL;
  v3 = num == 0x550000000000000LL;
  mallocLen = -1LL;
  if ( v2 || v3 )
    mallocLen = 24 * num_1 + 8;
  strClass = (struct NewClass *)operator new[](mallocLen);
  realNum = num_1 - 1;
  strClass->num = num_1;
  v7 = strClass->newBuf.buf;
  result = &strClass->newBuf;
  if ( num_1 )
  {
    do
    {
      --realNum;
      *((_QWORD *)v7 - 1) = &g_vtable;
      *(_QWORD *)v7 = 0LL;
      *((_QWORD *)v7 + 1) = 0LL;
      v7 += 24;
    }
    while ( realNum != -1 );
  }
  return result;
}
1、申请24*num + 8大小的buf
struct newBuf *__fastcall newClassFun(unsigned __int64 num)
{
  __int64 num_1; // rbx
  bool v2; // cf
  bool v3; // zf
  unsigned __int64 mallocLen; // rdi
  struct NewClass *strClass; // rax
  signed __int64 realNum; // rcx
  char *v7; // rdx
  struct newBuf *result; // rax

  num_1 = num;
  v2 = num < 0x550000000000000LL;
  v3 = num == 0x550000000000000LL;
  mallocLen = -1LL;
  if ( v2 || v3 )
    mallocLen = 24 * num_1 + 8;
  strClass = (struct NewClass *)operator new[](mallocLen);
  realNum = num_1 - 1;
  strClass->num = num_1;
  v7 = strClass->newBuf.buf;
  result = &strClass->newBuf;
  if ( num_1 )
  {
    do
    {
      --realNum;
      *((_QWORD *)v7 - 1) = &g_vtable;
      *(_QWORD *)v7 = 0LL;
      *((_QWORD *)v7 + 1) = 0LL;
      v7 += 24;
    }
    while ( realNum != -1 );
  }
  return result;
}
1、申请24*num + 8大小的buf
2、前8个字节放入一个g_vtable虚函数表(包含一个空函数,和一个j___ZdlPv释放函数)
3、后16个size,清0用于存放输入数据。
4、没发现啥漏洞。
四、delete函数
void __fastcall freeNewClass(__int64 index)
{
  __int64 *buf; // rdx
  __int64 *v2; // rbx
  void (*v3)(); // rax

  buf = (__int64 *)g_array_mallocBuf[index];
  if ( buf )
  {
    v2 = &buf[3 * *(buf - 1)];
    while ( v2 != buf )
    {
      while ( 1 )
      {
        v2 -= 3;
        v3 = *(void (**)())*v2;
        if ( v3 == nullsub_1 )
          break;
        ((void (__fastcall *)(__int64 *))v3)(v2);
        buf = (__int64 *)g_array_mallocBuf[index];
        if ( v2 == buf )
          goto LABEL_6;
      }
    }
LABEL_6:
    operator delete[](v2 - 1);
  }
  g_array_mallocBuf[index] = 0LL;
}
1、函数会定位到最后一个buf位置,读取其vtable,并执行。
void __fastcall freeNewClass(__int64 index)
{
  __int64 *buf; // rdx
  __int64 *v2; // rbx
  void (*v3)(); // rax

  buf = (__int64 *)g_array_mallocBuf[index];
  if ( buf )
  {
    v2 = &buf[3 * *(buf - 1)];
    while ( v2 != buf )
    {
      while ( 1 )
      {
        v2 -= 3;
        v3 = *(void (**)())*v2;
        if ( v3 == nullsub_1 )
          break;
        ((void (__fastcall *)(__int64 *))v3)(v2);
        buf = (__int64 *)g_array_mallocBuf[index];
        if ( v2 == buf )
          goto LABEL_6;
      }
    }
LABEL_6:
    operator delete[](v2 - 1);
  }
  g_array_mallocBuf[index] = 0LL;
}
1、函数会定位到最后一个buf位置,读取其vtable,并执行。
2、从最后一个检索到第一个连续执行。
3、漏洞:malloc和new的buf都存储在一个数组中,没有进行区分。delete一个通过malloc申请的buf,构造vtable表,就可以执行任意函数。
五、printf函数
__int64 __fastcall PrintfByIndex(__int64 index, __int64 a2, __int64 a3)
{
  __int64 v3; // rbp
  unsigned __int64 v4; // rbx
  char *v5; // rdx

  v3 = 0LL;
  v4 = 0LL;
  if ( g_array_mallocBufSize[index] )
  {
    do
    {
      v5 = (char *)g_array_mallocBuf[index] + v3;
      ++v4;
      v3 += 24LL;
      __printf_chk(1LL, &unk_4010E6, v5 + 8);
    }
    while ( g_array_mallocBufSize[index] > v4 );
  }
  return __printf_chk(1LL, &unk_4010E4, a3);
}
打印没发现啥漏洞,通过构造应该能够打印出想要的地址。
__int64 __fastcall PrintfByIndex(__int64 index, __int64 a2, __int64 a3)
{
  __int64 v3; // rbp
  unsigned __int64 v4; // rbx
  char *v5; // rdx

  v3 = 0LL;
  v4 = 0LL;
  if ( g_array_mallocBufSize[index] )
  {
    do
    {
      v5 = (char *)g_array_mallocBuf[index] + v3;
      ++v4;
      v3 += 24LL;
      __printf_chk(1LL, &unk_4010E6, v5 + 8);
    }
    while ( g_array_mallocBufSize[index] > v4 );
  }
  return __printf_chk(1LL, &unk_4010E4, a3);
}
打印没发现啥漏洞,通过构造应该能够打印出想要的地址。
六、漏洞分析
通过上面分析只发现一个明显的漏洞,当使用delete删除的时候没有区分是通过malloc创建的还是new创建的,因此可以使用delete释放通过malloc的buf时,会首先判断24个字节buf的前8字节是否存在vtable,如果存在,则执行其函数,因此可以执行一个任意函数。
但是这里存在一些限制:
1)无法设置参数,因此想要拿到 shell,最容易的办法是通过 one-gadget。
2)要求其存放的是一个指针,指针内容为要执行的函数。因此可以执行got.plt中的函数,但是发现无论是put函数还是其他函数都无法构造合理的参数。
3)如何获取libc的地址?

七、获取libc地址
发现程序存在一个不会执行的函数,其会打印出libc的函数puts地址
__int64 sub_400E10()
{
  signed __int64 v1; // [rsp-8h] [rbp-8h]

  v1 = 684069LL;
  return __printf_chk(0LL, &v1, &puts);
}
可以通过这个函数获得libc地址
__int64 sub_400E10()
{
  signed __int64 v1; // [rsp-8h] [rbp-8h]

  v1 = 684069LL;
  return __printf_chk(0LL, &v1, &puts);
}
可以通过这个函数获得libc地址
在程序开始输入用户名时,其会将输入的名字存放到一个全局变量中。
 __printf_chk(1LL, "Please input your name: ", v3);
  readData(g_inputName, 16LL);
此buf为16个字节,因此我们可以将g_inputName 与 g_inputName+8 作为vtable的地址,然后把想要执行的函数地址写入到 g_inputName中,就可以构建执行任意函数的通道。
此buf只有16个字节,只能写入2个函数,并且这2个函数被直接执行,无法做到控制。我们可以把要执行的函数地址写入到g_inputName中,然后将main函数地址写入到 g_inputName + 8位置,这样就可以无限循环执行任意函数了。
九、漏洞利用实现
1、使用malloc malloc一块大小为24个字节的buf。随便输入任意数据。
2、再次使用malloc ,malloc一个大块内存。
3、输入payload到第2次malloc的buf中。
4、调用delete函数,删除第一次malloc的buf,将执行任意函数。
 __printf_chk(1LL, "Please input your name: ", v3);
  readData(g_inputName, 16LL);
此buf为16个字节,因此我们可以将g_inputName 与 g_inputName+8 作为vtable的地址,然后把想要执行的函数地址写入到 g_inputName中,就可以构建执行任意函数的通道。
此buf只有16个字节,只能写入2个函数,并且这2个函数被直接执行,无法做到控制。我们可以把要执行的函数地址写入到g_inputName中,然后将main函数地址写入到 g_inputName + 8位置,这样就可以无限循环执行任意函数了。

[峰会]看雪.第八届安全开发者峰会10月23日上海龙之梦大酒店举办!

最后于 2019-3-23 13:10 被ODPan编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//