Step 154. 드림핵 웹 (누적 32) — 약점 목록과 문제 선별 전략

Step 154. 드림핵 웹 (누적 32) — 약점 목록과 문제 선별 전략

Level 2 — 보안 입문과 공격 스킬 기초 | 난이도 ★★★☆☆ | 예상 소요 시간 3시간

전제: Step 151~153을 마쳤다. 드림핵 웹 문제 누적 24개, 리서치 기록과 기법 카드(Step 105)를 쓰고 있다.

  • 준비물: 드림핵 계정, 지금까지의 풀이 기록(문제명·소요 시간·성공 여부), 파이썬 3, 개인 위키.
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
  • 안내: 플랫폼 화면 묘사는 "화면 예시"입니다. 통계 스크립트의 출력은 이 책의 샘플 기록으로 실측한 것이고, 여러분의 기록을 넣으면 숫자가 달라집니다 — 그 달라진 숫자가 오늘의 산출물입니다.

30문제를 넘기면 데이터가 쌓입니다. 어떤 유형은 10분 컷인데, 어떤 유형은 매번 막힙니다. 이 차이를 "느낌"으로 알고 있는 것과 숫자로 알고 있는 것은 다릅니다. 약점 유형을 정확히 아는 것이 Level 3 트랙 선택의 근거가 되고, 대회에서 "내가 잘하는 문제부터" 고르는 선별력이 득점을 만듭니다.

오늘은 두 가지를 합니다. 첫째, 풀이 기록을 표로 만들어 파이썬으로 통계를 냅니다 — 유형별 평균 소요 시간과 막힘 횟수가 약점을 가리킵니다. 둘째, 그 결과로 약점 목록과 강점 목록을 완성하고, 약점 하나를 골라 재훈련합니다. 그리고 평소처럼 실전 8문제를 추가해 누적 32를 만듭니다.


1. 학습 목표

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

  • 풀이 기록을 CSV 형식(날짜·문제명·유형·소요분·결과)으로 유지한다
  • 파이썬 스크립트로 유형별 평균 시간·막힘 횟수를 집계한다
  • "개념 부족 / 도구 미숙 / 리서치 실패"의 3분류로 막힌 이유를 기록한다
  • 약점·강점 목록을 작성하고, 약점 유형 하나의 재훈련 계획을 세운다
  • 강점 데이터로 "어떤 문제부터 풀지" 선별 전략을 세운다

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

오늘의 도구 한눈에 보기

구분 내용
언어·환경 파이썬 3(csv·collections만 사용, 표준 라이브러리), 드림핵 워게임
오늘의 도구 wargame_stats.py(풀이 기록 집계 스크립트), 기법 카드 목록(Step 105)
필요한 개념 유형 분류, 평균 소요 시간, 막힘 원인 3분류, 재훈련 루프
오늘의 산출물 누적 32문제 + 약점/강점 유형 목록 + 재훈련 1회

2-1. 왜 "느낌"이 아니라 "기록"인가

기억은 최근 문제에 왜곡됩니다. 어제 Blind SQLi에서 고전했으면 "나는 SQLi가 약점"이라고 느끼지만, 기록을 펼쳐 보면 XSS에서 막힌 횟수가 더 많은 경우가 흔합니다. 약점 파악의 첫 규칙은 느낌을 숫자로 바꾸는 것입니다.

대회에서도 같습니다. 제한 시간 안의 득점은 "풀 수 있는 문제를 얼마나 빨리 알아보는가"로 결정됩니다. 그 판단의 재료가 유형별 평균 소요 시간입니다 — 내 데이터로 만든, 세상에 하나뿐인 선별표입니다.

2-2. 기록 형식 — 다섯 칸이면 충분하다

문제 하나당 다섯 칸만 적습니다.

날짜, 문제명, 유형, 소요분, 결과(성공/재도전/풀이참조)
  • 유형은 기법 카드(Step 105)의 분류를 그대로 씁니다 — 카드와 기록이 같은 언어를 써야 서로를 갱신합니다.
  • 소요분은 첫 접속부터 플래그 제출까지입니다. 대충이라도 재는 것이 안 재는 것보다 낫습니다.
  • 결과의 "재도전"은 시간 안에 못 풀고 접은 것, "풀이참조"는 write-up을 본 것입니다. 둘 다 실패가 아니라 훈련 데이터의 라벨입니다.

2-3. 막힘 원인 3분류 — 약점 목록의 핵심 칸

