Step 200. Webhacking.kr 16~30 — 기법의 창의적 조합

Step 200. Webhacking.kr 16~30 — 기법의 창의적 조합

Level 3 — CTF 실전과 공격 스킬 심화 | 난이도 ★★★★☆ | 예상 소요 시간 8시간(이틀~사흘 권장)

전제: Step 199를 마쳤다. Webhacking.kr 1~15 구간의 유형 분류와 공통 루틴이 몸에 붙어 있다.

  • 준비물: Step 199와 같은 도구(브라우저 개발자 도구, Burp Suite, 파이썬) + Step 199에서 만든 요점 표, 우회 후보 장부.
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
  • 합법 연습장 안내: Webhacking.kr은 운영자가 공식적으로 풀이를 개방한 합법 워게임입니다. 이 사이트의 문제 서버 외에는 오늘의 기술을 쓰지 않습니다.

16번부터는 게임의 성질이 바뀝니다. 단일 기법으로 풀리는 문제가 줄고, 인코딩 우회 + SQLi, 파일 업로드 + 세션 조작처럼 기법의 조합을 요구하는 문제가 늘어납니다. 한 개의 열쇠가 아니라 두 개의 열쇠를 순서대로 돌려야 하는 자물쇠입니다.

이 구간의 진짜 수업은 "이 문제는 뭘 물어보는 걸까"를 출제자 관점에서 읽는 훈련입니다. 보이는 방어를 먼저 정리하고, 우회 후보를 표로 만들어 소거해 나가는 작업 — 그 장부가 쌓일수록 처음 보는 문제의 답이 보이기 시작합니다. 외부 플랫폼이라 서버 화면은 출력 예시로 표기하고, 로컬에서 검증 가능한 기법은 실측으로 확인합니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • 문제의 "보이는 방어"를 목록화하고 우회 후보를 표로 만들어 소거한다
  • 두 가지 이상의 기법이 겹친 문제를 단계별로 분해해 공략한다
  • 커뮤니티 힌트를 정답이 아닌 방향성 수준까지만 활용한다
  • 풀이를 본 문제를 "왜 그게 답인지" 재현하고 일주일 뒤 재풀이한다
  • 16~30 구간 완주 후 Web 트랙 약점 유형을 갱신한다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 웹 브라우저 + 개발자 도구, Burp Suite(Repeater 중심), 파이썬(페이로드 생성·인코딩)
오늘의 기법 방어 목록화, 우회 후보 표 소거법, 기법 조합 분해, 힌트의 단계적 사용
필요한 개념 필터 우회의 세 층(문자·구문·전송), 세션과 인증의 결합, 출제자 관점 읽기
오늘의 산출물 16~30 풀이 기록 + 약점 유형 갱신표

2-1. 조합 문제의 해부 — 자물쇠가 두 개다

16~30 구간의 문제를 열면 하나의 기법이 통하지 않는 경험을 하게 됩니다. 예를 들어 인젝션은 되는데 필터가 절반을 지우고, 필터는 우회했는데 결과가 화면에 안 나오는 식입니다. 이것은 기법이 틀린 것이 아니라 문제가 두 층이라는 뜻입니다.

조합 문제의 정석 절차는 분해입니다. "1층: 무엇이 막고 있는가 → 2층: 1층을 통과하면 무엇이 기다리는가"를 나눠서 각각 독립된 문제로 풉니다. 한꺼번에 두 층을 뚫으려는 시도가 이 구간 최대의 시간 도둑입니다.

2-2. 보이는 방어 목록화와 소거법

문제를 열었을 때 첫 산출물은 코드가 아니라 방어 목록입니다.

