Step 307. ★ Project — Finalizing the Exam-Format Report Template

Step 307. ★ Project — Finalizing the Exam-Format Report Template

Level 4 — Expert | Difficulty ★★★☆☆ | Estimated time: 1 day

Prerequisites: you finished Step 128 (the MS2 report) and Steps 305–306 (4 machines + 4 drafts). You are comfortable writing Markdown.

  • What you need: the 4 report drafts accumulated in Steps 305–306, a Python runtime, a Markdown editor, and (optionally) pandoc or Obsidian.
  • ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.
  • Measurement guide: the validation-script run results in this chapter are output actually executed in the writing environment (Python 3.12.14, Windows). The sample content of the exam report (machines, flags) is fabricated example material.
  • Rules caution: OSCP report requirements can change. Before sitting the exam, re-confirm the evidence rules in the official exam guide.

Step 128 built the report’s skeleton; Steps 305–306 built the habit of recording. Today we finalize those two into a final template at exam specification. The OSCP requires you to submit a report within 24 hours after the practical ends — the difference between someone who spends those 24 hours "agonizing over structure" and someone who spends them only "filling content" decides the pass. Once you’ve built the template today, plus a validation script that catches what’s missing, exam day’s report starts with half the work already done.


1. Learning Objectives

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

  • Translate OSCP report requirements (reproducible description, evidence screenshot rules) into a template structure
  • Finalize a Markdown template with sections for standalone machines and the AD set
  • Run a script that checks required sections, evidence keywords, and unfinished placeholders
  • Perform "phase-by-phase recording" — filling the template alongside the attack — on real machines
  • Build a Markdown → PDF conversion pipeline

2. Background Knowledge — Today’s Tools and Concepts

Today’s Tools at a Glance

Category Details
Language/environment Markdown + Python (validation script) + a PDF conversion tool (pandoc or Obsidian)
Today’s commands python validate_report.py report.md, pandoc report.md -o report.pdf
Concepts needed Reproducible description, evidence rules (local/proof + hostname + ip), Step 128’s six-part structure
Today’s deliverable oscp-report-template.md + validate_report.py + 2 reports written in the template (6 cumulative)

2-1. The Exam Report’s Three Requirements

Compress what the exam guide demands into three lines (check the official guide for details):

① Reproducibility — a reviewer can reproduce the penetration by the same procedure from the document alone
② Evidence completeness — the contents of local.txt/proof.txt + a screenshot showing
   that machine's identifying information (hostname, ip) together
③ Code attribution — if you used a public exploit, state the original source and what you modified

How is this different from Step 128’s six-part structure (summary, recon, path, escalation, evidence, remediation)? The philosophy is the same; the enforcement is different. Step 128 was the standard for "a good report"; the exam report is a specification that gets points docked if you don’t match it. That’s why we fix it as a template.

2-2. Template Design — A Repeating Block per Machine

The exam has multiple machines, so the template is designed as a repeating structure of one machine = one block.

# OSCP Exam Report            ← document head: author, OSID, exam date/time
# Machine N: [name] (IP)      ← repeat this block per machine
  ## Summary                  ← attack path in one sentence
  ## 1. Enumeration           ← full scan + detail scan output
  ## 2. Initial foothold      ← vulnerability grounds + all commands
  ## 3. Privilege escalation  ← recon findings + all commands
  ## 4. Evidence              ← local/proof + hostname + ip captures
  ## 5. Remediation           ← connected 1:1 to the findings
# AD Set                      ← set-specific block: includes a chain diagram
# Appendix A. Tool list       ← confirmation that no prohibited tools were used
# Appendix B. Exploit modifications ← original source and diff

There is one design principle — on exam day, there must be nothing to think about regarding structure. Leaving only blank-filling is the template’s job.

2-3. The Validation Script — Automatic Checks for "What’s Missing"

