Step 154. DreamHack Web (Running Total: 32) — Your Weakness List and Problem-Picking Strategy

Step 154. DreamHack Web (Running Total: 32) — Your Weakness List and Problem-Picking Strategy

Level 2 — Introduction to Security and Attack Skill Basics | Difficulty ★★★☆☆ | Estimated time: 3 hours

Prerequisites: Steps 151~153 complete. 24 DreamHack web problems solved so far, and you’re keeping research records and technique cards (Step 105).

  • What you need: a DreamHack account, your solve records so far (problem name, time taken, success/failure), Python 3, and your personal wiki.
  • ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.
  • Note: platform screen descriptions are a "Screen example." The statistics script’s output was measured with this book’s sample records — your numbers will differ when you plug in your own records, and those differing numbers are today’s artifact.

Once you pass 30 problems, data piles up. Some types take you 10 minutes flat, while others block you every single time. Knowing this difference as a "feeling" and knowing it in numbers are not the same thing. Knowing your weak types precisely becomes the basis for choosing a Level 3 track, and in competitions the picking skill of "my strong problems first" is what produces points.

Today you do two things. First, you turn your solve records into a table and compute statistics in Python — per-type average time and stuck counts point to your weaknesses. Second, with those results you complete a weakness list and a strength list, pick one weakness, and retrain it. And as usual, you add 8 live problems to reach 32 cumulative.


1. Learning Objectives

By the end of this chapter, you will be able to:

  • Maintain solve records in CSV format (date, problem name, type, minutes spent, result)
  • Aggregate per-type average time and stuck counts with a Python script
  • Record the reason for getting stuck in three categories: "concept gap / tool inexperience / research failure"
  • Write weakness and strength lists, and set a retraining plan for one weak type
  • Build a "which problems to solve first" picking strategy from strength data

2. Background Knowledge — Today’s Tools and Concepts

Today’s Tools at a Glance

Category Details
Language/environment Python 3 (uses only csv and collections — standard library), DreamHack wargame
Today’s tools wargame_stats.py (solve-record aggregation script), technique-card list (Step 105)
Concepts needed Type classification, average time spent, the three-way classification of stuck causes, the retraining loop
Today’s artifact 32 problems cumulative + weakness/strength type lists + 1 retraining session

2-1. Why "Records," Not "Feelings"

Memory is distorted toward recent problems. If you struggled with Blind SQLi yesterday, you feel "SQLi is my weakness" — but spread the records out and it’s common to find you’ve been stuck on XSS more often. The first rule of weakness assessment is turning feelings into numbers.

The same holds in competitions. Your score within the time limit is decided by "how fast you can recognize the problems you can solve." The material for that judgment is per-type average time — a one-of-a-kind picking table made from your own data.

2-2. Record Format — Five Fields Are Enough

Write just five fields per problem.

date, problem name, type, minutes spent, result (solved/retry/writeup-used)
  • Type uses the technique-card (Step 105) classification as-is — cards and records must speak the same language to update each other.
  • Minutes spent is from first connection to flag submission. Measuring roughly beats not measuring at all.
  • In result, "retry" means you folded without solving in time, and "writeup-used" means you read a write-up. Both aren’t failures — they’re labels on your training data.

2-3. The Three-Way Classification of Stuck Causes — The Core Field of the Weakness List

A weakness list is incomplete with just "which type." You need why you got stuck to set the direction of retraining.

Cause Symptom Retraining direction
Concept gap Can’t explain how the technique works Regress to the relevant concept chapter + local reproduction
Tool inexperience Knows the concept but burns time on Burp/sqlmap operations Repeat practice with the tool alone
Research failure Can’t find search keywords, never reaches the document Review Step 153’s research loop

Even for the same "stuck on SQLi," different causes get different prescriptions. The habit of picking one of these three and writing it down every time you’re stuck is today’s core.

2-4. Strengths Are a List Too

Record only weaknesses and you have half a document. A strength list tells you ① which problem types to grab first in a competition, ② the basis for confidence, ③ the areas where you can help others. Your list of "types solvable within 10 minutes" is your signature.


