Report writing for pentesters
TL;DR: A pentest report is the only artefact the client (and OffSec graders) actually keep. Structure it so an exec reads the first page and decides priorities, an engineer reads the per-finding sections and reproduces the bug, and a future tester reads the methodology and continues the work. OSCP and OSEP both require a report; failing to write it well is a quiet way to fail an otherwise-pass exam.
What goes in a report (in this order)
- Cover page — title, client, dates, your name/handle, classification.
- Executive summary — half a page. Plain English. Risk in business terms. No CVEs in this section.
- Methodology — scope, approach, tools, constraints, dates.
- Findings table — one row per finding: title, severity, affected asset, status.
- Per-finding detail — see template below.
- Appendix — full command logs, screenshots, evidence dumps, raw output.
The per-finding template
For every finding, in this order:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
### F-007: Authenticated SQL injection in /api/orders
**Severity:** High (CVSS 8.1 — AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N)
**Affected asset:** https://app.example.com/api/orders
**Status:** Confirmed (exploited; data extracted)
**Description.** The `sort` parameter on `/api/orders` is concatenated into a
SQL statement without parameterisation, allowing authenticated users to read
arbitrary data from the database.
**Proof of concept.**
1. Authenticate as a low-privileged user.
2. Send the following request:
```http
GET /api/orders?sort=id;SELECT%20current_user-- HTTP/1.1
- The response body contains
postgres— the DB superuser.
Screenshot: Figure 7 — Burp Repeater showing extracted username.
Impact. A low-privileged user can extract all records from any table, including password hashes (users.password_hash), API keys (integrations.api_key), and PII (customers.*).
Recommendation.
- Use parameterised queries (
text("... ORDER BY :sort")). - Allowlist the
sortparameter against the set of valid column names. - Apply principle of least privilege to the DB role used by the web app.
References. OWASP A03:2021 — Injection.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
## OSCP-specific report requirements
Current OffSec rubric expects:
- A single PDF.
- For every machine you compromised: enumeration → vulnerability → exploitation → privilege escalation → flag, with screenshots showing the flag content + `ipconfig`/`ifconfig` + `whoami`.
- For the AD set: full attack path with steps.
- Tooling commands included verbatim (so a marker can reproduce).
- Submit within 24 hours of exam end. **The clock is brutal — write as you go.**
## OSEP-specific additions
- Custom payloads: include the source code in the appendix.
- Evasion: explain *what* you bypassed and *how* you confirmed the bypass (e.g. "AMSI bypass verified via `AmsiScanBuffer` returning `AMSI_RESULT_CLEAN` against `amsiutils`").
- AD chain: a diagram is worth its space.
## Severity calibration
Use a known scale (CVSS 3.1 or 4.0) and *show your vector*. Don't write "High" with no justification. Common OSCP/OSEP mistake: rating "Found credentials in a comment" as Critical — it's High *if* the credentials are valid for production, Medium otherwise.
| Sev | Means |
|---|---|
| Critical | Unauth RCE, full data exposure, takeover |
| High | Auth RCE, privesc, broad data exposure |
| Medium | Limited data exposure, narrow privesc, exploitable with conditions |
| Low | Information disclosure with no direct path to higher impact |
| Informational | Best-practice gap, defence-in-depth |
## Write-as-you-go discipline (the only way to survive OSCP)
Per-host folder structure:
exam/ 10.10.10.5/ scans/ # nmap, gobuster, nikto, ffuf output exploits/ # PoCs, edits screenshots/ # numbered, with brief filenames notes.md # markdown log of everything you did loot/ # hashes, creds, files ```
Take screenshots at the moment of finding, not later. Every screenshot should answer: where am I, who am I, what command did I run, what did it return. The corners of the screenshot must include your attacker hostname/IP for chain-of-custody.
Evidence checklist per host
- Initial nmap scan (top of file:
nmap --versionand timestamp). - Vulnerability identification command + output.
- Exploitation command + first shell line.
- Initial
whoami,ipconfig/ifconfig,hostname. - Privesc command + first SYSTEM/root line.
- Final
whoami+ flag filecat/type.
Tools
- CherryTree or Obsidian — the per-engagement note repo.
- Joplin — solid markdown alternative with snippet attachments.
- PwnDoc / SysReptor — full report-generation pipelines used by professional firms; both have free tiers.
- OffSec OSCP report template — download the official one and use it as your skeleton.
Sanity passes before submission
- Spell check.
- Every screenshot is labelled and referenced from prose.
- Every command can be copy-pasted and runs.
- Severity ratings are consistent across the report.
- Executive summary mentions only what’s in the body.
- The cover page actually has your name on it.