[드림핵 시스템 해킹] PIE & RELRO

asdf·2025년 1월 13일

pwnable

목록 보기
17/36

PIC와 PIE


PIC

리눅스에서 ELF는 실행 파일(Executable)과 공유 오브젝트(Shared Object)로 두 가지가 존재합니다. 실행 파일은 일반적인 실행 파일이 해당하고, 공유 오브젝트는 libc.so와 같은 라이브러리 파일이 해당합니다.
공유 오브젝트는 기본적으로 재배치가 가능하도록 설계되어 있습니다. 재배치가 가능하다는 것은 메모리의 어느 주소에 적재되어도 코드의 의미가 훼손되지 않음을 의미합니다. 컴퓨터 과학에서는 이런 성질을 만족하는 코드를 Position-Independent Code(PIC)라고 부릅니다.

PIE

Position-Independent Executable(PIE)은 무작위 주소에 매핑돼도 실행 가능한 실행 파일을 뜻합니다. ASLR이 도입되기 전에는 실행 파일을 무작위 주소에 매핑할 필요가 없었습니다. 이후에 ASLR이 도입되었을 때는 실행 파일도 무작위 주소에 매핑될 수 있게 하고 싶었으나 리눅스의 실행 파일 형식은 재배치를 고려하지 않고 설계되었었기 때문에, 이미 널리 사용되는 실행 파일의 형식을 변경하면 호환성 문제가 발생할 것이 분명했습니다. 그래서 개발자들은 원래 재배치가 가능했던 공유 오브젝트를 실행 파일로 사용하기로 했습니다.

PIE 우회

ASLR환경에서 PIE가 적용된 바이너리는 실행될 때 마다 다른 주소에 적재됩니다. 그래서 코드 영역의 가젯을 사용하거나, 데이터 영역에 접근하려면 바이너리가 적재된 주소를 알아야 합니다. 이 주소를 PIE 베이스, 또는 코드 베이스라고 부르며 이를 구하는 과정은 Return Oriented Programming과 크게 다르지 않습니다.
코드 베이스를 구하기 어렵다면 반환 주소의 일부 바이트만 덮는 공격을 고려해볼 수도 있습니다. 이러한 공격을 Partial Overwrite라고 부릅니다.

RELRO


RELRO는 쓰기 권한이 불필요한 데이터 세그먼트에 쓰기 권한을 제거합니다.
RELRO는 적용 범위에 따라 Partial RELRO와 Full RELRO로 구분됩니다.

Partial RELRO

Partial RELRO가 적용된 바이너리는 got와 관련된 섹션이 .got와 .got.plt로 두 개가 존재합니다. 전역 변수중에서 실행되는 시점에 바인딩 되는 변수는 .got에 위치합니다. 바이너리가 실행될 때는 이미 바인딩이 완료되어있으므로 이 영역에는 쓰기 권한을 부여하지 않습니다.
반면 실행 중에 바인딩되는 변수는 .got.plt에 위치합니다. 이 영역은 실행 중에 값이 써져야 하므로 쓰기 권한이 부여됩니다.
그러므로 이 경우 여전히 GOT overwrite는 유효합니다.

Full RELRO

Full RELRO가 적용되면 라이브러리 함수들의 주소가 바이너리의 로딩 시점에 모두 바인딩됩니다. 따라서 GOT에는 쓰기 권한이 부여되지 않습니다. 그러므로 Full RELRO가 적용되면 GOT overwrite를 사용할 수 없습니다.

RELRO 우회

앞에서 설명하였듯이 Partial RELRO의 경우 .got.plt 영역에 대한 쓰기 권한이 존재하므로 GOT overwrite공격을 활용할 수 있습니다.
Full RELRO의 경우 .got에도 쓰기 권한이 제거되었습니다. 그래서 공격자들은 덮어쓸 수 있는 다른 함수 포인터를 찾다가 라이브러리에 위치한 hook을 찾아냈습니다. 원래 이 함수 포인터는 동적 메모리의 할당과 해제 과정에서 발생하는 버그를 쉽게 디버깅하기 위해서 만들어졌습니다.
malloc함수의 코드를 살펴보면, 함수의 시작 부분에서 __malloc_hook이 존재하는지 검사하고, 존재하면 이를 호출합니다. __malloc_hook은 libc.so에서 쓰기 가능한 영역에 위치하므로 libc가 매핑된 주소를 알 때, 이 변수를 조작하여 실행 흐름을 조작할 수 있습니다. 이와 같은 공격 기법을 통틀어 Hook Overwrite라고 부릅니다.

profile
Rainy Waltz(a_hisa)

0개의 댓글