When to use
Use this skill for client-deliverable reports:
- External red-team engagements with a signed SOW
- Pentest reports going to a CISO / IT-Sec team (not a triager)
- Findings that will be reviewed by both technical and non-technical stakeholders
- Reports that need DOCX/PDF output (not just markdown / platform UI)
Do NOT use for:
- Bug-bounty platform submissions (use
report-writing/bugcrowd-reportinginstead) - Quick proof-of-concept memos
- Internal team writeups
The 6-section format per finding
This is the canonical structure each finding follows:
## Finding F##: <descriptive title>
**Severity:** Critical / High / Medium / Low / Informational
**Status:** Confirmed / Patched mid-engagement / Suspected (1 signal)
**CVSS 3.1:** <score> (<vector>)
**Affected Asset:** <URL / IP / app name>
### 1. Subject
<One-line statement of the issue. Plain English, no jargon.>
### 2. Observations
<Bulleted list of what was observed during testing. Concrete facts only — no interpretation yet.>
- <Observation 1>
- <Observation 2>
- ...
### 3. Description
<Technical explanation of the vulnerability. 2-4 paragraphs. Reader should understand WHY the observations indicate a vulnerability, what the underlying flaw is.>
### 4. Impact
<What an attacker could achieve. Concrete attacker outcomes, NOT generic CIA triad statements. Tie to the client's business — money, data, reputation, regulatory exposure.>
### 5. Recommendation
<Specific, actionable remediation. Vendor patch, configuration change, code-level fix. Avoid "implement security best practices" — say what specifically.>
### 6. Proof of Concept (PoC)
<Steps to reproduce, numbered. Include the exact HTTP requests, payloads, tools used.>
**Step 1:** <action>
```http
<full HTTP request or curl one-liner>
Step 2: <action>
<response excerpt>
Screenshot:

---
## Severity & status disciplines
### Severity table (client-facing — different from CVSS-only)
| Severity | Business definition | CVSS rough range |
|---|---|---|
| Critical | Direct revenue/data loss without prerequisites | 9.0-10.0 |
| High | Full account/system takeover with limited prerequisites | 7.0-8.9 |
| Medium | Significant data exposure or partial compromise | 4.0-6.9 |
| Low | Information disclosure with limited exploitation path | 0.1-3.9 |
| Informational | Hygiene finding, no immediate exploit | N/A |
### Status field (red-team-specific)
This is the field that distinguishes red-team deliverables from bug-bounty reports. Use one of:
- **Confirmed** — reproduced multiple times, with full PoC
- **Confirmed; patched mid-engagement** — was reproducible, client patched during the test window (still ship the finding — see `mid-engagement-ir-detection`)
- **Confirmed; partially reproducible** — works but needs specific conditions
- **Suspected (1 signal)** — single indicator, not confirmed (rare — usually drop)
- **Out-of-band** — finding from passive recon, not actively tested
---
## Mistakes to avoid (from authorized-engagement)
### 1. Don't retract findings that stopped reproducing
If a finding was confirmed and then stopped working, that is almost always a CLIENT PATCH, not a finding-was-false. The correct response is "Confirmed; patched mid-engagement" with timestamps showing when it broke. See `mid-engagement-ir-detection`.
### 2. Don't hedge in the Impact section
Bad: "An attacker could potentially be able to access user data, which may lead to..."
Good: "An attacker reads any user's profile data. Demonstrated on test user `victim@target.com` at 14:22 IST."
### 3. Don't generic-CIA the impact
Bad: "Loss of confidentiality and integrity of customer data"
Good: "Read access to 247,000 customer records including PAN cards, addresses, GST numbers. India DPDPA Section 33 mandates 72-hour breach disclosure to DPB."
### 4. Don't list every recon finding as a finding
Recon notes (subdomains found, ports open, technologies fingerprinted) belong in a separate **Recon / Attack Surface** appendix, not the findings list. A finding must have an attacker-attainable outcome.
### 5. Don't bury the PoC
Each finding MUST have reproducible steps. The PoC section is what proves the finding to a skeptical reader. If you can't write the PoC clearly, the finding probably isn't ready to ship.
---
## Document-level structure
-
Executive Summary (1 page, non-technical)
- Engagement overview (dates, scope)
- Risk posture summary (heat-map: <X critical, Y high, Z medium...>)
- Top 3 strategic recommendations
- Comparison to industry baseline (optional)
-
Engagement Details
- Scope (in-scope, out-of-scope, exclusions)
- Methodology (recon → exploit → reporting; or align with PTES / OSSTMM)
- Tools used
- Timeline (start / end / key milestones)
- Team
-
Risk Summary Table
F# Title Severity Status F01 ... Critical Confirmed ... -
Findings (one per ## section, in severity order — Critical first)
-
Attack Surface / Recon Appendix
- Subdomains discovered
- Open ports / services
- Technology fingerprints
- APKs found
- Credentials in breach corpora (count + sample only — redact)
- Identity-fabric map (IdP, MFA posture)
-
Indicators of Compromise (IoCs)
- Source IPs used during testing (so SOC can correlate)
- User-Agent strings
- Test accounts created
- Files uploaded (with cleanup status)
-
Cleanup Statement
- Confirmation that all test artifacts (accounts, uploads, persistence) were removed
- Outstanding cleanup items requiring client action
-
Appendices (raw output, screenshots index, full target list)
---
## DOCX generation pipeline (markdown → docx with embedded images)
```bash
# Prerequisite: pandoc installed
brew install pandoc
# Convert
pandoc REPORT_FINAL.md \
-o REPORT_FINAL.docx \
--resource-path=engagement_log/poc \
--reference-doc=~/.claude/skills/redteam-report-template/templates/reference.docx \
--toc \
--toc-depth=2 \
--highlight-style=tango
# Verify image count
python3 -c "
from docx import Document
d = Document('REPORT_FINAL.docx')
imgs = [r for r in d.part.rels.values() if 'image' in r.target_ref]
print(f'Embedded images: {len(imgs)}')
print(f'Paragraphs: {len(d.paragraphs)}')
print(f'Headings: {sum(1 for p in d.paragraphs if p.style.name.startswith(\"Heading\"))}')
"
Image filename convention
screenshots/F<NN>_<descriptive>.png
Examples:
F01_locked_accounts.png
F02a_saml_landing.png
F02b_saml_ca_block_page.png
F03_sqli_timing_chart.png
F15_saml_metadata.png
Variants get letter suffixes (F02a, F02b). Always zero-pad finding number.
Writing tone — for client deliverables
| Section | Tone |
|---|---|
| Subject | Plain English, jargon-free, 1 line |
| Observations | Bulleted facts, past tense ("observed that...") |
| Description | Technical but accessible; assume CISO reader |
| Impact | Business-translated; tie to revenue/regulation |
| Recommendation | Imperative, specific, actionable |
| PoC | Operator-level technical; copy-pasteable |
Always:
- Use past tense for observations ("The endpoint returned a 200 status code")
- Use present tense for descriptions of the flaw ("The application does not validate...")
- Use imperative for recommendations ("Apply patch ... by ...")
- Number reproduction steps; never "first... then... also..."
Never:
- "Could potentially" — prove it or drop it
- "It might be possible" — same
- "We recommend implementing security best practices" — say which one specifically
- "The application is vulnerable to..." without saying what specifically
Audience translation — same finding, different framing
Example: hardcoded JWT in APK
| Section | Technical framing | CISO framing | Board framing |
|---|---|---|---|
| Impact | "JWT signing key extracted from APK enables forging admin |