PEN-200 Series, Part 1: Note-Taking and the Report That Actually Scores Points

What to capture during a test, three note-taking setups that hold up for 24 hours straight, a terminal-logging command you can verify right now, and the exact report structure the OSCP+ documentation requirements describe.

8 min read
ibrahimsql
1,538 words

PEN-200 Series, Part 1: Note-Taking and the Report That Actually Scores Points

This opens a series that follows the PEN-200 module order end to end — the plan is in the OSCP+ preparation guide. Starting with note-taking instead of a technical module looks backwards until the exam clock is at hour 20 and you're trying to remember which curl flag got you the shell on the second machine. Documentation is scored like everything else, and the official rules are specific about it:

"Your documentation should be thorough enough that your attacks can be replicated step-by-step by a technically competent reader. [...] failure to provide sufficient documentation will result in reduced or zero points being awarded."

That is from the OSCP+ Exam Guide's documentation requirements section. This post is about building a note-taking habit during PEN-200 so that sentence is never a problem on exam day.

What to capture, as you go

Four things, for every command that matters:

  1. The exact command, copy-pasted, not retyped from memory afterward.
  2. The full output, or the part that mattered, with context.
  3. One line on why you ran it — what you expected, what you got.
  4. A screenshot at the moments the exam guide specifically requires one: initial access and privilege escalation on each machine, each with the proof file content and the target's IP address in the same frame.

Writing the "why" is the part people skip, and it's the part that makes a report readable months later. "Ran this because the robots.txt disallow entry pointed here" is one sentence and saves you from re-deriving your own reasoning during the 24-hour report window, when you're exhausted and the methodology has to come from memory.

Don't retype commands into your notes — capture the terminal

Retyping commands from memory after the fact is how reports get subtly wrong: a flag gets dropped, an IP gets mistyped, and now the report doesn't reproduce. Two ways to capture a session as it happens, both built into any Kali install:

script — wraps a whole shell session into a typescript file:

Bash
script -t 2> timing.log session.log
# ... do all your work in this shell ...
exit
# replay it later at the original speed:
scriptreplay -t timing.log session.log

tmux pipe-pane — logs one pane continuously without wrapping the shell, so it works even for a pane you're already in:

tmux pipe-pane -o -t 0 'cat >> ~/notes/pane-0.log'

Either one means the exact command you ran is sitting in a file, not in your memory, when you sit down to write the report. Pick one and build the habit on every practice machine starting now — not in week 11 of the 12-week plan, when it's too late to become automatic.

Turning a captured session into report text automatically

A script-captured log is complete, but it's also noisy: the Script started on... header, the shell prompt repeated on every line, the Script done on... footer. Pasting it raw into a report is readable but ugly. A small parser turns it into a clean fenced block in seconds — here's one, tested against real script output:

Python
#!/usr/bin/env python3
"""Turn a `script`-captured terminal session into Markdown command blocks.
 
Usage: python3 log2md.py session.log > session.md
"""
import re
import sys
 
HEADER_RE = re.compile(r'^(Script (started|done) on|COMMAND(_EXIT_CODE)?=)')
PROMPT_RE = re.compile(r'^\S+@\S+:\S+[#$]\s?')
 
def clean(lines):
    out = []
    for line in lines:
        if HEADER_RE.search(line):
            continue
        out.append(PROMPT_RE.sub('', line.rstrip('\n')))
    return out
 
def main():
    path = sys.argv[1] if len(sys.argv) > 1 else '/dev/stdin'
    with open(path, errors='replace') as f:
        lines = clean(f.readlines())
    text = '\n'.join(lines).strip('\n')
    text = re.sub(r'\n{3,}', '\n\n', text)
    print('```bash')
    print(text)
    print('```')
 
if __name__ == '__main__':
    main()

Run against a real captured session:

Text
$ cat fake-session.log
Script started on 2026-10-15 22:42:19+00:00 [COMMAND="bash" <not executed on terminal>]
root@kali:~# whoami
root
root@kali:~# id
uid=0(root) gid=0(root) groups=0(root)
root@kali:~# exit
Script done on 2026-10-15 22:42:30+00:00 [COMMAND_EXIT_CODE="0"]
$ python3 log2md.py fake-session.log

produces this, ready to paste into the report template from the section above:

