A good VAPT or vulnerability report template has a clear title, a one-paragraph summary, the affected asset and scope, the vulnerability type and CWE, a severity backed by a CVSS 4.0 vector, a description, numbered steps to reproduce, a proof-of-concept request and response, business impact, specific remediation, references and a disclosure timeline.
Key takeaways
- One report per vulnerability. Lead with impact, then prove it with steps anyone can reproduce in under five minutes.
- Always include the full CVSS 4.0 vector string, not just a number, and justify each metric in a sentence.
- Show the raw HTTP request and response. Redact real secrets and personal data, but keep enough to prove the bug.
- Remediation should name the fix (for example, "enforce object-level authorization in the handler"), not just "sanitize input."
- Most rejections come from four causes: duplicates, out-of-scope assets, no demonstrable impact and irreproducible steps.
- Full VAPT reports add an executive summary, scope and methodology, a findings table and retest status on top of individual findings.
What goes into a vulnerability report
If you are a pentester delivering a VAPT (vulnerability assessment and penetration testing) engagement, a bug bounty hunter submitting to a program or a player in a Raw Ops competition, the reader of your report has the same three questions: Is this real? How bad is it? How do we fix it? Every section in the template exists to answer one of them. If you're coming from flag-based games, how a CTF differs from a report-based round explains the shift, and practitioners can sharpen their testing on War Zone raw targets.
| Section | Answers | What reviewers look for |
|---|---|---|
| Title | What and where | Vulnerability type + component + impact in one line |
| Summary | How bad | Two to four sentences a manager can read |
| Asset and scope | Is it ours, is it allowed | Exact URL, host, version, environment; confirmation it's in scope |
| Type and CWE | What class of bug | A specific CWE, not a vague parent category |
| Severity (CVSS 4.0) | How bad, objectively | Full vector string plus a rationale per metric |
| Description | Why it happens | Root cause in plain language |
| Steps to reproduce | Is it real | Numbered, deterministic, with test accounts and preconditions |
| PoC request/response | Is it real | Raw HTTP or script output, redacted where needed |
| Impact | Why the business should care | Concrete data, users or actions affected |
| Remediation | How to fix | Specific code or config change, plus defense in depth |
| References | Where to learn more | CWE, OWASP, vendor advisories |
| Timeline | What happened when | Discovery, report, triage, fix, retest, disclosure |
Copy-ready vulnerability report template (Markdown)
This template works for a single finding in a pentest report, a bug bounty submission or a Raw Ops report. It's plain Markdown, so it pastes cleanly into Jira, GitHub, Confluence or whichever tool your event or program uses. Replace everything in {curly braces}.
# {Vulnerability type} in {component/endpoint} allows {impact}
| Field | Value |
|--------------------|-------|
| Report ID | {RPT-YYYY-NNN} |
| Reporter | {Name / handle, contact} |
| Date submitted | {YYYY-MM-DD} |
| Asset | {URL / host / app + version / environment} |
| Vulnerability type | {e.g. Broken Object Level Authorization (IDOR)} |
| CWE | {CWE-NNN: Name} |
| Severity | {Critical / High / Medium / Low / None} |
| CVSS 4.0 score | {x.x} |
| CVSS 4.0 vector | {CVSS:4.0/AV:_/AC:_/AT:_/PR:_/UI:_/VC:_/VI:_/VA:_/SC:_/SI:_/SA:_} |
| Status | {New / Triaged / Fixed / Retested} |
## 1. Summary
{2-4 sentences: what the bug is, where it is, what an attacker
can do with it and who is affected. Write this last.}
## 2. Asset and scope
- Target: {https://app.example.com/api/v2/...}
- Environment: {production / staging / competition target}
- Version / build: {if known}
- In scope because: {quote the scope line or program rule}
- Test accounts used: {attacker account, victim account}
## 3. Vulnerability type and CWE
- Class: {OWASP category, e.g. API1:2023 Broken Object Level Authorization}
- CWE: {CWE-NNN: Name} ({link})
## 4. Severity (CVSS 4.0)
Vector: {CVSS:4.0/...}
Score: {x.x} ({severity})
Rationale:
- AV:{N/A/L/P}: {why}
- AC:{L/H}: {why}
- AT:{N/P}: {why}
- PR:{N/L/H}: {why}
- UI:{N/P/A}: {why}
- VC/VI/VA:{H/L/N}: {impact on the vulnerable system}
- SC/SI/SA:{H/L/N}: {impact on other systems}
## 5. Description
{Root cause in plain language. Which check is missing or broken,
and why the application trusts attacker-controlled input.}
## 6. Steps to reproduce
Preconditions: {accounts, roles, feature flags, browser}
1. {Step}
2. {Step}
3. {Step}
Expected result: {what should happen}
Actual result: {what happens}
## 7. Proof of concept
### Request
```http
{raw HTTP request, secrets redacted}
```
### Response
```http
{raw HTTP response, trimmed, personal data masked}
```
{Optional: script, screenshot or video link}
## 8. Impact
{Concrete consequences: which data, how many records or users,
what an attacker can chain it with, regulatory exposure.}
## 9. Remediation
- Primary fix: {specific code/config change}
- Defense in depth: {logging, rate limits, tests}
- Verification: {how the team can confirm the fix}
## 10. References
- {CWE link}
- {OWASP / vendor advisory link}
- FIRST CVSS v4.0 specification: https://www.first.org/cvss/v4-0/
## 11. Timeline
| Date | Event |
|------------|-------|
| {YYYY-MM-DD} | Vulnerability discovered |
| {YYYY-MM-DD} | Report submitted |
| {YYYY-MM-DD} | Triaged / acknowledged |
| {YYYY-MM-DD} | Fix deployed |
| {YYYY-MM-DD} | Fix retested |
| {YYYY-MM-DD} | Public disclosure (if agreed) |
Write the summary last. Once the steps, PoC and impact are down, the summary almost writes itself, and it will match what you proved instead of what you hoped to prove.
Filled example: IDOR on /api/v2/users/{id}
Here is the template filled in for a realistic finding: an insecure direct object reference (IDOR), also called broken object level authorization (BOLA). It is the first entry in the OWASP API Security Top 10 (2023). The target is fictional: shop.example.com.
# IDOR in GET /api/v2/users/{id} exposes any customer's profile and address
| Field | Value |
|--------------------|-------|
| Report ID | RPT-2026-014 |
| Asset | https://shop.example.com/api/v2/users/{id} (production) |
| Vulnerability type | Broken Object Level Authorization (IDOR) |
| CWE | CWE-639: Authorization Bypass Through User-Controlled Key |
| Severity | High |
| CVSS 4.0 score | 7.1 |
| CVSS 4.0 vector | CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N |
## 1. Summary
The user profile endpoint GET /api/v2/users/{id} returns the full
profile of whatever numeric ID is requested, without checking that
it belongs to the logged-in user. Any registered customer can read
the name, email, phone number and saved addresses of every other
customer by changing the ID. IDs are sequential, so the whole user
base can be enumerated with a simple loop.
## 2. Asset and scope
- Target: https://shop.example.com/api/v2/users/{id}
- Environment: production (in scope per program rule 2.1:
"*.example.com APIs, authenticated testing with own accounts")
- Test accounts: attacker (user id 10482), victim (user id 10483),
both created by the reporter
## 3. Vulnerability type and CWE
- OWASP API1:2023 Broken Object Level Authorization
- CWE-639: Authorization Bypass Through User-Controlled Key
## 4. Severity (CVSS 4.0)
Vector: CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
Rationale:
- AV:N reachable over the internet
- AC:L no special conditions; change one path parameter
- AT:N no race, no non-default configuration required
- PR:L any free self-registered customer account works
- UI:N victim does nothing
- VC:H full profile of every customer is disclosed
- VI:N PUT/PATCH on other IDs returned 403; no write access shown
- VA:N no availability impact
- SC/SI/SA:N no impact beyond this application
## 5. Description
The handler loads the user record by the {id} path parameter and
returns it. It verifies that the request has a valid session token
but never compares the requested id with the session's user id.
Authentication is enforced; object-level authorization is not.
## 6. Steps to reproduce
Preconditions: two customer accounts (A = 10482, B = 10483).
1. Log in as account A and copy the Bearer token from any API call.
2. Send GET /api/v2/users/10482 with A's token. You see A's profile.
3. Send the same request with the id changed to 10483.
Expected: 403 Forbidden or 404 Not Found.
Actual: 200 OK with account B's full profile.
## 7. Proof of concept
### Request
GET /api/v2/users/10483 HTTP/2
Host: shop.example.com
Authorization: Bearer eyJhbGciOi...[redacted, account A]
Accept: application/json
### Response
HTTP/2 200 OK
Content-Type: application/json
{
"id": 10483,
"name": "B*** S****",
"email": "b****@example.org",
"phone": "+91 98*** ***12",
"addresses": [ { "line1": "[masked]", "city": "Pune", "pin": "411***" } ],
"created_at": "2026-09-19T10:22:41Z"
}
Enumeration check: 10 requests only (own ID plus adjacent IDs),
stopped immediately per program rules. Result: 10 of 10 returned 200.
Request log attached as evidence.
## 8. Impact
Any customer can harvest the personal data of all customers:
names, emails, phone numbers and delivery addresses. This enables
targeted phishing and fraud, and may be a notifiable personal data
breach under laws such as India's DPDP Act 2023 or the GDPR.
Sequential IDs make full enumeration trivial.
## 9. Remediation
- Primary fix: in the handler, load the record with both the path id
and the authenticated user id (or return 404 when they differ).
For admin roles, check an explicit permission instead.
- Better: expose GET /api/v2/users/me and drop the id parameter
for customer-facing calls.
- Defense in depth: centralize object-level authorization in a
policy layer; add automated tests that call every object endpoint
with a second user's token; alert on one token reading many IDs.
- Note: switching to UUIDs makes guessing harder but is NOT a fix.
- Verification: repeat step 3; expect 403/404.
## 10. References
- https://cwe.mitre.org/data/definitions/639.html
- https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/
- https://cheatsheetseries.owasp.org/cheatsheets/Insecure_Direct_Object_Reference_Prevention_Cheat_Sheet.html
- https://www.first.org/cvss/v4-0/
## 11. Timeline
| Date | Event |
|------------|-------|
| 2026-09-19 | Discovered during authenticated API testing |
| 2026-09-19 | Report submitted |
| 2026-09-20 | Triaged, severity confirmed as High |
| 2026-09-24 | Fix deployed |
| 2026-09-25 | Fix retested: 403 returned for foreign IDs |
That vector scores 7.1 (High) in CVSS 4.0. Try it yourself in our CVSS 4.0 calculator, then change single metrics to see how sensitive the score is. With PR:N (no login needed), the same bug would score 8.7. If the tester had also shown write access with VI:H, it would reach 8.6. Each of those claims must be proven in the PoC, not assumed.
Stay inside the rules while proving impact. The example accesses one victim account the reporter owns and sends ten enumeration requests, then stops. Mass-downloading real customer data to "prove" a bug turns a valid report into a policy violation, and in many programs into a legal problem.
Scoring severity with CVSS 4.0
CVSS 4.0 was published by FIRST on 1 November 2023 as the successor to 3.1; check which version a program asks for before you score. For reports, three changes matter most:
- Attack Requirements (AT) is new. It captures conditions outside the attacker's control, like winning a race or a non-default configuration. Don't overload Attack Complexity (AC) with them.
- Scope is gone. Instead of
S:C, impact is split into the vulnerable system (VC/VI/VA) and subsequent systems (SC/SI/SA). A stored XSS that hits users' browsers is usually low on the vulnerable system and has impact on subsequent systems. - Name the score type. A base-only score is CVSS-B. Adding threat metrics gives CVSS-BT, and environmental metrics give CVSS-BE or CVSS-BTE. Researchers normally report CVSS-B, and the vendor adds threat and environmental context.
Scores between versions aren't directly comparable, so re-score in 4.0 rather than converting a 3.1 number. The severity bands are the same: None 0.0, Low 0.1–3.9, Medium 4.0–6.9, High 7.0–8.9, Critical 9.0–10.0. Our CVSS 4.0 guide walks through every metric with examples.
Executive summary template for full VAPT reports
A full VAPT report bundles many findings for a client. The individual findings use the template above. The document around them needs its own structure, and the executive summary is the page leadership reads. Keep it to one page and avoid jargon.
## Executive summary
**Engagement:** {Client} commissioned {Tester/Company} to perform a
{black/gray/white-box} vulnerability assessment and penetration test of
{systems} between {start date} and {end date}.
**Scope:** {N} web applications, {N} APIs, {N} external IPs,
{mobile apps / internal network / cloud accounts}. Out of scope: {…}.
**Methodology:** Testing followed {OWASP WSTG / OWASP MASVS /
PTES / NIST SP 800-115} and combined automated scanning with manual
testing. Severity is rated with CVSS 4.0 (base score, CVSS-B).
**Overall risk:** {Critical / High / Medium / Low}
| Severity | Count | Fixed at retest |
|----------|-------|-----------------|
| Critical | {n} | {n} |
| High | {n} | {n} |
| Medium | {n} | {n} |
| Low | {n} | {n} |
| Info | {n} | n/a |
**Key risks (plain language):**
1. {e.g. Any logged-in customer can view other customers' personal data.}
2. {…}
3. {…}
**Strategic recommendations:**
1. {e.g. Centralize authorization checks for all API objects.}
2. {e.g. Add security test cases to the CI pipeline.}
3. {e.g. Retest all High and Critical findings within 30 days.}
**Positive observations:** {controls that worked well}
Full VAPT report structure
- Cover and document control: client, version, classification (confidential), distribution list, revision history.
- Executive summary: the template above.
- Scope and rules of engagement: targets, test window, source IPs, accounts provided, exclusions.
- Methodology and severity model: standards followed and how CVSS 4.0 was applied.
- Findings summary table: ID, title, severity, CVSS score, status, sorted by severity.
- Detailed findings: one section per finding, using the report template.
- Retest results: status of each finding after fixes.
- Appendices: tools used, raw scan output, evidence and glossary.
How to write a vulnerability report that gets accepted
1. Lead with impact
"Any customer can read every other customer's address" beats "Missing authorization check on user endpoint." Triagers skim dozens of reports a day. Make the title and the first sentence of the summary carry the business consequence.
2. Make it reproducible in five minutes
- Number every step, and state preconditions: account type, role, feature flags, browser.
- Include exact requests. Copy them from Burp Suite or your browser's dev tools as raw HTTP or
curl. - Say what you expected and what actually happened.
- If a race or timing is involved, share the script and how many attempts it takes.
- Retest your own steps from a clean session before submitting.
3. Don't inflate severity
Over-rating is the fastest way to lose a triager's trust. Score what you demonstrated, not the theoretical worst case. Common inflation patterns:
- Setting
PR:Nwhen the attack needs a logged-in account. - Claiming
VI:Hfor a read-only IDOR because "they could probably write too." - Rating self-XSS, missing security headers or a version banner as High.
- Scoring a whole chain at the highest link's severity without proving each step.
If you believe an escalation is possible but couldn't prove it within the rules, say so in the Impact section and leave the vector at what you proved.
4. One vulnerability per report, but show chains
Split unrelated bugs into separate reports so each can be tracked and fixed. When bugs chain, for example an open redirect plus an OAuth misconfiguration that leads to account takeover, write one report for the chain, explain each link and reference the individual reports.
5. Give fixes developers can act on
Name the vulnerable function or layer if you can, the correct control (parameterized queries, object-level authorization, output encoding for the right context) and how to verify the fix. Link to the relevant OWASP Cheat Sheet.
6. Handle evidence responsibly
Redact tokens, passwords and third-party personal data. Use your own test accounts wherever possible. Store evidence securely and delete it when the program or contract says to.
Common reasons vulnerability reports get rejected
| Rejection | Why it happens | How to avoid it |
|---|---|---|
| Duplicate | Someone reported it first, or it's a known issue | Report fast and precisely. Check public disclosures and the program's known-issues list. |
| Out of scope | Asset, subdomain or bug class excluded by the policy | Re-read the scope before testing. Quote the scope line in the report. |
| No security impact / informative | Missing headers, version disclosure, self-XSS, logout CSRF | Show a realistic attack scenario, or chain it with something that has impact. |
| Not reproducible | Missing preconditions, expired tokens, environment differences | Retest from a clean account. Include exact requests and timestamps. |
| Scanner output only | Pasted tool results with no manual verification | Verify manually and explain the exploit path. |
| Works as intended | Documented or accepted behavior | Read the docs. Argue the risk if you still believe it's a flaw. |
| Policy violation | DoS, social engineering, excessive data access | Stop at the minimum proof and follow the rules of engagement. |
How Raw Ops on CTF7 triages reports
Raw Ops is CTF7's "anti-CTF": there are no flags. Participants investigate a realistic target, find real-world-style vulnerabilities and submit reports much like the template above. Points come after triage, and severity is scored with CVSS 4.0. That makes it a close simulation of a VAPT engagement or a private bug bounty program, and a strong way to practice writing reports under time pressure.
Submit
Players submit a structured report: title, asset, vulnerability type, description, steps to reproduce, PoC, impact and a proposed CVSS 4.0 vector.
Validate
Triagers check scope, reproduce the steps and confirm the impact shown. Reports that can't be reproduced may be sent back for more information.
Deduplicate
Reports for the same root cause are grouped, so a bug is rewarded once rather than every time it's reported.
Score
Triagers confirm or adjust the CVSS 4.0 vector, and the score drives points. Chaining multiple weaknesses into a bigger impact is encouraged.
Exact point rules, triage windows and duplicate policies are set per event by the organizer, so read the event brief. Organizers get post-event analytics on report quality alongside solve and leaderboard data, which is useful for hiring assessments and training. If you're planning one, tell us about your event. Our explainer on what Raw Ops is and why it has no flags covers the format in depth, and hiring security talent with CTFs shows how recruiters read these reports.
Where to go next
- Score it properly. Use the CVSS 4.0 calculator and scoring guide to justify every metric in your vector.
- Know where reports fit. CTF vs bug bounty covers scope, legality and rewards, and how to go from flags to a first valid report.
- Write up what you learn. The CTF writeup guide has a template for flag-based challenges, which complements this one for findings.
- Practice under a clock. Raw Ops scores reports like these after triage, and the Raw Ops format page explains how events run.