Dream hack 자료를 통해 학습한 RELRO 보호기법과 Hook Overwrite를 정리한 글입니다.
RELRO 라는 보호기법을 이해하기 위해서는 ROP에 대한 기본적인 내용이해가 필요합니다.
RELRO
이전에 Dream hack문제를 해결할때 ROP 공격기법을 사용해 본적이 있습니다.
이때 함수의 주소를 구하기 위해 PLT를 사용하고 GOT에 값을 덮어쓰는 방식을 사용했습니다.
그런데 여기에는 전제조건이 필요합니다.
GOT에 쓰기 권한이 부여되어 있어야만 한다는 조건입니다.
그리고 개발자들은 데이터 보호와 이러한 취약점을 해결하기 위해
쓰기 권한을 제거하는 RELocation Read-Only(RELRO) 를 개발하였습니다.
정리하면
RELRO는 쓰기 권한이 불필요한 data segment의 쓰기권한을 제거하는 보호기법입니다.
이때, RELRO는 두가지로 나뉘게 됩니다.
부분적으로 적용하는 Partial RELRO,
가장 넓은 영역에 적용하는 Full RELRO 두가지로 나뉩니다.
Partial RELRO는 부분적으로 적용하는 경우인데
바이너리의 섹션헤더를 분석하면 주로 .init_array와 .fini_array에
쓰기 권한을 제거 하어 덮어쓰는 공격을 수행하기 어려워지게 합니다.
하지만 부분적 적용이기때문에 .got와 .plt 영역에 대한 쓰기 권한은 여전히 존재해
GOT Overwrite 공격이 가능합니다.
그럼 넓은 영역을 적용하는 Full RELRO에서는
.init_array와 .fini_array뿐만 아니라 .got 영역까지 쓰기 권한이 제거되어 공격이 어려웠습니다.
Hook Overwrite
개발된 보호기법을 우회하고자 공격자들이 찾은것이
Hook을 사용한 Hook Overwrite라는 RELRO를 우회하는 방법입니다.
Hooking이라는 것은 OS가 code를 실행할때 이를 낚아채 다른 code를 실행하게 하는 것을
말하는데 이때 사용되는 코드가 hook입니다.
이러한 코드가 개발 시점에서 존재하는 이유는 디버깅의 편의 때문입니다.
hooking을 사용한다면 원하는 부분을 디버깅을 하는데 편리하기 때문에
사용하게 되는데 이러한 부분을 이용한 것입니다.
libc.so에서는 대표적으로 malloc 와 free, realloc 의 hook변수가 정의되어 있으며
malloc 을 예시로 들겠습니다
아래 코드가 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);
// ...
코드를 살펴보면 함수 시작부분에서 __malloc_hook이 존재하는지 검사하고 존재한다면 호출하고 있습니다.
이때 __malloc_hook은 libc.so에서 쓰기 가능한 영역에 위치하고 있기 때문에
변수를 조작해 malloc 호출로 실행흐름 조작이 가능해 지게 됩니다.
다른 free 나 realloc 또한 같은 방식으로 __free_hook, __realloc_hook 이라는
hook변수를 사용하고 있으며 마찬가지로 쓰기 가능한 영역에 있어 실행흐름 조작이 가능합니다.
이러한 흐름으로 RELRO 보호기법을 우회하는 공격기법
Hook Overwrite를 이용해 실행흐름을 조작이 가능하기에
ROP와 같은 공격으로 이어지게 되는 것입니다.
PIE
PIE (Position-Independent Executable)라는 것은 ASLR 보호기법이 적용된 바이너리는 실행시마다
스택, 힙, 공유 라이브러리등이 무작위 주소로 맵핑되는 것을 코드영역까지 적용하게 해주는 기술입니다.
이 기술은 정확하게는 보호기법은 아니지만 ASLR과 맞물려 공격을 어렵게 만들었습니다.
이전까지 문제들을 살펴보면 다른 주소는 계속 변경되었지만 main함수나 소스코드에 제공되는 함수들의
위치는 매 실행마다 같은 주소에 위치했었습니다.
그렇기 대문에 고정된 주소의 가젯을 활용해 ROP공격들을 수행했으나 PIE 기술이 적용된다면
함수의 주소를 구하는 것부터 시작해야합니다.
Code base를 구하는 방식은 이전 ROP에서 libc base를 구하는 방식과 비슷한데
이는 라이브러리처럼 PIC구조를 가지기 때문입니다.
(PIC: 어느 주소에 공유 오브젝트가 적재되어도 의미가 훼손되지 않도록 만족하는 구조)
ASLR 보호기법 개발이전에는 주소가 바뀌는 것을 예상하고 개발하지 않았고
ASLR기법으로 인해 주소가 바뀌어 재배치되면
바이너리가 매핑되는 주소가 바뀌어 코드가 제대로 실행이 되지 않게됩니다.
해서 상대참조를 하는 방식을 사용하고 재배치가 가능했던 공유 오브젝트를 사용하게 되었습니다.
1. Code base 구하기
ASLR환경에서 매번 실행될때 마다 다른 주소에 적재되기 때문에 가젯을 사용하거나
바이너리가 적재된 주소를 알아야 하는데 이 주소를 Code base 또는 PIE base라고 합니다.
Code base를 구하는 방법은 libc base 주소를 구할때 처럼 코드섹션내 임의의 주소를 읽고
이 주소에서 offset을 빼는 것으로 Code base를 찾을 수 있습니다.
2. Partial Overwrite
Code base를 구하기 어렵다면 반환 주소의 일부 바이트만 덮는 공격또한 가능한데
이러한 공격을 Partial Overwrite라고 합니다.
정적 분석과 동적분석을 한다면 함수 흐름을 볼수 있고
이때, 함수 반환 주소는 호출 함수의 내부를 가르키고 있기에 반환 주소를 예측할 수 있습니다.
또한, ASLR 특성상 코드영역의 주소도 하위 12비트 값은 항상 같다는 특징이 있는데
만약 사용하려는 코드 가젯의 주소와 반환 주소가 하위 한 바이트만 값이 다르다면
값을 덮어서 원하는 코드 실행을 할수 있게 되는 것입니다.
하지만 두 바이트이상의 다른 주소 흐름으로 옮길 경우
ASLR로 섞이는 주소를 맞춰야 하기 때문에 브루트 포싱공격으로 확률적 성공이 됩니다.
RELRO, Hook, PIE에 대한 Exploit Code 작성등은 문제풀이를 통해 작성하도록 하겠습니다.
Reference
1. https://cw00h.github.io/til/hacking/TIL220804/
2. https://dreamhack.io/lecture/courses/99
3. https://dreamhack.io/lecture/courses/113
'Reference > Pwnable_Study' 카테고리의 다른 글
OOB(Out of Bounds) 취약점 (1) | 2023.01.07 |
---|---|
Pwndocker와 LD_PRELOAD, patchelf (libc 제공된 바이너리 문제) (0) | 2023.01.03 |
fork()와 exev() (0) | 2022.10.13 |
Code Auditing (0) | 2022.10.13 |
환경변수(environ) (0) | 2022.10.13 |