약점 목록은 "어떤 유형"만으로는 불완전합니다. 왜 막혔는가가 있어야 재훈련의 방향이 정해집니다.

원인 증상 재훈련 방향
개념 부족 기법의 동작 원리를 설명 못 한다 해당 개념 챕터로 회귀 + 로컬 재현
도구 미숙 개념은 아는데 Burp/sqlmap 조작에서 시간을 삼킨다 도구만 따로 반복 연습
리서치 실패 검색 키워드를 못 찾아 문서에 도달 못 한다 Step 153의 리서치 루프 복습

같은 "SQLi에서 막힘"이라도 원인이 다르면 처방이 다릅니다. 막힐 때마다 이 셋 중 하나를 골라 적는 습관이 오늘의 핵심입니다.

2-4. 강점도 목록이다

약점만 적으면 절반의 문서입니다. 강점 목록은 ① 대회에서 먼저 잡을 문제 유형, ② 자신감의 근거, ③ 남을 도와줄 수 있는 영역을 알려 줍니다. "10분 안에 풀리는 유형"의 목록이 곧 여러분의 시그니처입니다.


3. 따라 하기

3-1. 풀이 기록을 CSV로 모으기

개인 위키의 기록을 CSV 한 파일로 모읍니다. 형식:

날짜,문제명,유형,소요분,결과
09-01,web-basic-01,SQLi,12,성공
09-01,web-basic-02,XSS,9,성공
09-02,cookie-01,쿠키/세션,15,성공
...

기록이 흩어져 있다면 오늘의 첫 작업이 그 수집입니다. 소요 시간이 기억나지 않는 옛 문제는 대략값이라도 적고, 결과 칸은 정직하게 — 풀이를 봤다면 "풀이참조"입니다. 데이터의 정직함이 분석의 정확도입니다.

3-2. 통계 스크립트 — wargame_stats.py

집계는 파이썬에 맡깁니다. 표준 라이브러리만 쓰는 40줄짜리 스크립트입니다.

import csv, sys
from collections import defaultdict

path = sys.argv[1] if len(sys.argv) > 1 else "wargame_log.csv"

times = defaultdict(list)   # 유형 -> [소요분]
fails = defaultdict(int)    # 유형 -> 막힌 횟수(재도전+풀이참조)
solved = defaultdict(int)   # 유형 -> 성공 횟수
total = 0

with open(path, encoding="utf-8") as f:
    for row in csv.DictReader(f):
        t = row["유형"].strip()
        times[t].append(int(row["소요분"]))
        total += 1
        if row["결과"].strip() == "성공":
            solved[t] += 1
        else:
            fails[t] += 1

print(f"총 {total}문제 / 유형 {len(times)}종")
print(f"{'유형':14s} {'문제수':>4s} {'평균분':>6s} {'성공':>4s} {'막힘':>4s}")
print("-" * 40)
for t in sorted(times, key=lambda x: -fails[x]):
    n = len(times[t])
    avg = sum(times[t]) / n
    print(f"{t:14s} {n:>6d} {avg:>6.0f} {solved[t]:>4d} {fails[t]:>4d}")

print()
weak = [t for t in times if fails[t] >= 2]
print("약점 유형(2회 이상 막힘):", ", ".join(weak) if weak else "없음")
strong = [t for t in times if fails[t] == 0 and solved[t] >= 2]
print("강점 유형(무실패 2회+):", ", ".join(strong) if strong else "없음")

defaultdict는 처음 보는 유형이 나와도 자동으로 칸을 만들어 주는 사전입니다. 정렬 키 lambda x: -fails[x]로 막힘이 많은 유형이 위에 오도록 했습니다.

3-3. 샘플 데이터로 실행해 보기

이 책의 샘플 기록(20문제)으로 실행한 결과입니다 — 2026-09-09 실측이며, 여러분의 CSV를 넣으면 결과가 달라집니다.

총 20문제 / 유형 12종
유형              문제수    평균분   성공   막힘
----------------------------------------
SQLi                4     28    2    2
XSS                 4     27    2    2
JWT/토큰              1     31    0    1
SSTI                1     52    0    1
SSRF                1     44    0    1
비즈니스 로직             1     27    0    1
쿠키/세션               1     15    1    0
명령어 인젝션             1     14    1    0
LFI/파일포함            2     14    2    0
파일 업로드              2     22    2    0
접근제어/IDOR           1     10    1    0
CSRF                1     13    1    0