3. Follow Along

3-1. Gathering Solve Records into a CSV

Collect your personal wiki’s records into one CSV file. Format:

date,problem,type,minutes,result
09-01,web-basic-01,SQLi,12,solved
09-01,web-basic-02,XSS,9,solved
09-02,cookie-01,cookie/session,15,solved
...

If your records are scattered, today’s first task is that collection. For old problems whose time you can’t recall, write an estimate anyway, and keep the result field honest — if you read the solution, it’s "writeup-used." Data honesty is analysis accuracy.

3-2. The Statistics Script — wargame_stats.py

Leave aggregation to Python. A 40-line script using only the standard library.

import csv, sys
from collections import defaultdict

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

times = defaultdict(list)   # type -> [minutes]
fails = defaultdict(int)    # type -> stuck count (retry + writeup-used)
solved = defaultdict(int)   # type -> success count
total = 0

with open(path, encoding="utf-8") as f:
    for row in csv.DictReader(f):
        t = row["type"].strip()
        times[t].append(int(row["minutes"]))
        total += 1
        if row["result"].strip() == "solved":
            solved[t] += 1
        else:
            fails[t] += 1

print(f"{total} problems total / {len(times)} types")
print(f"{'type':14s} {'count':>5s} {'avg_min':>7s} {'solved':>6s} {'stuck':>5s}")
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:>5d} {avg:>7.0f} {solved[t]:>6d} {fails[t]:>5d}")

print()
weak = [t for t in times if fails[t] >= 2]
print("Weak types (stuck 2+ times):", ", ".join(weak) if weak else "none")
strong = [t for t in times if fails[t] == 0 and solved[t] >= 2]
print("Strong types (no fails, 2+ solves):", ", ".join(strong) if strong else "none")

defaultdict is a dictionary that automatically creates a slot even for a type it sees for the first time. The sort key lambda x: -fails[x] puts the most-stuck types on top.

3-3. Running It on Sample Data

Here’s the result of running it on this book’s sample records (20 problems) — measured 2026-09-09; your CSV will produce different results.

20 problems total / 12 types
type            count avg_min solved stuck
----------------------------------------
SQLi                4      28      2     2
XSS                 4      27      2     2
JWT/token           1      31      0     1
SSTI                1      52      0     1
SSRF                1      44      0     1
business logic      1      27      0     1
cookie/session      1      15      1     0
command injection   1      14      1     0
LFI/file include    2      14      2     0
file upload         2      22      2     0
access control/IDOR 1      10      1     0
CSRF                1      13      1     0

Weak types (stuck 2+ times): SQLi, XSS
Strong types (no fails, 2+ solves): LFI/file include, file upload

How to read it: look at three things. ① The stuck column — 2 or more is a weakness candidate. ② Avg minutes — an outlier like SSTI at 52 is a signal that "the concept isn’t in your hands yet." ③ The bottom two lines — the weak/strong types the script picked out. In the sample data, SQLi and XSS came out as weaknesses, LFI and file upload as strengths.

3-4. Writing the Weakness List — Attaching Causes

Transfer the script results into a wiki document, attaching causes with 2-3’s three-way classification.

**Weakness list (as of 32 cumulative)**
1. SQLi (stuck 2 of 4 times, avg 28 min)
   - Cause: concept gap — I still can't hand-design the true/false difference for Blind
   - Retraining: DVWA SQLi Blind, manually without sqlmap, 3 times
2. XSS (stuck 2 of 4 times, avg 27 min)
   - Cause: research failure — slow to reach filter-bypass keywords ("xss filter bypass cheat sheet")
   - Retraining: type out 10 payloads from PayloadsAllTheThings' XSS entry by hand

**Strength list**
1. LFI/file include (2 solves, no fails, avg 14 min) — fast at reading path hints
2. File upload (2 solves, no fails, avg 22 min) — extension-bypass patterns are second nature

Filling guide: your document just needs your numbers and causes in this format. If the cause field is empty, it’s a report card, not a weakness list — a cause is what makes it a prescription.

