利用 _IO_2_1_stdout_
泄露libc
感觉现在打stdout也比较常见了,遇见了就学一下。一般来说是需要爆破的,而且因为不同版本ASLR的不同,成功的可能性也有所不同。ubuntu16下是1/16,18下是1/4096。如果只能爆破的话最好用try-except语句。
1. FILE 结构体
这两条命令可以看stdout结构体,再看puts和printf的执行流程大致理解为什么我们要劫持 _IO_2_1_stdout_
的_flags、_IO_write_base、_IO_write_ptr
,并篡改为p64(0xfbad1800)+p64(0)*3+'\x00'
1 | pwndbg> p stdout |
因为ctf中一直都是用这一个payload就能leak,结构和函数就不再赘述。
2. 原理
为了泄露处一些libc上的地址,我们需要修改 _IO_2_1_stdout_
的_flags、_IO_write_base、_IO_write_ptr
。步骤如下:
- 一般我们将
_flags
设置为0xfbad18**。
目的是为了设置_IO_CURRENTLY_PUTTING=0x800,_IO_IS_APPENDING=0x1000,IO_MAGIC=0xFBAD0000
- 设置
_IO_write_base
指向想要泄露的地方;_IO_write_ptr
指向泄露结束的地址。 - 之后遇到puts或printf,就会将_IO_write_base指向的内容打印出来。
3. 利用
一般都是利用unsorted bin
的在tcache
或fastbin
的fd上main_arena
的地址(同一个chunk既在unsortbin上,也在tcache上),配合UAF之类的漏洞覆盖低位爆破stdout的地址,然后分配到stdout结构体处,达到泄露的目的。
但是,不是直接正好分配到stdout上,而是分配到stdout-0x43的的地方因为这里正好有合适的chunk,因此一般来说成功非预期申请之后payloads是'a'*0x33+p64(0xfbad1800)+p64(0)*3+'\x00'
。这个地方在2.23中有比较固定的offset,即低3bytes为dd*5 ,*号处有16种可能,但是知道printf的低3byte就知道了,因为偏移是固定的。
4. 例题
UAF
2021 长城杯 King in heap I
逆向分析:libc-2.23,增删改,给出了printf的低三byte,因此不需要爆破;存在UAF。
我们fake一个chunk,通过uaf将其放入fastbin中,然后申请出来,就可以修改真chunk的size位,将真的FB Chunk放入UB之中,这样就可以在FB中连接到main arena + 88
1 | #fake chunk |
此时的bin情况如下
1 | fastbins |
1 | #get main arena |
此时的bin情况如下
1 | fastbins |
于是我们就可以通过覆盖地位申请到 stdout - 0x43的位置了
1 | #hijack stdout |
于是等到下一次执行puts或者printf的时候就能够leak libc了。它会leak出来一大堆东西,所以接收一般是这样的
1 | p.recv(0x40) |
1 | [*] libcbase==>0x7f737e67f000 |
接下来正常df做法就行,值得注意的是,打完stdout之后IO会有所不同(换行符),需要重写交互
完整exp如下:
1 | from pwn import * |