Bash
whoami
root
id
uid=0(root) gid=0(root) groups=0(root)
exit

The regexes are deliberately simple — a different prompt format (zsh, a custom PS1, a different hostname pattern) needs a one-line tweak to PROMPT_RE. That's the point: it's thirty lines you can read and adapt in under a minute during the exam, not a dependency to install and trust. Run it over every script log right after you capture it, and your "Steps to reproduce" sections are most of the way written before you've consciously started writing the report.

A note-taking setup that survives 24 hours

The tool matters less than the structure. Three real options, pick one before you start PEN-200's modules so your module notes and your exam notes live in the same format:

  • CherryTree — a hierarchical notebook with per-node syntax highlighting for code blocks. The long-standing default in offensive security courses for a reason: fast, offline, and a tree structure maps naturally onto "target → service → finding."
  • Obsidian — plain Markdown files on disk, which means they're grep-able during the exam and diffable in git for your practice machines. Slower to structure than CherryTree but the files outlive the tool.
  • A plain directory of Markdown files, one per target, no app at all. Works everywhere, costs nothing, and is genuinely enough if you're disciplined about a consistent template — which matters more than the tool.

Whichever you pick, the node/file structure should mirror the exam control panel: one top-level entry per target, with fixed subsections inside each. Here's a template that matches what the next section asks for in the final report, so your working notes convert into report sections with minimal rewriting:

Markdown
# TARGET-NAME (10.10.X.X)
 
## Scope & objectives
-
 
## Enumeration
### Ports
```
<!-- nmap output -->
```
 
### Services
-
 
## Exploitation
### Vulnerability
-
 
### Exploit code
- Source: <url>
- Modified: yes/no
- Changes made and why:
 
### Steps to reproduce
1.
 
### Proof — local.txt
```
<!-- screenshot reference + ip a output -->
```
 
## Privilege escalation
### Vector
-
 
### Steps to reproduce
1.
 
### Proof — proof.txt
```
<!-- screenshot reference + ip a output -->
```
 
## Remediation
-

Fill this in live, not from memory at the end. The gap between "I'll write it up properly later" and actually doing it is where points are lost — not because the work wasn't done, but because it wasn't recorded while it was fresh.

Exploit code: what the rules actually ask for

This trips people up because the instinct is to paste everything. The official guidance is narrower:

"If you have not made any modifications to an exploit, you should only provide the URL where the exploit can be found. Do not include the full unmodified code [...] If you have modified an exploit, you should include: the modified exploit code, the URL to the original exploit code, the command used to generate any shellcode (if applicable), highlighted changes you have made, and an explanation of why those changes were made."

In practice: keep a one-line note next to every public exploit you run — the exact URL and whether you touched it. If you did touch it, a diff (diff original.py modified.py) pasted into your notes does the "highlighted changes" part for you with zero extra writing later.

The report structure, mapped to what's scored

The OSCP+ body of knowledge lists the report's required pieces almost verbatim as learning objectives:

  • An executive summary — non-technical, written for someone who will never read the technical findings, stating business impact and overall risk.
  • A technical summary — the methodology, scope, and tools at a level a technical reader skims before diving into individual findings.
  • Per-target findings — vulnerability, evidence, exact reproduction steps, and a recommendation, in the structure above.
  • Appendices — raw scan output, wordlists used, anything that supports a finding without cluttering it.

Two suggested templates exist officially, a Word version and an OpenOffice/LibreOffice version. You can use your own as long as it covers the same ground — a Markdown template rendered through SysReptor or noraj's OSCP template works and is what most people actually write in, because Markdown-in, PDF-out beats formatting a 40-page Word document by hand at hour 23. Whichever you choose, use it for every practice machine from here on — not just the three exam simulation weeks — so filling it in is routine, not a new skill you're learning under a countdown.

What this buys you later in the series

Every module from here on assumes you're logging the session and filling in notes as you go, because the content of those notes is what later posts build on: enumeration output becomes the evidence for Part 2, exploitation steps becomes the reproduction steps for the SQL injection and client-side modules, and the whole directory becomes your own searchable reference by the time Active Directory shows up and you need to remember how you kerberoasted a target three practice machines ago.

Part 2 covers information gathering — the first module where your notes start actually filling up.

---
Share this post:

What do you think?

React to show your appreciation

Related Posts