PIC (Position-Independent Code)
- 메모리의 어느 주소에 적재되어도 코드의 의미가 훼손되지 않는다는 성질
libc.so
와 같은 공유 오브젝트(Shared Object, SO)는 기본적으로 Relocation이 가능하도록 설계되어있다.- 재배치가 가능하다는 것은 PIC 특성이 있다는 것
- pic가 적용된 코드는 절대 주소를 사용하지 않으며,
rip
를 기준으로 데이터를 상대 참조(Relative Addressing)하기 때문에 바이너리가 무작위 주소에 매핑돼도 제대로 실행될 수 있습니다.
상대 참조(Relative Addressing)
- 어떤 값을 기준으로 다른 주소를 지정하는 방식
PIE (Position-Independent Executable)
- ASLR 이 코드영역에도 적용되는 보안 기법
- 무작위 주소에 매핑돼도 실행 가능한 실행파일
- PIE는 재배치가 가능하므로, ASLR이 적용된 시스템에서는 실행 파일도 무작위 주소에 적재됩니다. 반대로, ASLR이 적용되지 않은 시스템에서는 PIE가 적용된 바이너리더라도 무작위 주소에 적재되지 않습니다.
-프로그램이 실행 중에 있을 때 PIE base를 leak할 수 있다면, 그를 기반으로 함수들에 대한 offset을 구하여 exploit을 진행해주면 됩니다. - PIE의 코드는 모두 PIC이다.
PIE 우회
- 코드 베이스 구하기
- ASLR + PIE 적용된 바이너리는 실행될 때 마다 다른 주소에 적재된다.
- 코드 영역의 가젯을 사용하거나, 데이터 영역에 접근하려면 바이너리가 적재된 주소를 알아야한다.
- 이 주소를 PIE 베이스, 코드 베이스라고 한다.
- PIE베이스(코드 베이스) = 코드영역의 임의 주소 - 오프셋
- 이 주소를 PIE 베이스, 코드 베이스라고 한다.
- 코드 영역의 가젯을 사용하거나, 데이터 영역에 접근하려면 바이너리가 적재된 주소를 알아야한다.
- Partial Overwrite
- 반환 주소의 일부 바이트만 덮는 공격
- 코드 베이스 구하기 어려운경우 사용.
- 함수의 반환 주소는 Caller의 내부를 가리킨다.
- 특정 함수의 호출 관계는 정적 분석 또는 동적 분석으로 쉽게 확인할 수 있으므로, 공격자는 반환주소를 예측할 수 있다.
- ASLR의 특성 상, 코드 영역으 주소도 하위12bit값은 항상 같다. 따라서 사용하려는 코드 가젯의 주소가 반환 주소와 하위 한 바이트만 다르다면, 이 값만 덮어서 원하는 코드를 실행시킬 수 있다.
- 두 바이트 이상이 다른 주소로 실행 흐름을 옮기고자 한다면, ASLR로 뒤섞이는 주소를 맞춰야 하므로 브루트 포싱이 필요하며, 공격이 확률에 따라 성공하게된다.
RELRO (RELocation Read-Only)
- 불필요한 데이터 세그먼트에 쓰기 권한을 제거하는 보호기법이다.
- Partial RELRO : 부분적으로 적용
- Full RELRO : 가장 넓은 영역에 적용
- Lazy Binding : 함수가 처음 호출될 때 함수의 주소를 구하고 이를 GOT에 적는다.
- 실행중에 GOT 테이블을 업데이트해야하므로 GOT에 쓰기권한이 부여된다.
- ELF의 데이터 세그먼트에는 프로세스의 초기화 및 종료와 관련된
.init_array
,.fini_array
가 있다.- 시작과 종료에 실행할 함수들의 주소를 저장하고있다. No RELRO시 해당 부분을 조작할 수 있다.
이러한 데이터 세그먼트 영역에 불필요한 쓰기권한을 제거하여 보호하기위해 RELRO를 적용한다.
Partial RELRO
- Full RELRO가 default이며, PIE를 해제하면 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;
}
- 실행하여 자신의 메모리맵을 출력해보면,
0x404000 ~ 0x405000
까지의 주소에는 w권한이있다.
objdump -h [파일명]
으로 하단의 섹션 헤더를 참조해보면 해당영역에는 .got.plt, data, .bss가 할당되어있다.- 이 섹션들에는 w가 가능하다.
.init_array
와.fini_array
는 각각 0x403e10와 0x40e18에 할당되어있는데, 모두 쓰기권한이 없는 0x403000`0x404000에 존재하므로 w가 불가능하다.
| Partial RELRO가 적용된 바이너리는 .got
와 .got.plt
두개가 있다. 전역변수 중에서 실행되는 시점에 바인딩(now binding)되는 변수는 .got
에 위치한다. 바이너리가 실행될 때는 이미 바인딩이 완료되어있으므로 이 영역에 쓰기권한을 부여하지 않는다.
| 실행중에 바인딩(lazy binding)되는 변수는 .got.plt
에 위치한다. 이 영역은 실행 중에 값이 싸져야 하므로 쓰기 권한이 부여된다. Partial RELRO가 적용된 바이너리에서 대부분 함수들의 GOT entry는 .got.plt
에 저장된다.
Full RELRO
gcc -o frelro relro.c
로 컴파일하면 default로 Full RELRO가 설정된다.- 결론 : got에는 쓰기권한이 제거되어 있으며 data와 bss에만 쓰기권한이있다.
- 실행하여 자신의 메모리맵을 출력해보면,
5561f7d7b000-5561f7d7c000
까지의 주소에는 w권한이있다. - 해당 파일 /home/unstraw0454/dreamhack/PIE_RELRO/frelro가 매핑된 주소는
5561f7d77000
임을 알 수 있다.
.data
섹션의 오프셋은0x4000
이다. 이를 파일이 매핑된 주소0x5561f7d77000
에 더하면,0x5561F7D7B000
이 나오며, 쓰기권한이 있는 위치에 속한다..bss
섹션의 오프셋은0x4010
이고, 이를 매핑된 주소에 더하면 마찬가지로 쓰기권한이 있는 위치에 속하게된다.- Full RELRO가 적용되면 라이브러리 함수들의 주소가 바이너리의 로딩 시점에 모두 바인딩(GOT에 함수 주소가 기록)된다. 따라서 GOT에는 쓰기 권한이 부여되지 않는다.
RELRO 기법 우회
- Partial RELRO :
.init_array
와.fini_array
에 대한 쓰기 권한이 제거되어 두 영역을 덮어쓰는 공격을 수행하기 어려워진다..got.plt
영역에 대한 쓰기권한이 존재하므로 GOT overwrite 공격 가능
- Full RELRO :
.init_array
,.fini_array
뿐만 아니라.got
영역에도 쓰기 권한이 제거- hook(라이브러리에 위치한 덮어쓸 수 있는 다른 함수 포인터)를 조작하는 공격 가능
- malloc hook
malloc
함수 호출 ->__malloc_hook
(libc.so에서 쓰기 가능 영역에 위치) 존재 검사 -> 존재시 호출- Hook Overwrite : libc가 매핑된 주소를 알 때, 이 변수를 조작하고
malloc
을 호출해 실행 흐름을 조작할 수 있다.
- Hook Overwrite : libc가 매핑된 주소를 알 때, 이 변수를 조작하고
- free hook
- malloc hook
- hook(라이브러리에 위치한 덮어쓸 수 있는 다른 함수 포인터)를 조작하는 공격 가능
Hooking
Hooking : OS가 어떤 코드를 실행하려할 때, 이를 낚아채 다른 코드(Hook)가 실행되게하는 것
- 함수에 훅을 심어 다른 함수의 호출을 모니터링하거나, 함수에 기능을 추가 제거 수정이 가능하다.
첫 번째는 훅 오버라이트(Hook Overwrite)로, 훅의 특징을 이용한 공격 기법입니다. Glibc 2.33 이하 버전에서 libc 데이터 영역에는 malloc()과 free()를 호출할 때 함께 호출되는 훅(Hook)이 함수 포인터 형태로 존재합니다. 이 함수 포인터를 임의의 함수 주소로 오버라이트(Overwrite)하여 악의적인 코드를 실행하는 기법을 배울 것입니다. Full RELRO가 적용되더라도 libc의 데이터 영역에는 쓰기가 가능하므로 Full RELRO를 우회하는 기법이기도 합니다.
두 번째는 libc 내에 존재하는 가젯인 원가젯(one-gadget)입니다. 기존에는 셸을 실행하려면 여러 개의 가젯을 조합해서 ROP Chain을 구성했지만, 원가젯은 단일 가젯만으로도 셸을 실행할 수 있는 매우 강력한 가젯입니다. 하지만 원가젯은 Glibc 버전마다 다르게 존재하며, 사용하기 위한 제약 조건도 모두 다릅니다. 일반적으로 Glibc 버전이 높아질수록 제약 조건을 만족하기가 어려워지는 특성이 있습니다.
fho
'Pwn' 카테고리의 다른 글
_rtld_global._dl_rtld_lock_recursive 는 어디에 있냐. (0) | 2025.05.03 |
---|---|
CTF 문제풀이에서 Dockerfile 활용법 (0) | 2025.03.13 |
[PWN Dreamhack] basic_rop_x64 & basic_rop_x86 (0) | 2025.03.10 |
ROP 완벽 이해와 추가 예제 풀이 (0) | 2025.02.26 |
[PWN Dreamhack] RTL, ROP (0) | 2025.02.22 |