Step 291. ★ CTF #7: Live-Fire Library Validation — An Asset’s Value Is Proven Only at a Competition
Level 3 — The CTF Competition Cycle | Difficulty ★★★☆☆ | Estimated time: 2 days (competition participation + half a day of retrospective)
Prerequisites: Step 290 (exploit library v1) complete, Python 3 (for the measurement script).
- What you need: one CTF competition for your team to enter, Step 290’s library repository, a memo for time records, Python 3. The competition scenes in this chapter are screen examples; the local time measurements and the path-failure reproduction are marked as measured (2026-09-09, Python 3.12).
- ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.
- This is a validation chapter — the day you confirm the asset you built actually runs in the field.
Library v1 looks perfect inside the drawer. Comments attached, an index in place, local verification done. But an asset’s true value shows only under competition pressure — does a time-pressed hand find the template within 10 seconds, and does the skeleton hold when the competition server looks different from the local demo?
Today’s competition has a different primary goal. Not points — library validation. When a similar type appears, do you write from scratch like before, or do you start from a template in 5 minutes? This competition measures that difference. Points will follow, of course. When validation goes well, points come along.
1. Learning Objectives
By the end of this chapter, you will be able to:
- Open the library index as the competition starts and reach a type-match verdict within 10 seconds
- Record and compare time spent on template-used versus non-template problems
- Classify where templates collapse in the field (dependencies, paths, input formats)
- Apply the verdict rule "if it doesn’t fit within 5 minutes, write from scratch"
- Close the competition retrospective with library-improvement patch commits
2. Background Knowledge — Today’s Tools and Concepts
Today’s Tools at a Glance
| Category | Details |
|---|---|
| Language/environment | CTF competition platform, Step 290’s library, Python 3 (for measurement) |
| Today’s commands | Running library templates, python step291_measure.py (local measurement example) |
| Concepts needed | Type matching, the 5-minute verdict rule, the three factors of field collapse, improvement patches |
| Today’s deliverable | Template usage record table + a library improvement commit (v2) |
2-1. Type Matching — The 10-Second Verdict Procedure
When you open a problem at a competition, the question order must be fixed before you dig through the library.
Type matching, 3 questions (goal: 10 seconds):
1. What is this problem's I/O shape? (TCP prompt / web form / file download)
2. Do we have a template of the same shape? (skim only the 'conditions of use' lines in the index)
3. If yes, are the edit points a 5-minute job? (just HOST/PORT/a few values, or a different structure?)
If question 3 is "the structure differs," that template doesn’t fit this problem. Even with the same shape, the internal protocol often differs — then you take only the ingredients (payloads) and write the skeleton fresh.
2-2. The 5-Minute Verdict Rule — The Trap of Forced Matching
The source’s warning — you spend more time forcing a problem into a template. This is the most common accident at the first competition after building a library. The desire to use your asset clouds the judgment.
The rule is simple. If the first send hasn’t gone out within 5 minutes of starting to modify a template, fold it and write from scratch. Five minutes is double the time a template takes from edit to run in local verification — even with margin, a "fitting template" should already be working by then.
2-3. Where It Collapses in the Field — Three Factors
Code that passed local verification breaks at a competition at roughly three points.
| Factor | Symptom | Prevention |
|---|---|---|
| Dependencies | The competition PC lacks pwntools/specific packages | Keep a standard-library utility alongside (Step 290’s net.py) |
| Paths | Running from another directory can’t find files | No relative paths; absolute paths anchored on __file__ |
| Formats | The competition server’s prompt/newlines differ from the demo | Isolate EXPECT as an edit point; the habit of printing the first receive |
We actually reproduced the path factor. What happens when you run code that opens a payload file by relative path from outside the library folder — see the measurement in 3-3.
2-4. Measurement — "It Got Faster" in Numbers
This is a validation chapter, so impressions are banned. Two numbers to record.
Template usage record table (filled in real time during the competition):
| problem | type | template | match verdict | edit→first send | solved | notes |
|----------|-----------|------------|---------------|-----------------|--------|-------|
| pingpong | prompt | tcp_prompt | 8 sec | 4 min | O | edited EXPECT only |
| noteapp | web SSTI | (none) | — | — | X | reused payloads only |
The "edit → first send" time is the library’s report card. If the same type took 30 minutes from scratch at the previous competition, 4 minutes is 26 minutes the library earned you. The retrospective’s "total time saved" is computed as this table’s sum.
3. Follow Along
3-1. Before the Competition — The Environment Rehearsal
The day before the competition, run the entire library once on the PC you’ll use at the competition — not the PC you wrote it on. The dependency factor must all be filtered out here.
Environment rehearsal checks (on the competition PC):
[ ] Right after git clone, does the index generator run as-is?
[ ] Does each template run against the local demo server?
[ ] Does the Python version match the templates (f-strings, etc.)?
[ ] Is the index README placed where you'll view it mid-competition (second monitor/printout)?
3-2. Local Measurement — A Baseline for Template Solve Times
Don’t set the "5-minute verdict" baseline by feel — measure it locally. We ran a script (step291_measure.py) that spins up a fresh demo server each time and measures the template solve time over 5 runs.
Measured (2026-09-09, Python 3.12):
=== Template-based solve-time measurement (5 runs, local demo) ===
Run 1: 0.067s flag captured=success
Run 2: 0.076s flag captured=success
Run 3: 0.074s flag captured=success
Run 4: 0.058s flag captured=success
Run 5: 0.069s flag captured=success
Average 0.069s / max 0.076s
* Local measurements — the competition server adds network latency
How to read it: pure execution is under 0.1 seconds — meaning nearly all of a template solve time is the human’s edit-point editing time. The localhost-vs-competition-server difference (tens to hundreds of ms) is noise inside that. Conclusion: the 5 minutes of the 5-minute verdict is calibrated not to technology but to human editing time, and for a familiar template, 1–2 minutes should be the norm.
3-3. Reproducing the Path Failure — A Preventive Shot for Mid-Competition Accidents
Let’s actually reproduce the path factor from 2-3. Run code that opens a payload by relative path from somewhere other than the library folder:
Measured (2026-09-09 — running a ctf_lib script from a tmp_test folder):
Current working directory: C:UsersdlqhtDocumentskimitasks2026-09-0821-57-12-c850bf8ctmp_test
FileNotFoundError occurred: [Errno 2] No such file or directory: 'payloads\web_payloads.py'
Fix: use an absolute path anchored on __file__
Absolute path: C:...tmp_testctf_libpayloadsweb_payloads.py
Read succeeded: 908 bytes
How to read it: the same script dies depending on where you run it. Mid-competition you end up copying files all over the place, so this accident is nearly guaranteed. The prevention is a one-line rule — file-opening code is written only relative to Path(__file__).resolve().parent. Apply this rule to every template before this competition.
3-4. Running the Competition — Executing Type Matching and the 5-Minute Verdict
When the competition starts, open the index README beside you. Each time you open a problem, run 2-1’s three questions and fill 2-4’s record table.
Screen example (mid-competition operations):
14:05 Opened problem 'pingpong' — TCP prompt type. 3-second index scan → tcp_prompt_template matched
14:06 Copied template, edited HOST/PORT/EXPECT (the prompt is '$ ', not '> ')
14:09 First send succeeded, response checked → started hunting the TRIGGER
14:31 Solved. Record: match 8 sec, edit→first send 3 min, solved in 26 min
15:20 Opened problem 'notepad' — web. No template → reused only the SSTI probes from payloads/web_payloads.py
Record: wrote the skeleton fresh; estimated 10 min saved by reusing detection payloads
16:40 Problem 'streamer' — looked prompt-type so matched the template, but it's a double-prompt structure
First send failed after 5 min of modification → verdict rule invoked, wrote from scratch (solved afterward)
Record: 'double prompt' → library improvement item
How to read it: the last case is this competition’s key scene. The template not fitting is not a failure but a discovery — the structural variant "double prompt" just got registered as a library blind spot. Thanks to the 5-minute verdict rule, the loss stopped at 5 minutes.
3-5. The Retrospective — Summing Saved Time and Patching Improvements
After the end, sum the record table.
=== Library validation retrospective (template) ===
Solved using templates: 2 problems (completion criterion met)
Payloads-only reuse: 1 problem
Forced match then discarded: 1 case (5-minute loss — verdict rule worked)
Estimated total time saved: about 50 min (vs. same types at previous competitions)
Improvement items (→ v2 patch):
[ ] Double-prompt handling — add a variant template chaining recvuntil twice
[ ] Missing web template — add one requests-based session template
[ ] Found 2 files without the path rule applied — patch them all
And make the patch commit on the spot.
cd ctf_lib
git add .
git commit -m "v2: double-prompt template added, path rule applied everywhere (competition #7 retrospective)"
A retrospective must end in a commit for the library to stay a living asset. An improvement item that ends with "I’ll fix it later" eats the same 5 minutes again at the next competition.
4. Missions & Exercises
Mission — Live-Fire Library Deployment and the v2 Patch
- The day before the competition, run the environment rehearsal checklist (3-1) on the competition PC.
- Verify the path rule (
__file__-anchored) is applied to every template. - During the competition, apply the type-matching three questions and the 5-minute verdict rule, and fill the record table (2-4) in real time.
- Aim for 2+ problems solved using templates.
- After the end, estimate the total time saved and close the improvement items as a commit (v2).
Exercises
Exercise 1. Why does the type-matching three-question procedure ask question 3 ("are the edit points a 5-minute job")? What misjudgment arises from questions 1 and 2 alone?
Exercise 2. In 3-2’s measurement, a template’s pure execution was under 0.1 seconds. Then why is the 5-minute verdict’s unit minutes, not seconds?
Exercise 3. Explain why a template that opens files by relative path breaks mid-competition, from the "execution location" standpoint, and write the one-line prevention rule.
Exercise 4. Why is a template case discarded after 5 minutes for not fitting called a "discovery" rather than a "failure"? How should that case be handled in the retrospective?
5. Model Answers & Completion Criteria
Mission Model Answer
Verify against these criteria.
- Traces of the rehearsal: is there a record of running right after clone on the competition PC — checking only on the writing PC is not a rehearsal.
- The record table’s real-timeness: are per-problem match verdicts and edit times present with timestamps — a table filled from memory after the end has inflated numbers.
- The 5-minute verdict in action: if there’s a forced-match discard case, did it stop near 5 minutes? If there isn’t — look back on whether everything truly fit, or whether you failed to use the verdict rule.
- Completion criterion: 2+ problems solved using templates.
- The v2 commit: are the improvement items in the repository with a commit message — if only the retrospective document exists and the code is unchanged, you did only half the validation.
Exercise Answers
Answer 1. Because even with the same shape, a different internal structure means the template doesn’t fit. Questions 1 and 2 compare only "the outer look of I/O" — even sharing the TCP-prompt shape, structural differences like double prompts or mixed binary get filtered at question 3. Skip question 3 and you fall into forced matching, clinging to a template you thought fit, where modification time exceeds problem-solving time.
Answer 2. Because most of a template solve time is not the computer’s execution time but the human’s editing time. Execution is under 0.1 seconds, but checking HOST/PORT, fitting EXPECT to the competition server’s format, and hunting the TRIGGER are keyboard work by a human. Network latency is noise-level inside that. So the verdict criterion is 5 minutes — a margin over the time a human realistically finishes edits in (1–2 minutes).
Answer 3. Because a relative path resolves against the directory the command was run from (cwd), not the script file’s location. Mid-competition you copy templates into per-problem folders, so the cwd changes every time, and the same file opens in one place and dies with FileNotFoundError: [Errno 2] in another. Prevention rule: file-opening paths are always written relative to Path(__file__).resolve().parent.
Answer 4. Because that case gave you the coordinates of a library blind spot. The information "doesn’t fit double prompts" is an asset that makes next competition’s match verdicts more accurate. Handling comes in two branches — ① note the "cases where it breaks" in the conditions-of-use comment, or ② add a new template covering the variant in v2. Either way, it must remain as a record for the 5-minute loss to be recovered as knowledge.
Completion Criteria Checklist
- [ ] I completed the environment rehearsal (run right after clone) on the competition PC
- [ ] I applied the
__file__-anchored path rule to every template - [ ] I recorded type-match verdict times and edit→first-send times during the competition
- [ ] I applied the 5-minute verdict rule (with time records for any discard cases)
- [ ] I achieved 2+ problems solved using templates
- [ ] I estimated the total time saved and recorded it in the retrospective
- [ ] I made a v2 commit reflecting the improvement items
6. Common Pitfalls & Fixes
Wall 1. The templates won’t run at all on the competition PC
Symptom: they worked on the writing PC, but import errors and syntax errors appear on the competition PC.
Cause: Python version differences or missing packages — the dependency factor of 2-3. If the writing environment and competition environment differ, this is guaranteed to blow up.
Fix: the right answer is doing 3-1’s environment rehearsal the day before, on the competition PC. And structural prevention — keep utilities that run on the standard library alone, as in Step 290, and the skeleton survives even in environments without pwntools.
Wall 2. The child process’s output won’t read and my measurement script dies
Measured (2026-09-09 — actually occurred while writing the measurement script):
UnicodeDecodeError: 'cp949' codec can't decode byte 0xec in position 38: illegal multibyte sequence
Cause: on Korean Windows, subprocess.run(..., text=True) reads output in the system default encoding (cp949). If the child prints UTF-8 Korean, decoding breaks.
Fix: specify encoding="utf-8" — subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8"). It’s a chronic disease of Python parent-child relationships on Korean Windows, so make it a default habit for measurement and automation scripts.
Wall 3. With a library, I feel like I’ve forgotten how to write from scratch
Symptom: when a type with no template appears, your hands stop.
Cause: library dependence — templates have started doing your thinking for you.
Fix: that’s why the 2-4 record table has a "no template" column. If the time for fresh-written cases has grown versus before, that type is the next drill topic. The library must be a delegation of repetitive work, not a muscle replacement — maintain the write-from-scratch muscle separately with Step 288-style drills.
Wall 4. I can’t keep the 5-minute verdict and keep modifying
Symptom: "just a little more and it’ll work" eats 20 minutes.
Cause: sunk cost — the 5 minutes already spent feel too precious to stop.
Fix: fix the verdict sentence in advance — ask "from this state right now, is writing from scratch faster?" The 5 minutes already spent don’t enter the answer. And don’t throw away the discarded template — park it in wip/. At the retrospective it’s evidence of "why it didn’t fit." The precious 5 minutes don’t vanish; they’re exchanged into an improvement item.
Wall 5. I spend the competition fixing the library instead of solving problems
Symptom: you start a fundamental template improvement mid-competition.
Cause: the excitement of discovery — you see a blind spot and want to fix it immediately.
Fix: fix the allowed scope of mid-competition edits — values inside the edit-points block only. All structural improvements are recorded only and sent to the retrospective. Mid-competition, the library is not for fixing but for using while taking memos.
7. Summary
Today’s Concepts
| Concept | One-line explanation |
|---|---|
| Type-matching 3 questions | I/O shape → index scan → edit-workload verdict, within 10 seconds |
| 5-minute verdict rule | No first send within 5 minutes → discard — the cutoff line against forced matching |
| Three factors of field collapse | Dependencies / paths / input formats — all filterable before the competition |
| Edit→first-send time | The library’s report card — execution is 0.1s, the rest is human editing |
| Improvement patch commit | The retrospective’s closing format — as v2 code, not a memo |
Today’s Tools & Templates
| Tool/template | What it does |
|---|---|
| Environment rehearsal checklist | Pre-validation on the competition PC — blocks dependency accidents |
step291_measure.py |
Measuring the local baseline of template solve times |
__file__ path rule |
Opening files regardless of execution location |
| Template usage record table | Real-time records of match/edit/solve times |
| v2 patch commit | Exchanging the competition’s discoveries into assets |
The Core Instinct
Measure a library’s value not by storage volume but by time recovered per competition. If you saved 50 minutes at today’s competition, those 50 minutes are the interest on the time invested in the library. And the interest compounds — v2 earns bigger time at the next competition.
Conversely, remember — templates don’t substitute for judgment. The 5-minute verdict and the decision "this one doesn’t fit" remain your job. The bigger the asset grows, the more important the sense of when to put it down becomes.
Once every box is checked, Step 291 is complete.