CTF/WACon CTF 2022

[Clear] WACon CTF 2022 interspace Writeup

Vardy 2022. 6. 27. 21:49

주어진 소스파일을 분석해보자.

#!/usr/bin/env python3

import difflib
import itertools
import random
import os
import re
import signal
import string
import time

def bye(*args):
    print("Bye!")
    exit(1)

signal.signal(signal.SIGALRM, bye)
signal.alarm(5)

STEPS = 10
FLAG = os.environ.get("FLAG", "WACon{fake-flag}")

print("Flag distance calculator:")

for i in range(STEPS):
    candidate = input("> ").strip()
    if candidate == FLAG:
        print("Congratulations!")
        exit(0)
    elif candidate:
        print(1 - difflib.SequenceMatcher(None, candidate, FLAG).ratio()) 
        ##### ............................................................... (1)

bye()

(1)의 내용이 함수인데, 유사도를 반환해주는 함수이다.(1-ratio 형식으로 반환해주길래 헷갈려서, 테스트 코드에서는 원래 ratio가 반환되도록 처리했다.)

즉, 유사도를 높혀가며 플래그를 추출해내면 된다.

하지만 단순히 BruteForce를 하기에는 경우의수가 너무 많아서 확률을 줄여보기로 하였다.

첫 번째로는 사용되는 문자들을 추출해보았다. 로컬에서 테스트해본결과 전혀 사용되지 않는 문자는 ratio가 0으로 나오기때문에, 출력가능한 문자를 하나씩 넣어보면서 ratio가 0이 아닌 문자들을 추출해보았다.

from pwn import *
import base64
import binascii
import sys
import string
from itertools import product, permutations

p=remote("175.123.252.156", 9000)

flagPrintable = string.printable[:94]+"zzzzzz"
#print(flagPrintable)
#print(len(flagPrintable))

realFlagPrintable = b''

c = 0
for i in range(len(flagPrintable)) :#0):#len(flagPrintable)) :
    p=remote("175.123.252.156", 9000)
    print(p.recv(1024)) 
    for i in range(10) :
        i = c+i
        str = bytes(flagPrintable[i],"utf-8")
        print(str)
        #req = b"WAcon{" + str + b"}\n"
        p.send(str+b"\n")
        #req = b"WACon{" + b"interspace_is_made_of_"+b"tr"+str+b"_and_distance" + b"}\n"
        #print(req)
        #p.send(req)
        recv = float(p.recv(1024).split(b'\n')[0].decode('utf-8'))
        #recv = p.recv(1024)
        realRatio = 1-recv
        print(realRatio)
        if realRatio != 0.0 :
            realFlagPrintable += str
        #print(recv)
    c += 10
    if c==100 :
        break
    p.close()

print(realFlagPrintable) # acdefimnoprstACW_{}

위 코드의 실행 결과 사용되는 문자열의 종류을 알 수 있었다.

 

다음으로는 각 문자를 하나씩 개수를 늘려가며, 확률이 최대가 되는 지점을 찾아 각 문자가 몇번씩 사용되는지 파악해보았다.

예를들어 a문자를 WACon{a}, WACon{aa}, WACon{aaa} ... 처럼 입력해보았을 때, a가 4번 사용될때까지는 ratio가 증가하다가 a가 5번 사용되면 다시 ratio가 감소한다. 이런 케이스에서는 a가 4번 사용된다는 원리이다.

for i in range(len(realFlagPrintable)) :#0): #len(realFlagPrintable)) :
    #print(bytes(realFlagPrintable[i],"utf-8"))
    p=remote("175.123.252.156", 9000)
    print(p.recv(1024))
    c = b''
    #countArr = [-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1]
    #ratioArr = [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0]
    for j in range(10) :
        req = b"WAcon{" + c + b"}\n"
        p.send(req)
        recv = float(p.recv(1024).split(b'\n')[0].decode('utf-8'))
        realRatio = 1-recv
        if realRatio > ratioArr[i] :
            ratioArr[i] = realRatio
            countArr[i] = j
        print(realRatio)
        print(c)
        print("")
        c+=bytes(realFlagPrintable[i],"utf-8")
    p.close()

print(countArr) # [4, 2, 2, 5, 1, 3, 1, 3, 1, 2, 2, 3, 2, 0, 0, 0, 6, 0, 0] // acdefimnoprstACW_{} // WACon{}

 

위 결과를 통해 얻은 내용을 정리하면, WACon{} 안에 들어갈 내용은 아래와 같다.

aaaaccddeeeeefiiimnnnopprrssstt______ //37자

위 내용까지 추출하고 문제 제목이 interspace임을 기반으로 해당 문자열이 포함되어있을 것이라고 생각하고, _가 6번 쓰이니 총 7단어가 나올것이라고 생각하고 게싱을 시작했다..

 

꽤 오랜 시간이 걸려서 결국 플래그를 획득했다.

FLAG : WACon{interspace_is_made_of_inter_and_space}

 

----------------------------------------------------------------------------------------------------------------------

ST4RT 팀에서는 총 두 팀으로 나눠서 이번 대회에 참가했는데, 다른 팀은 아래 아이디어로 문제를 해결했다고 한다. 

위 방법으로 케이스를 줄여놓고 아래 아이디어를 적용시켰으면 보다 빠르게 플래그를 획득 할 수 있었을 것 같다.

 

반응형