본문 바로가기

Dreamhack/Lecture & Practice

[Lecture] RELRO (RELocation Read-Only)

1. lazy binding

  • 함수가 처음 호출될 때, 함수의 주소를 구하고 GOT 테이블에 업데이트하는 것을 의미한다.
  • 이는 바이너리가 실행 중에 GOT테이블을 업데이트할 수 있는 쓰기 권한이 있어야 가능하다.

2. ELF의 데이터 세그먼트

  • 프로세스 초기화 및 종료와 관련된 .init_array,  .fini_array가 존재한다.
  • .init_array,  .fini_array는 프로세스의 시작과 종료에 실행할 함수들의 주소를 저장하고 있다.
    이에 해당 위치에 공격자가 임의로  값을 쓸 수 있다면, 프로세스의 실행 흐름을 조작할 수 있는 것이다.
  • 이러한 문제점을 해결하고자, 프로세스의 데이터 세그먼트를 보호하는 "RELRO (RELocation Read-Only)"를 사용한다.

3. RELRO

  • 쓰기 권한이 불필요한 데이터 세그먼트는 쓰기 권한을 제거하는 기법이다.

 

① Partial RELRO

  • RELRO를 부분적으로 적용하는 것

② Full RELRO

  • 가장 넓은 영역에 RELRO를 적용하는 것

4.실습

4-1. Partial RELRO

 

① Checksec을 이용하여, 보호 기법을 확인한다.

  • gcc는 기본적으로 Full RELRO를 적용하며,  PIE를 해제할 경우에는 Partial RELRO를 적용한다.

② 실습 코드는 바이너리의 메모리 맵을 출력해주는 것이다.

  • 출력 결과를 확인해보면, 0x601000 ~ 0x602000까지 주소에 쓰기 권한이 있는 것을 확인할 수 있다.

③ objdump를 이용하여, Partial RELRO 바이너리의 섹션 헤더를 확인할 수 있다.

  • 0x601000 ~ 0x602000의 영역에는 .got.plt , .data, .bss가 할당된 것을 볼 수 있으며,
    이는
    해당 섹션에 대한 쓰기 권한이 존재함을 알 수 있다. 
  • 하지만 .init_array와 .fini_array에는 쓰기 권한이 존재하지 않음을 알 수 있다.

 


   [참고]

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

    ① .got

         - 전역변수 중에서 실행되는 시점이 바인딩되는 변수는 .got에 위치한다.
         - 바이너리가 실행될 때, 이미 바인딩이 완료되어 있으므로 쓰기 권한이 부여되지 않는다. 
  

    ② .got.plt
         - 실행 중에 바인딩되는 변수는 .got.plt에 위치한다.
         - 해당 영역은 실행 중에 값이 써져야 하므로 쓰기 권한이 부여된다.
         - Partial RELRO가 적용된 바이너리에서 대부분 함수들의 GOT엔트리는 .got.plt에 저장된다.

 

4-2. Full RELRO

① Checksec을 이용하여, 보호 기법을 확인한다.

② 실습 코드는 바이너리의 메모리 맵을 출력해주는 것이다.

  • 출력 결과를 확인해보면, 0x55e891cbe000 ~ 0x55e891cbf000까지 주소에 쓰기 권한이 있는 것을 확인할 수 있다.

③ objdump를 이용하여, Partial RELRO 바이너리의 섹션 헤더를 확인할 수 있다.

  • .data과 .bss에만 쓰기 권한이 있는 것을 알 수 있다.

5. RELRO 우회

5-1. Partial RELRO

  • .got.plt영역에 대한 쓰기 권한이 존재하므로 GOT Overwrite공격이 가능하다.
  • Lazy binding을 사용하므로, 라이브러리 함수들의 GOT엔트리는 쓰기가 가능한 것이다.
  • .init_array와 .fini_array에 대한 쓰기 권한이 제거되어 두 영역을 덮는 것은 어렵다.

5-2. Full RELRO

  • Lazy binding을 사용하지 않으며, 라이브러리 함수들의 주소는 바이너리가 로드되는 시점에 바인딩된다.
  • .init_array와 .fini_array, .got영역에 대한 쓰기 권한도 제거되어 있다.
  • libc의 malloc hook, free hook과 같은 함수 포인터를 조작하는 공격으로 우회할 수 있다.
  • HOOK Overwrite
    - malloc 코드를 살펴보면, 함수의 시작 부분에서 __malloc_hook이 존재하는지 검사하고, 존재하면 이를 호출한다.
    - __malloc_hook은 libc.so에서 쓰기 가능한 위치에 있기 때문에,
      공격자가 libc가 매핑된 주소를 알 때, 이 변수를 조작하고 malloc을 호출하여 실행 흐름을 조작할 수 있다.

    [참고]

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


 

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





 

'Dreamhack > Lecture & Practice' 카테고리의 다른 글

[Lecture] OOB (Out of bounds)  (0) 2022.08.09
[Practice] Hook Overwrite  (0) 2022.07.14
[Lecture] PIE (Position-Independent Executable)  (0) 2022.07.12
[Practice] ROP - GOT Overwrite  (0) 2022.07.02
[Practice] Return to Library  (0) 2022.07.01