Minimal CLI helpers for authorized penetration-testing lab organization and reporting.
Project description
reconlab
Minimal Python CLI helpers for authorized penetration-testing lab work. The project focuses on lab organization, scan parsing, and reporting workflows. It does not include exploit automation and is not intended for use against real third-party systems.
Install
From PyPI (recommended)
pipx install reconlab # isolated install, recommended for CLI tools
# or:
pip install reconlab # if you already manage your own Python env
From source (for development)
git clone https://github.com/0xsl0th/reconlab.git
cd reconlab
python -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
Commands at a glance
| Command | What it does |
|---|---|
workspace init |
Scaffold a target folder, metadata file, and a richer report scaffold. |
workspace suggest |
Generate methodology from scans/ — merges every scan file by default. |
workspace status |
Summarize a workspace — scans, methodology freshness, findings, and the next step. |
workspace check |
Lint report.md for wrap-up readiness — unfilled findings and sections. Exits non-zero on issues. |
finding add / finding list |
Capture findings into report.md; print the current findings table. |
workflow show / workflow list |
Print one workflow as Markdown / list the workflow registry — no scan or workspace needed. |
make-hosts |
Print an /etc/hosts entry plus a copy-paste shell command. Read-only. |
parse-nmap |
Tabular summary of open services from an nmap XML scan. |
suggest-next |
Methodology generator over an explicit scan file (underlies workspace suggest). |
report-init / obsidian-note |
Deprecated — superseded by workspace init / workspace suggest; slated for removal. |
Run reconlab --help (or reconlab <command> --help) for full option
lists, or reconlab --version to print the installed version.
Lab session walkthrough
End-to-end flow for a typical lab target. Replace placeholders with the real values your lab gives you.
# 1. Scaffold a workspace with metadata baked in.
reconlab workspace init target \
--ip 10.10.10.5 \
--host app.corp.local \
--domain corp.local \
--platform lab
cd target
# 2. (Optional) Print the /etc/hosts entry and a copy-paste shell command.
# Does NOT modify /etc/hosts — you run the printed command yourself.
reconlab make-hosts 10.10.10.5 app.corp.local
# 3. Run nmap into the workspace's scans/ folder.
sudo nmap -sV -sC -p- -oA scans/initial 10.10.10.5
# 4. Generate prioritized, service-by-service methodology as an Obsidian vault.
# Metadata from the workspace (target IP / host / domain) is filled in
# automatically — no need to repeat the flags.
reconlab workspace suggest
# 5. Open notes/methodology/index.md in Obsidian (or any Markdown editor).
# Work through the per-service checklists; capture screenshots into
# screenshots/, recovered files into loot/, credentials into creds/.
# 6. Record findings into report.md as you go. The first call replaces the
# placeholder finding; later calls auto-increment the finding number.
reconlab finding add \
--title "SMB null session enabled" \
--severity high \
--service smb \
--port 445 \
--evidence screenshots/01-null-session.png
reconlab finding list # quick check of what is in the report so far
Layout produced by workspace init:
target/
├── .reconlab.json # target metadata (ip, host, domain, platform)
├── report.md # engagement report scaffold with frontmatter
├── scans/ # raw nmap output (xml/nmap/gnmap)
├── screenshots/ # one set per finding
├── loot/ # recovered files (sanitized before report)
├── notes/ # working notes + generated methodology vault
├── exploits/ # adapted exploit code with attribution
└── creds/ # recovered credentials — gitignored
After workspace suggest, notes/methodology/ contains an Obsidian-friendly
vault with one MOC (index.md) and one note per detected service.
Usage
Parse nmap XML
reconlab parse-nmap scans/target.xml
Example output:
Host Names Port Proto Service Product Version
---------- -------------- ---- ----- ------- ------- -------
10.10.10.5 app.corp.local 80 tcp http nginx 1.18.0
Make an /etc/hosts entry
make-hosts is a read-only helper. It prints the line you should add to
/etc/hosts plus a ready-to-paste shell command. It never modifies any file.
Positional form:
reconlab make-hosts 10.10.10.5 app.corp.local dev.corp.local Dev.corp.local DEV.corp.local
Output:
# Add this to /etc/hosts:
10.10.10.5 app.corp.local dev.corp.local Dev.corp.local DEV.corp.local
# Or run:
echo "10.10.10.5 app.corp.local dev.corp.local Dev.corp.local DEV.corp.local" | sudo tee -a /etc/hosts
Flag form (equivalent output):
reconlab make-hosts \
--ip 10.10.10.5 \
--host app.corp.local \
--aliases dev.corp.local Dev.corp.local DEV.corp.local
In the flag form, the first value after --aliases is consumed by the flag and
any further positional arguments are appended as additional aliases — so the
shell-natural --aliases a b c syntax works. Repeating the flag
(--aliases a --aliases b) is also supported. Hostname casing is preserved
exactly as typed.
Manage a lab workspace
workspace init scaffolds a target folder with a richer report template and
persists target metadata so downstream commands inherit it.
reconlab workspace init target \
--ip 10.10.10.5 \
--host app.corp.local \
--domain corp.local \
--platform lab \
-o ~/labs
workspace suggest merges every scan file in scans/ (oldest → newest) and
generates methodology into notes/methodology/ (Obsidian vault by default),
notes/methodology.md (--output-format md), or notes/methodology.json
(--output-format json):
# From inside the workspace, no flags needed.
reconlab workspace suggest
# Or explicitly point at a workspace.
reconlab workspace suggest ~/labs/target --output-format md --force
Multi-scan behavior:
- A typical engagement runs a TCP full scan, a UDP top-N scan, and a targeted
-sV -sCrescan. Drop all three intoscans/; methodology generation will union open ports across them and merge service metadata. - Records are keyed by
(host, port, proto). When the same port appears in more than one scan, the most recent non-placeholder value wins for service name, product, and version — so a later-sVoverrides an earlier banner-only scan, but an unscanned re-discovery cannot erase real data captured earlier. - Pass
--latestto revert to single-scan mode (most-recently-modified file only). Useful if older scans inscans/are stale or experimental.
Re-running refuses to overwrite the previous methodology unless --force is
passed; unrelated files in the workspace are never touched.
Check workspace status
workspace status prints a one-screen summary of where a workspace stands —
scan count, whether methodology has been generated (and whether it is stale
relative to scans/), recorded findings by severity, and a single suggested
next step.
# From inside the workspace, or point at one explicitly.
reconlab workspace status
reconlab workspace status ~/labs/target
Workspace: target
Path: /home/op/labs/target
Target 10.10.10.5 · app.corp.local · corp.local
Platform lab
Scans 1 file (latest: initial.xml)
Methodology notes/methodology/ — STALE (newer scans present)
Findings 1 recorded — 1 High
Next: scans/ changed since the methodology was generated — re-run `reconlab workspace suggest --force`.
The Next line is state-driven: it points at workspace suggest when no
methodology exists yet, flags a stale methodology once newer scans land, and
nudges toward finding add after methodology is in place. It never modifies
the workspace — workspace status is read-only.
Check report readiness
workspace check lints the workspace's report.md and flags the scaffold
placeholders still left unfilled — per-finding fields (Severity, Description,
Evidence, Impact, Remediation) and the Executive Summary. It exits non-zero
when issues remain, so it works as a pre-handover gate or a CI step.
# From inside the workspace, or point at one explicitly.
reconlab workspace check
reconlab workspace check ~/labs/target
Report check: /home/op/labs/target/report.md
Report:
- Executive Summary is still the scaffold placeholder.
Findings:
Finding 1 — SMB null session enabled
- **Impact** is still a scaffold placeholder
- **Remediation** is still a scaffold placeholder
3 issues found — fill these in before handing the report over.
finding add fills in Severity, Affected, and (with --service) Description,
but Impact and Remediation are always left for you — workspace check is the
reminder that they, and the Executive Summary, still need attention. Like
workspace status, it is read-only.
Record findings into the report
finding add appends a structured finding block to report.md inside the
workspace. The first call replaces the placeholder ### Finding 1 — _Title_
left there by workspace init; subsequent calls auto-increment the finding
number after the highest existing one.
reconlab finding add \
--title "SMB null session enabled" \
--severity high \
--service smb \
--port 445 \
--evidence screenshots/01-shares.png \
--evidence loot/null-session-listing.txt
What finding add does:
- Reads
.reconlab.jsonand pre-fillsAffected:with the workspace's target IP and hostname. - If
--servicematches a known workflow (smb,http, ...), seeds theDescription:from the workflow's report-note (with[TARGET_IP]/[TARGET_HOST]/[DOMAIN]substituted) and adds a**Methodology:** see [[notes/methodology/services/<service>]]cross-link. --descriptionoverrides the workflow seed when you want custom text.- Each
--evidence PATHadds a bullet underEvidence:. Repeat the flag for multiple files (--evidence a.png --evidence b.png). - Severity is restricted to
critical | high | medium | low | info.
finding list prints the current findings as a one-line-per-finding table —
handy for sanity-checking what is captured so far without opening the file:
# Severity Title Service:Port
- -------- --------------------------- ------------
1 High SMB null session enabled smb:445
2 Medium HTTP debug endpoint exposed http:80
Browse the workflow registry
workflow list and workflow show expose the workflow registry directly —
useful for "remind me what to check for LDAP" or for picking a starting point
on a target you haven't scanned yet. Neither command needs a workspace or a
scan file.
Workflows fall into three categories:
service-enum— per-service enumeration methodology (SMB, HTTP, LDAP, …). These are whatsuggest-next/workspace suggestauto-select from a scan.post-foothold— what to do once you have a shell or a valid credential:linux-privesc,windows-privesc,ad-foothold.lateral-movement—pivoting: tunneling and internal network access.
Post-foothold and lateral-movement workflows have no ports, so a scan never
auto-selects them — reach them with workflow show.
reconlab workflow list
Category ID Display Priority Ports
---------------- --------------- --------------- -------- --------------------
service-enum smb SMB 10 139/tcp, 445/tcp
service-enum http HTTP 15 80/tcp, 8000/tcp, …
…
post-foothold linux-privesc Linux Privesc 50 -
post-foothold windows-privesc Windows Privesc 51 -
post-foothold ad-foothold AD Foothold 52 -
lateral-movement pivoting Pivoting 60 -
# Print one workflow (Markdown) to stdout with placeholders intact
reconlab workflow show smb
reconlab workflow show linux-privesc
# Or substitute target metadata while rendering
reconlab workflow show ad-foothold --domain corp.local
The Markdown is the same per-workflow content that workspace suggest and
suggest-next write into the methodology document. Those two commands also
append an After a Foothold footer pointing at the post-foothold workflows,
so the generated methodology carries you past initial access.
Initialize a report folder (deprecated)
Deprecated. Superseded by
workspace init; will be removed in a future release. Running it prints a deprecation notice on stderr. Migrate withreconlab workspace init <name>— you get the same folder tree, plus persisted metadata and a richer report scaffold.
report-init is the simpler predecessor of workspace init — it creates the
folder tree only, without metadata or the richer report scaffold.
reconlab report-init target --output-dir labs
Creates:
labs/target/
├── loot/
├── notes/
├── scans/
├── screenshots/
└── report.md
Generate an Obsidian note template (deprecated)
Deprecated. Superseded by
workspace init+workspace suggest; will be removed in a future release. Running it prints a deprecation notice on stderr (the note itself still goes to stdout, so existing redirects keep working). Migrate toworkspace suggest, whose generated vault is richer and stays in sync with scans.
obsidian-note is a simple single-file note scaffold from before the Obsidian
vault renderer existed. For new work, prefer workspace init + workspace suggest — the generated vault is richer and stays in sync with scan results.
reconlab obsidian-note target --ip 10.10.10.5 > target.md
Suggest the next steps from an nmap scan
suggest-next is the underlying methodology generator. For the typical case
(one workspace, drop a scan into scans/, get a vault back) prefer
reconlab workspace suggest — it picks up the latest scan and the workspace
metadata automatically. Reach for suggest-next directly when you want to
point at an arbitrary scan file or override the target metadata.
It turns parsed nmap results into a service-based, prioritized methodology
document. Each detected service gets a checklist, command table, expected output,
verification steps, troubleshooting matrix, and a draft report note. Lab placeholders
([USER], [PASS], [LHOST], [LPORT]) are preserved for the operator to fill in.
Supported input formats:
xml— nmap XML (-oXor-oA); the most reliable parser.normal— human-readable nmap output (.nmapfiles from-oNor-oA).grepable— grepable nmap output (.gnmapfiles from-oGor-oA).auto(default) — infers from the file extension; if the path has no extension, it is treated as annmap -oAbasename and the matching sibling file is probed inxml → normal → grepablepriority.
# Auto-detect: scans/target resolves to scans/target.xml if present,
# otherwise scans/target.nmap, otherwise scans/target.gnmap.
reconlab suggest-next \
-i scans/target \
--input-format auto \
--target 10.10.10.5 \
--host app.corp.local \
--domain corp.local \
-o outputs/next.md
# Force a specific parser
reconlab suggest-next -i scans/target.nmap --input-format normal --target 10.10.10.5
reconlab suggest-next -i scans/target.gnmap --input-format grepable --target 10.10.10.5
reconlab suggest-next -i scans/target.xml --input-format xml --target 10.10.10.5
The simplest invocation derives the target IP from the scan and prints Markdown to stdout:
reconlab suggest-next -i scans/target.xml
Excerpt of the default md output:
# Methodology — 10.10.10.5
## Detected Services
| Port | Proto | Service | Product | Version |
|------|-------|---------|---------|---------|
| 445 | tcp | microsoft-ds | Samba | 4.15.0 |
| 3389 | tcp | ms-wbt-server | - | - |
## SMB (139/445) — Share & RPC Enumeration
### Checklist
- [ ] Fingerprint SMB dialect and signing posture.
- [ ] Attempt null-session share listing.
...
Supported service-enum workflows in this release: SMB, HTTP, HTTPS, FTP, SSH,
DNS, SMTP, LDAP, Kerberos, MSSQL, MySQL, RDP, WinRM, SNMP, NFS (15 total), plus
three post-foothold workflows — linux-privesc, windows-privesc,
ad-foothold — and one lateral-movement workflow, pivoting. Run
reconlab workflow list for the live registry with
categories, priorities, and port mappings. Open ports without a service-enum
workflow are listed under an Unmapped Services section so nothing is
silently dropped.
Obsidian vault output
--output-format obsidian writes a small Obsidian-friendly vault folder instead of a
single Markdown file. The directory passed via -o is created (and intermediate
parents) if it does not already exist.
reconlab suggest-next \
-i scans/target \
--target 10.10.10.5 \
--host app.corp.local \
--domain corp.local \
--output-format obsidian \
-o notes/target
Generated layout:
notes/target/
├── index.md # MOC: frontmatter, scope, detected services, prioritized
│ # wikilinks to each service note, optional report draft
├── services/
│ ├── smb.md # frontmatter + Checklist / Commands / Expected output /
│ ├── http.md # Verification / Troubleshooting / Report note / Related
│ ├── rdp.md
│ └── … # one per detected & mapped service
└── unmapped.md # only present when the scan has open ports without a workflow
- Filenames use the lowercase canonical service ID (
smb.md,winrm.md). - Cross-references render as aliased wikilinks:
[[services/smb|SMB]],[[services/winrm|WinRM]]— paths are stable and machine-friendly, while the alias keeps the rendered text human-friendly. - Each note carries YAML frontmatter (
title,target,service,priority,status: in-progress, tags) so Obsidian dataview / search queries work immediately.
By default the command refuses to overwrite existing files in the vault directory.
Pass --force to overwrite, or point -o at an empty/new directory. Files in the
target directory that the tool does not generate are left untouched.
# Re-render and overwrite previously generated notes
reconlab suggest-next -i scans/target --output-format obsidian -o notes/target --force
The md default behavior (single Markdown file, optional -o) is unchanged.
JSON output
--output-format json writes a single structured JSON document instead of
Markdown or a vault: target metadata, detected and unmapped services, and the
full prioritized per-workflow methodology (checklists, commands, troubleshooting,
report notes) as data. Like md, it prints to stdout when -o is omitted.
# To a file, or piped into jq for scripting.
reconlab suggest-next -i scans/target --output-format json -o outputs/methodology.json
reconlab suggest-next -i scans/target --output-format json | jq '.workflows[].service_id'
Target placeholders ([TARGET_IP], [TARGET_HOST], [DOMAIN]) are substituted;
operator placeholders ([USER], [PASS], ...) are left intact for downstream
tooling. --output-format accepts md, json, and obsidian.
Development
Install the dev extras (pytest + ruff), then run syntax checks, lint, and
tests:
python -m pip install -e ".[dev]"
python -m compileall src tests
ruff check .
python -m pytest
GitHub Actions runs ruff check . and pytest on every push and pull request
across Python 3.11 and 3.13 (see .github/workflows/tests.yml).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file reconlab-0.4.0.tar.gz.
File metadata
- Download URL: reconlab-0.4.0.tar.gz
- Upload date:
- Size: 62.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b5e6fc1b2945bf29bcb85bf8e50f1680c13fe5dbe689a714e962bc35b37a600
|
|
| MD5 |
2ca00c5fd5a1485a103030a60aa160c9
|
|
| BLAKE2b-256 |
0440e3b6055c21acdfa3292aa6bdbc34d6dd1b821ee0bd2a554c031fcda6b1d2
|
Provenance
The following attestation bundles were made for reconlab-0.4.0.tar.gz:
Publisher:
publish.yml on 0xsl0th/reconlab
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
reconlab-0.4.0.tar.gz -
Subject digest:
9b5e6fc1b2945bf29bcb85bf8e50f1680c13fe5dbe689a714e962bc35b37a600 - Sigstore transparency entry: 1545273468
- Sigstore integration time:
-
Permalink:
0xsl0th/reconlab@019d9c330cd98649fabfa8fe6410d0f927fc9703 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/0xsl0th
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@019d9c330cd98649fabfa8fe6410d0f927fc9703 -
Trigger Event:
release
-
Statement type:
File details
Details for the file reconlab-0.4.0-py3-none-any.whl.
File metadata
- Download URL: reconlab-0.4.0-py3-none-any.whl
- Upload date:
- Size: 48.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f4e279958d7bcf62eae335f8b196058a16354cfbe8dc245a8d86e5ab6376049e
|
|
| MD5 |
c1fdeecaff185542c57db9a3e364fe39
|
|
| BLAKE2b-256 |
c2a2a62fc7df969c4974a3660549baaca43e1cbdadda5742d09e5e6eefb0805c
|
Provenance
The following attestation bundles were made for reconlab-0.4.0-py3-none-any.whl:
Publisher:
publish.yml on 0xsl0th/reconlab
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
reconlab-0.4.0-py3-none-any.whl -
Subject digest:
f4e279958d7bcf62eae335f8b196058a16354cfbe8dc245a8d86e5ab6376049e - Sigstore transparency entry: 1545273578
- Sigstore integration time:
-
Permalink:
0xsl0th/reconlab@019d9c330cd98649fabfa8fe6410d0f927fc9703 -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/0xsl0th
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@019d9c330cd98649fabfa8fe6410d0f927fc9703 -
Trigger Event:
release
-
Statement type: