ff

分析

首先ida查看一下该程序,程序一共提供了四种功能,分别是add,delete,show,edit四个函数,其中show函数只能够调用一次,edit函数只能调用两次。比较特殊的一个点就是该程序使用的GLIBC 2.32。我们首先来分析一下所有的函数,首先是add函数

<!-- more -->

__int64 add()
{
  __int64 result; // rax
  unsigned int i; // [rsp+8h] [rbp-18h]
  unsigned int size; // [rsp+Ch] [rbp-14h]
  void *size_4; // [rsp+10h] [rbp-10h]

  puts("Size:");
  size = myRead();
  if ( size > 0x7E )
    size = 0x7F;
  size_4 = malloc(size);
  for ( i = 0; i <= 0xF; ++i )
  {
    if ( !noteList[i] )
    {
      noteList[i] = size_4;
      global_index = i;
      break;
    }
  }
  result = noteList[global_index];
  if ( result )
  {
    puts("Content:");
    read(0, size_4, size);
    result = 0LL;
  }
  return result;
}

从这里可以看出,一共最多可以分配0x10个堆块,并且每个堆块的大小要<=0x90,将新申请的堆块的index赋给了global index。看一下delete函数。

void del()
{
  free((void *)noteList[global_index]);
}

函数很简单,删除当前global index,并且这里没有清空列表中存储的堆块指针,也就是存在UAF。但是需要注意的是这里只能删除了global index指向的堆块。结合add函数来看,只能删除新申请的堆块,之前的旧的堆块无法进行删除。再来看一下show函数。

ssize_t show()
{
  return write(1, (const void *)noteList[global_index], 8uLL);
}

也就是输出了当前global index指向堆块的前8字节。最后看一下edit函数

ssize_t edit()
{
  puts("Content:");
  return read(0, (void *)noteList[global_index], 0x10uLL);
}

这里也是对global index指向的堆块进行0x10字节大小的edit

利用

从上述的函数分析来看,这里的对于堆块的操作仅仅限于当前的堆块。程序中存在一个UAF

那么这里利用UAF我们仅仅可以泄漏出堆地址,并且这还是由于2.32特性的原因。其最主要的一个点就是进行了tcache->fd指针的加密。

#define PROTECT_PTR(pos, ptr) \\\\
  ((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr)  PROTECT_PTR (&ptr, ptr)

也就是进行了抑或加密。那么这里就和其他版本的tcache不一样了。我们看一下释放一个堆块之后的堆块内容。

pwndbg> heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x555555757310 (size : 0x20cf0)
       last_remainder: 0x0 (size : 0x0)
            unsortbin: 0x0
(0x80)   tcache_entry[6](1): 0x5555557572a0 --> 0x555555757 (invaild memory)
pwndbg> x/20gx  0x5555557572a0
0x5555557572a0: 0x0000000555555757      0x0000555555757010
0x5555557572b0: 0x0000000000000000      0x0000000000000000
0x5555557572c0: 0x0000000000000000      0x0000000000000000
0x5555557572d0: 0x0000000000000000      0x0000000000000000
0x5555557572e0: 0x0000000000000000      0x0000000000000000
0x5555557572f0: 0x0000000000000000      0x0000000000000000
0x555555757300: 0x0000000000000000      0x0000000000000000
0x555555757310: 0x0000000000000000      0x0000000000020cf1
0x555555757320: 0x0000000000000000      0x0000000000000000
0x555555757330: 0x0000000000000000      0x0000000000000000
pwndbg>

也就是说如果我们此时调用show函数就可以泄漏出一个堆地址。那么得到这个堆地址之后就可以利用两次edit的机会构造double free,覆写fd指针,使得我们可以分配到pthread_tcache_struct结构体所在的堆块进而控制tcachecountentry指针,从而实现任意的地址分配。