[Dreamhack] 메모리 Hook

형준·2023년 5월 29일
0

Dreamhack 개념

목록 보기
2/2

Hook 🔥


  • hook : 갈고리라는 뜻으로 운영체제가 어떤 코드를 실행하려고 할 때, 이를 낚아채어 다른 코드가 실행되게 하는 것을 Hooking(후킹) 이라고 부른다.

  • Hook Overwrite : Glibc 2.33 이하 버전에서는 malloc()free()를 호출할 때 함께 훅(Hook)이 함수 포인터 형태로 존재한다. 이 함수 포인터를 Overwrite하여 코드를 실행하면, Full RELRO를 우회할 수 있다.
    (Full RELRO에도 libc에는 쓰기가 가능하기 때문이다)

Hook의 작동 방식
- libc는 디버깅 편의를 위해 hook 변수가 정의 되어 있다.
ex) __malloc_hook ,__free_hook ,--realloc_hook
- malloc()함수는 __malloc_hook의 값이 NULL인지 검사합니다.
- NULL이 아니라면 hook에 인자를 넘기고 __malloc_hook이 가리키는 함수를 먼저 실행한다.

  • 이 값들은 .bss영역에 있기 때문에 쓰기가 가능하므로 조작 가능하다.






Hook Overwrite ✍️


Hook 변수를 덮어 쓰고, 인자를 전달하면 공격할 수 있다.

  • malloc("/bin/sh") : __malloc_hooksystem함수의 주소로 덮는다.

이제는 사라진 hook 변수
: 악용되기 쉽고, 다발적으로 사용할 때 성능에 악영향을 주는 이유로,
Glibc 2.34 부터는 제거되었다.






실습 🧪


문제 🚩

// 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] Arbitrary-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;
}
  • [1] Buffer Overlflow를 일으킬 수 있다.
  • [2] 임의 주소에 임의 값을 넣을 수 있다.
  • [3] 임의 인자를 주고 free를 호출할 수 있다.

checksec 🔓

    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled


설계 📘

- Library의 변수 및 함수들의 주소 구하기

  • libc에서 __free_hook, system@, "/bin/sh" 문자열을 구하자

  • readelf -sr libc-2.27.so | grep " __free_hook@"
    readelf -s libc-2.27.so | grep " system@"
    strings -tx libc-2.27.so | grep "/bin/sh"

[1]에서 변수와 함수의 주소를 구한다.
[2]에서 __free_hook의 값을 system함수의 주소로 덮어쓴다.
[3]에서 "/bin/sh"를 해제한다.



Exploit ⚔️

  • libc의 시작 주소를 구하기 위하여 __libc_start_main을 확인한다.
    __libc_start_main : _start()에서 __libc_start_main()을 호출하면 main()이 호출된다.

    (gdb) b * main
    (gdb) r
    (gdb) bt

  • bt를 이용하면 함수를 호출한 관계를 볼 수 있다. 그 방법으로 main의 RET를 참고하여 이전 함수인 __libc_start_main을, __libc_start_main의 RET를 이용하여 _start()를 구할 수 있는 것 같다.
    x/i (bt에서 나온 __libc_start_main의 주소)를 이용하면 <__libc_start_main + offset>으로 나타난 offset을 구할 수 있다.

  • __libc_start_main의 offset을 readelf를 이용하여 구하면 0x21b10이다.

  • offset은 +231이다.

    따라서 __libc_start_main+231을 릭한 후, 해당값에서 0x21b10 +231을 빼면 libc_base를 구할 수 있다. 이를 이용하면 다른 libc기반 함수들도 사용할 수 있다.
    참고로 __libc_start_main+231의 값은 main의 ret주소이다.

이를 이용하여 __libc_start_main+231의 주소를 leak한 후, libc_base를 구하고, __free_hooksystem으로 바꾼다. 이후 /bin/sh를 free의 인자로 넣는다.



마치며


다음에는 이를 활용한 예제를 write-up 하겠습니다.

profile
>_0v

0개의 댓글