-
[HackCTF]Unexploitable #3 Write-up표튜터와 함께하는 Pwnable/HackCTF Write-up 2019. 8. 13. 12:50
이번에 풀어볼 문제는 Unexploitalbe #3이다.
풀면서 들었던 생각은 자주자주 풀지않아서 그런지 머리가 굳는 느낌이다..ㅠ
저번에도 그랬지만 어쨌든 풀어서 참 다행이다.. ㅎㅎ
해당 바이너리는 NX가 걸려있고 Partial RELRO이다.
그러므로 .data, stack, heap 영역에 실행권한이 없으며
Got Overwrite가 가능하다는 것을 알 수 있었다.
우선 바이너리를 실행시켜보겠다.
특정한 문자열이 출력되고나서 사용자로부터 입력을 받고 있었다.
IDA를 이용해보니 역시나 fgets함수 즉, 사용자로부터 입력을 받는 부분에서
버퍼오버플로우를 일으킬 수 있는 취약점이 있다는 것을 알 수 있다.
조금 더 자세히 들여다보면 rbp-0x10 즉, 16개의 버퍼의 크기보다
훨씬 큰 0x100을 입력할 수 있으므로 BOF가 가능하며 16(버퍼크기) + 8(sfp) = 24의
dummy로 덮어주면 ret를 핸들링 할 수 있다는 것도 확인할 수 있었다.
그렇다면 "그 다음은 어떻게 진행해야할까?" 하고 곰곰히 생각해보았다..
fwrite와 fgets를 이용해서 ROP를 사용하면 될 것 같다는 생각이 들었고
그렇다면 사용할 수 있는 gadget이 있는지 확인해봐야겠다는 생각이 들었다.
우선 ROP를 하기위해 필요한 것들을 구해보자.
그에 앞서 이용될 함수에 대해 생각해보자. 우선 fwrite함수에 필요한
인자는 4개, fgets에 필요한 인자는 3개이다. 입력할 수 있는 길이가 충분하기 때문에
인자들은 RTC(Return to CSU)를 이용하면 셋팅해줄 수 있을 것이다.
또한 바이너리에 어떠한 함수가 존재하는지 확인해보니
이전 문제와 같이 gift라는 함수가 존재했고
"Useless gadget for you"라는 문자열이 있었다.
"쓸모없다지만 선물이니까 언젠가 쓸 일이 있겠지"하고 넘어가자
RTC를 이용해서 함수를 호출할 수 있는지 확인해보았다.
fwrite함수의 인자를 셋팅하려는데 문제가 발생한다. fwrite의 rcx, 즉 4번째 인자 stdout을
셋팅해주지 못한다는 것이다. 그렇다면 이 rcx를 어떻게하면 셋팅해줄 수 있을까??
이 문제를 푸는 핵심이 바로 이 부분을 해결하는 것이다.
우선 fwrite 함수가 어떻게 동작하는지 확인해보았다.
0x601050의 내부에 있는 값 0x7ffff7dd2620을 QWORD PTR로 참조해서
rax에 저장하고 rax에 저장한 값을 rcx에 저장해서 인자를 넘기고 있었다.
그러므로 fwrite함수를 사용하기 위해서는
stdout이 저장되어있는 0x601050 주소 내부의 값을 참조하도록
인자를 넘겨줘야 한다는 것을 알 수 있었다.
현재 4번째 인자인 rcx를 제외한 모든 인자를 RTC로 셋팅할 수 있다.
우리에게 필요한 rcx를 셋팅하는 방법은 다음과 같다.
pop rdi, ret gadget을 이용해서 rdi에 stdout가 저장된 고정주소영역 0x601050을 저장한다.
그 다음 rdi에 저장된 값(0x601050)을 QWORD PTR로 참조한 것을 rcx에 저장하는
gadget을 이용하면 rcx에 우리가 원하던 stdout을 인자로 넘겨줄 수 있게된다.
이 gadget은 아까 우리가 본 gift함수에 들어존재하고 있었다!!
Payload를 살짝보자면 다음과 같아진다.
이제 우리는 fwrite 함수를 사용할 수 있게 되었다. RTC를 이용해서
fwrite를 호출하여 fgets의 실제 주소를 받아올 수 있다.
그렇게 받아온 fgets함수에서 fgets의 offset을 빼주면
libc의 주소를 구할 수 있고 libc를 구할 수 있으니 stdin의 실제주소와
system함수의 실제주소도 쉽게 구할 수 있게 되었다~~
그렇다면 하나씩 진행해보자!!
위의 사진에서 볼 수 있듯, 간단하게 fgets의 offset을 구했다.
마찬가지의 방법으로 system offset도 구할 수 있었다.
stdin의 경우 fgets의 실제주소와의 offset차이를 이용해서 구해보기로 했다.
즉, libc + fgets offset + (stdin addr - fgets_addr) = stdin의 실제주소이다.
이제 ROP에 이용할 fwrite와 fgets 모두 사용할 수 있게 되었다.
Payload는 원리는 다음과 같이 구성해볼 수 있다.
1. fwrite함수를 이용해서 fgets의 실제주소와 offset을 통해 libc addr를 구한다.
2. libc addr을 기반으로 system함수와 stdin을 구해서 사용할 수 있도록 한다.
3. fgets 함수를 이용하여 고정주소 영역에 "/bin/sh"을 저장한다.
4. 마찬가지로 고정주소 영역에 system함수의 실제주소를 저장해준다.
-> 여기서 system함수의 실제주소를 고정주소 영역에 넣어서 사용하는 이유는
RTC를 이용해서 함수를 호출할 때 QWORD PTR을 이용해 해당 주소 내부에
저장된 값을 QWORD만큼 참조해서 호출하기 때문이다.
이제 일반적인 ROP를 하듯 위에서 설명한대로 Payload를 구성하면된다.
한 가지 다른점은 RTC를 사용하면 Payload가 길어져서
256길이를 넘길 수 있기 때문에 main을 다시 호출해서 해결하면 된다.
( Payload를 더 줄일 수 있을 것 같은데 일단은 푸는 것에 목적을...ㅎㅎ )
또한 이 문제에서 살짝 알고 갈 것은 바로 고정주소 영역 bss에 stdout과 stdin이
존재한다는 것이다. bss영역의 시작 주소를 보면 0x601050이다. 아까 위에서 봤던
낯이 익은 주소이다. 바로 stdout의 주소이며 0x601060에는 stdin이 존재한다.
< Payload >
이런식으로 진행하게되면~~
짜잔~~ FLAG를 볼 수 있었다ㅎㅎ
반응형'표튜터와 함께하는 Pwnable > HackCTF Write-up' 카테고리의 다른 글
[HackCTF] Unexploitable #1, Unexploitable #2 Write-up (0) 2019.07.18 [HackCTF] Basic_FSB Write-up (0) 2019.06.11 [HackCTF] World Best Encryption Tool Write-up (0) 2019.06.10 [HackCTF] Register Write-up (0) 2019.05.26 [HackCTF] Yes or no Write-up (2) 2019.05.26 댓글