본문 바로가기
Hacking & Security/System Hacking

[DreamHack] Stage 8 : Background: RELRO와 RELRO 우회

by 희디 2022. 4. 6.

지난 코스에서는 함수가 처음 호출될 때 함수의 주소를 구하고 이를 GOT에 적는 lazy binding을 소개받아 공부했다. 

lazy binding을 하는 바이너리는 실행중에 GOT 테이블을 업데이트 할 수 있어야 하므로 GOT에 쓰기 권한이 부여된다.

 

 ELF의 데이터 세그먼트에는 프로세스의 초기화 및 종료와 관련된 .init_array, .fini_array가 있다. 이 영역들은 프로세스의 시작과 종료에 실행할 함수들의 주소를 저장하고 있는데, 여기에 공격자가 임의로 값을 쓸 수 있다면, 프로세스의 실행 흐름이 조작될 수 있다.

 

그래서 이의 문제를 해결하고자 프로세스의 데이터 세그먼트를 보호하는 RELocation Read-Only , RELRO를 개발하였다. 이건 쓰기 권한이 불필요한 데이터 세그먼트에 쓰기 권한을 제거한다. RELRO는 RELRO를 부분적으로 적용하는 Partial RELRO이고, 나머지는 가장 넓은 영역에 RELRO를 적용하는 Full RELRO이다. 

 

<Partial RELRO>

아래 예시코드는 자신의 메모리 맵을 출력하는 바이너리 소스코드이다.

// Name: relro.c
// Compile: gcc -o prelro relro.c -no-pie -fno-PIE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
  FILE *fp;
  char ch;
  fp = fopen("/proc/self/maps", "r");
  while (1) {
    ch = fgetc(fp);
    if (ch == EOF) break;
    putchar(ch);
  }
  return 0;
}

이 실습 gcc는 Full RELRO를 기본적용하며 PIE를 해제하면 partial RELRO을 적용한다. 바이너리의 RELRO여부도 checksec로 검사할 수 있다.

- partial RELRO 권한 

 

prelro를 실행해보면 0x601000부터 0x602000까지의 주소에는 쓰기 권한이 있는 것을 확인할 수 있다.

figure4의 섹션 헤더를 참조해보면 해당 영역에는 .got.plt, .data, .bss가 할당되어 있다.

따라서 이 섹션들에는 쓰기가 가능하다.

반면, .init_array와 .fini_array는 각각 0x600e10과 0x600e18에 할당되어 쓰기가 불가능하다.

+) .got, .got.plt의 차이점?!

 

Partial RELRO가 적용된 바이너리는 got와 관련된 섹션이 .got와 .got.plt로 두 개가 존재한다.

전역 변수 중에서 실행되는 시점에 바인딩(now binding)되는 변수는 .got에 위치한다.

바이너리가 실행될 때는 이미 바인딩이 완료되어있으므로 이 영역에 쓰기 권한을 부여하지 않는다.
반면 실행 중에 바인딩(lazy binding)되는 변수는 .got.plt에 위치한다.

이 영역은 실행 중에 값이 써져야 하므로 쓰기 권한이 부여됩니다. Partial RELRO가 적용된 바이너리에서 대부분 함수들의 GOT 엔트리는 .got.plt에 저장된다.

 

- Full RELRO 

 

처음 partail RELRO에 있던 -no-pie -fno-PIE를 지우고 컴파일하고 Full RELRO가 적용된 바이너리가 생성된다.

// Name: relro.c
// Compile: gcc -o prelro relro.c -no-pie -fno-PIE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main() {
  FILE *fp;
  char ch;
  fp = fopen("/proc/self/maps", "r");
  while (1) {
    ch = fgetc(fp);
    if (ch == EOF) break;
    putchar(ch);
  }
  return 0;
}

frelro를 실행하여 메모리 맵을 확인하고, 이를 섹션 헤더 정보와 종합해보면 got에는 쓰기 권한이 제거되어 있으며 data와 bss에만 쓰기 권한이 있다. Full RELRO가 적용되면 라이브러리 함수들의 주소가 바이너리의 로딩 시점에 모두 바인딩된다. 따라서 GOT에는 쓰기 권한이 부여되지 않는다.

 

<Full RELRO 바이너리의 메모리 맵> 

 

<RELRO 기법 우회> 

 

Partial RELRO, Full RELRO의 적용 방식과 효과를 확인해 봤는데 전자의 경우, .init_array와 .fini_array에 대한 쓰기 권한이 제거되어 두 영역을 덮어쓰는 공격을 수행하기 어려워진다. 하지만 .got.plt 영역에 대한 쓰기 권한이 존재하므로 GOT ㅐoverwrite 공격을 활용할 수 있다. 후자의 경우, .init_array와 .fini_array 뿐만 아니라, .got 영역에도 쓰기 권한이 제거되었다. 그래서 덮어쓸 다른 함수 포인터 hook을 찾아냈다. hook에는 malloc hook과 free hook이 있다. 

malloc 함수의 코드를 살펴보면 함수의 시작 부분에서 __malloc_hook이 존재하는지 검사하고 존재하면 이를 호출한다. 이는 libc.so에서 쓰기 가능한 영역에 위치한다. 그래서 libc가 매핑된 주소를 알 때 이 변수를 조작하고 malloc을 호출하여  실행흐름을 조작할 수 있다. 이것이 바로 Hook Overwrite이다. 

 

- 예시 코드 glibc malloc 소스 코드 

void *
__libc_malloc (size_t bytes)
{
  mstate ar_ptr;
  void *victim;
  void *(*hook) (size_t, const void *)
    = atomic_forced_read (__malloc_hook); // read hook
  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);
  // ...

 

정리하면, 프로세스의 데이터 영역에는 got, init array, fini array를 비롯해 공격에 사용될 수 있는 여러 변수가 있다. 

Partial RELRO 바이너리는 got에 쓰기 권한이 남아있어서 GOT Overwrite로 이를 우회할 수 있지만, Full RELRO가 적용되면 데이터 영역의 모든 불필요한 쓰기 권한이 삭제되면서 Hook Overwrite 등의 새로운 공격 기법을 사용해야 한다. 

 

  • RELocation Read-Only(RELRO): 불필요한 데이터 영역에 쓰기 권한을 제거.
  • Partial RELRO: init array, fini array 등 여러 섹션에 쓰기 권한을 제거. Lazy binding을 사용하므로 라이브러리 함수들의 GOT 엔트리는 쓰기가 가능. GOT Overwrite등의 공격으로 우회가 가능.
  • Full RELRO: init array, fini array 뿐만 아니라 GOT에도 쓰기 권한을 제거. Lazy binding을 사용하지 않으며 라이브러리 함수들의 주소는 바이너리가 로드되는 시점에 바인딩됨. libc의 malloc hook, free hook과 같은 함수 포인터를 조작하는 공격으로 우회할 수 있음.

'Hacking & Security > System Hacking' 카테고리의 다른 글

[DreamHack] Stage 8 : 함께 실습 (Hook Overwrite)  (0) 2022.04.06
[DreamHack] Stage 8 : PIE  (1) 2022.04.06
Lazenca PIE, RELRO  (0) 2022.03.25
[Lazenca] ASLR  (0) 2022.03.23
[Lazenca] NX Bits(MS : DEP)  (2) 2022.03.23