NX bit기법이 적용되어있는 바이너리를 공격할 때 사용되는 기법이다. (Never Execute)
말 그대로 원래 사용해야하는 함수뿐만 아니라 공유 라이브러리에 올라와 있는 다른 함수까지 사용할 수 있게 되어 쉘을 여는 공격기법이다.
당연히 system함수를 주로 노리게 된다.
우선 printf함수를 가지고 있는 공유 라이브러리(libc.so.6)가 메모리에 올라와있을것이다.
실제 프로그램으로 예를 들어보겠다.
일반적인 bof로는 흐름을 바꿔도 쉘을 열만한 취약점을 찾을 수 없다.
하지만 rtl 기법을 사용한다면 아까 말했듯이 printf와 scanf를 로드하고 있는 라이브러리인 lib.so.6가 모두 메모리에 올라가기 때문에
해당 라이브러리에 있는 함수들을 모두 사용할 수 있다. 쉘을 따기 위해서 system()함수를 사용해보겠다.
print system -> 현재 메모리에 올라가있는 system함수의 주소를 출력해준다.
이제 본격적으로 rtl 기법을 적용해보자
main 함수를 실행시키면 다음과같은 스택 구조를 가진다.
20바이트의 더미를 넣고 RETN부분에 system주소와 실행할 주소를 찾아 넣으면 되겠다.
실습을 시작하기 전에 aslr이 걸려있으면 공격이 성공 할 수 없기 때문에 sysctl -w kernel.randomize_va_space=0로 aslr을 끄고 시작했다.
gcc -m32 -mpreferred-stack-boundary=2 -fno-pic -no-pie -fno-stack-protector -o ./rop ./rop.c
이제 다음엔 system함수에 넣어줄 문자열을 찾아야한다.
걍 아무데서나 출력가능한 문자열 하나 긁어서 /bin/sh를 그 이름으로 복사해서 실행시키는 방법도 있고
system함수내의 execl 함수에 /bin/sh문자열이 들어있으므로 그 위치를 찾아내서 참조하는 방법도 있다.
1. 출력가능한 문자 찾아서 그 위치 쓰기
- x/100x main이나 system
-00 (널문자) 가 뒤에 붙어있는 출력가능한 아스키값 찾기
-그 아스키값 주소 복사
-cp /bin/sh ./찾은 출력가능한 아스키값
-export PATH=$PATH:`pwd`
-페이로드 구성
2. execl함수 내의 /bin/sh문자열 찾아 참조하기
-프로그램 작성
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
long sys = system함수 주소
while ( memcmp((void *)sys, "/bin/sh\x00", 8) ) sys++;
return 0;
}
(주소가 32비트 형식이므로 -m32옵션 줘야됨
-찾은 주소로 페이로드 구성
마지막으로 페이로드를 만들어보자
dummy 20byte + system함수주소 + dummy 4byte + system함수인자주소
함수주소와 인자주소 사이의 4바이트는 system함수구조가 ebp+4를 인자로 받지 않고 +8을 인자로 받기 때문에 그 사이에 더마 바이트를 넣어주는것이다. 사이의 4바이트가 리턴주소라는데 이건 RTL Chain기법을 공부할때 더 자세히 쓰겠다.
성공적으로 쉘을 반환했다!!