3-5. Retraining 1 Weakness — Regression Training

Pick the list’s top priority and return to Steps 135~147’s DVWA or Steps 148~150’s Juice Shop. There’s one rule — remove one of the aids you walked with back then. If you used sqlmap, go manual; if you read the solution, go without it. The goal of retraining isn’t "it solves" but confirming "the point where I was stuck is now visible."

Append the retraining result to the weakness list: "Retraining 1 session — manual Blind, 40 min → success. I can design the true/false response difference now. Next goal: 20 min."

3-6. 8 Live Problems — 32 Cumulative

As usual, add 8 problems at difficulty 1–2. Today’s difference: before starting, spread out your strength list.

① Skim the problem list and guess the type from the title/description (Screen example)
② Grab 2 that look like your strong types first and use them as a warm-up
③ Deliberately pick 1 weak type and challenge it, doubling as retraining
④ The rest as usual — if stuck, the research loop (Step 153)
⑤ Add everything to the records CSV and re-run the script to see the change

Mark problems that ended as "retry" or "writeup-used" on a list to re-solve a week later. A problem you read the solution for doesn’t drop out of the training data — it gets a retest reservation.


4. Missions & Exercises

Mission — 32 Cumulative and Completing the Weakness/Strength Lists

  1. Build a solve-records CSV (at least 24 entries; 32 if you’re at the 32-cumulative mark) and aggregate it with wargame_stats.py
  2. Write the weakness list — including per-type stuck counts + the three-way cause classification + a retraining plan
  3. Write the strength list — no-fail types with supporting average times
  4. Retrain the #1 weak type in DVWA/Juice Shop and append the result to the list
  5. Add 8 DreamHack web problems to reach 32 cumulative, and update the CSV and statistics

Exercises

Exercise 1. Explain why weakness assessment must be done with records rather than "feelings," connecting it to the distortion of memory.

Exercise 2. Within the three-way classification of stuck causes (concept gap / tool inexperience / research failure), write how the prescription differs by cause even for the same SQLi blockage.

Exercise 3. Explain how the strategy of solving "my strong problems first" in a competition turns into points, connecting it to average-time data.

Exercise 4. Why is the procedure of marking a write-up-read problem as "retest scheduled" and re-solving it a week later necessary? What data gets contaminated if you just move on?


5. Model Answers & Completion Criteria

Mission Model Answer

The completed form of items 1–2 is 3-4’s format. Final checklist to add:

[Self-check questions]
- Does each weakness entry have both a "cause" and a "retraining plan"?
- Does the strength list have supporting numbers (counts, average times)?
- Is the CSV's result field honest? (You didn't mark writeup-used as solved, right?)
- Was the list updated after retraining? (Is it a living document?)

Example of item 3’s strength list (based on the sample data): "LFI/file include — 2 solves no fails, avg 14 min. Strength is the speed of reading path clues in hints. Designated as a first-strike type in competitions."

Once item 5 is done, the statistics change once more. Re-running the script to check "did weaknesses shrink, did new types appear" is the finishing touch — the list is not a snapshot but an updating document.

How to verify: ① does the CSV row count match the cumulative problem count? ② does each weakness entry have one of the three cause categories written? ③ was a retraining-record line appended to the list? ④ when picking the next problem, do you actually spread out the strength list — this last one is the document’s effectiveness test.

Exercise Answers

Answer 1. Memory is distorted by recency and emotional intensity — the type you struggled with yesterday feels like "the weakest type," but cumulative records commonly show a different order. Retraining resources are limited, so a distorted diagnosis spends time on the wrong type. Numbers accumulate without distortion, so the standard for diagnosis must be records.

Answer 2. For a concept gap, the prescription is relearning the principle (chapter regression, local reproduction); for tool inexperience, the prescription is repeating only the tool’s operations while leaving the concept alone; for research failure, the prescription is search-loop training. Without distinguishing the three, all that remains is the lumped-together plan of "study SQLi more," and the actual stuck point stays exactly where it was.

