Return Address Overwrite: 반환 주소를 악성 함수의 주소로 덮어서 셸 획득
Stack Canary: 스택 프레임의 반환 주소 전에 랜덤한 카나리를 주입하여 반환 주소를 덮기 어렵게 함
Return to Shellcode: 카나리를 우회하고, 셸 코드를 주입한 버퍼의 주소로 반환 주소를 덮어서 셸 획득
ASLR: 임의 버퍼의 주소를 알기 어렵게 함
NX: 각 세그먼트에 불필요한 실행권한을 제거함으로써 공격자가 임의 버퍼에 주입한 코드를 실행하기 어렵게함
Return to Library
프로세스에 실행 권한이 있는 메모리 영역은 일반적으로 바이너리의 코드영역과 바이너리가 참조하는 라이브러리의 코드 영역
Return to Libc : 라이브러리에 구현되어 있는 함수들로 NX를 우회하고 쉘을 획득하는 공격 기법
분석
$checksecrtl#최신 리눅스 커널에서 ASLR은 기본으로 적용되며 특별이 언급되지 않는다면 ASLR은 적용된 것[*]'/home/dreamhack/rtl'Arch:amd64-64-littleRELRO:PartialRELROStack:CanaryfoundNX:NXenabledPIE:NoPIE (0x400000)---------------------------//Name:rtl.c//Compile:gcc-ortlrtl.c-fno-PIE-no-pie#include <stdio.h>#include <stdlib.h>#include <unistd.h>constchar*binsh="/bin/sh";#ASLR이 적용돼도 PIE가 적용되지 않으면 코드 세그먼트와 데이터 세그먼트의 주소는 고정되므로 /bin/sh의 주소는 고정되어 있음intmain(){charbuf[0x30];setvbuf(stdin,0,_IONBF,0);setvbuf(stdout,0,_IONBF,0);//Addsystemfunctiontoplt's entry system("echo 'system@plt'"); #plt에 system 추가, plt에 어떤 라이브러리 함수가 등록되어있다면, 그 함수의 PLT 엔트리를 실행함으로써 함수를 실행 가능 #ASLR이 걸려있어도 PIE가 적용되어있지 않다면 PLT의 주소는 고정되므로 이 방법으로 라이브러리 함수 실행 가능(Return to 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;}
익스플로잇 설계
/bin/sh는 ASLR이 적용돼도 PIE가 적용되지 않으면 코드 세그먼트와 데이터 세그먼트의 주소는 고정되므로 /bin/sh의 주소는 고정되어 있음
버퍼오버플로우
익스플로잇
1. 카나리 우회
Exploit Tech: Return To Shellcode 에서와 마찬가지로, 첫 번째 입력에서 적절한 길이의 데이터를 입력하면 카나리를 구할 수 있습니다.( 0x38)
2. rdi값을 “/bin/sh”의 주소로 설정 및 셸 획득
카나리를 구했으면, 이제 두 번째 입력으로 반환 주소를 덮을 수 있습니다. 그러나 NX로 인해 지난 강의에서와 같이 buf에 셸 코드를 주입하고 이를 실행할 수는 없습니다. 이 강의에서는 다른 방법으로 셸을 획득해야 합니다.
공격을 위해 알고 있는 정보를 정리해보면 다음과 같습니다.
“/bin/sh”의 주소를 안다.
system 함수의 PLT 주소를 안다. ==> system 함수를 호출할 수 있다.
Exploit Tech: Return Address Overwrite에서 system(“/bin/sh”)을 호출하면 셸을 획득할 수 있음을 배웠습니다. x86-64의 호출 규약에 따르면 이는 rdi=”/bin/sh” 주소인 상태에서 system 함수를 호출한 것과 같습니다.
이 예제에서는 “/bin/sh”의 주소를 알고, system 함수를 호출할 수 있으므로 “/bin/sh”의 주소를 rdi의 값으로 설정할 수 있다면 system(“/bin/sh”)를 실행할 수 있습니다. 이를 위해선 리턴 가젯을 활용해야 합니다.
리턴 가젯 ⛓
리턴 가젯(Return gadget)은 다음과 같이 ret 명령어로 끝나는 어셈블리 코드 조각을 의미합니다. pwntools 설치 시 함께 설치되는 ROPgadget 명령어를 사용해서 다음과 같이 가젯을 구할 수 있습니다.
이제까지의 강의에서는 어떤 함수의 주소 또는 셸 코드의 주소로 반환 주소를 덮어서 한 번에 셸을 획득했습니다. 그러나 NX로 인해 셸 코드를 실행할 수 없는 상황에서, 단 한 번의 함수 실행으로 셸을 획득하는 것은 일반적으로 불가능합니다.
리턴 가젯은 반환 주소를 덮는 공격의 유연성을 높여서 익스플로잇에 필요한 조건을 만족할 수 있도록 돕습니다. 예를 들어 이 예제에서는 rdi의 값을 “/bin/sh”의 주소로 설정하고, system 함수를 호출해야 합니다. 리턴 가젯을 사용하여 반환 주소와 이후의 버퍼를 다음과 같이 덮으면, pop rdi로 rdi를 “/bin/sh”의 주소로 설정하고, 이어지는 ret로 system 함수를 호출할 수 있습니다.
addr of ("pop rdi; ret") <= return address
addr of string "/bin/sh" <= ret + 0x8
addr of "system" plt <= ret + 0x10
대부분의 함수는 ret로 종료되므로, 함수들도 리턴 가젯으로 사용될 수 있습니다. 리턴 가젯을 사용한 더욱 복잡한 공격은 다음 강의인 Exploit Tech: Return-Oriented Programming에서 소개하겠습니다.
카나리를 우회하고, system(“/bin/sh”)를 호출할 계획을 세웠으므로 이를 코드로 구현하겠습니다. 반드시 직접 구현해보면서 이해하기 바랍니다.
익스플로잇 구현
카나리 우회
리턴 가젯 찾기
익스플로잇
system 함수로 rip가 이동할 때 스택은 반드시 0x10단위로 정렬되어 있어야함 why? system 함수 내부에 있는 movaps 명령어때문 -> 이 명령어는 스택이 0x10단위로 정렬되어 있지 않으면 segmentation fault를 발생시킴
만약 system 함수를 이용할 때 익스플로잇이 제대로 작성됬을때도 segmentation Fault가 발생한다면 system 함수의 가젯을 8바이트 뒤로 미루기(no-opgadget을 system 함수 전에 추가하여 미루기)
system("echo 'system@plt'");
#plt에 어떤 라이브러리 함수가 등록되어 있다면 그 함수의 PLT 엔트리를 실행함으로써
함수 실행 가능, ASLR이 걸려 있어도 PIE가 적용되어 있지 않다면 PLT의 주소는 고정되므로 이 방법으로 라이브러리 함수 실행 가능 -> Return to PLT
$ ROPgadget --binary rtl
Gadgets information
============================================================
...
0x0000000000400285 : ret
...
Unique gadgets found: 83
$