Jade CTF 2022 - Data Storage (canary, pie)
Info
(32/630) solves
description
In his DBMS course, Shekhar was learning about CRUD operations. He was taught these operations in SQL, but he wanted to try them out in C. He wrote a program for reading data from input, and then scrambling it so other users can’t figure out what is stored. He gave me the binary to test it, could you help me out?
nc 34.76.206.46 10003
for player
1 | . |
Analysis
Mitigation
1 | Arch: amd64-64-little |
카나리가 있고 PIE도 있다..
Source Code
코드는 간단하다. main()에서 database_store()를 호출하는데 이 함수에 취약한 부분이 존재한다. 그래서 이 함수 하나만 보면 된다.
Vulnerability
1 | unsigned __int64 database_store() |
코드가 복잡해보이지만(?) 기능 분석을 할 필요는 없다. 취약점을 찾는 것이 목표이기 때문에 필요한 부분만 읽으면 된다.
그러면 fsb와 bof가 대놓고 보이는 것을 찾을 수 있다.
fsb로 필요한 것을 leak하고 bof를 이용해서 ROP를 하면 된다. 말은 이렇게 간단하지만 익스하는데 꽤나 오래걸렸고 생각보다 스트레스를 받았었다.
Exploit
local 기준으로 익스를 작성했는데 remote는 잘 되지 않는 문제(?)가 발생했다. (로되리안 ㅜ)
local과 remote의 libc가 달라서 스택에 값이 쌓이는게 달라졌다. 그래서 leak할 offset을 루프를 돌려서 찾는 과정이 추가적으로 필요하다.
아래부터 remote 기준으로 설명을 한다.
Exploit Scenario
- 사전 작업: fsb를 이용하여 canary, pie offset 찾기
- canary leak하고 main으로 돌리기
- pie leak하고 main으로 돌리기
- got 출력해서 libc 구하기
system('/bin/sh\x00')
으로 흐름 돌리기
pre-work
leak canary & pie
canary leak을 위한 코드는 아래와 같다.
1 | from pwn import * |
제일 먼저 해야할 일은 canary를 leak하는 것이다. offset을 찾기 위해서 루프를 돌 때 try-except을 이용해서 예외처리를 해주었다. 만약 예외가 발생한다면 다시 remote에 연결해주었는데, p.close()
를 안해준다면 프로세스가 계속 살아있기 때문에 remote에 새로 연결해주기 전에 close를 해주어야 한다.
fsb를 이용해서 leak하고 유효한 주소를 가져오는 과정에서 예외가 발생할 가능성이 존재한다. 주소를 출력할 때 16진수 값이거나 (nil)
을 출력해주는데 후자일 경우 int(leak, 16)
에서 에러가 발생하기 때문에 except으로 가서 예외처리를 해줄 수 있다.
1 | [+] Opening connection to 34.76.206.46 on port 10003: Done |
루프가 끝나고 pause를 해줬기 때문에 멈추는 곳을 보면 바로 canary를 찾을 수 있다.
1 | [+] Opening connection to 34.76.206.46 on port 10003: Done |
offset은 77임을 바로 알 수 있다.
pause하는 조건은 주석에 !!!
한 곳을 보면 알 수 있다. ]?\n
은 이 함수 초반에 출력하는 문자열 중 일부이다. ret를 이 함수로 돌려주었기 때문에 이것이 출력된다면 leak한 값이 canary임을 바로 확인할 수 있다.
사실 canary가 구해져도 바로 ret로 돌릴 수가 없다. 왜냐하면 pie base의 값이 랜덤이기 때문에 아래와 같은 base이길 기대해야한다.
1 | 0x561aa9000000 |
내가 돌리고 싶은 주소는 pie_base + 0x1578
인데 디버깅을 하다보면 0x01578
이 고정인 것을 알 수 있다. 2.5바이트가 고정인데 0.5바이트만 쓸 수 없으니 2바이트만 덮을 수 있다. ret를 2바이트만 덮으면 어차피 끝에 null이 자동으로 붙기 때문에 1/16의 확률로 내가 원하는 함수로 흐름을 돌릴 수 있게 된다.
canary leak을 성공하고 흐름 돌리는 것도 성공하면 이제 pie leak을 하면 된다. pie leak도 canary를 leak한 것과 동일하게 진행하면 된다. 다만 차이점은 canary는 rip를 바꾸면서 검증이 바로 되어서 알 수 있는 반면에 pie는 offset과 함께 출력해주면서 pie 주소인 것을 leak하면 된다.
그렇게 구한 leak할 pie offset은 85이다.
Exploit Code
문제 풀 때의 기억을 살리고 싶어서 그 때 작성했던 익스 코드를 그대로 올린다.
1 | from pwn import * |
Flag
1 | jadeCTF{sh3llc0ding_but_w1th_4_tw1st} |
tmi
롸업 적으니까 이 문제는 매우 간단한 문제임을 깨달았다. 부끄럽지만 pie가 걸려있는데 어떻게 ROP를 해야할지 고민을 했다. 일단 local에서 진행할 때도 마지막 2 바이트만 덮을 생각을 못했었고, local 후에 remote의 libc가 다른데 pie가 걸려있는데 그걸 어떻게 알아내야 하는지.. 등등 고민을 꽤나 했다. (offset을 브포하면 된다.)
이 문제 덕에 주소 일부만 덮을 수 있다는 생각과 루프, try-except 등을 이용하여 offset을 구하는 과정을 배울 수 있었다.