In a long document, "I forgot the summary" or "the proof screenshot section is empty" are the mistakes people are worst at finding. So, as a set with the template, we build a script that checks a draft’s required elements. What it checks:

  • Do the required sections (summary through remediation) exist?
  • Are the evidence keywords (local.txt, proof.txt, hostname) present?
  • Are there enough command/output code blocks?
  • Are any unfinished placeholders (blank lines, "paste-here"-type markers) left?

Right before the 24-hour report deadline, one run of this script tells you whether it’s "in a submittable state."

2-4. Markdown → PDF — Securing the Submission Format

Exam submission is PDF. Two conversion paths are standard.

Tool Command/method Characteristics
pandoc pandoc report.md -o report.pdf Scriptable, good reproducibility. Separate install required
Obsidian Export → PDF Convert while checking right in the editor

The writing environment (Windows Git Bash, WSL Ubuntu) did not have pandoc installed (measured: which pandoc returned nothing). Check whether your environment is the same first; if not installed, install it or take the Obsidian path. What matters is completing one conversion before the exam — if your first PDF conversion happens on exam day, you’ll be solving problems like missing images inside the deadline.


3. Follow Along

3-1. Writing the Template File

Create oscp-report-template.md. The skeleton (the same structure verified with the manuscript):

# OSCP Exam Report

Author: __________
OSID: __________
Exam date: ____ / __ / __

---

# Machine 1: [machine name] (10.10.x.x) — standalone machine, 20 points

