Step 199. Webhacking.kr 1~15 — 국내 워게임 종합 선물 세트
Level 3 — CTF 실전과 공격 스킬 심화 | 난이도 ★★★☆☆ | 예상 소요 시간 6시간(이틀 권장)
전제: Step 132~150 구간의 웹 기법(SQLi, XSS, 쿠키·세션, Burp Suite)을 익혔고, 드림핵 웹 입문 문제를 풀어 봤다.
- 준비물: 웹 브라우저와 개발자 도구(F12), Burp Suite(또는 브라우저 쿠키 편집 기능), 인코딩 변환용 파이썬, 시도 기록용 메모장.
- ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
- 합법 연습장 안내: 오늘의 무대인 Webhacking.kr(
webhacking.kr)은 운영자가 공식적으로 풀이를 개방한 국내 최고(最古)의 웹 워게임입니다. 이 사이트의 문제 서버 외에는 오늘의 기술을 쓰지 않습니다.
지금까지 웹 기법을 하나씩 배웠다면, 이제는 종합 선물 세트를 풀 차례입니다. Webhacking.kr은 문제마다 "그래 이거지" 싶은 트릭이 숨어 있는 워게임으로 유명합니다. 1~15번 구간은 기초~중급이라 지금 실력으로 도전할 수 있지만, 상식 밖의 한 끗을 요구하는 문제가 섞여 있어 방향 감각을 단련하기에 좋습니다.
오늘의 목표는 점수가 아닙니다. 처음 보는 문제 앞에서도 흔들리지 않는 공통 루틴 — 소스 보기, 주석·숨김 파일 확인, 파라미터 조작, 필터 우회 실험 — 을 몸에 붙이는 것입니다. 외부 플랫폼이라 서버 화면은 출력 예시로 표기하고, 배운 기법 중 로컬에서 복습 가능한 부분은 실측으로 확인합니다.
1. 학습 목표
이 챕터를 끝내면 다음을 할 수 있습니다:
- Webhacking.kr의 문제 목록 구조(점수·풀이 수)를 읽고 난이도를 가늠한다
- 웹 문제 공통 루틴(소스 보기 → 주석/숨김 파일 → 파라미터 조작 → 필터 우회)을 순서대로 적용한다
- 쿠키 값 조작, 인코딩 사슬 풀기 등 1~15번 구간의 대표 유형을 분류하고 접근 전략을 세운다
- 막힌 문제에 "시도한 페이로드 목록"을 남기고 다음 문제로 넘어가는 리듬을 유지한다
- 푼 문제마다 한 줄 요점을 정리해 기법 목록을 갱신한다
2. 배경 지식 — 오늘의 도구와 개념
오늘의 도구 한눈에 보기
| 구분 | 내용 |
|---|---|
| 언어·환경 | 웹 브라우저 + 개발자 도구, Burp Suite, 파이썬(인코딩 변환) |
| 오늘의 기법 | 소스 보기, 주석·숨김 파일 찾기, 쿠키·파라미터 조작, 인코딩 사슬 해독, 필터 우회 |
| 필요한 개념 | base64·URL·hex 인코딩 구별, 쿠키의 구조(이름=값), 클라이언트 측 검사의 한계 |
| 오늘의 산출물 | 1~15번 풀이 기록 + 문제당 한 줄 요점 정리 |
2-1. Webhacking.kr의 구조 읽기
Webhacking.kr은 문제 번호제입니다. 각 문제에 점수와 풀이 수가 붙어 있어, 이 두 숫자가 난이도의 신호입니다. 점수가 낮고 풀이 수가 많은 문제는 입문용, 점수가 높고 풀이 수가 적은 문제는 트릭이 꼬인 문제입니다.
문제를 풀면 인증 키(auth key) 형태의 정답을 제출해 점수를 얻는 구조입니다. 정답 문자열 자체보다 "어떻게 그 문자열에 도달했는가"가 오늘의 기록 대상입니다.
2-2. 웹 문제 공통 루틴 — 네 박자
어떤 번호든 처음 여는 순간의 동선은 정해져 있습니다.
① 소스 보기 — Ctrl+U로 HTML 전체를 읽는다 (주석, 숨겨진 입력, JS 링크)
② 숨김 자원 확인 — robots.txt, .git, 백업 파일(.bak, ~) 같은 단골 경로
③ 파라미터 조작 — URL의 ?id=1 같은 값을 바꿔 본다
④ 필터 우회 실험 — 막히는 문자가 보이면 우회 후보를 다섯 가지 이상 시도한다
Bandit에서 배운 "읽기 → 탐색 → 검증 → 기록"의 웹 버전입니다. 힌트 없이 명령부터 치는 버릇은 여기서도 시도를 난사로 만듭니다.
2-3. 1~15번 구간의 유형 지도
이 구간에서 전형적으로 만나는 유형을 분류해 두면 문제를 열었을 때 "어느 서랍의 문제인가"를 빠르게 판별할 수 있습니다.
| 유형 | 신호 | 첫 시도 |
|---|---|---|
| 소스·주석 읽기 | 화면은 단순한데 소스에 주석이 있다 | 주석 속 힌트·숨겨진 필드 찾기 |
| 쿠키 조작 | 로그인 없이 "관리자만" 메시지 | 쿠키 값을 바꿔 재요청 |
| 인코딩 사슬 | 알 수 없는 문자열이 답의 재료 | base64/hex/URL 중 무엇인지 판별 후 해독 |
| 클라이언트 측 검사 | JS가 "안 돼"라고 막는다 | 검사는 브라우저에 있으니 우회 가능 — Burp로 직접 전송 |
| 기초 SQLi | id 파라미터, 로그인 폼 | ', ' OR 1=1-- 계열 시도 |
| 필터 우회 | 특정 문자가 지워지거나 막힌다 | 대소문자 혼합, 이중 입력, 인코딩 우회 |
2-4. 클라이언트 측 검사는 검사가 아니다
자바스크립트로 "관리자만 접근 가능합니다"라고 막는 문제는 이 구간의 단골입니다. 그 검사 코드는 여러분의 브라우저에서 실행됩니다. 내 컴퓨터에서 도는 검사는 내가 끌 수 있습니다 — 개발자 도구에서 JS를 끄거나, Burp Suite로 검사를 거치지 않는 요청을 직접내면 됩니다.
"화면이 막았다"가 아니라 "어느 층에서 막았는가"를 보는 눈이 핵심입니다. 서버에서 막힌 것(403 응답)과 브라우저에서 막힌 것(alert)은 전혀 다른 벽입니다.
3. 따라 하기
3-1. 가입과 문제 목록 파악
webhacking.kr에 접속해 회원가입을 하고 문제 목록을 엽니다 (서버 화면은 출력 예시 — 접속은 여러분이 직접):
[출력 예시 — 문제 목록 화면의 모습]
Challenge 01 100 pts solved by 5,xxx
Challenge 02 150 pts solved by 2,xxx
...
Challenge 15 300 pts solved by 8xx
읽는 법: 풀이 수가 천 단위면 상식 선에서 풀리는 문제, 백 단위로 떨어지면 트릭이 있는 문제입니다. 1번부터 순서대로 가되, 각 문제에 30분~1시간의 상한을 정하세요. 한 문제에 반나절을 태우는 것보다 열다섯 문제를 두 바퀴 도는 것이 이 구간에서는 낫습니다.
3-2. 유형 A — 소스와 주석 읽기 (출력 예시)
문제를 열자마자 Ctrl+U로 소스를 봅니다. 이 구간의 첫 문제들은 소스에 답의 실마리가 놓여 있는 경우가 많습니다.
<!-- 출력 예시 — 문제 소스에서 발견하는 단서들 -->
<!-- TODO: 임시로 만들어 둔 관리자 페이지 admin_temp.php 는 나중에 지우자 -->
<input type="hidden" name="debug" value="0">
읽는 법: 주석의 파일명, hidden 입력의 value, 주석 처리된 링크 — 셋이 소스 읽기 유형의 단골 단서입니다. debug=0을 1로 바꿔 보는 것이 첫 실험이 됩니다.
3-3. 유형 B — 쿠키 값 조작
"관리자만 볼 수 있습니다"류 문제에서 개발자 도구(Application 탭)나 Burp로 쿠키를 열어 보면 값이 평문인 경우가 있습니다. 쿠키의 구조와 조작은 로컬에서 그대로 복습됩니다 (2026-09-09 파이썬 3.12.14 실측):
cookie = "user=guest; role=user; time=1700000000"
kv = dict(p.split("=", 1) for p in cookie.split("; "))
kv["role"] = "admin"
forged = "; ".join(f"{k}={v}" for k, v in kv.items())
print(forged)
원본 쿠키: user=guest; role=user; time=1700000000
조작 쿠키: user=guest; role=admin; time=1700000000
읽는 법: 쿠키는 이름=값을 세미콜론으로 이은 문자열일 뿐입니다. 서버가 이 문자열을 그대로 믿는다면, 값을 바꿔 끼우는 것만으로 신분이 바뀝니다. 워게임에서 role, admin, level 같은 이름의 쿠키가 보이면 값을 바꿔 재전송하는 것이 정석입니다.
왜: 쿠키는 클라이언트가 보관하고 클라이언트가 보내는 데이터입니다. "사용자가 보관하는 증명서"는 사용자가 위조할 수 있다 — 이 명제가 세션 공격 전체의 뿌리입니다 (Step 134 복습).
3-4. 유형 C — 인코딩 사슬 풀기
답의 재료로 낯선 문자열이 주어지는 유형입니다. 첫 작업은 판별입니다. base64는 영대소문자+숫자++/에 끝의 = 패딩, hex는 0~9와 a~f만, URL 인코딩은 % 기호가 신호입니다. 로컬 실측으로 확인합니다 (2026-09-09 파이썬 3.12.14 실측):
import base64, urllib.parse
double = "Wm14aFp5MXNhV3RsTFhSbGVIUT0=" # 이중 인코딩 예시
once = base64.b64decode(double).decode()
twice = base64.b64decode(once).decode()
print(once) # ZmxhZy1saWtlLXRleHQ=
print(twice) # flag-like-text
1회 디코딩: ZmxhZy1saWtlLXRleHQ=
2회 디코딩: flag-like-text
판별이 애매할 때는 실제로 디코딩해 보고 "읽히는 문자열이 나오는가"로 검증합니다 (2026-09-09 실측):
'aGVsbG8=': base64 디코딩 가능 -> b'hello' (읽히는 문자열: True)
'hello123': base64 디코딩 가능 -> b'\x85\xe9e\xa3]\xb7' (읽히는 문자열: False)
'68656c6c6f': base64 아님 — hex로 해석하면 b'hello'
읽는 법: 'hello123'처럼 base64로도 "디코딩은 되는" 문자열이 함정입니다. 디코딩됐다가 아니라 읽히는가가 판별 기준입니다. 한 번 풀었는데 또 =로 끝나는 문자열이 나오면 이중 인코딩입니다 — 끝날 때까지 반복합니다.
3-5. 유형 D — 필터 우회의 사고 과정 (출력 예시)
SQLi나 명령 입력란에 넣은 페이로드가 일부 지워져 돌아오는 문제입니다. 핵심은 "막힌 문자의 목록"을 먼저 확보하는 것입니다.
[출력 예시 — 필터 탐지 과정]
입력: ' OR 1=1-- → 결과: 11 (공백, OR, 따옴표 소거 의심)
입력: '||1=1# → 결과: 정상 반영 (|| 와 # 는 통과)
읽는 법: 필터가 보이면 우회 후보를 다섯 가지 이상 표로 적고 하나씩 소거합니다. 대소문자 혼합(Or), 같은 문자 두 번(OORR → 안쪽 OR만 지워지면 OR이 남음), URL 이중 인코딩, 주석 대체(-- 대신 #)가 단골 후보입니다. 그리고 이 사이트의 문제들은 문제 제목이나 번호 자체가 힌트인 경우가 있습니다 — 상식 밖의 한 끗이 막히면 화면 밖의 정보를 다시 보세요.
3-6. 막혔을 때의 리듬 — 시도 목록과 시간 상한
한 문제에 매달리지 않기 위한 장치 두 개입니다.
첫째, 시도한 페이로드 목록을 남깁니다.
[문제 07 시도 기록]
- ' OR 1=1-- → 필터에 걸림
- ' || 1=1# → 통과, 그러나 결과 없음
- 소스 재검토 → JS 파일 별도 존재 확인 중 (여기서 중단, 1시간 경과)
둘째, 상한(30분~1시간)이 오면 미련 없이 다음 문제로 갑니다. 시도 기록이 있으면 나중에 돌아왔을 때 같은 길을 다시 걷지 않습니다.
3-7. 한 줄 요점 정리
푼 문제마다 한 줄씩 적습니다.
[출력 예시 — 요점 정리 형식]
01: 소스 주석에 임시 페이지 경로 — 소스 보기가 첫 명령
02: 쿠키의 time 값 조작 — 쿠키는 내가 쓰는 문자열
05: 이중 base64 — 디코딩 결과가 또 인코딩 문자열
09: JS 검사 우회 — 막는 코드가 내 브라우저에 있다
이 표가 쌓이면 "나는 어떤 유형에서 시간을 쓰는가"가 보입니다. 그것이 Step 200의 약점 분석과 이어지는 재료입니다.
4. 미션과 연습문제
미션 — 1~15번 공략과 요점 표
- Webhacking.kr에 가입하고 문제 목록에서 점수·풀이 수로 난이도 지도를 그립니다
- 1번부터 순서대로 도전하되 문제당 30분~1시간 상한을 지킵니다
- 모든 문제에 공통 루틴 네 박자를 적용하고, 막힌 문제는 시도한 페이로드 목록을 남기고 넘어갑니다
- 1~15번 중 12개 이상을 해결하고, 푼 문제마다 한 줄 요점을 적은 표를 완성합니다
연습문제
문제 1. 웹 문제 공통 루틴 네 박자를 순서대로 말하고, 각 단계에서 "발견하면 유력한 단서"를 하나씩 들어 보세요.
문제 2. 문자열 68656c6c6f가 주어졌습니다. base64와 hex 중 무엇으로 해석해야 하며, 그 판별 근거는 무엇인가요?
문제 3. 자바스크립트가 "관리자만 접근 가능합니다"라는 alert를 띄우며 막는 문제에서, 우회가 가능한 이유를 "검사 코드가 실행되는 장소"의 관점에서 설명해 보세요.
문제 4. 막힌 문제를 넘길 때 "시도한 페이로드 목록"을 남기라고 했습니다. 이 기록이 없으면 재도전할 때 어떤 낭비가 생기나요?
5. 모범 답안과 완료 기준
미션 모범 답안
완성된 요점 표의 예시입니다 (문제별 실제 답은 여러분의 풀이로 채웁니다):
[Webhacking.kr 1~15 요점 표 — 예시 형식]
번호 | 결과 | 유형 | 한 줄 요점
01 | 해결 | 소스/주석 | 주석 속 임시 경로가 입구
02 | 해결 | 쿠키 | time 값을 미래로 조작
03 | 해결 | 인코딩 | 이중 base64, 두 번 풀기
...
07 | 보류 | 필터 우회 | || 와 # 는 통과 확인, 재도전 예정
검증하는 법: ① 해결 칸이 12개 이상인가. ② 각 줄의 요점이 "기법 이름 + 핵심 트릭" 형태로 읽는 사람이 재현할 수 있게 적혔는가. ③ 보류 문제에 시도 목록이 붙어 있는가. ④ 정답 문자열 자체는 기록에서 뺐는가(워게임 예의 — 사고 과정만 남깁니다).
연습문제 해답
문제 1 해답. ① 소스 보기(주석·hidden 필드), ② 숨김 자원 확인(robots.txt, .bak), ③ 파라미터 조작(URL의 id 값 변경), ④ 필터 우회 실험(막힌 문자 목록 작성 후 후보 소거). 각 단계는 앞 단계의 정보를 받아 좁혀 가는 구조입니다.
문제 2 해답. hex입니다. 0~9와 a~f만으로 구성되어 있고 길이가 짝수이며, bytes.fromhex로 해석하면 hello가 됩니다 (3-4 실측에서 확인). base64는 대소문자가 섞이고 = 패딩이 붙는 경우가 많아 겉모습부터 다릅니다. 겉모습이 애매하면 직접 디코딩해 보고 "읽히는 문자열인가"로 검증합니다.
문제 3 해답. 그 검사 코드는 여러분의 브라우저에서 실행됩니다. 내 컴퓨터에서 도는 코드는 내가 통제할 수 있으므로, 개발자 도구로 JS를 끄거나 Burp Suite로 검사를 거치지 않는 요청을 직접내면 우회됩니다. 서버가 스스로 검사하지 않는 한 클라이언트 측 검사는 안내판일 뿐입니다.
문제 4 해답. 같은 페이로드를 다시 시도하는 낭비가 생깁니다. 레이스가 아닌 이상 어제 실패한 페이로드는 오늘도 실패합니다. 시도 목록이 있으면 "어디까지가 확인된 실패인가"가 보여서 새 시도만 쌓을 수 있고, 힌트를 보게 되더라도 내 사고가 어디서 꺾였는지 대조할 수 있습니다.
완료 기준 체크리스트
- [ ] Webhacking.kr 문제 목록에서 점수·풀이 수로 난이도를 가늠할 수 있다
- [ ] 공통 루틴 네 박자(소스 보기 → 숨김 자원 → 파라미터 조작 → 필터 우회)를 순서대로 적용한다
- [ ] 쿠키가
이름=값문자열이며 조작 가능함을 설명할 수 있다 - [ ] base64/hex/URL 인코딩을 겉모습으로 판별하고 파이썬으로 해독할 수 있다
- [ ] 클라이언트 측 검사와 서버 측 검사를 구분해 설명할 수 있다
- [ ] 막힌 문제에 시도 목록을 남기고 상한 내에 다음 문제로 넘어갔다
- [ ] 미션: 1~15번 중 12개 이상 해결 + 요점 표 완성
6. 흔한 실수와 해결
벽 1. 회원가입이나 접속 자체가 안 된다
증상: 사이트가 열리지 않거나 가입 메일이 오지 않습니다.
원인: 오래된 워게임이라 점검 중이거나, 스팸함으로 메일이 갔거나, 회사·학교 네트워크가 막은 경우가 있습니다.
해결: 다른 네트워크(테더링 등)에서 시도하고, 스팸함을 확인하세요. 사이트 자체가 점검 중이면 그 기간은 드림핵·PortSwigger로 같은 유형 훈련을 대체합니다 — 오늘의 목표는 번호가 아니라 루틴이니까요.
벽 2. 분명 맞는 답을 넣었는데 틀렸다고 나온다
증상: 인증 키를 제출했는데 계속 오답 처리됩니다.
원인: 복사 과정의 앞뒤 공백, 대소문자 오기, 줄바꿈 포함이 대부분입니다.
해결: 제출 전에 키를 메모장에 붙여 육안으로 확인하세요. 공백이 섞인 것 같으면 파이썬 strip()으로 정리한 뒤 다시 제출합니다.
벽 3. 필터 우회에서 후보가 두세 개 만에 마른다
증상: 대소문자 혼합과 인코딩 한 번을 시도하고 손을 듭니다.
원인: 우회 후보의 서랍이 얕습니다.
해결: 후보를 표로 만드세요. 문자 단위(대소문자, 이중 입력, 인코딩), 구문 단위(||, #, /**/ 공백 대체), 전송 단위(URL 이중 인코딩, 파라미터 위치 변경)의 세 층으로 나누면 층마다 서너 개씩, 열 개가 넘는 후보가 나옵니다. 다섯 가지 미만에서 포기하는 것은 이 구간에서는 이릅니다.
벽 4. 한 문제에 반나절을 태운다
증상: 7번 하나에 하루를 쓰고 진도가 멈춥니다.
원인: "이번엔 될 것 같은데"의 함정 — 근거 없는 재시도의 반복입니다.
해결: 상한을 타이머로 물리화하세요(30분~1시간). 넘길 때는 반드시 시도 목록을 남깁니다. 이 사이트 문제는 "상식 밖의 한 끗"이 답인 경우가 많아, 붙잡는 시간이 아니라 보는 각도가 해결합니다.
벽 5. 풀이를 보고 "아 그거였어?"만 남는다
증상: 막혀서 검색한 풀이를 보고 넘어갔는데 손에 남는 것이 없습니다.
원인: 정답만 보고 사고 경로를 복기하지 않았기 때문입니다.
해결: 풀이를 봤다면 반드시 "나는 왜 이걸 못 떠올렸나"를 한 줄 적고, 그 문제는 일주일 뒤에 다시 풉니다. 재풀이에 성공해야 그 문제가 내 것이 됩니다.
7. 정리
오늘의 개념
| 개념 | 한 줄 설명 |
|---|---|
| Webhacking.kr | 운영자가 공식 개방한 국내 웹 워게임 — 번호제 + 인증 키 제출 |
| 공통 루틴 | 소스 보기 → 숨김 자원 → 파라미터 조작 → 필터 우회 |
| 쿠키 조작 | 클라이언트 보관 문자열을 바꿔 끼우는 신분 위조 |
| 인코딩 사슬 | base64/hex/URL이 겹쳐진 답 재료 — 판별 후 끝까지 해독 |
| 클라이언트 측 검사 | 내 브라우저에서 도는 검사 — 끄거나 우회할 수 있다 |
| 시도 목록 | 확인된 실패의 지도 — 재도전의 출발선 |
오늘의 기법과 도구
| 기법·도구 | 하는 일 |
|---|---|
Ctrl+U / 개발자 도구 |
소스·쿠키·네트워크 요청 열람 |
| Burp Suite Repeater | 검사를 거치지 않는 요청 직접 전송 |
base64.b64decode() |
base64 해독 (끝의 =가 신호) |
bytes.fromhex() |
hex 문자열 해독 (0~9·a~f만으로 구성) |
urllib.parse.unquote() |
URL 인코딩(%27 등) 복원 |
| 우회 후보 표 | 문자·구문·전송 세 층의 후보를 소거하는 장부 |
명령어보다 중요한 감각
이 사이트의 문제들은 정직한 실력과 별개로 "상식 밖의 한 끗"을 요구할 때가 있습니다. 필터가 보이면 우회를 다섯 가지 이상 시도하고, 그래도 안 되면 문제 제목과 번호, 화면 구석의 문구까지 의심하세요. 그리고 답이 안 나올 때 붙잡는 시간이 아니라 바꾸는 각도가 해결한다는 것 — 상한을 정하고 기록을 남기고 넘어가는 리듬 자체가 실력입니다. 쌓인 요점 표가 Step 200의 심화 구간에서 여러분의 지도가 됩니다.
전부 체크되면 Step 199 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.