본문 바로가기
Pwnable/Techniques

RELRO

by Anatis 2021. 12. 29.

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;
        puchar(ch);
    }
    return 0;
}

 

objdump -h ./prelro

./prelro:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000400238  0000000000400238  00000238  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  0000000000400254  0000000000400254  00000254  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  0000000000400274  0000000000400274  00000274  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .gnu.hash     0000001c  0000000000400298  0000000000400298  00000298  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .dynsym       00000090  00000000004002b8  00000000004002b8  000002b8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynstr       0000004c  0000000000400348  0000000000400348  00000348  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .gnu.version  0000000c  0000000000400394  0000000000400394  00000394  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version_r 00000020  00000000004003a0  00000000004003a0  000003a0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rela.dyn     00000030  00000000004003c0  00000000004003c0  000003c0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.plt     00000048  00000000004003f0  00000000004003f0  000003f0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .init         00000017  0000000000400438  0000000000400438  00000438  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .plt          00000040  0000000000400450  0000000000400450  00000450  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .text         000001a2  0000000000400490  0000000000400490  00000490  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .fini         00000009  0000000000400634  0000000000400634  00000634  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .rodata       00000016  0000000000400640  0000000000400640  00000640  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 15 .eh_frame_hdr 0000003c  0000000000400658  0000000000400658  00000658  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 16 .eh_frame     00000100  0000000000400698  0000000000400698  00000698  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 17 .init_array   00000008  0000000000600e10  0000000000600e10  00000e10  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 18 .fini_array   00000008  0000000000600e18  0000000000600e18  00000e18  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 19 .dynamic      000001d0  0000000000600e20  0000000000600e20  00000e20  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 20 .got          00000010  0000000000600ff0  0000000000600ff0  00000ff0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .got.plt      00000030  0000000000601000  0000000000601000  00001000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .data         00000010  0000000000601030  0000000000601030  00001030  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 23 .bss          00000008  0000000000601040  0000000000601040  00001040  2**0
                  ALLOC
 24 .comment      00000029  0000000000000000  0000000000000000  00001040  2**0
                  CONTENTS, READONLY

prelro를 실행해보면 0x601000~0x602000까지의 주소에는 쓰기 권한이 존재한다. 위의 .got.plt, .data, .bss가 할당되어 있다. 이 섹션들에는 쓰기가 가능하다.

 

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

 

Full RELRO가 적용된 바이너리의 메모리 맵을 확인해보자.

 

objdump -h ./frelro

./frelro:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .interp       0000001c  0000000000000238  0000000000000238  00000238  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  0000000000000254  0000000000000254  00000254  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .note.gnu.build-id 00000024  0000000000000274  0000000000000274  00000274  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .gnu.hash     0000001c  0000000000000298  0000000000000298  00000298  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .dynsym       000000d8  00000000000002b8  00000000000002b8  000002b8  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .dynstr       00000091  0000000000000390  0000000000000390  00000390  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .gnu.version  00000012  0000000000000422  0000000000000422  00000422  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .gnu.version_r 00000020  0000000000000438  0000000000000438  00000438  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rela.dyn     000000c0  0000000000000458  0000000000000458  00000458  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .rela.plt     00000048  0000000000000518  0000000000000518  00000518  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 10 .init         00000017  0000000000000560  0000000000000560  00000560  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .plt          00000040  0000000000000580  0000000000000580  00000580  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .plt.got      00000008  00000000000005c0  00000000000005c0  000005c0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .text         000001d2  00000000000005d0  00000000000005d0  000005d0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 14 .fini         00000009  00000000000007a4  00000000000007a4  000007a4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 15 .rodata       00000016  00000000000007b0  00000000000007b0  000007b0  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 16 .eh_frame_hdr 0000003c  00000000000007c8  00000000000007c8  000007c8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 17 .eh_frame     00000108  0000000000000808  0000000000000808  00000808  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 18 .init_array   00000008  0000000000200da8  0000000000200da8  00000da8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 19 .fini_array   00000008  0000000000200db0  0000000000200db0  00000db0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 20 .dynamic      000001f0  0000000000200db8  0000000000200db8  00000db8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 21 .got          00000058  0000000000200fa8  0000000000200fa8  00000fa8  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 22 .data         00000010  0000000000201000  0000000000201000  00001000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 23 .bss          00000008  0000000000201010  0000000000201010  00001010  2**0
                  ALLOC
 24 .comment      00000029  0000000000000000  0000000000000000  00001010  2**0
                  CONTENTS, READONLY

frelro의 메모리 맵을 보면 got에는 쓰기 권한이 제거되어 있고 data와 bss에만 쓰기 권한이 있다.Full RELRO가 적용되면 라이브러리 함수들의 주소가 바이너리의 로딩 시점에서 모두 바인딩된다.

 

RELRO 기법 우회

Partial RELRO의 경우, .init_array와 .fini_array에 대한 쓰기 권한이 제거되어 두 영역을 덮어쓰는 공격을 수행하기 어려워진다. 하지만, .got.plt 영역에 대학 쓰기 권한이 존재하므로 GOT overwrite 공격을 활용할수 있다. 

 

Full RELRO의 경우. init_array, .fini_array 뿐만 아니라 .got 영역에도 쓰기 권한이 제거되어있다. 라이브러리에 위치한 hook 함수 포인터를 이용하는 것이다.

대표적으로 malloc hook과 free hook이다. 이 함수 포인터는 동적 메모리의 할당과 해제 과정에서 발생하는 버그를 디버깅하기 쉽게 하려고 만들었다.

 

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);
  // ...

glibc malloc 소스코드를 보면 __malloc_hook이 존재하는지 검사하고,  존재하면 __malloc_hook을 호출한다. 이는 libc.so에서 쓰기 가능한 영역에 위치한다. libc가 매핑된 주소를 알 때, 이 변수를 조작하고 malloc을 호출하여 실행 흐름을 조작할 수 있다. 이를 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과 같은 함수 포인터를 조작하는 공격으로 우회할 수 있다.

'Pwnable > Techniques' 카테고리의 다른 글

Memory Corruption: Format String Bug  (0) 2022.01.05
Out of Bounds  (0) 2021.12.30
RELRO  (0) 2021.12.29
PIE  (0) 2021.12.29
Exploit Tech: Return Oriented Programming  (0) 2021.12.27

댓글