ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [HackCTF] Unexploitable #1, Unexploitable #2 Write-up
    표튜터와 함께하는 Pwnable/HackCTF Write-up 2019. 7. 18. 13:24

    요즘 다른거 하느라 바빠서 진짜 오랜만에 포너블을 풀었다.. ㅠㅠ

    많이 까먹었을까봐 걱정했는데 그래도 새록새록 떠올라서 다행이다.

    이번에 풀어볼 문제는 Unexploitable #1과 Unexploitable #2 라는 문제이다.

    두 문제가 같은 방식으로 풀리므로 한 번에 진행하도록 하겠다.

    기준은 Unexploitable #1을 기준으로 설명을 진행하도록 하겠다.



    음.. 푸는 방법은 쉽게 떠올랐지만 뭔가 gadget을 사용하는 센스가

    늦게 떠올라서 생각보다 시간이 걸렸다... 우선 바이너리를 보도록 하겠다.




    64bit이며 dynamically linked 방식을 이용하고 있다.





    Mitigation의 경우 NX bit만 걸려있어서 .data, stack, heap 영역에

    실행권한이 없고 Partial RELRO이기 때문에 Got Overwrite가 가능하다.





    문제를 실행시켜보았다.

    힌트로 보여지는 어떠한 문자열이 나오고 사용자로부터 입력을 받고 있다.

    입력을 하고 나서는 아무일도 일어나지 않는 것으로 보아

    사용자로부터 입력받는 부분에서 취약점이 존재할 것이고

    RTL이야기와 system@plt를 말하는 것으로 봐서는 이 부분이

    힌트일 가능성이 높을 것이라고 일단은 추측해보았다.





    이번에는 어셈블리어 코드와 IDA로 분석해보도록 하겠다.

    fgets 함수의 인자로 넘어가는 레지스터들을 보니 $rbp-0x10부터

    입력을 받고 있었고 최대 0x40만큼 입력이 가능한 것으로 보아

    이 부분에서 버퍼오버플로우가 일으킨다면 

    RET가 핸들링이 가능하므로 취약하다는 것을 알 수 있었다. 또한

    rbp-0x10 + sfp(8) = 24, 즉 24개의 dummy이후에 RET가 존재함을 알 수 있었다.




    IDA에서는 gift라는 이름의 함수가 보였다.





    음.. 힌트대로 이 함수를 문제풀 때 사용하면 된다는 것이 확실해졌다.





    어셈블리어 코드로도 보았더니 edi에 0x4007f8을 넣어 System함수의

    인자로 전달해주고 있었다. 음.. 그렇다면 우리는 이 edi에 넘어가는 0x4007f8대신

    "/bin/sh"이 들어있는 주소를 넘겨줘야 할 것 같다는 생각을 할 수가 있을 것이다.




    우선 보류하고 계속해서 문제를 풀어보자!!

    그렇다면 다시 돌아와서 우리는 RET를 무엇으로 덮어서

    exploit을 진행할지 고민해봐야한다. 음.. 일단은 우리가 필요한것들이

    무엇이었는지 정리해보면 

    1. dummy이후에 어떤 주소로 ret를 덮을 것인지

    2. 어떤 함수를 leak 할 것인지 

    3. "/bin/sh"을 어느 영역에 넣을 것인지

    4. 인자를 저장해서 넘겨줄 gadget



    하지만 이 문제에서는 2번 과정이 필요가 없다. 그 이유는

    leak을 하지 않아도 "/bin/sh"만 고정주소 영역에 저장할 수 있다면

    gift 함수를 이용해서 system("/bin/sh")을 실행시킬 수 있기 때문이다.




    그러므로 1, 3, 4만 진행하면된다.

    처음에는 Return to Csu를 이용해서 fgets 함수로

    "/bin/sh\x00"을 저장하려고 하였다. 하지만 그러기엔 0x40 즉, 64개의

    길이로는 부족하다. 그래서 다른 방법이 필요했다. 풀이방법은

    다알았으나 이 문제의 관건이 바로 어떻게 "/bin/sh\x00"을 고정주소 영역에

    저장해줄 것이냐라는 것을 알 수 있었다.




    최대한 적은 길이를 이용해야 길이제한에 걸리지 않는데

    fgets의 인자를 다시 셋팅해서 호출하는 방법이 아닐 것이라고

    생각했고 ROPgadget Tool을 이용해서 쓸만한게 Gadget이

    있는지와 fgets 실행부분을 차근차근 확인해 보았다. 




    결과적으로 이 부분을 이용하면 된다는 것을 깨달았다!!!

    fgets 부분에서 우리는 저장할 부분에 대한 인자만

    바꾸어주면 된다고 생각했기 때문에 pop rbp, ret 가젯을

    이용한다면 충분히 fgets 호출직전을 ret값으로 덮어서

    저 인자를 핸들링 하여 fgets를 사용할 수 있을 것이라고 생각했다.




    다행스럽게 원하는 gadget이 존재했다.




    그 다음은 "/bin/sh\x00"을 저장할 고정주소영역(bss)을 구해주었다.




    dummy(24) + pop rbp, ret gadget + bss+0x10(고정주소) + 0x40074d(main+113번째 주소)

    이런식으로 진행하게되면 아래와 같은 화면을 볼 수 있다.


    bss의 주소값에 +0x10를 해주는 이유는 fgets에서 사용될 때 rbp-0x10을

    한 값을 사용하기 때문에 이 점을 생각해주어야 한다. 우리는 위의 과정으로

    rbp에 저장위치를 인자로 fgets 함수를 이용해서 우리가 원하는 "/bin/sh\x00"을 집어넣을 수가 있었다.



    다음으로는 다시 main함수의 ret가 찾아온다. 이 때 ret로 넘어가는 값은

    분석해보면알겠지만 "/bin/sh\x00"이 저장된 주소에서 16바이트 떨어진 부분이다.

    그러므로 sendline으로 binsh을 보낼때 뒤에 원하는 ret를 넣어주면 된다.



    아까 위에서 말했듯 이제 한 가지 과정이 남았는데 그것은 gift 함수를 이용하는 것이다.

    하지만 gift함수의 system을 이용하기위해선 rdi를 우리가 넣어놓은 "/bin/sh\x00"의

    주소로 바꾸어주어야한다. 그렇기 때문에 우리가 구해야할 것은 rdi를 핸들링 할 수 있는

    gadget이 될 것이다. 그러므로 다시 한번 ROPgadget를 사용해보았다.




    하나가 딱 존재하고 있었다. 이 주소값을 RET에 덮어주었고

    rdi를 bss영역으로 주기위해서 분석을 해보았더니 




    bss영역의 주소에서 0x20만큼 떨어진 부분에 넣어줘야한다는 것을 알 수 있었다.

    그러므로 역시 뒤에 붙여주었다. 또한 그 다음에 진행될 RET역시 bss영역의주소에서

    0x28만큼 떨어져있다는 것을 확인 할 수 있었다.




    gift 함수의 시작을 RET로 넘기면 엉뚱한 문자열이

    system함수의 인자 rdi로 저장되기 때문에 gadget의 RET는

    gift + 9 의 주소를 넘겨주면 된다.!! 이런식으로 진행하게되면!!




    system("/bin/sh")이 실행된다~~~




    But.... 쉘이 따지지 않았다... 왜일까...





    그 이유는 바로 BSS영역에 있다. 우리는 쓰기권한이 존재하는

    영역에 값을 넣어줘야 사용이 가능한데  system함수는 스택을 크게 쓴다고 한다. 


    정확히는 _dl_runtime_resolve_xsavec 함수에서

    스택을 많이 사용한다고 하는데 자세하게는 모르겠다. 스택을 적게 주면

    이 부분에서 더이상 진행하지 못한다는 것을 확인할 수 있었다.

    ( 자세히 아시는 분 댓글 부탁드립니다 )


    때문에 BSS에 쓰기권한이 있는 곳으로 넓게 잡아 줘야한다고 한다.

    그렇지 않으면 system함수 실행 중에 크래쉬가 나게 된다.




    * Thanks to alkyne *




    그래서 우리는 그저 bss 영역의 주소를 늘려서 진행해주면된다.

    기존의 bss = 0x601060 => 0x601060 + 0x900

    (적당히 큰 수면서 쓰기권한 존재영역을 넘지 않게..)





    < Payload >





    < Unexploitable #1 >

    다음과 같이 exploit을 진행하면~~ 짜잔!! FLAG를 볼 수 있었다ㅎㅎ 

    FLAG를 보니 Unexploitable #1 은  이 방법이 아니라 출제자가

    다른방법으로 풀이하기를 원한 것 같다.




    < Unexploitable #2 >

    이 첫 번째 방법을 이용하면 Unexploitable #2도 풀 수 있다.

    그러므로 따로 Write-up을 작성하지 않겠다.



    < Unexploitable #1 >은 다른방법의 풀이도 있으므로 곧 올리겠습니다.

    반응형

    댓글

Designed by Tistory.