Report writing for pentesters

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)

  1. Cover page — title, client, dates, your name/handle, classification.
  2. Executive summary — half a page. Plain English. Risk in business terms. No CVEs in this section.
  3. Methodology — scope, approach, tools, constraints, dates.
  4. Findings table — one row per finding: title, severity, affected asset, status.
  5. Per-finding detail — see template below.
  6. 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
  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.

  1. Use parameterised queries (text("... ORDER BY :sort")).
  2. Allowlist the sort parameter against the set of valid column names.
  3. 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 --version and timestamp).
  • Vulnerability identification command + output.
  • Exploitation command + first shell line.
  • Initial whoami, ipconfig / ifconfig, hostname.
  • Privesc command + first SYSTEM/root line.
  • Final whoami + flag file cat/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

  1. Spell check.
  2. Every screenshot is labelled and referenced from prose.
  3. Every command can be copy-pasted and runs.
  4. Severity ratings are consistent across the report.
  5. Executive summary mentions only what’s in the body.
  6. The cover page actually has your name on it.

References