-
[HackCTF] RTC Write-up표튜터와 함께하는 Pwnable/HackCTF Write-up 2019. 5. 10. 03:04
이번 문제는 RTC이다. 우어~ 다 풀었는데 마지막 "/bin/sh"에서
시간을 많이 소비했다. 그래도 결국 풀어서 기분은 좋다~
스터디 세미나에서 Py0zz1가 발표했던 Return to Csu 기법을
알고 있어서 생각보다는 오래 걸리지 않았다.
RTC가 딱 봐도 Return To Csu라고 판단해서 저걸쓰면 되겠구나
라는 생각이 들었기 때문이다.ㅎㅎ
바이너리는 NX가 걸려있고 Partial RELRO이므로
이전 문제와 같이 Stack, Heap, Data영역에 실행권한이
없으며 Got Overwrite가 가능하다.
문제를 실행시켜보았다!
사용자로부터 입력을 받는 간단한 구조였다.
IDA와 GDB를 이용해서 각각 코드와 어셈블리어로 보았다.
read 함수를 이용해서 입력을 받고 있었고 512개까지 입력이 가능했다.
또한 0x40 + sfp(8) = 72개의 dummy를 입력하면
ret 이전까지 접근할 수 있다는 것도 알 수 있었다.
그럼 이제 Return To Csu를 위해 필요한 것들을 하나씩 구해보도록 하겠다.
공격원리는 다음과 같다. 이 문제에서 필요한 ROP할 때 사용되는
마땅한 gadget이 없기 때문에 Return To Csu를 사용하여 인자를 맞춰줄 것이다.
사용되는 함수 목록을 확인하면 __libc_csu_init이 보인다.
인자를 셋팅하는 부분을 확인할 수 있다.
하지만 이것만 가지고는 이해할 수 없다. 그럼 아래의 그림을 보자.
원리는 간단하다. 위에서 인자를 셋팅하고 ret를 0x4006a0을 넘겨준다.
그렇게 되면 r13 - > rdx로 r14 -> rsi로, r15d -> edi로 넘어가며
우리가 원하는 인자들이 셋팅된다!! 마지막으로 Call이 진행되면서 실행이 되는데
이때 qword ptr로 Call을 호출하기 때문에 사용 시에 주의해야한다.
예시로 보통의 ROP에서 read@plt를 이용해서 함수를 호출하지만
이 문제에서는 qword ptr을 이용하므로 read@got를 호출해야
제대로 참조하여 호출하게 된다. 이 부분을 간과했다가 다 풀어놓고 오래걸렸다;;
또한 cmp rbx, rbp를 해서 같아야 다시 인자를 셋팅할 수 있으므로
뒤에 Payload를 보면 알겠지만 같게 맞춰주기 위해 rbx는 0, rbp는 1로 준다.
이 문제에서는 system@got를 구할 수 없다. 또한 csu로 인자를 셋팅한 뒤 Call하는 부분을 보면
r15d를 rdi가 아니라 edi로 넣는다. 주소길이를 신경써야한다.
다행이 고정주소 영역은 edi에 만족하는길이를 가지고 있고 어처피 dword ptr이므로bss영역에"/bin/sh"과
system 함수의 주소를 넣고 system 함수 주소가 담긴 bss영역의주소를 call 인자(r12)로 넘겨주어야 제대로 함수가 동작할 것이다.이렇게 안해도 된다... 그냥 write@got를 system의 실제주소로
GOT Overwrite하여 write@got를 호출해주면 edi의 길이를 가지기 때문에 굳이
bss영역에 system함수의 주소를 넣어 길이를 맞춰주지 않아도 된다..(왜이렇게 했지..ㅎㅎ)
고정 주소영역인 bss의 주소를 구했다.
다음으로는 system 주소를 leak하기 위해 write 함수를 이용해서
read함수의 leak를 구할 것이다. 그 다음 read 함수와 system 함수의
offset차이를 이용해서 system 함수의 주소를 leak할 것이다.
그러므로 read@got와 write@got를 구했다.
이런식으로 read 함수와 system 함수의 Offset도 구했다.
Offset은 또한 libc에서도 구할 수 있다.
이제 Payload를 구성해보도록 하겠다.
< Payload >( 문제가 생겨서 조금 긴 Payload를 임시적으로 등록해놓았습니다. 해결 후 수정하겠습니다.)72개의 dummy이후에 ret가 있으므로 ret를 아까 구해놓은
인자를 셋팅할 수 있는 csu gadget의 주소를 주어서 인자를 셋팅한다.
호출할 함수는 read 함수 주소를 leak하기 위한 write 함수이다.
아까 말한대로 plt가 아니라 got를 이용해서 호출을 진행해야한다.
그 이유는 dword ptr로 참조하여 호출하기 때문이다.
rbx 셋팅 전에 0을 하는 이유는
이 부분 때문이다. rsp를 8만큼 더하기 때문에 그냥 셋팅하게되면
하나씩 밀려서 인자가 이상하게 들어가게 된다.
rbx, rbp를 각각 0, 1을 주는 이유는 위에서 설명했듯이
다음 인자 셋팅을 진행하여 계속해서 공격을 진행하기 위해서이다.
저 부분이 충족되지 않으면 이전에 셋팅된 인자로 다시 함수를 호출할 것이다.
이제 leak한 read함수와 read와 system함수 offset 차이를 이용해서
system 함수의 주소를 leak한다. 그 후 고정주소영역 bss에 "/bin/sh"를
입력할 수 있도록 인자를 셋팅해서 진행해주고
bss+8("/bin/sh\x00")에system 함수를 저장해준다. 그 뒤, bss("/bin/sh")를 인자로bss+9 (system 함수가 저장된 주소)를 호출하여system("/bin/sh")를진행하게되면 깔끔하게 쉘을 딸 수 있게 된다~~
< 수정한 Payload >
분명히 맞게 구성했는데 로컬에서만 Payload가 성공되는 문제가
발생했다. 원인은 바로 개행문자(0xa)와 딜레이!!
제때 받아주지 않게되면 입력버퍼가 밀려서
값이 한꺼번에 들어가는 경우가 발생했다.
그래서 이럴 경우 두 가지로 신경써주면된다.
입력할 때 개행 문자까지 신경써서 방해되지않게
버퍼를 사용하던지 아니면 개행 문자를 안받아주고
딜레이를 sleep(0.1)로 처리해주는 방법이 있다.
여기서는 sleep을 이용해서 잡아주었다!!
이전에 말했던 gdb에서의 원리와 같은 이유이다.
< 불필요함을 수정한 Payload >
FLAG를 볼 수 있었다. 이번 문제 역시 굉장히 재미있는 문제였다~
반응형'표튜터와 함께하는 Pwnable > HackCTF Write-up' 카테고리의 다른 글
[HackCTF] Register Write-up (0) 2019.05.26 [HackCTF] Yes or no Write-up (2) 2019.05.26 [HackCTF] SysROP Write-up (0) 2019.05.07 [HackCTF] Pwning Write-up (2) 2019.05.03 [HackCTF] g++ pwn Write-up (0) 2019.04.30 댓글