Answer 3. Since the time limit is fixed, scoring is a battle of total "time per problem." Clearing a 14-minute-average strong type first yields more flags in the same time, and the remaining time can be invested in researching weak types. The basis for setting the picking order is exactly per-type average time — so your records become your competition strategy document.

Answer 4. Leaving a write-up-read problem as "solved" contaminates the statistics’ solved column and average times — the worst misdiagnosis, where a type you actually can’t solve gets picked as a strength. The "retest scheduled" mark keeps that data honest while verifying "did it truly become mine" through a retest a week later. The moment it solves in the retest, that problem finally becomes genuine success data.

Completion Criteria Checklist

  • [ ] I built a solve-records CSV (date, problem name, type, minutes, result)
  • [ ] I aggregated per-type average times and stuck counts with wargame_stats.py
  • [ ] The weakness list has the three-way cause classification and a retraining plan
  • [ ] The strength list has supporting numbers
  • [ ] I retrained 1 weakness in DVWA/Juice Shop and recorded it
  • [ ] I wrote the result field honestly (distinguishing writeup-used)
  • [ ] Mission: 32 problems cumulative + weakness/strength lists complete

6. Common Pitfalls & Fixes

Wall 1. The script dies with ValueError: invalid literal for int() with base 10: 'fifteen'

Cause: a row has a non-numeric value in the minutes field (error measured 2026-09-09).
Fix: skim the minutes column in the CSV and leave only numbers. For problems you didn’t time, fill in an estimate (e.g., 30) anyway — an estimate beats a blank.

Wall 2. I get KeyError: 'result'

Cause: the header row is missing or the column names differ — the five columns date,problem,type,minutes,result must be exactly right (error measured 2026-09-09).
Fix: compare the CSV’s first line against 3-1’s format. If you saved from Excel, save with the "CSV UTF-8" encoding.

Wall 3. Timing itself feels awkward and I keep forgetting

Cause: the normal friction of a new habit.
Fix: don’t try to time perfectly. Glancing at the clock when you open a problem and again when you solve it — those two glances are enough. A 5-minute error doesn’t affect the analysis. Consistency matters more than accuracy.

Wall 4. I hate revisiting problems I failed, so my weakness list is empty

Cause: the weakness list feels like a "roster of failures."
Fix: change the framing — this list is a guide to "where your skill will rise fastest next." Filling one weakness raises your average more than sharpening a strength further. The fuller the list, the clearer the coordinates of your growth.

Wall 5. I wander every time over what to use for type classification

Cause: your classification vocabulary isn’t fixed.
Fix: use the technique-card (Step 105) classification as-is. Cards and records must speak the same language so that when you learn a new technique, you can update both at once. If it’s ambiguous, add a new type to the cards — that’s growth of the list too.


7. Summary

Today’s Concepts

Concept One-line explanation
Solve-records CSV Raw data in five fields: date, problem, type, minutes, result
Per-type average time The key metric separating weaknesses from strengths — feelings into numbers
Three-way stuck-cause classification Concept gap / tool inexperience / research failure — the cause sets the prescription
Weakness list Type + cause + retraining plan as one set — the coordinates of growth
Strength list No-fail types + supporting numbers — material for competition picking strategy
Retraining Regression training: remove one aid and solve again

Today’s Commands & Tools

Tool What it does
python wargame_stats.py records.csv Aggregates per-type counts, avg minutes, solved/stuck
csv.DictReader Python standard tool for reading columns by header name
defaultdict(list/int) Auto-creates slots for types seen for the first time
Technique cards (Step 105) Standard vocabulary for type classification
DVWA / Juice Shop Regression training grounds for weakness retraining

An Instinct More Important Than Commands

32 wargame problems aren’t "32 problems" — they’re "relationship data between about 12 types and me." The weakness list you made today is a document you’ll spread open again and again through the rest of this book — updated every time you learn a new technique, read as a picking table before every competition. A pro isn’t someone without weaknesses — a pro is someone who holds their weaknesses as a list. And the person who starts filling from the first line of that list is the one who eventually becomes someone without weaknesses.


Once every box is checked, Step 154 is complete. Click the checkbox in the sidebar to save your progress.