## Summary
(Attack path in one sentence: "Obtained a shell via the ___ vulnerability
 in the web service on port 80, then escalated to SYSTEM via a ___ misconfiguration.")

## 1. Enumeration
### nmap full-port scan
(command: nmap -p- --min-rate 1000 10.10.x.x — paste output)
### Service detail scan
(command: nmap -sC -sV -p [discovered ports] 10.10.x.x — paste output)
(one or two lines of interpretation per service: version, known-vulnerability candidates)

## 2. Initial Foothold
### The vulnerability and its grounds
(what is vulnerable and why — confirmed grounds, not guesses)
### Attack process (reproducibly)
(every command run, in order)
(initial shell evidence screenshot: including whoami + ip a)

## 3. Privilege Escalation
### Recon and findings
### Escalation process
(every command run, in order)

## 4. Evidence
### local.txt
(screenshot showing local.txt contents + hostname + ip a together)
### proof.txt
(screenshot showing proof.txt contents + hostname + ip a together)

## 5. Remediation
1. [severity] (fix per finding — connected 1:1 to the findings in the body)

---

# Machine 2: AD Set (10.10.x.0/24 range) — 40 points

## Summary
(Chain in one sentence: "Entered via the client's ___, harvested ___ credentials,
 moved via ___, and compromised the domain controller.")

## Chain Diagram
[client IP] --(technique: ___, credential: ___)--> [middle server IP] --> [DC IP]

## 1. First machine compromise
## 2. Domain information gathering
## 3. Credential harvesting and lateral movement
## 4. Obtaining Domain Admin
## 5. Evidence for each machine in the set
## 6. Remediation

---

# Appendix A. Tools Used
(tool name — purpose — one line on compliance with prohibited-tool restrictions)

# Appendix B. Modifications to Referenced Exploit Code
(original source, modified parts as a diff)

The writing environment made this entire template into a file and used it for validation — you should start by copying this skeleton as-is, but as you use it, your own sections (e.g., "post-escalation recon") will appear. That is the template becoming yours.

3-2. Writing the Validation Script

Create validate_report.py. The full code (the version actually executed):

# OSCP report template/draft validation script
# Purpose: check whether a report (Markdown) in progress has the exam format's required elements.
# Run: python validate_report.py <report.md>
import re
import sys
from pathlib import Path

# Sections that must exist inside a machine block (standalone machine basis)
REQUIRED_SECTIONS = ["Summary", "1. Enumeration", "2. Initial Foothold", "3. Privilege Escalation", "4. Evidence", "5. Remediation"]
# Keywords to confirm in the evidence section
EVIDENCE_KEYWORDS = ["local.txt", "proof.txt", "hostname"]
# Unfinished placeholders that must not remain
PLACEHOLDERS = ["paste output", "____", "TODO", "fill in"]


def check(path: Path) -> int:
    text = path.read_text(encoding="utf-8")
    fails = 0

    print(f"Checking: {path.name} ({len(text.splitlines())} lines)n")

    for sec in REQUIRED_SECTIONS:
        ok = re.search(rf"^##s.*{re.escape(sec)}", text, re.MULTILINE) is not None
        print(f"[{'PASS' if ok else 'FAIL'}] required section present: {sec}")
        fails += 0 if ok else 1

    for kw in EVIDENCE_KEYWORDS:
        ok = kw in text
        print(f"[{'PASS' if ok else 'FAIL'}] evidence keyword included: {kw}")
        fails += 0 if ok else 1

    code_blocks = len(re.findall(r"^```", text, re.MULTILINE)) // 2
    ok = code_blocks >= 3
    print(f"[{'PASS' if ok else 'FAIL'}] at least 3 command/output code blocks: {code_blocks}")
    fails += 0 if ok else 1

    for ph in PLACEHOLDERS:
        found = ph in text
        print(f"[{'FAIL' if found else 'PASS'}] no unfinished placeholder: '{ph}'"
              + ("  ← found" if found else ""))
        fails += 1 if found else 0

    print(f"nResult: {'PASS — submittable' if fails == 0 else f'{fails} item(s) need work'}")
    return fails


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python validate_report.py <report.md>")
        sys.exit(2)
    p = Path(sys.argv[1])
    if not p.exists():
        print(f"File not found: {p}")
        sys.exit(2)
    sys.exit(0 if check(p) == 0 else 1)

How to read it: three groups of checks — ① find the 6 required sections with the regex ^## .*section-name, ② confirm the 3 evidence keywords exist, ③ FAIL if a placeholder remains. The exit codes are designed too — 0 pass, 1 needs work, 2 argument/file error. It’s built to become a part of a pipeline that runs automatically before the PDF conversion later.

3-3. Measured — Script Run Results

Here are the results of actually running it against two files in the writing environment.

Run against the empty template (oscp_report_template.md, 117 lines) — it catches the placeholders precisely:

Checking: oscp_report_template.md (117 lines)

[PASS] required section present: Summary
[PASS] required section present: 1. Enumeration
[PASS] required section present: 2. Initial Foothold
[PASS] required section present: 3. Privilege Escalation
[PASS] required section present: 4. Evidence
[PASS] required section present: 5. Remediation
[PASS] evidence keyword included: local.txt
[PASS] evidence keyword included: proof.txt
[PASS] evidence keyword included: hostname
[PASS] at least 3 command/output code blocks: 5
[FAIL] no unfinished placeholder: 'paste output'  ← found
[FAIL] no unfinished placeholder: '____'  ← found
[PASS] no unfinished placeholder: 'TODO'
[PASS] no unfinished placeholder: 'fill in'

Result: 2 item(s) need work
(exit code 1)

Run against a completed sample report (85 lines):

Checking: report_sample_filled.md (85 lines)

[PASS] required section present: Summary
(…snip…)
[PASS] at least 3 command/output code blocks: 4
[PASS] no unfinished placeholder: 'paste output'
[PASS] no unfinished placeholder: '____'
[PASS] no unfinished placeholder: 'TODO'
[PASS] no unfinished placeholder: 'fill in'

Result: PASS — submittable
(exit code 0)

How to read it: the raw template FAILing means the script is working correctly — it expresses exactly the state "structure is in place but still blank." On exam day, when your finished report shows "Result: PASS — submittable," it’s in a submittable state.

3-4. 2 Practice Machines — Filling the Template Alongside the Attack

Following Step 306, take down 2 more machines (6 cumulative). This time there’s one rule.

Not "write after solving everything" but "fill the template at each phase"

When enumeration ends, immediately paste the output into ## 1. Enumeration; when you get a shell, immediately fill ## 2. The feeling that recording slows the attack is normal — and that delay shrinks to within 10 minutes around 6–8 cumulative machines. That is this drill’s harvest.

3-5. Reproducibility Review and PDF Conversion

After the takedown, take the document alone and grade it yourself — "could I reproduce this from the document alone?" Fill in missing commands and screenshots, run the validation script, and convert to PDF.

python validate_report.py lame-report.md
pandoc lame-report.md -o lame-report.pdf   # if pandoc is installed

Open the PDF and visually confirm the images (evidence captures) are actually embedded — if the Markdown’s image paths are relative, they often drop out during conversion. Completing this whole pipeline (write → validate → convert → eye check) at least twice before the exam is one of today’s mission conditions.


4. Missions & Exercises

Mission — Finalize the Template and Apply It for Real

  1. Create oscp-report-template.md from 3-1’s skeleton
  2. Create 3-2’s validate_report.py and confirm for yourself that the empty template FAILs and a filled report PASSes
  3. Record 2 practice machines "alongside the attack" in the template, completing 2 reports (6 cumulative)
  4. Run the validation script on the finished reports and confirm "PASS"
  5. Complete the Markdown → PDF conversion, open the PDF, and visually confirm the evidence images are embedded

Exercises

Exercise 1. What is the difference between the exam report and Step 128’s report — same philosophy, but different as a "specification"?

Exercise 2. Why is the template designed as a repeating structure of "one machine = one block"?

Exercise 3. Explain why the validation script FAILing on an empty template is "evidence of correct behavior."

Exercise 4. Give one concrete accident scenario explaining why the first PDF conversion must be done during practice, not on exam day.


5. Model Answers & Completion Criteria

Mission Model Answer

The mission’s completed state: ① the template file exists and every section carries blank-filling guidance; ② the script outputs "needs work" (FAIL) on the template and "pass" (PASS) on a finished report; ③ the 2 machines’ reports are written exactly in the template format; ④ the PDF conversion was completed.

How to verify: ① if you copy-paste the template’s machine block, does it directly become the next machine’s format — is it a repeating structure? ② if even one FAIL appears in the script output, did you fix that item and re-run? ③ do the 2 finished reports’ ## 4. Evidence sections have captures showing hostname+ip? ④ are no images missing in the PDF? ⑤ if the perceived time delay of recording mid-attack exceeded 30 minutes — split the after-phase recording into smaller pieces on the next machine (e.g., 3 minutes right after getting a shell).

Exercise Answers

Answer 1. Step 128’s structure was the recommended standard for "a good report," but the exam report is a grading specification where breaking the requirements costs points. The philosophy — evidence-centered, reproducible description, findings connected to remediation — is the same; what differs is enforcement. So the exam version is not redesigned each time but fixed as a template, so the day’s 24 hours go only to filling content, not agonizing over structure.

Answer 2. Because the exam has multiple machines, and each machine demands the same information (summary through evidence through remediation). A repeating structure guarantees two things — no machine drops a section, and a grader moving between machines finds the same information in the same position. Document consistency is grading ease, and grading ease is points.

Answer 3. The template is intentionally in a state of "structure present, content blank," so a correct validator must report that state as FAIL — a validator that passes blanks would also pass a finished report with blanks mixed in on exam day, losing trust. Conversely, PASSing on a filled report confirms the checks aren’t excessive either. "Can it produce both FAIL and PASS?" is the validation of the validation tool.

Answer 4. Scenario: first pandoc run 2 hours before the report deadline — the PDF comes out with all 14 images missing because the paths are relative, and you blow the deadline fixing paths. Conversion is not "it’ll work" — it’s a pipeline: write → validate → convert → eye check; all four boxes must be verified for exam day to have no variables. The two full PDF runs in practice are the procedure that moves this accident into the practice room.

Completion Criteria Checklist

  • [ ] I can explain the exam report’s three requirements (reproducibility, evidence, code attribution)
  • [ ] I finalized the template with a per-machine repeating structure
  • [ ] I confirmed for myself that the validation script produces both FAIL and PASS
  • [ ] I recorded 2 machines in the template alongside the attack (6 cumulative)
  • [ ] The finished reports received "PASS" from the validation script
  • [ ] I completed the Markdown → PDF conversion and visually confirmed the images
  • [ ] Mission: template + script + 2 reports + PDF run complete

6. Common Pitfalls & Fixes

Wall 1. Running the script prints File not found or the usage message

Symptom: only Usage: python validate_report.py <report.md> prints and it exits (exit code 2).
Cause: you ran it without an argument, or the path is wrong — non-ASCII characters or spaces in the filename are the suspects.
Fix: run it from the folder containing the file, and if the name has spaces, wrap it in quotes — python validate_report.py "my report.md".

Wall 2. The structure is right but "no unfinished placeholder" FAILs

Symptom: [FAIL] no unfinished placeholder: '____' ← found.
Cause: a placeholder you left mid-writing remains — the script caught what your eyes had scanned past.
Fix: this is exactly why you use the script. Search the string in your editor and fill every occurrence. And if you unify the template’s placeholder notation (e.g., ____), searching gets faster.

Wall 3. No pandoc — command not found

Symptom: pandoc: command not found (on Windows, a "’pandoc’ is not recognized as an internal or external command…" type message).
Cause: pandoc is not a default-installed tool — it wasn’t installed in the writing environment either.
Fix: ① install it from the official distribution, or ② use Obsidian’s PDF export. Whichever you choose, fix the path during practice. Postponing the conversion-tool decision to exam week is the real danger.

Wall 4. Recording slows the attack and both become a mess

Symptom: recording breaks your flow; attacking leaves recording behind.
Cause: this is normal — that delay is the training target itself.
Fix: split the delay smaller — not "per section" but "per command": make it a habit to paste output into the document the moment an important command returns. From 6–8 cumulative machines, the perceived delay shrinks to within 10 minutes. Being slow now doesn’t mean lack of skill; it means muscle is forming.

Wall 5. Evidence images drop out of the PDF

Symptom: captures that showed in Markdown break in the PDF.
Cause: the image paths (relative) mismatched the working folder at conversion time.
Fix: fix the report and images into one folder structure (e.g., images/ under report/). Run the conversion command from that folder, and after converting, open the PDF and visually confirm the images are embedded — "converted" and "complete" are different stages.


7. Summary

Today’s Concepts

Concept One-line explanation
Exam report’s 3 requirements Reproducible description · complete evidence (local/proof+hostname+ip) · exploit source attribution
Machine-block repeating structure One machine = one block — so no machine drops a section
Validation script Automatic checks of required sections, evidence keywords, placeholders — verified both FAIL and PASS
Phase-by-phase recording Not "write after solving" but "fill at each phase" — the delay shrinks at 6–8 machines
Submission pipeline Write → validate → PDF convert → eye check — a full run before the exam is mandatory

Today’s Commands & Tools

Tool What it does
oscp-report-template.md The finalized exam-format template — machine-block repeating structure
python validate_report.py report.md Automatic required-element check — 0 pass / 1 needs work
pandoc report.md -o report.pdf Submission-format conversion (Obsidian path if not installed)
Step 128’s six-part structure The philosophical prototype of today’s template
Step 305’s evidence folder The supplier of the evidence sections

The Core Instinct

Exam day’s 24 hours stretch exactly as long as the structure you built in practice. With a template, the report turns from "writing work" into "filling work," and with a validation script, the pre-submission anxiety ends with one line: "Result: PASS." What you finalized today is not a document format but time allocation in the exam room. And this template remains after the exam ends — because a real-world penetration test report demands exactly the same three things: reproduction, evidence, and attribution.


Once every box is checked, Step 307 is complete.