약점 유형(2회 이상 막힘): SQLi, XSS
강점 유형(무실패 2회+): LFI/파일포함, 파일 업로드

읽는 법: 세 가지를 봅니다. ① 막힘 열 — 2회 이상이면 약점 후보입니다. ② 평균분 — SSTI 52분처럼 튀는 숫자는 "개념이 아직 손에 없다"는 신호입니다. ③ 맨 아래 두 줄 — 스크립트가 뽑아 준 약점/강점 유형입니다. 샘플 데이터에서는 SQLi와 XSS가 약점으로, LFI와 파일 업로드가 강점으로 잡혔습니다.

3-4. 약점 목록 작성 — 원인을 붙이기

스크립트 결과를 위키 문서로 옮기되, 2-3의 3분류로 원인을 답니다.

**약점 목록 (누적 32 기준)**
1. SQLi (4회 중 2회 막힘, 평균 28분)
   - 원인: 개념 부족 — Blind에서 참/거짓 차이 설계를 아직 손으로 못 그린다
   - 재훈련: DVWA SQLi Blind를 sqlmap 없이 수동으로 3회
2. XSS (4회 중 2회 막힘, 평균 27분)
   - 원인: 리서치 실패 — 필터 우회 키워드("xss filter bypass cheat sheet")에 도달이 느림
   - 재훈련: PayloadsAllTheThings의 XSS 항목 페이로드 10개 직접 타이핑

**강점 목록**
1. LFI/파일포함 (2회 무실패, 평균 14분) — 경로 힌트 읽기가 빠르다
2. 파일 업로드 (2회 무실패, 평균 22분) — 확장자 우회 패턴이 손에 익음

채우기 안내: 여러분의 문서는 이 형식에 여러분의 숫자와 원인이 들어가면 됩니다. 원인 칸이 비어 있으면 약점 목록이 아니라 성적표입니다 — 원인이 있어야 처방이 됩니다.

3-5. 약점 1개 재훈련 — 회귀 훈련

목록의 1순위를 골라 Step 135~147의 DVWA나 Step 148~150의 Juice Shop으로 돌아갑니다. 규칙은 하나입니다 — 당시 걸었던 보조 장치를 하나 뺍니다. sqlmap을 썼다면 수동으로, 풀이를 봤다면 안 보고. 재훈련의 목표는 "풀리는 것"이 아니라 "막혔던 지점이 이제는 보이는 것"을 확인하는 데 있습니다.

재훈련 결과를 약점 목록에 덧붙입니다: "재훈련 1회 — 수동 Blind 40분 → 성공. 참/거짓 응답 차이 설계가 이제 된다. 다음 목표 20분."

3-6. 실전 8문제 — 누적 32

평소대로 난이도 1~2에서 8문제를 추가합니다. 오늘의 차이점은 시작 전에 강점 목록을 펴는 것입니다.

① 문제 목록을 훑으며 제목·설명에서 유형을 추측한다 (화면 예시)
② 강점 유형으로 보이는 것 2개를 먼저 잡아 워밍업으로 쓴다
③ 약점 유형 1개를 의식적으로 골라 재훈련 겸용으로 도전한다
④ 나머지는 평소대로 — 막히면 리서치 루프(Step 153)
⑤ 전부 기록 CSV에 추가하고 스크립트를 다시 돌려 변화를 본다

"재도전"이나 "풀이참조"로 끝난 문제는 일주일 뒤 다시 풀 목록에 표시해 둡니다. 풀이를 본 문제는 훈련 데이터에서 빠지는 것이 아니라 재시험 예약이 걸리는 것입니다.


4. 미션과 연습문제

미션 — 누적 32와 약점/강점 목록 완성

  1. 풀이 기록 CSV(최소 24건, 누적 32 기준이면 32건)를 만들고 wargame_stats.py로 집계합니다
  2. 약점 목록을 작성합니다 — 유형별 막힘 횟수 + 원인 3분류 + 재훈련 계획 포함
  3. 강점 목록을 작성합니다 — 무실패 유형과 평균 시간 근거
  4. 약점 1순위 유형을 DVWA/Juice Shop에서 재훈련하고 결과를 목록에 덧붙입니다
  5. 드림핵 웹 8문제를 추가해 누적 32개를 만들고, CSV와 통계를 갱신합니다

연습문제

문제 1. 약점 파악을 "느낌"이 아니라 기록으로 해야 하는 이유를, 기억의 왜곡과 연결해 설명해 보세요.

