[Clear] WACon CTF 2022 interspace Writeup
주어진 소스파일을 분석해보자.
#!/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 팀에서는 총 두 팀으로 나눠서 이번 대회에 참가했는데, 다른 팀은 아래 아이디어로 문제를 해결했다고 한다.
위 방법으로 케이스를 줄여놓고 아래 아이디어를 적용시켰으면 보다 빠르게 플래그를 획득 할 수 있었을 것 같다.