티스토리 뷰

CTF write-up

Defcon CTF 2017 peROPdo [ pwnable ]

marshimaro aSiagaming 2017. 7. 8. 00:01

Defcon에 있는 pwnable 영역의 문제들은 대회 이후에 꼭 풀어보고자 하였으나

실력 부족이 처참히 느껴져 천천히 풀어보기로 했었는데 지금부터 하나씩 다시 풀어보려고한다.


peropdo라는 문제였는데 statically linked 바이너리에 stripped라 분석하는데 힘이 들었다.

예전에 한번 봤던 write-up 에서 open함수를 srop를 이용하여 exploit 했던 것이 기억나 그쪽으로 방향을 잡아서 풀어보았다.

방향성을 미리 잡고 풀게된 문제지만 여러모로 배우게 된 것이 많은 문제인 것 같다.





우선 바이너리의 보호기법을 체크해보았다.

보시다싶이, NX말고는 바이너리 자체에는 보호기법이 걸려있지 않다.

서버의 ASLR + NX니까 보호기법자체는 별로 문제가 되지 않을 것같다.






32bit binary라서 calling convention은 함수의 stack-frame의 offset을 기준으로 참조하게 된다.

_start함수에서 시작하니 main함수의 지점은 마지막으로 push해주는, 즉 첫번째 인자인 저 주소일 것이다.






우선 어떤 식으로 바이너리가 돌아가는지 알기 위해 실행을 몇번해보며 관찰해보았다.

내가 입력해준 값만큼 주사위를 던져서 나온 값들을 준다.

근데 의아한 점이 같은 이름으로 계속 새롭게 시작하면서 던져주게되면 같은 숫자들이 나온다.

아마도 여기서 rand()함수를 통해 값을 가져오는 것이지 않을까 살포시 생각이 들었다.






그리고 처음에 input name을 다르게 줘서 던져보았더니

이전과는 숫자가 다르게 나온다.

srand()를 통해 seed값을 설정해주는 것 같은데, 이게 우리의 name값을 참조하는 것 같다.







이제 IDA, Binary Ninja, GDB-PEDA를 통해 분석을 해볼 것이다.







dice를 굴려서 값을 받아오는 루틴인데, 값을 받는 위치가 좀 특이하다.

ebp_plus_4라는 변수는 실제로 base_pointer의 위치 + 4 에 존재한다고 IDA에 나온다.

그리고 loop를 돌 count는 ebp에 저장되며, 그 count만큼 loop를 돈다.

4byte단위로 증가하며, dice의 값들이 차곡차곡 들어간다.


그래서 이 부분에서 하나의 취약점이 발생하게 된다.







dice를 23번 이상으로 굴려주게되면, Segmentation fault를 띄운다.

그럼 어디에서 이 문제가 터지는 것일까?

일단,  조금씩 내려가며 분석을 추가로 진행해보겠다.





우리가 굴린 주사위의 값은 

위와 같이 4byte의 값들이 쌓이게 된다.






그리고 이와 같이 v4변수에 값들을 참조하여 넣어주고, % 연산을 통하여 1~6범위에서 값이 나오도록 설정해준다.

사실 Segementation fault가 뜬 시점에서 주사위 값이 stdout으로 무엇이 나오는지는 이제 관심밖이다.

왜 프로그램이 어디서 뻗어버렸을까?







바이너리를 실행시켜놓고 디버거를 붙여본 결과, 저 주소에서 SIGSEGV가 발생하였다.

leave의 instruction에서는 mov esp, ebp가 실행되게 되는데, ebp가 참조불가능한 이상한 주소값을 가지고 있기 때문에 SIGSEGV가 발생하게 되는 것이다.

그럼 저 값은 어디서 가져온 것일까???






dice를 23번 굴리게 되었을 때, 23번째의 dice값은 현재 esp가 가리키는 첫번째 값이다.

따라서 저기서 pop ebp 인스트럭션이 진행되므로, ebp에는 저 참조할 수 없는 값이 들어가게 되는 것이다.






따라서 여기서 leave instruction이 진행할려다가 SIGSEGV를 보내고 프로그램을 종료시키는 것이다.

그럼 우리는 이것을 어떻게 exploit할 것인가???


아까 처음에 name에 따라 rand()함수로 보내주는 우리의 dice값이 바뀐다고 하였다.

그러면 이 name값을 조정해주는 것은 seed값을 조정해주어 rand값을 control할 수 있다는 것이다.


입력받는 함수의 인자상태를 보았을 때, 아마도 scanf함수를 통해 입력을 받는 것 같다.

scanf는 정말정말 싫어하는데 payload를 구성할 때, 필터링되는 리스트가 많기 때문이다.

그래서 이 점을 유의하여서 payload를 구성해줘야 한다.


일단, 우리는 조정할 seed값을 구해야하며 어떤 값으로 저 ebp를 바꿔줄지도 정해야한다.

aslr상태라 stack을 leak한다던가 stack에서 뭔가를 하기에는 힘들어 보인다.