문제 2. 막힘 원인 3분류(개념 부족/도구 미숙/리서치 실패)에서, 같은 SQLi 막힘이라도 원인에 따라 처방이 어떻게 달라지는지 써 보세요.

문제 3. 대회에서 "내가 잘하는 문제부터" 푸는 전략이 득점으로 이어지는 구조를, 평균 소요 시간 데이터와 연결해 설명해 보세요.

문제 4. 풀이를 본 문제를 "재도전 예정"으로 표시하고 일주일 뒤 다시 푸는 절차가 왜 필요한가요? 그냥 넘어가면 어떤 데이터가 오염되나요?


5. 모범 답안과 완료 기준

미션 모범 답안

1~2번의 완성형은 3-4의 형식입니다. 여기에 덧붙일 최종 점검 기준:

[자기 점검 질문]
- 약점 각 항목에 "원인"과 "재훈련 계획"이 함께 있는가?
- 강점 목록에 근거 숫자(횟수·평균 시간)가 있는가?
- CSV의 결과 칸이 정직한가? (풀이참조를 성공으로 적지 않았는가)
- 재훈련 후 목록이 갱신됐는가? (살아 있는 문서인가)

3번 강점 목록의 예 (샘플 데이터 기준): "LFI/파일포함 — 2회 무실패 평균 14분. 힌트의 경로 단서를 읽는 속도가 강점. 대회에서 우선 공략 유형으로 지정."

5번까지 마치면 통계가 한 번 더 바뀝니다. 스크립트를 다시 돌려 "약점이 줄었는가, 새 유형이 등장했는가"를 확인하는 것이 마무리입니다 — 목록은 스냅샷이 아니라 갱신되는 문서입니다.

검증하는 법: ① CSV 행 수가 누적 문제 수와 일치하는가. ② 약점 각 항목에 원인 3분류 중 하나가 적혀 있는가. ③ 재훈련 기록 한 줄이 목록에 덧붙었는가. ④ 다음 문제를 고를 때 강점 목록을 실제로 펴는가 — 이 마지막 하나가 문서의 실효성 시험입니다.

연습문제 해답

문제 1 해답. 기억은 최근성과 감정 강도에 왜곡됩니다 — 어제 고전한 유형이 "제일 약한 유형"으로 느껴지지만, 누적 기록은 다른 순서를 보여 주는 경우가 흔합니다. 재훈련 자원은 한정돼 있으므로, 왜곡된 진단은 엉뚱한 유형에 시간을 쓰게 합니다. 숫자는 왜곡 없이 누적되므로 진단의 기준은 기록이어야 합니다.

문제 2 해답. 개념 부족이면 원리를 다시 배우는 것(챕터 회귀·로컬 재현)이 처방이고, 도구 미숙이면 개념은 그대로 두고 도구 조작만 반복하는 것이 처방이며, 리서치 실패면 검색 루프 훈련이 처방입니다. 셋을 구분하지 않으면 "SQLi 공부 더 하기"라는 뭉뚱그린 계획만 남아, 정작 막힌 지점은 그대로 남습니다.

문제 3 해답. 제한 시간은 고정돼 있으므로 득점은 "문제당 소요 시간"의 합계 싸움입니다. 평균 14분짜리 강점 유형을 먼저 처리하면 같은 시간에 더 많은 플래그가 나오고, 남은 시간을 약점 유형의 리서치에 투자할 수 있습니다. 선별 순서를 정하는 근거가 바로 유형별 평균 소요 시간이라, 기록이 곧 대회 전략 문서가 됩니다.

문제 4 해답. 풀이를 본 문제를 "성공"으로 두면 통계의 성공 열과 평균 시간이 오염됩니다 — 실제로는 못 푸는 유형이 강점으로 잡히는 최악의 오진단입니다. "재도전 예정" 표시는 그 데이터를 정직하게 유지하면서, 일주일 뒤의 재시험으로 "진짜 내 것이 됐는가"를 검증하는 장치입니다. 재시험에서 풀린 순간 그 문제는 비로소 진짜 성공 데이터가 됩니다.

