PIE (Position Independent Executable)
1. PIE
PIE (Position Independent Executable) 란?
전체가 위치 독립 코드(PIC) 로 이뤄진 실행 가능한 바이너리이다.
무작위 주소에 매핑되어도 실행 가능한 실행 파일이다.
ASLR 이 코드 영역에도 적용되게 해주는 기술이다.
→ ASLR 을 적용해도 코드 영역의 주소는 변하지 않는다.
PIE 는 재배치가 가능하기 때문에,
ASLR 이 적용된 시스템에서는 실행 파일도 무작위 주소에 적재된다.
반대로, ASLR 이 적용되지 않은 시스템에서는 PIE 가 적용된 바이너리라도 무작위 주소에 적재되지 않는다.
리눅스는 기본적으로 ASLR 이 적용된 상태이기 때문에
PIE 가 적용되면 바이너리가 실행될 때마다 바이너리의 주소가 랜덤화된다.
// Name : address.c
#include <stdio.h>
char *buf = "Hello";
void func() {
printf("Hello\n");
}
void main(){
printf("[.data] : %p\n",buf);
printf("[Function] : %p\n",func);
}
buf 의 위치와 func 함수의 위치를 출력하는 코드이다.
위 코드를 이용하여 PIE 가 적용됐을 때와 그렇지 않을 때를 비교해보자
2. PIE 비교하기
1. PIE 적용
$ gcc -o pieaddress address.c
gcc 는 기본적으로 PIE 를 적용하여 컴파일 한다.
- -fPIE (컴파일 옵션)
- -pie (링커 옵션)
위의 옵션을 사용하여도 PIE 를 적용하여 컴파일 할 수 있다.
-fPIE 옵션은 컴파일 단계에서 수행되고, -pie 옵션은 링킹 단계에서 수행된다.
-pie 옵션을 주지 않고 -fPIE 옵션만 준다면 바이너리에 PIE 가 적용되지 않는다.
checksec 명령어로 확인해보자
[*] '/home/gunp4ng/pwnable/pie/pieaddress'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled
Stripped: No
PIE enabled 로 PIE 가 적용된 것을 볼 수 있다.
file 명령어로 확인해보자
PIE 가 적용된 파일은 "pie executable"로 뜨는 것을 알 수 있다.
파일을 실행해보자
파일을 실행할 때마다 전역변수와 사용자 정의 함수의 주소가 변경된다.
gdb 로 어셈블리를 확인해보자
Dump of assembler code for function main:
0x0000000000001183 <+0>: endbr64
0x0000000000001187 <+4>: push rbp
0x0000000000001188 <+5>: mov rbp,rsp
0x000000000000118b <+8>: mov rax,QWORD PTR [rip+0x2e7e] # 0x4010 <buf>
0x0000000000001192 <+15>: mov rsi,rax
0x0000000000001195 <+18>: lea rax,[rip+0xe6e] # 0x200a
0x000000000000119c <+25>: mov rdi,rax
0x000000000000119f <+28>: mov eax,0x0
0x00000000000011a4 <+33>: call 0x1070 <printf@plt>
0x00000000000011a9 <+38>: lea rax,[rip+0xffffffffffffffb9] # 0x1169 <func>
0x00000000000011b0 <+45>: mov rsi,rax
0x00000000000011b3 <+48>: lea rax,[rip+0xe5e] # 0x2018
0x00000000000011ba <+55>: mov rdi,rax
0x00000000000011bd <+58>: mov eax,0x0
0x00000000000011c2 <+63>: call 0x1070 <printf@plt>
0x00000000000011c7 <+68>: nop
0x00000000000011c8 <+69>: pop rbp
0x00000000000011c9 <+70>: ret
End of assembler dump.
코드가 매우 작은 주소에 위치하는 것을 볼 수 있다.
→ 메모리의 어느 위치에 매핑되어도 작동하도록 상대주소로 되어있기 때문이다.
2. PIE 적용 X
$ gcc -no-pie -o address address.c
gcc 에서 -no-pie 옵션을 사용하면 PIE 를 적용하지 않고 컴파일 할 수 있다.
checksec 명령어로 확인해보자
[*] '/home/gunp4ng/pwnable/pie/address'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
SHSTK: Enabled
IBT: Enabled
Stripped: No
No PIE 로 PIE 가 적용되지 않은 것을 알 수 있다.
file 명령어로 확인해보자
PIE 가 적용되지 않은 파일은 "executable" 인 것을 알 수 있다.
파일을 실행해보자
파일을 여러번 실행해도 전역변수와 사용자 정의 함수의 주소가 변하지 않는다.
gdb 로 어셈블리를 확인해보자
Dump of assembler code for function main:
0x0000000000401170 <+0>: endbr64
0x0000000000401174 <+4>: push rbp
0x0000000000401175 <+5>: mov rbp,rsp
0x0000000000401178 <+8>: mov rax,QWORD PTR [rip+0x2eb9] # 0x404038 <buf>
0x000000000040117f <+15>: mov rsi,rax
0x0000000000401182 <+18>: lea rax,[rip+0xe81] # 0x40200a
0x0000000000401189 <+25>: mov rdi,rax
0x000000000040118c <+28>: mov eax,0x0
0x0000000000401191 <+33>: call 0x401060 <printf@plt>
0x0000000000401196 <+38>: lea rax,[rip+0xffffffffffffffb9] # 0x401156 <func>
0x000000000040119d <+45>: mov rsi,rax
0x00000000004011a0 <+48>: lea rax,[rip+0xe71] # 0x402018
0x00000000004011a7 <+55>: mov rdi,rax
0x00000000004011aa <+58>: mov eax,0x0
0x00000000004011af <+63>: call 0x401060 <printf@plt>
0x00000000004011b4 <+68>: nop
0x00000000004011b5 <+69>: pop rbp
0x00000000004011b6 <+70>: ret
End of assembler dump.
정해진 주소에 코드가 위치하는 것을 볼 수 있다.
전역변수 buf 와 사용자 정의 함수 func 도 절대주소로 고정되어 있다.
3. PIE 우회
1. code base
데이터 영역에 접근하기 위해서는 바이너리가 적재된 주소를 알아야 한다.
이 주소를 PIE base 또는 Code base 라고 한다.
코드 영역의 임의 주소를 읽고 오프셋을 빼면 구할 수 있다.
2. Partial Overwrite
코드 베이스를 구하지 못할 때 반환 주소의 일부 바이트만 덮는 공격을 할 수 있다.
이런 공격 기법을 Partial Overwrite 라고 한다.
ASLR 은 하위 12비트 값이 항상 같다는 특성이 있다.
→ 코드 영역의 주소도 하위 12비트 값은 항상 같다.
코드 가젯의 주소가 반환 주소와 한 바이트만 다르면, 이 값을 덮어서 원하는 코드를 실행할 수 있다.
만약 두 바이트 이상이 다르면 브루트 포싱을 통해 공격해야 한다.
'Hacking > Pwnable' 카테고리의 다른 글
PIC (0) | 2024.10.12 |
---|---|
가젯 찾는법 (ROPgadget, ropsearch, rp-lin) (0) | 2024.09.18 |
ROP (x64) (0) | 2024.08.25 |
ROP (x86) (0) | 2024.08.16 |
RTL Chaning (x64) (0) | 2024.07.27 |