하지만 우리에겐 이 바이너리상에선 언제나 고정된 주소를 가지고 있는 data, bss섹션이 존재한다.

그리고 이 영역은 writable하다.





처음에 받는 name값은 .bss섹션에 들어가므로 bss섹션의 머지않은 거리에 있는 주소를 찾아주면 될 것같다.






이 간단한 코드를 통해 seed값을 4byte범위 내에서 찾아 줄 수 있다.

그리고 x86, 32bit의 system call convention은 인자를 ebx, ecx, edx순으로 받게 된다.

따라서 이 레지스터들에 대한 rop gadget도 찾아주면 될 것 같다.


exploit방향을 open함수를 통해 플래그를 열고 bss섹션에 read로 읽어주며, write로 stdout을 통해 플래그를 출력해 줄 것이다.





ropper나 ropsearch 같은 것들을 통해 가젯을 찾아주면 되는데, 오늘 알았지만

ropper가 찾지못하는 가젯도 ropsearch는 찾아주더라. -> 이쪽으로 애용하자....

위에서 말하였듯이, scanf를 통해 입력을 받으므로, 가젯선택을 잘해야한다.

0x0b같은 값은 거기서 입력이 끊기게 되므로 payload가 온전히 구성되지 않는다.


방향성과 어떻게 익스를 해야할지 다 알았으므로 이제는 payload만 구성해주면 된다.




Flag is : Thanks to Kenshoto for the inspiration! 5fbb34920c457b2e0855a174b8de3ebc


solve_peropdo.py






댓글
  • 프로필사진 ㅇㅇ 플글밍 공부하다가 ctf에 관심생겨서 들여다 보았더니 역시 모르는 게 많네요 어디부터 공부를 시작 하는게 나을까요? 2017.07.20 21:22
  • 프로필사진 marshimaro aSiagaming ctf도 영역이 여러개로 나뉘는데 어디쪽에 관심이 이 있으신지부터가....... ㅎㅎ 2017.07.21 13:53 신고
  • 프로필사진 ㅇㅇ 아;;; 깜박하고 안적었네요ㅋㅋㅋ
    시스템이랑 리버싱 그리고 네트워크 쪽에 관심이 있습니다
    이제 대학생이다 보니 파이썬 c 밖에 모르는데 어디서 부터 공부를 시작해야 할지 감이 안잡히더라구요 ㅎㅎㅎ

    2017.07.21 22:12
  • 프로필사진 marshimaro aSiagaming 보통 시스템해킹을 천천히 공부하게되면 자연스럽게 리버싱도 같이 익히게 되더라구요.
    저같은 경우는 처음에 리눅스 서버 구축하는 것을 시작으로 이후에 시스템해킹, 리버싱쪽 공부를 하게되었고 지금도 하고있습니당.
    그래서 상대적으로 처음에 공부할 때, 리눅스에 대한 이질감은 적은채로 시작했져.
    Hackerschool이라는 사이트의 FTZ와 LOB가 처음 입문할때는 괜찮은 것 같더라구용 ㅎㅎ
    2017.07.22 20:12 신고
  • 프로필사진 ㅇㅇ 일단 친절하게 답변 해 주셔서 감사합니다!!
    일단 리눅스에 익숙해지는게 우선이겠네요
    처음 공부하실때 기법같은 것을 주로 찾아보셨나요 아니면 위와 같은 write-up을 주로 찾아보며 공부하셨나요?
    2017.07.22 21:58
  • 프로필사진 marshimaro aSiagaming 기법을 찾아보기보단... 일단 기본적인 메모리구조나 베이스부터 다지는 삽질을 좀 많이했어요 ㅋㅋ 지금도 왕초보지만 ㅠ 2017.07.22 22:58 신고
  • 프로필사진 plz 질문이 있습니다..

    read 함수랑 write함수에서 왜 ebx에 각각 3이랑 1을 넣어주신거죠???

    제가 알기론 fd를 넣는걸로 알고 있는데 이 fd가 무슨값이 들어가는지 어떻게 알아내죠???

    검색을 해봐도 정확히 원하는 답이 안 나와서 질문드립니다
    2018.03.13 16:14
  • 프로필사진 marshimaro aSiagaming open을 통해 flag파일을 오픈하게되면, 기존에 할당되어 있던 0,1,2에 대한 파일디스크립터 다음에 3번에 할당되게 됩니다. 그래서 3번 파일디스크립터를 통해 read로 플래그를 읽어온거고, 읽어온 플래그는 bss영역에 저장시켰습니다 ㅎㅎ 그리고 마지막에 1번 파일디스크립터(stdout)으로 bss에 저장된 플래그를 출력시킨거에요 ㅎㅎ 2018.03.21 10:04 신고
  • 프로필사진 marshimaro aSiagaming 그리고 x86에서 함수 호출규약 상, int 0x80을 호출할 때, ebx, ecx, edx...순으로 인자를 구성해줘야되서 ebx에 첫 번째 인자인 fd를 넣어준거에요 ㅎㅎ 2018.03.21 14:13 신고
댓글쓰기 폼