문제: DreamHack — 64se64
분류: Misc (정적 분석 / 인코딩)
난이도: 🥉 Bronze 3
FLAG:
DH{be046b7522aa2e28f276e0c910521f23a0587005a8377370a11e5e4dc15fb2ab}
문제 개요
| 항목 | 내용 |
|---|---|
| 문제명 | 64se64 |
| 난이도 | 🥉 Bronze 3 |
| 분류 | Misc |
| 제공 파일 | index.html (zip 내부) |
| 서버 | host8.dreamhack.games:21995 (정적 HTML) |
| 핵심 기법 | base64 디코딩 → ASCII 코드 → 문자열 변환 |
문제명 64se64는 곧 "base64 in base64" 의 말장난이다.
제공된 zip에는 단 한 개의 HTML 파일이 들어 있고, 그 안 hidden form input에 박힌 긴 base64 문자열이 곧 플래그를 출력하는 파이썬 스크립트다.
서버에 접속할 필요도 없이 정적 분석만으로 끝나는 입문용 문제.
🔬 분석
Step 1 — zip 해제 & 파일 확인
먼저 다운로드한 zip을 풀고 어떤 파일이 들어있는지 본다.
$ unzip 62e3ec4c-1259-4d31-8620-75a83fd9bd98.zip
inflating: index.html
$ file index.html
index.html: HTML document, ASCII text서버 측 동작이 없는 순수 정적 HTML 한 장짜리 문제다.
Step 2 — index.html 분석
파일을 열어 보면 친근한 인사말과 함께 form 하나가 있는데, 사용자가 채울 입력 필드는 없고 type="hidden" 으로 박힌 긴 문자열이 보인다.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Welcome</title>
</head>
<body>
<h1>Welcome! 👋</h1>
<form method="POST">
<input type="hidden" name="64se64_encoding"
value="IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwphc2M9WzY4LCA3MiwgMTIzLCA..." >
</form>
</body>
</html>값 끝이 = 로 끝나면서 A-Z a-z 0-9 + / 만 사용된 전형적인 base64 인코딩 문자열이다.
name까지 친절하게 64se64_encoding 이라고 알려주고 있으니 그대로 디코딩만 해주면 된다.
💣 핵심 기법
Layer 1 — base64 → Python 스크립트
base64 문자열을 그대로 base64 -d 해보면 파이썬 소스가 튀어나온다.
#!/usr/bin/env python3
asc = [68, 72, 123, 98, 101, 48, 52, 54, 98, 55, 53, 50, 50, 97, 97, 50,
101, 50, 56, 102, 50, 55, 54, 101, 48, 99, 57, 49, 48, 53, 50, 49,
102, 50, 51, 97, 48, 53, 56, 55, 48, 48, 53, 97, 56, 51, 55, 55,
51, 55, 48, 97, 49, 49, 101, 53, 101, 52, 100, 99, 49, 53, 102,
98, 50, 97, 98, 125]
arr = [0 for i in range(68)]
for i in range(0, 68):
arr[i] = chr(asc[i])
flag = ''.join(arr)
print(flag)Layer 2 — ASCII 코드 배열 → 문자열
asc 리스트는 길이 68의 ASCII 코드값 배열이다. 첫 세 값만 미리 풀어 봐도 이미 답이 보인다.
| index | code | chr |
|---|---|---|
| 0 | 68 | D |
| 1 | 72 | H |
| 2 | 123 | { |
| ... | ... | ... |
| 67 | 125 | } |
D, H, { 로 시작하고 } 로 끝나니, 그대로 합치면 DH{...} 포맷의 플래그가 나온다는 것이 보장된다.
🎯 풀이 / Payload 설계
두 단계가 전부다.
- hidden input 의
value값을 base64 디코드해서 파이썬 스크립트를 얻는다. - 그 스크립트를 그대로 실행 — 또는 ASCII 배열만 직접
chr()로 합쳐도 동일하다.
스크립트 자체가 곧 풀이라서 별도 역산은 필요 없다.
🚀 Full Exploit
solve.py — base64 디코드 후 그대로 exec() 해서 플래그를 뽑는다.
#!/usr/bin/env python3
"""
64se64 — DreamHack Misc Bronze 3
풀이: hidden input의 base64를 디코드하면 Python 스크립트가 나오고,
그 안의 ASCII 코드 배열을 chr()로 변환하면 플래그가 나온다.
"""
import base64
b64 = ("IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwphc2M9WzY4LCA3MiwgMTIzLCA5OCwgMTAx"
"LCA0OCwgNTIsIDU0LCA5OCwgNTUsIDUzLCA1MCwgNTAsIDk3LCA5NywgNTAsIDEw"
"MSwgNTAsIDU2LCAxMDIsIDUwLCA1NSwgNTQsIDEwMSwgNDgsIDk5LCA1NywgNDks"
"IDQ4LCA1MywgNTAsIDQ5LCAxMDIsIDUwLCA1MSwgOTcsIDQ4LCA1MywgNTYsIDU3"
"LCA0OCwgNDgsIDUzLCA5NywgNTYsIDUxLCA1NSwgNTUsIDUxLCA1NSwgNDgsIDk3"
"LCA0OSwgNDksIDEwMSwgNTMsIDEwMSwgNTIsIDEwMCwgOTksIDQ5LCA1MywgMTAy"
"LCA5OCwgNTAsIDk3LCA5OCwgMTI1XQphcnI9WzAgZm9yIGkgaW4gcmFuZ2UoNjgp"
"XQpmb3IgaSBpbiByYW5nZSgwLDY4KToKICAgIGFycltpXT1jaHIoYXNjW2ldKQpm"
"bGFnPScnLmpvaW4oYXJyKQpwcmludChmbGFnKQ==")
script = base64.b64decode(b64).decode()
print("[+] Decoded Python script:")
print(script)
print("[+] Executing...")
exec(script)실행 결과는 다음과 같다.
$ python3 solve.py
[+] Decoded Python script:
#!/usr/bin/env python3
asc=[68, 72, 123, 98, 101, 48, 52, 54, ...]
arr=[0 for i in range(68)]
for i in range(0,68):
arr[i]=chr(asc[i])
flag=''.join(arr)
print(flag)
[+] Executing...
DH{be046b7522aa2e28f276e0c910521f23a0587005a8377370a11e5e4dc15fb2ab}
📝 결론
문제명에 답이 있다.
64se64 는 곧 base64 안의 base64, 그러니까 한 꺼풀만 더 벗기면 끝이라는 힌트다. 입문자가 base64 인코딩이 무엇인지 직관적으로 체감하게 만든 좋은 문제 설계.
진짜 인코딩은 두 번이 아니라 한 번이었다.
겉보기는 "base64 안에 base64"라는 이름값을 하지만, 실제로는 base64 한 번 + ASCII 코드 변환 한 번이다. ASCII 배열을 그대로 본문에 박아둔 것도 일종의 "사람이 알아보기 어려운 인코딩"인 셈.
Comments
댓글
댓글을 남기려면 로그인이 필요해요. (네이버 · 구글 계정)
댓글 불러오는 중…