완료 기준 체크리스트

  • [ ] 풀이 기록 CSV(날짜·문제명·유형·소요분·결과)를 만들었다
  • [ ] wargame_stats.py로 유형별 평균 시간·막힘 횟수를 집계했다
  • [ ] 약점 목록에 원인 3분류와 재훈련 계획이 있다
  • [ ] 강점 목록에 근거 숫자가 있다
  • [ ] 약점 1개를 DVWA/Juice Shop에서 재훈련하고 기록했다
  • [ ] 결과 칸을 정직하게(풀이참조 구분) 적었다
  • [ ] 미션: 누적 32문제 + 약점/강점 목록 완성

6. 흔한 실수와 해결

벽 1. 스크립트가 ValueError: invalid literal for int() with base 10: '열다섯'로 죽는다

원인: 소요분 칸에 숫자가 아닌 값이 들어간 행이 있습니다 (2026-09-09 실측 에러).
해결: CSV에서 소요분 열을 훑어 숫자만 남기세요. 시간을 안 잰 문제는 대략값(예: 30)으로라도 채우세요 — 빈칸보다 추정이 낫습니다.

벽 2. KeyError: '결과'가 뜬다

원인: 헤더 행이 없거나 열 이름이 다릅니다 — 날짜,문제명,유형,소요분,결과 다섯 개가 정확히 있어야 합니다 (2026-09-09 실측 에러).
해결: CSV 첫 줄을 3-1의 형식과 대조하세요. 엑셀에서 저장했다면 인코딩을 "CSV UTF-8"로 저장하세요.

벽 3. 시간을 재는 것 자체가 어색해서 자꾸 잊는다

원인: 새 습관의 정상적인 마찰입니다.
해결: 완벽하게 재려 하지 마세요. 문제를 열 때 시계를 보고, 풀면 다시 보는 두 번의 눈길이면 됩니다. 5분 오차는 분석에 영향이 없습니다. 지속이 정확도보다 중요합니다.

벽 4. 막힌 문제를 다시 보기 싫어서 약점 목록이 비어 있다

원인: 약점 목록이 "실패의 명단"처럼 느껴지기 때문입니다.
해결: 관점을 바꾸세요 — 이 목록은 "다음에 실력이 가장 빨리 오르는 지점"의 안내서입니다. 강점을 더 갈고닦는 것보다 약점 하나를 메우는 것이 평균을 더 많이 올립니다. 목록이 찰수록 성장의 좌표가 선명해지는 것입니다.

벽 5. 유형 분류를 뭘로 해야 할지 매번 헤맨다

원인: 분류 어휘가 정해져 있지 않아서입니다.
해결: 기법 카드(Step 105)의 분류를 그대로 쓰세요. 카드와 기록이 같은 언어를 써야, 새 기법을 배웠을 때 양쪽을 한 번에 갱신할 수 있습니다. 어중간하면 카드에 새 유형을 추가하는 쪽으로 — 그것도 목록의 성장입니다.


7. 정리

오늘의 개념

개념 한 줄 설명
풀이 기록 CSV 날짜·문제명·유형·소요분·결과 다섯 칸의 원시 데이터
유형별 평균 소요 시간 약점과 강점을 가르는 핵심 지표 — 느낌을 숫자로
막힘 원인 3분류 개념 부족 / 도구 미숙 / 리서치 실패 — 원인이 처방을 정한다
약점 목록 유형 + 원인 + 재훈련 계획이 한 세트 — 성장의 좌표
강점 목록 무실패 유형 + 근거 숫자 — 대회 선별 전략의 재료
재훈련 보조 장치를 하나 빼고 다시 푸는 회귀 훈련

오늘의 명령어·도구

도구 하는 일
python wargame_stats.py 기록.csv 유형별 문제수·평균분·성공/막힘 집계
csv.DictReader 헤더 이름으로 열을 읽는 파이썬 표준 도구
defaultdict(list/int) 처음 보는 유형의 칸을 자동 생성
기법 카드 (Step 105) 유형 분류의 표준 어휘
DVWA / Juice Shop 약점 재훈련용 회귀 훈련장

명령어보다 중요한 감각

워게임 32문제는 "32개의 문제"가 아니라 "12개 남짓한 유형과 나의 관계 데이터"입니다. 오늘 만든 약점 목록은 이 책의 나머지 구간에서 반복적으로 다시 펼칠 문서입니다 — 새 기법을 배울 때마다 갱신되고, 대회를 앞둘 때마다 선별표로 읽힙니다. 프로는 약점이 없는 사람이 아니라, 자기 약점을 목록으로 갖고 있는 사람입니다. 그리고 그 목록의 첫 줄부터 메우기 시작한 사람이 결국 약점이 없는 사람이 됩니다.


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