[출력 예시 — 방어 목록과 우회 후보 표]
관찰된 방어:
  1. 따옴표(') 입력 시 소거됨
  2. 공백이 %20으로도 막힘
  3. 응답에 결과가 안 보임 (성공/실패 신호만)

우회 후보:
  후보                          | 1층 통과? | 비고
  큰따옴표(") 대체              | X         | 같은 필터에 걸림
  백슬래시 이스케이프(\')        | X         | 역슬래시도 소거
  /**/ 공백 대체                 | O         | 1층 통과 확인 → 2층으로

이 표의 가치는 "아직 안 해 본 것"이 한눈에 보인다는 데 있습니다. 막힌 것 같을 때 실제로는 후보가 남아 있는 경우가 대부분입니다.

2-3. 출제자 관점 읽기

"이 문제는 뭘 물어보는 걸까"를 뒤집어 읽는 훈련입니다. 출제자는 보통 하나의 개념을 묻습니다. 문제가 준 재료 — 폼의 개수, 파라미터의 이름, 에러 메시지의 어조, 문제 제목 — 를 나열하면 묻고 싶은 개념이 윤곽을 드러냅니다.

로그인 폼과 파일 업로드가 함께 있다면 업로드된 파일로 인증을 우회하는 그림이고, 세션 발급과 권한 확인이 다른 페이지에 나뉘어 있다면 그 사이의 간극을 묻는 문제입니다. 재료의 목록이 곧 문제의 설계도입니다.

2-4. 힌트의 단계적 사용

이 구간부터는 커뮤니티 힌트의 존재를 인정하되, 사용 단계를 정해 둡니다.

1단계: 문제 번호 + "유형" 수준까지만 검색 (예: "17번 SQLi 계열인가")
2단계: 그래도 막히면 "방향" 수준까지 (예: "쿠키를 봐라")
3단계: 정답 페이로드는 보지 않는다 — 봤다면 그 문제는 독립 해결이 아니다

정답을 본 문제는 "왜 그게 답인지"를 직접 재현으로 증명하고, 일주일 뒤 재풀이에 성공해야 비로소 푼 것으로 셉니다. 이 규칙은 Step 202의 독립 해결 점검에서 그대로 적용됩니다.


3. 따라 하기

3-1. 구간 진입 전 — Step 199 요점 표 복기

16번을 열기 전에, 지난 챕터에서 만든 요점 표를 훑습니다. 1~15에서 만난 유형(소스 읽기, 쿠키, 인코딩, 필터 우회)이 16~30에서는 재료로 재배치됩니다. "아는 기법의 목록"이 곧 이 구간의 탄약고입니다.

3-2. 조합 문제 시연 — 인코딩 우회 + 인젝션 (출력 예시 + 로컬 실측)

필터가 ' OR 1=1의 공백과 따옴표를 막는 문제를 가정합니다. 페이로드를 그대로 넣는 대신 인코딩 층을 씌우는 사고 과정입니다. 페이로드 변환 자체는 로컬에서 그대로 재현됩니다 (2026-09-09 파이썬 3.12.14 실측):

import urllib.parse
raw = "' OR 1=1-- "
urlenc = urllib.parse.quote(raw)
print(f"원본 페이로드: {raw!r}")
print(f"URL 인코딩: {urlenc}")
print(f"복원 확인: {urllib.parse.unquote(urlenc)!r}")
원본 페이로드: "' OR 1=1-- "
URL 인코딩: %27%20OR%201%3D1--%20
복원 확인: "' OR 1=1-- "

읽는 법: 필터가 URL 디코딩 "전에" 검사한다면 인코딩된 형태가 필터를 지나 서버에서 복원됩니다. 반대로 필터가 디코딩 후에 검사한다면 이중 인코딩(%2527)을 시도합니다. 어느 층에서 검사하는지가 우회 방향을 결정합니다 — 그래서 2-2의 방어 목록에 "검사 층" 추정을 함께 적습니다.

3-3. 응답이 안 보이는 문제 — 신호 만들기 (출력 예시)

성공과 실패가 화면 글자가 아니라 미세한 차이로만 나타나는 문제입니다.

[출력 예시 — 신호 관찰]
페이로드 A (거짓 조건): 응답 길이 812바이트, "try again"
페이로드 B (참 조건):   응답 길이 826바이트, "try again" + 숨은 주석 한 줄

읽는 법: 화면이 같아 보여도 응답 길이, 응답 시간, 상태 코드, 숨은 주석이 다를 수 있습니다. Burp Repeater로 같은 요청을 두 번 보내 "무엇이 같고 무엇이 다른가"를 비교하는 것이 Blind 계열 문제의 출발점입니다 (Step 137의 감각 복습).

3-4. 세션 + 업로드 조합 문제의 분해 (출력 예시)

업로드 기능과 권한 검사가 함께 있는 문제를 분해해 봅니다.

[출력 예시 — 분해 장부]
1층: 업로드 필터 — .php는 거부, .php5/.phtml/.phP는?
2층: 업로드 후 경로 — 파일명이 그대로인가, 랜덤인가
3층: 세션 검사 — 업로드는 되는데 실행 페이지가 관리자 세션 요구?

읽는 법: 층마다 독립된 실험을 설계합니다. 1층 확장자 우회가 되는지는 Burp로 업로드만 반복해 확인하고, 통과가 확인되면 그때 2층으로 내려갑니다. 층을 섞어서 실험하면 어느 층에서 막혔는지 알 수 없습니다.

3-5. 풀이를 본 문제의 재현 절차

정답을 봐 버린 문제의 처리 절차입니다.

  1. 풀이를 덮고, 방금 본 답이 답인지를 내 언어로 세 문장 씁니다
  2. 페이로드를 처음부터 다시 만들어 서버에 통과시킵니다 — 복사·붙여넣기 금지
  3. "내 원래 시도와 정답의 차이"를 요점 표에 적습니다
  4. 일주일 뒤 알림을 걸어 두고, 그날 아무것도 보지 않고 재풀이합니다

재풀이 성공이 곧 흡수 완료의 증거입니다. 실패하면 그 유형은 약점 목록에 올라갑니다.

3-6. 30번 완주 후 — 약점 유형 갱신

구간을 마치면 자가 평가표를 갱신합니다.

[출력 예시 — 약점 유형 갱신]
유형                  | 1~15 구간 | 16~30 구간 | 평가
소스/주석 읽기        | 강        | 강         | 유지
쿠키·세션 조작        | 강        | 중         | 세션 고정류 재훈련
인코딩 사슬           | 중        | 강         | 성장
필터 우회             | 중        | 중         | 후보 층 넓히기 진행 중
Blind/신호 만들기     | -         | 약         | Step 137 복습 후 재도전
조합 분해             | -         | 중         | 층 나누기는 됨, 속도 과제

읽는 법: 이 표의 "약" 칸이 다음 학습의 주소입니다. 정답을 보고 "아 그거였어?"가 반복된 유형은 패턴 인식 데이터가 부족한 것이니, 풀이를 볼 때마다 "나는 왜 이걸 못 떠올렸나"를 적어 두세요.

3-7. 2시간 상한의 내부 배분

문제당 상한 2시간도 그냥 흘려내면 한 가설에 다 쓰기 십상입니다. 내부 배분을 정해 둡니다.

0:00~0:20  정찰 — 소스·파라미터·쿠키·JS의 재료 목록 작성
0:20~0:40  방어 목록 + 우회 후보 표 완성
0:40~1:50  후보 소거 실험 (한 후보에 20분 이상 쓰지 않기)
1:50~2:00  정리 — 소거된 후보와 다음 재도전 시 첫 시도를 기록

읽는 법: 1~15 구간의 상한(30분~1시간)보다 두 배 넓지만, 구조는 같습니다. 정찰과 표 작성에 처음 40분을 쓰는 것이 이 구간의 습관입니다 — 조합 문제일수록 앞의 40분이 뒤의 80분을 결정합니다. 2시간이 지나도 못 풀면 Step 199와 같은 규칙으로 넘깁니다. 시도 목록을 남기고, 다음 문제로.


4. 미션과 연습문제

미션 — 16~30 공략과 약점 갱신

  1. 16~30번에 도전하되 문제당 최대 2시간 상한을 지킵니다
  2. 각 문제에서 "보이는 방어"를 먼저 정리하고, 우회 후보를 표로 만든 뒤 하나씩 소거합니다
  3. 못 푼 문제는 커뮤니티 힌트를 2-4의 단계 규칙 안에서만 사용합니다
  4. 풀이를 본 문제는 3-5의 재현 절차를 거치고 일주일 뒤 재풀이합니다
  5. 16~30 중 10개 이상 해결 후, 3-6 형식의 약점 유형 갱신표를 완성합니다

연습문제

문제 1. 조합 문제를 "한꺼번에" 풀려고 할 때 생기는 대표적인 시간 낭비는 무엇이며, 분해가 이를 어떻게 막나요?

문제 2. 필터가 따옴표를 소거하는 문제에서 후보 표를 만들 때, 문자 층·구문 층·전송 층에서 각각 후보를 하나씩 들어 보세요.

문제 3. 힌트 사용의 1단계와 3단계의 차이를 "독립 해결"의 관점에서 설명해 보세요.

문제 4. 풀이를 본 문제를 일주일 뒤에 재풀이하는 이유는 무엇인가요? 재현(3-5의 2번)만으로는 부족한 이유와 함께 답해 보세요.


5. 모범 답안과 완료 기준

미션 모범 답안

완성된 기록의 골격입니다 (실제 답과 유형은 여러분의 풀이로 채웁니다):

[16~30 공략 장부 — 예시 형식]
번호 | 결과     | 층 구조          | 핵심 트릭                | 재풀이
16   | 해결     | 필터 1층         | /**/ 공백 대체           | 해당 없음
17   | 해결     | 인코딩+인젝션 2층 | 이중 URL 인코딩 후 인젝션 | 해당 없음
19   | 풀이 봄  | 세션+업로드 3층  | 확장자 대소문자 혼합      | 7/9 예정
...

검증하는 법: ① 해결 10개 이상인가. ② "층 구조" 칸이 채워져 있는가 — 조합 문제를 분해했다는 증거입니다. ③ 풀이를 본 문제에 재풀이 일정이 박혀 있는가. ④ 약점 갱신표의 "약" 칸이 다음 훈련 주소로 이어지는가.

연습문제 해답

문제 1 해답. 어느 층에서 막혔는지 알 수 없게 되는 낭비입니다. 페이로드를 통째로 바꾸면 1층을 통과한 건지 2층에서 죽은 건지 구분이 안 됩니다. 층별로 독립 실험을 하면 "1층 통과 확인 → 2층 실험"의 진행선이 생겨서, 실패해도 실패의 좌표가 남습니다.

문제 2 해답. 문자 층: 대소문자 혼합(' oR '), 같은 문자 두 번(OORR). 구문 층: /**/로 공백 대체, ||로 OR 대체, #으로 주석 대체. 전송 층: URL 인코딩 또는 이중 인코딩, 파라미터 위치·방식(GET→POST) 변경. 층을 나누면 후보가 마르지 않습니다.

문제 3 해답. 1단계("이 문제가 어느 유형인가")는 문제 읽기의 확인일 뿐 풀이의 핵심 사고 — 무엇이 막고 있고 어떻게 우회하는가 — 는 여전히 내 몫입니다. 3단계(정답 페이로드)를 보면 그 사고 자체가 대체되므로 독립 해결이 아닙니다. 그래서 정답을 본 문제는 재현과 재풀이라는 별도 절차로 흡수합니다.

문제 4 해답. 재현은 단기 기억의 확인이고, 재풀이는 장기 기억의 확인이기 때문입니다. 방금 본 답을 다시 만드는 것은 이해가 아니라 기억일 수 있습니다. 일주일 뒤에 아무것도 보지 않고 풀어야 "왜 그게 답인지"의 사고 경로가 내 것이 됐는지 검증됩니다.

완료 기준 체크리스트

  • [ ] 문제의 "보이는 방어"를 목록으로 정리할 수 있다
  • [ ] 우회 후보를 문자·구문·전송 세 층의 표로 만들고 소거할 수 있다
  • [ ] 조합 문제를 층별로 분해해 독립 실험을 설계한다
  • [ ] 힌트를 단계 규칙(유형→방향, 정답 금지) 안에서 사용했다
  • [ ] 풀이를 본 문제의 재현 절차와 일주일 재풀이 규칙을 지켰다
  • [ ] 미션: 16~30 중 10개 이상 해결 + 약점 유형 갱신표 완성

6. 흔한 실수와 해결

벽 1. 1~15 때의 솜씨로 안 풀린다

증상: 공통 루틴을 다 돌렸는데 실마리가 없습니다.
원인: 이 구간은 루틴의 "결과물"을 조합해야 하는 문제가 섞여 있습니다. 루틴은 입구일 뿐입니다.
해결: 루틴을 돌린 뒤 산출물(발견한 폼, 파라미터, 필터 목록)을 나란히 놓고 "이 중 무엇과 무엇이 연결되는가"를 보세요. 조합의 실마리는 루틴 산출물의 교집합에 있습니다.

벽 2. 우회 후보가 정말로 다 떨어졌다

증상: 표의 후보를 전부 소거했는데도 막힙니다.
원인: 가정이 틀렸을 가능성 — "이게 필터다"라고 본 것이 필터가 아니거나, 입구 자체가 다른 곳에 있습니다.
해결: 방어 목록의 첫 줄을 의심하세요. "정말 따옴표가 막히는가, 아니면 따옴표가 포함된 ‘문맥’이 막히는가"처럼 관찰을 한 단계 쪼개면 후보가 다시 생깁니다.

벽 3. 응답이 전부 똑같아 보인다

증상: 성공 조건을 넣어도 실패 조건과 화면이 같습니다.
원인: 화면 글자만 보고 있기 때문입니다. 신호는 글자 밖에 있습니다.
해결: Burp Repeater로 두 요청을 나란히 놓고 응답 길이·상태 코드·응답 시간·숨은 주석을 비교하세요. 차이가 하나라도 있으면 그것이 오라클(판별 신호)입니다.

벽 4. 힌트를 보기 시작하면 끝까지 보게 된다

증상: "유형만" 보려다 정답까지 읽어 버립니다.
원인: 검색 결과 화면의 구조상 정답이 같은 화면에 있습니다.
해결: 검색은 "문제 번호 + 유형" 단어까지만 하고, 글을 열 때는 첫 문단만 읽는 규칙을 지키세요. 정답을 봐 버렸다면 인정하고 3-5의 재현 절차로 전환하면 됩니다 — 숨기는 것보다 회복 절차가 중요합니다.

벽 5. 풀이 수가 적은 문제에서 위축된다

증상: solved by 숫자가 백 단위인 문제 앞에서 손이 멈춥니다.
원인: 풀이 수는 난이도 신호일 뿐 내 실력의 판정이 아닙니다.
해결: 풀이 수가 적은 이유는 오래된 문제, 귀찮은 문제, 헷갈리는 문제 등 다양합니다. 방어 목록과 후보 표라는 절차는 어떤 문제에서도 똑같이 작동합니다. 절차를 믿고 2시간 상한만 지키세요.


7. 정리

오늘의 개념

개념 한 줄 설명
조합 문제 두 층 이상의 방어가 겹친 문제 — 층별 분해가 정석
방어 목록화 보이는 방어를 먼저 적는 것 — 후보 표의 원료
소거법 우회 후보를 표로 만들어 하나씩 지우는 탐색
출제자 관점 읽기 문제의 재료 목록에서 묻는 개념을 역산하는 기술
힌트의 단계적 사용 유형 → 방향까지만, 정답은 보지 않기
재현과 재풀이 본 답을 내 손으로 다시 만들고, 일주일 뒤 다시 풀기

오늘의 기법과 도구

기법·도구 하는 일
방어 목록 + 우회 후보 표 막힘의 지도화 — 남은 후보가 보인다
urllib.parse.quote() 페이로드 URL 인코딩 (이중 인코딩은 두 번 적용)
Burp Repeater 비교 응답 길이·시간·코드로 숨은 신호 찾기
층별 독립 실험 조합 문제를 1층씩 끊어서 검증
약점 유형 갱신표 구간 완주 후 다음 훈련 주소를 정하는 지도

명령어보다 중요한 감각

16~30 구간이 가르치는 것은 기법이 아니라 구조 읽기입니다. 막혔을 때 "내가 모르는 기법이 있다"고 의심하기 전에 "내가 본 재료를 다 썼는가"를 먼저 물으세요. 이 구간의 답은 대부분 화면에 있는 재료의 조합 안에 있습니다. 그리고 정답을 본 문제는 실패가 아니라 데이터입니다 — "나는 왜 못 떠올렸나"의 답이 쌓이면, 그것이 곧 여러분만의 패턴 사전이 됩니다.


전부 체크되면 Step 200 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.