728x90
반응형

배경 개념

c 언어에서 메모리의 동적 할당과 해제를 담당하는 함수에는 malloc, free, realloc 등이 있다. 이 함수들은 libc.so에 구현되어 있다. 
libc에는 이 함수들의 디버깅 편의를 위해 훅 변수가 정의되어 있다. malloc 함수는 __malloc_hook을, free 함수는 __free_hook, realloc 함수는 __realloc_hook이라는 훅 변수를 사용한다. 이 함수들은 libc.so의 bss 및 data 섹션에 포함된다. bss, data 섹션은 쓰기가 가능하므로, 실행 중에 조작이 가능하다.

훅을 실행할 때 기존 함수에 전달한 인자를 그대로 전달해주기 때문에  __malloc_hook을 system 함수의 주소로 덮고, malloc("/bin/sh")를 호출해서 쉘을 따는 방식의 공격이 가능하다.

__free_hook이나 __malloc_hook과 같은 훅은 Full RELRO가 적용된 바이너리에도 라이브러리의 훅에는 쓰기 권한이 있기 때문에 공격에 악용되기 쉬워 Glibc 2.34 버전부터 제거되었다. 

문제

문제 파일로 Dockerfile,  fho,  fho.c,  flag,  libc-2.27.so 파일을 제공해준다. fho.c를 보자.

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

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

int main() {
  char buf[0x30];
  unsigned long long *addr;
  unsigned long long value;

  setvbuf(stdin, 0, _IONBF, 0);
  setvbuf(stdout, 0, _IONBF, 0);

  puts("[1] Stack buffer overflow");
  printf("Buf: ");
  read(0, buf, 0x100);
  printf("Buf: %s\n", buf);

  puts("[2] Arbitary-Address-Write");
  printf("To write: ");
  scanf("%llu", &addr);
  printf("With: ");
  scanf("%llu", &value);
  printf("[%p] = %llu\n", addr, value);
  *addr = value;

  puts("[3] Arbitrary-Address-Free");
  printf("To free: ");
  scanf("%llu", &addr);
  free(addr);

  return 0;
}

친절하게 bof 일으키고, overwrite하고, free 할 수 있게 되어있다. bof하는 부분에서 버퍼의 내용을 출력해줌으로, 스택의 내용을 읽을 수 있다.

우리가 hook overwrite를 하는데 필요한 __free_hook, system, '/bin/sh/' 문자열은 libc에 정의되어 있으므로, libc_base만 알면 주소를 알 수 있다. main 함수는 __libc_start_main이라는 라이브러리 함수가 호출하는데, main 함수의 ret을 읽으면 그 주소를 기반으로 libc_base 계산이 가능하다.

$ gdb fho
pwndbg> b *main
Breakpoint 1 at 0x8ba
pwndbg> r
pwndbg> bt
#0  0x00005625b14008ba in main ()
#1  0x00007f5ae2f1cc87 in __libc_start_main (main=0x5625b14008ba <main>, argc=1, argv=0x7ffdf39f3ed8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffdf39f3ec8) at ../csu/libc-start.c:310
#2  0x00005625b14007da in _start ()
pwndbg> x/i 0x00007f5ae2f1cc87
   0x7f5ae2f1cc87 <__libc_start_main+231>:  mov    edi,eax

#1 부분에서 볼 수 있듯이 main()의 반환 주소는 0x00007f5ae2f1cc87이고, x/i로 출력해보면 <__libc_start_main+231>이다.

$ readelf -s libc-2.27.so | grep " __libc_start_main@"
  2203: 0000000000021b10   446 FUNC    GLOBAL DEFAULT   13 __libc_start_main@@GLIBC_2.2.5

__libc_start_main 함수 자체의 libc_offset이 0x21b10이므로 main 함수의 ret를 leak하고, 그 값에서 0x21b10+231을 빼주면 libc_base를 구할 수 있다!!

 

풀이

는 드림핵 정책에 따라 공개하지 않습니다,,

728x90
반응형
nivr4y