Step 151. 드림핵 웹 입문 — 첫 실전 문제
Level 2 — 보안 입문과 공격 스킬 기초 | 난이도 ★★☆☆☆ | 예상 소요 시간 2.5시간
전제: Step 150까지 마쳤다. 개발자 도구로 HTML 소스와 쿠키를 볼 수 있고, Base64 디코딩과 간단한 요청 조작을 안다.
- 준비물: 인터넷 연결, 드림핵 계정(오늘 만듭니다), 개발자 도구, 개인 위키(Write-up 저장소).
- ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
- 합법 연습장 안내: 드림핵(dreamhack.io)은 문제마다 "공격해도 되는 서버"를 공식적으로 발급해 주는 합법 학습 플랫폼입니다. 문제 서버 주소와 내 컴퓨터의 로컬 랩 외에는 오늘의 기술을 쓰지 않습니다.
지금까지의 무대 — Bandit, Natas, DVWA, Juice Shop — 은 전부 "정해진 교과서"였습니다. 오늘부터는 "남이 낸 시험 문제"를 풉니다. 드림핵은 한국어로 된 CTF(해킹 방어 대회 형식의 문제 풀이) 플랫폼으로, 문제마다 실제로 공격할 수 있는 임시 웹 서버를 발급해 줍니다. 취약점을 찾아 서버 어딘가에 숨겨진 플래그(flag)를 읽어 제출하면 정답입니다.
무서울 것 없습니다. 첫 문제들은 여러분이 이미 아는 기술 그대로입니다 — 소스 보기, 쿠키 읽기, 간단한 입력 조작. 오늘은 플랫폼 사용법을 익히고, 첫 문제 유형을 로컬 랩에서 복습한 뒤, 실전 문제 8개를 깨는 것이 목표입니다.
1. 학습 목표
이 챕터를 끝내면 다음을 할 수 있습니다:
- 드림핵 워게임의 문제 구성 요소(제목·설명·URL·플래그 제출)를 안다
- 문제의 제목과 설명에서 공격 대상 힌트를 읽어 낸다
- 소스 보기·쿠키 해독 유형을 로컬 랩에서 재현해 푼다
- 정찰 → 가설 → 실험 → 플래그의 풀이 사이클을 실전 문제에 적용한다
- 정해진 형식의 Write-up을 작성한다
2. 배경 지식 — 오늘의 도구와 개념
오늘의 도구 한눈에 보기
| 구분 | 내용 |
|---|---|
| 언어·환경 | 드림핵 워게임(브라우저) + 파이썬 3 + Flask(로컬 복습 랩) |
| 오늘의 명령 | 개발자 도구 소스 보기, Application 탭의 쿠키, base64.b64decode() |
| 필요한 개념 | CTF와 플래그, DH{...} 형식, 문제 배포 서버, 풀이 사이클 |
| 오늘의 산출물 | 웹 문제 8개 해결 + Write-up 2편 |
2-1. CTF와 플래그 — 정답의 모양
CTF 문제의 정답은 플래그라는 특별한 문자열입니다. 드림핵의 플래그는 DH{여기에_정답이_들어감} 형식입니다. 문제 서버를 공격해 이 문자열을 찾아낸 뒤, 문제 페이지의 제출 칸에 넣으면 점수를 받습니다.
플래그는 "내가 이 서버의 비밀에 실제로 도달했다"는 증거입니다. 화면 캡처나 설명이 아니라 서버가 숨겨 둔 문자열 자체를 가져와야 하니, 풀이에는 변명의 여지가 없습니다. 이것이 CTF가 실력의 척도가 되는 이유입니다.
2-2. 문제 페이지 읽는 법
드림핵의 웹 문제는 대개 이렇게 구성됩니다 (화면 예시 — 실제 화면은 가입 후 확인):
제목: cookie-monster 난이도: ★ 1 풀이 수: 1,234명
설명: 이 사이트는 쿠키로 권한을 관리합니다. 관리자만 플래그를 볼 수 있습니다.
[서버 접속하기] → http://host3.dreamhack.games:12345/
[플래그 제출] DH{____________________}
제목과 설명이 곧 힌트입니다. cookie라는 단어가 보이면 쿠키 조작 문제이고, source가 보이면 소스 읽기 문제입니다. "난이도 1부터"라는 규칙을 지키세요 — 처음부터 어려운 문제를 고르면 배우기 전에 멘탈이 깨집니다.
2-3. 풀이 사이클 — 실전판 네 박자
Step 96의 사이클이 웹 문제용으로 진화했습니다.
① 정찰 — 문제 서버의 페이지를 전부 둘러본다 (소스 보기, 링크, 폼, 쿠키)
② 가설 — "이 기능은 내부에서 무엇을 할까?" 취약점 후보를 적는다
③ 실험 — 입력을 바꾸고, 쿠키를 고치고, Burp로 요청을 다시 보낸다
④ 플래그 — DH{...}를 찾아 제출하고, 풀이를 기록한다
초보는 ①을 10초 만에 끝내고 ③만 반복합니다. 난이도 1 문제의 플래그는 대부분 ① 정찰만 철저히 해도 보입니다. 먼저 "읽는" 사람이 이깁니다.
2-4. 첫 문제 유형 미리보기
난이도 1~2의 웹 문제는 세 가지 유형이 주를 이룹니다.
| 유형 | 문제가 숨긴 것 | 여러분이 할 일 |
|---|---|---|
| 소스 읽기 | HTML 주석·JS 파일에 플래그 | 개발자 도구로 소스 전체 읽기 |
| 쿠키 | role=guest 같은 권한 쿠키 |
값을 디코딩하고, 바꿔치기 |
| 간단한 입력 조작 | 검증 없는 입력창 | SQLi·명령 문자 등 시험 입력 |
전부 이미 배운 것들입니다. 오늘 로컬 랩에서 이 유형들을 다시 한번 손에 익히고 실전으로 갑니다.
3. 따라 하기
3-1. 로컬 랩 — 소스에 숨겨진 플래그 찾기
드림핵 문제가 어떻게 생겼는지, 똑같은 구조를 내 컴퓨터에 만듭니다. firstprob_lab.py:
from flask import Flask, make_response
app = Flask(__name__)
@app.route("/")
def index():
html = """<!doctype html>
<html><body>
<h1>Welcome!</h1>
<p>로그인하면 더 많은 기능이 열립니다.</p>
<!-- TODO: 배포 전 삭제. 임시 관리자 메모: flag{view_source_is_free} -->
</body></html>"""
resp = make_response(html)
resp.set_cookie("session_role", "Z3Vlc3Q=") # base64("guest")
return resp
if __name__ == "__main__":
app.run(port=5493)
실행 후(python firstprob_lab.py) 브라우저로 http://127.0.0.1:5493/에 접속합니다. 화면에는 "Welcome!"과 한 줄의 안내만 보입니다. 이제 정찰 모드로 — 개발자 도구(F12)를 열어 Elements 탭에서 HTML 전체를 봅니다.
서버가 실제로 보낸 소스 (2026-09-09 실측):
<!doctype html>
<html><body>
<h1>Welcome!</h1>
<p>로그인하면 더 많은 기능이 열립니다.</p>
<!-- TODO: 배포 전 삭제. 임시 관리자 메모: flag{view_source_is_free} -->
</body></html>
읽는 법: 브라우저 화면에는 없는 다섯 번째 줄이 소스에는 있습니다. <!-- ... -->는 HTML 주석으로 화면에 그려지지 않지만, 서버는 분명히 보냈습니다. 실전에서도 개발자 메모, 임시 비밀번호, 숨겨진 경로가 주석에서 단골로 나옵니다.
3-2. 쿠키 읽고 해독하기
같은 서버가 응답에 쿠키도 실어 보냈습니다. 개발자 도구 Application 탭 → Cookies에서 확인하거나, 응답 헤더로 봅니다.
응답 헤더 (2026-09-09 실측):
Set-Cookie: session_role=Z3Vlc3Q=; Path=/
값이 Base64 모양입니다 (끝의 =이 특징). 파이썬으로 해독합니다.
import base64
base64.b64decode("Z3Vlc3Q=").decode()
출력 (2026-09-09 실측):
guest
읽는 법: "당신의 권한은 guest"라는 선언이 내 브라우저 안에 들어 있습니다. 다음 질문은 자연스럽습니다 — 이 값을 YWRtaW4=(base64로 "admin")으로 바꾸면? 쿠키는 클라이언트가 마음대로 고칠 수 있고, 서버가 검증 없이 믿으면 권한 상승이 됩니다. Natas(Step 102~104)에서 이미 해본 바로 그 공격입니다.
왜: 쿠키 값을 보는 순서를 정해 두세요. ① Base64 모양인가 → 디코딩 ② 점이 두 개인가 → JWT(Step 150) ③ 둘 다 아니면 값 자체를 조작. 세 갈래면 난이도 1~2 쿠키 문제의 대부분을 커버합니다.
3-3. 드림핵 가입과 첫 문제 배포
이제 실전입니다 (화면 예시 — 플랫폼 화면은 여러분이 직접):
dreamhack.io에 접속해 회원가입 (이메일 인증)- 상단 메뉴
Wargame→ 카테고리에서web선택 - 난이도 순으로 정렬해 가장 쉬운 문제부터 클릭
- 문제 페이지의
서버 접속하기버튼 → 임시 서버 주소 발급
읽는 법: 발급되는 주소(http://hostN.dreamhack.games:포트/ 형태)는 여러분만의 일회성 연습 서버입니다. 일정 시간이 지나면 만료되니, 만료되면 다시 발급받으면 됩니다. 이 주소는 공격이 허가된 유일한 목표입니다.
3-4. 첫 문제 풀이 사이클 적용
난이도 1 문제 하나를 골랐다고 칩시다. 사이클을 의식적으로 적용합니다.
① 정찰: 메인 페이지 열기 → F12로 HTML 소스 전체 읽기 → Application 탭의 쿠키 확인
→ robots.txt 도 접속해 보기 (/robots.txt)
② 가설: "쿠키 이름이 role이네. 권한 문제겠다" / "주석이 있네. 소스 문제겠다"
③ 실험: 쿠키 값 디코딩·변경, 입력창에 시험 값, Burp로 요청 재전송
④ 플래그: DH{...} 발견 → 문제 페이지에 제출 → 기록
문제가 안 풀리면 ①으로 돌아가는 것이 정석입니다. "정찰에서 놓친 것이 있다"가 막힘의 원인 1위입니다.
3-5. Write-up 첫 작성
문제를 풀었다면, 기억이 뜨거울 때 바로 씁니다. 개인 위키에 dreamhack-001.md를 만들고 이 형식으로:
# 문제: (제목) — 난이도 ★
- **상황**: (문제 설명 한 줄 요약)
- **정찰에서 본 것**: (소스·쿠키·폼 중 눈에 띈 것)
- **가설**: (어떤 취약점이라고 생각했는가)
- **시도한 것**: (실제로 한 것, 실패 포함)
- **성공한 페이로드**: (결정적 한 수)
- **배운 점**: (다음에 바로 쓸 한 문장)
"시도한 것"에 실패를 꼭 적으세요. 실패 목록은 다음 문제에서 "이건 해봤다"를 지워 주는 자산입니다. 플래그 문자열 자체는 공개 장소에 올리지 않는 것이 CTF 예의입니다 — 개인 위키에만 보관하세요.
4. 미션과 연습문제
미션 — 첫 8문제와 기록 습관
- 로컬 랩(
firstprob_lab.py)에서 소스 속 플래그와 쿠키 값을 전부 찾아냅니다 - 드림핵에 가입하고 웹 카테고리 난이도 1~2 문제 8개를 해결합니다
- 8문제 모두에서 정찰 → 가설 → 실험 → 플래그 네 박자를 메모에 남깁니다
- 그중 2문제는 3-5 형식의 Write-up으로 완성합니다
연습문제
문제 1. 플래그가 "증거" 역할을 하는 이유를 설명해 보세요. 왜 화면 캡처로는 부족한가요?
문제 2. 브라우저 화면에는 보이지 않는데 HTML 소스에는 있는 정보는 어떻게 존재할 수 있나요? 3-1의 실측을 근거로 설명해 보세요.
문제 3. 쿠키 값을 만났을 때의 3갈래 판별 순서(Base64? JWT? 원시 조작?)를 각각의 식별 특징과 함께 써 보세요.
문제 4. 문제가 안 풀릴 때 사이클의 어느 박자로 돌아가야 하는지, 그 이유와 함께 설명해 보세요.
5. 모범 답안과 완료 기준
미션 모범 답안
1번의 로컬 랩 정답 (2026-09-09 실측): 소스 주석의 flag{view_source_is_free}, 쿠키 session_role=Z3Vlc3Q= → guest. 찾는 절차는 3-1·3-2 그대로입니다.
2~3번은 플랫폼에서 진행합니다. 8문제의 메모가 이런 모양이면 합격입니다 (화면 예시):
문제 A — 정찰: index 페이지 + 쿠키 role=guest 발견 / 가설: 쿠키 조작
/ 실험: role을 admin으로 변경 후 새로고침 / 플래그: 제출 완료
4번 Write-up의 "가설" 칸이 비어 있으면 사이클 ②를 건너뛴 것입니다 — 되돌아가서 채우세요.
검증하는 법: ① 드림핵 프로필의 해결 수가 8 이상인가. ② Write-up 2편에 "성공한 페이로드"가 구체적으로 적혀 있는가. ③ 각 문제 메모에 네 박자가 전부 있는가.
연습문제 해답
문제 1 해답. 플래그는 서버 안에 숨겨진 문자열 자체이므로, 그것을 제시했다는 것은 서버의 비밀 지점에 실제로 도달했다는 유일한 증거입니다. 화면 캡처는 조작·모방이 가능하지만, DH{...} 안의 무작위 문자열은 해당 서버를 뚫지 않고는 알 수 없습니다.
문제 2 해답. 서버는 HTML 문서 전체를 보내고, 브라우저는 그중 화면에 그릴 부분만 렌더링합니다. <!-- --> 주석은 "그리지 말라"는 표시일 뿐 전송은 됩니다. 3-1 실측에서 화면에는 없는 관리자 메모가 소스에는 그대로 있었습니다 — 화면은 서버 응답의 부분집합입니다.
문제 3 해답. ① 끝이 =이거나 영숫자++// 조합이면 Base64 후보 → 디코딩해 의미를 읽는다. ② 점(.)이 두 개면 JWT → Step 150의 해부 절차. ③ 둘 다 아니면 role=guest 같은 평문 → 값 자체를 바꿔 실험한다. 식별 특징은 각각 "패딩 문자", "점 두 개", "읽히는 평문"입니다.
문제 4 해답. 정찰(①)로 돌아갑니다. 난이도 1~2 문제의 막힘은 대부분 "못 푸는 것"이 아니라 "안 본 것" — 읽지 않은 JS 파일, 확인하지 않은 쿠키, 시도하지 않은 경로(robots.txt 등)가 원인입니다. 실험을 더 하는 것은 가설이 맞을 때만 유효합니다.
완료 기준 체크리스트
- [ ] 드림핵 문제 페이지의 구성 요소(제목·설명·서버 주소·제출 칸)를 안다
- [ ]
DH{...}플래그 형식을 알고 제출할 수 있다 - [ ] 개발자 도구로 HTML 주석 속 숨은 정보를 찾았다
- [ ] 쿠키 값을 Base64로 해독하고 변경 실험을 했다
- [ ] 정찰 → 가설 → 실험 → 플래그 사이클을 순서대로 적용했다
- [ ] 문제 서버 주소가 일회성·만료됨을 이해한다
- [ ] 미션: 웹 문제 8개 해결 + Write-up 2편 완성
6. 흔한 실수와 해결
벽 1. 문제 서버에 접속하면 연결이 안 된다
증상 (출력 예시):
이 사이트에 연결할 수 없음 / ERR_CONNECTION_TIMED_OUT
원인: 배포된 임시 서버가 만료됐거나, 회사·학교 네트워크가 특이 포트를 막고 있습니다.
해결: 문제 페이지에서 서버를 새로 배포받으세요. 그래도 안 되면 다른 네트워크(모바일 핫스팟 등)에서 시도합니다. 주소 앞의 http://와 포트 번호가 정확한지도 확인하세요.
벽 2. 소스를 봤는데 주석이 없다
원인: Elements 탭만 보고 Network의 원본 응답을 안 본 것일 수 있습니다. 프레임워크가 나중에 DOM을 바꾸면 처음 소스와 달라집니다.
해결: Ctrl+U(소스 보기)로 "서버가 보낸 그대로"를 보세요. JS 파일은 Sources 탭에서 하나씩 열고 Ctrl+F로 flag, DH{, TODO를 검색합니다.
벽 3. 쿠키를 바꿨는데 변화가 없다
원인: 쿠키를 고친 뒤 페이지를 새로고침하지 않았거나, 서버가 쿠키를 검증하고(서명된 세션) 있거나, 값 형식이 틀렸습니다.
해결: ① Application 탭에서 값 변경 후 반드시 새로고침 ② Base64로 바꾼 값이면 다시 인코딩했는지 확인 ③ 그래도 안 되면 "서버가 검증한다"가 가설 — Step 150의 서명 개념으로 넘어가세요.
벽 4. 플래그를 찾았는데 제출하면 틀렸다고 나온다
원인: 앞뒤 공백이 같이 복사됐거나, DH{와 }가 빠졌거나, 비슷한 문자(0과 O, 1과 l)를 잘못 옮겼습니다.
해결: 플래그는 DH{로 시작해 }로 끝나는 통째 복사가 원칙입니다. 메모장에 붙여 공백을 확인한 뒤 제출하세요.
벽 5. 30분째 아무 단서도 못 찾았다
원인: 난이도를 높게 골랐거나, 아직 안 배운 기법의 문제일 수 있습니다.
해결: ① 문제 페이지의 힌트와 풀이 수를 보세요 — 풀이 수가 적으면 어려운 문제입니다 ② 난이도 1로 내려가세요 ③ 그래도 막히면 기록해 두고 다음 문제로. 실패도 Write-up의 재료입니다.
7. 정리
오늘의 개념
| 개념 | 한 줄 설명 |
|---|---|
| CTF | 플래그를 찾아 제출하는 해킹 문제 풀이 대회·연습 형식 |
| 플래그 | 서버에 숨겨진 정답 문자열 — 드림핵은 DH{...} |
| 배포 서버 | 문제마다 발급되는 일회성 연습 서버 (만료됨) |
| 풀이 사이클 | 정찰 → 가설 → 실험 → 플래그 |
| Write-up | 상황·시도·페이로드·배운 점을 적는 풀이 기록 |
| 화면 ⊂ 소스 | 화면은 서버 응답의 일부 — 소스 보기가 정찰의 기본 |
오늘의 명령어·도구
| 명령·도구 | 하는 일 |
|---|---|
Ctrl+U / Elements 탭 |
서버가 보낸 HTML 원본 읽기 |
| Application 탭 → Cookies | 쿠키 확인·수정 |
base64.b64decode("...") |
쿠키 값 해독 |
/robots.txt 접속 |
숨겨진 경로 힌트 찾기 |
문제 페이지 서버 접속하기 |
일회성 연습 서버 발급 |
명령어보다 중요한 감각
실전 문제를 만나면 손을 멈추고 먼저 읽으세요. 난이도 1 문제의 플래그는 "공격해서 나오는 것"이 아니라 "철저히 읽으면 보이는 것"입니다. 그리고 기록은 미래의 나에게 보내는 편지입니다 — 오늘 푼 문제는 한 달 뒤의 내가 다시 못 풀 수 있습니다. Write-up 한 편이 그 재발을 막습니다. 마지막으로: 막히는 것은 실패가 아니라 다음 문제 선택의 정보입니다.
전부 체크되면 Step 151 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.