← Back to blog

Writing a High-Quality Bug Bounty Report: Structure, Impact Calculation, & Triaging to Payout

Writing a High-Quality Bug Bounty Report: Structure, Impact Calculation, & Triaging to Payout

Finding a critical vulnerability is only half the battle in modern bug bounty hunting. The other half—the half that dictates whether you get paid top-tier bounties in hours or spend three weeks arguing in comments—is how you write the report.

Triagers on platforms like HackerOne, Bugcrowd, and Intigriti, as well as internal Product Security (ProdSec) teams at tech giants, review dozens of submissions every hour. They face triage fatigue, aggressive Service Level Agreements (SLAs), and mountains of spam from automated scanner dumps.

When your submission arrives with cryptic one-line notes, noisy Burp Suite exports, or inflated severity scores, triagers are forced to spend mental bandwidth reverse-engineering your intent. The result is predictable: delayed reproduction, requests for clarification, disputed severities, or worse, outright closure as Informative or Cannot Reproduce.

Conversely, an airtight, professional report removes every ounce of friction. The triager verifies the bug in 90 seconds, selects the CVSS vector you calculated, and escalates it to the engineering team with payment approval on the same day.

Here is the offensive researcher’s blueprint for structuring high-impact reports, calculating mathematically sound CVSS scores, engineering deterministic Proofs of Concept (PoCs), and mastering triage queue dynamics.


1. The Anatomy of an Airtight Report Structure

A winning report follows a journalistic principle: Bottom Line Up Front (BLUF). A triager should understand the vulnerability class, target surface, and business exposure within 20 seconds of opening your ticket.

+-------------------------------------------------------------------------+
|                       THE 5-TIER REPORT ARCHITECTURE                    |
+-------------------------------------------------------------------------+
| 1. Executive Summary (BLUF)   -> Target, Vuln Type, Impact in 30 sec    |
| 2. Environment & Prerequisites-> Roles, Auth states, Target URLs        |
| 3. Step-by-Step Reproduction  -> Discrete, deterministic, numbered steps|
| 4. Clean Proof of Concept     -> Minimal cURL / Python / Short video    |
| 5. Impact & Remediation       -> Concrete blast radius + Fix guidance   |
+-------------------------------------------------------------------------+

1. The Executive Summary (BLUF)

Never bury the lead. Start with a succinct 3-to-4 sentence briefing that defines:

  1. Target Asset: Exact domain, API route, or mobile bundle.
  2. Vulnerability Type: CWE name (e.g., CWE-639: Authorization Bypass Through User-Controlled Key).
  3. Core Impact: What unauthorized action an attacker can execute.
  4. Preconditions: Whether the attack requires an authenticated account, elevated privileges, or user interaction.

Weak Summary:
“I found an IDOR in the billing section. You can see other people’s invoices. Please fix ASAP and award bounty.”

High-Impact Summary:
“An Insecure Direct Object Reference (IDOR / BOLA) in the GET /api/v2/organizations/{org_id}/invoices endpoint enables an authenticated attacker belonging to Organization A to exfiltrate proprietary billing records, line-item pricing, and customer PII belonging to any arbitrary organization by incrementing the numeric org_id path parameter. No user interaction or elevated administrative permissions are required.”

2. Environment & Prerequisites

Triagers often test against staging, sandboxed multi-tenant environments, or dedicated testing accounts. Disclose your setup parameters immediately:

Variable Recommended Specification Example
Base URL Exact origin tested https://app.target-enterprise.com
User A (Attacker) Role, account ID, test email Role: Member, ID: usr_912a, attacker@pentest.local
User B (Victim) Role, tenant ID, target email Role: Org Admin, Tenant: org_481b, victim@pentest.local
HTTP Client / Platform Runtime, browser, or CLI tool Firefox 129 / Burp Suite v2026.8 / Python 3.12
Feature Flags / Settings Prerequisite configurations Multi-currency billing must be enabled in Org Settings

3. Step-by-Step Reproduction Steps

Your reproduction section is an executable recipe. If a step fails, the report stalls.

  • Start from zero: Never assume the target account already has the necessary resources populated. Include the prerequisite object creation step.
  • Use numbered, discrete steps: Avoid paragraphs. Every user interaction or HTTP request should be a single numbered bullet.
  • Differentiate actors explicitly: Clearly label when an action is taken by Attacker (User A) versus Victim (User B).
### Steps to Reproduce

1. Log in to Account B (Victim) at `https://app.target.com/login`.
2. Navigate to `Settings -> Billing -> Invoices` and note the generated invoice ID (e.g., `inv_99812`).
3. Log out of Account B, and log in to Account A (Attacker) in an incognito browser session.
4. Verify that Account A has no access to Account B's invoices in the dashboard UI.
5. Send the following cURL request using Account A's Bearer token, replacing `{INVOICE_ID}` with `inv_99812`:
   [cURL Command]
6. Observe that the server returns HTTP/2 200 OK with Account B's full invoice data, including customer tax IDs and billing addresses.

2. Impact Calculation: Turning Findings into Top Payouts

The single greatest point of friction between security researchers and triage teams is Impact. Researchers often argue theoretical worst-case scenarios, while triage teams are bound by demonstrable business risk.

       [ THEORETICAL SPECULATION ]                    [ DEMONSTRABLE RISK ]
"If an attacker chains this with an 0-day      "An unauthenticated remote attacker
in Chrome, they could pivot into the AWS        can read 4.2M unencrypted customer
root account and delete production."           credit records via a single looped API call."
                  |                                                  |
                  v                                                  v
     [ Lowered to Low / N/A ]                           [ Validated as CRITICAL ]

The Golden Formula of Business Impact

To justify a High or Critical rating, translate technical exploitation into tangible business consequences:

  1. Confidentiality Blast Radius: Exactly what sensitive data is exposed? Is it public marketing data, internal metadata, customer PII (GDPR/CCPA impact), or secret credentials (API keys, AWS tokens)?
  2. Integrity Blast Radius: Can the attacker tamper with state? Can they alter financial balances, modify user permissions, or plant malicious backdoors?
  3. Availability Blast Radius: Does exploitation exhaust database pools, delete tenant partitions, or take down core authentication services?
  4. Volume & Automation: Can this attack be scripted across the entire user base (e.g., brute-forcing sequential 32-bit IDs) without rate limits?

CVSS v3.1 Matrix Breakdown for Bug Hunters

Do not guess CVSS scores. Calculate each metric accurately and provide the full CVSS vector string in your submission:

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N (Base Score: 8.1 - HIGH)
Metric Code Hacker’s Best Practice
Attack Vector (AV) AV:N Almost all web bugs are Network (AV:N). Do not claim AV:N if physical device access is required.
Attack Complexity (AC) AC:L vs AC:H Is execution deterministic (AC:L), or does it depend on winning an unpredictable race window or man-in-the-middle positioning (AC:H)?
Privileges Required (PR) PR:N / PR:L / PR:H PR:N: Unauthenticated. PR:L: Standard user. PR:H: Workspace Administrator.
User Interaction (UI) UI:N vs UI:R Does the exploit run autonomously (UI:N), or must the victim click a crafted link / accept a prompt (UI:R)?
Scope (S) S:U vs S:C Did the vulnerability cross security boundaries (e.g., escaping a sandboxed iframe to host origin, or SSRF pivoting to AWS IMDSv1)?
Confidentiality (C) C:H / C:L / C:N C:H: Complete database or full PII exfiltration. C:L: Partial non-sensitive info disclosure.

The CVSS Inflation Trap

One of the quickest ways to lose credibility with a triage team is CVSS Inflation:

  • Filing a Self-XSS (which requires copying code into Developer Tools) as CVSS 9.8 Critical.
  • Filing a Rate Limiting missing on contact form as CVSS 7.5 High Denial of Service.
  • Filing an IDOR leaking public user profiles as CVSS 8.6 High PII Exfiltration.

When you submit an inflated score, triagers are forced to downvote it, which immediately creates an adversarial dynamic. When you accurately submit an 8.1 and thoroughly defend it with empirical evidence, triagers respect your judgment and advocate for your payout with the program manager.


3. Proof of Concept (PoC) Engineering

The best PoCs are clean, minimal, non-destructive, and copy-pasteable. If a triager has to install four unknown npm packages or compile an unverified binary, your report will sit in the queue.

1. The Clean cURL Standard

Never copy raw HTTP requests directly from Burp Suite without stripping noise. Remove client-specific headers like sec-ch-ua, User-Agent, Accept-Language, Sec-Fetch-*, and browser analytics cookies.

# Clean, deterministic PoC cURL Command
curl -i -sS -X POST "https://api.target.com/v2/organization/billing/transfer" \
  -H "Host: api.target.com" \
  -H "Authorization: Bearer <ATTACKER_TOKEN>" \
  -H "Content-Type: application/json" \
  --data '{"victim_tenant_id":"org_88192","destination_account":"attacker_vault"}'

Key flags for reproducible cURL commands:

  • -i: Includes HTTP response headers (proves status code and cookies returned).
  • -sS: Silent mode, but shows errors if the network connection drops.
  • --path-as-is: Crucial when demonstrating path traversal or URL normalization bypasses (prevents curl from resolving ../ locally).

2. Standalone Python Reproduction Script

For complex exploits (such as race conditions, cryptographic flaws, or multi-step token exchanges), provide a self-contained Python script utilizing only Python’s standard library or requests.

#!/usr/bin/env python3
"""
Target: https://api.target.com
Vulnerability: Insecure Direct Object Reference in Invoice Export
Author: Shawn R. (Packets & Profits)
Usage: python3 poc_idor.py <ATTACKER_BEARER_TOKEN> <VICTIM_ORG_ID>
"""

import sys
import json
import urllib.request
import urllib.error

def verify_idor(attacker_token: str, victim_org_id: str):
    url = f"https://api.target.com/v2/organizations/{victim_org_id}/invoices"
    headers = {
        "Authorization": f"Bearer {attacker_token}",
        "Accept": "application/json",
        "User-Agent": "Security-Research-PoC"
    }

    req = urllib.request.Request(url, headers=headers, method="GET")
    
    try:
        with urllib.request.urlopen(req) as resp:
            status = resp.status
            body = resp.read().decode("utf-8")
            data = json.loads(body)
            
            print(f"[+] HTTP Status: {status}")
            print(f"[!] IDOR CONFIRMED: Successfully accessed victim tenant data!")
            print(f"[*] Records retrieved: {len(data.get('invoices', []))}")
            print(f"[*] First invoice leaked: {data['invoices'][0]['invoice_id']} | Amount: ${data['invoices'][0]['total']}")
            sys.exit(0)
            
    except urllib.error.HTTPError as e:
        print(f"[-] Access denied or failed: HTTP {e.code}")
        sys.exit(1)

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <ATTACKER_TOKEN> <VICTIM_ORG_ID>")
        sys.exit(1)
    verify_idor(sys.argv[1], sys.argv[2])

3. Screen Recordings vs. Screenshots: The Decision Matrix

Vulnerability Category Best PoC Media Rationale
IDOR / BOLA / SSRF Raw HTTP Request + Response Text is searchable, diffable, and immediate. Videos waste triager time.
Stored XSS / CSRF Screenshot of Alert Box + Payload A single screenshot showing document.domain in the modal proves execution.
Race Conditions / Limit Bypass Video (under 60s) or Turbo Intruder Log Proves concurrency and simultaneous multi-request acceptance.
Multi-Step Account Takeover Screen Recording (MP4, 720p/1080p) Shows both attacker and victim browsers side-by-side in real time.

Screen Recording Etiquette:

  • Keep it under 90 seconds.
  • Do not include background music or prolonged typing.
  • Use dual browser windows (one colored red for Attacker, one blue for Victim) so the triager can follow session state transitions instantly.

4. What Actually Gets You Paid & Triaged Faster

Triage teams operate under operational pressure. When you align your workflow with their internal success metrics, your reports fly through validation.

       RESEARCHER SUBMITS REPORT
                   │
                   ▼
┌──────────────────────────────────────┐
│       TRIAGER FIRST LOOK (<2 min)    │
│  - Is target in scope?               │
│  - Is vulnerability class eligible?  │
│  - Are steps clear & reproducible?   │
└──────────────────┬───────────────────┘
                   │
         Passes Quick Filter?
        ┌──────────┴──────────┐
        ▼                     ▼
     [ YES ]               [ NO ]
 Reproduce in sandbox    Request more info /
 Confirm impact          Flag as Informative /
 Escalate to Client      Queue delays 5-10 days
        │
        ▼
 Bounty Paid Fast ($$$)

The 5 Sins That Stall Reports

  1. The “Check out my video” trap: Submitting a blank report body with only a YouTube or Google Drive link. Many enterprise clients prohibit triagers from clicking unverified third-party external video links. Always include the full written reproduction in the report body.
  2. Testing in single-tenant isolation: Proving you can modify your own user profile does not prove an IDOR. You must demonstrate crossing an authorization boundary between two distinct test accounts you control.
  3. Leaving active attack artifacts: Fuzzing with test<script>alert(1)</script> on production forms and abandoning 500 polluted records in the company’s live system. Clean up your artifacts or use non-destructive payloads like "><b/onmouseover=console.log(1)>.
  4. Emotional dispute escalation: When a triager asks a clarifying question or challenges the severity, responding with personal attacks or threats to public-disclose guarantees your report gets locked.
  5. Noisy Burp project state dumps: Uploading a 200MB .burp file instead of isolating the single offending HTTP exchange.

Constructive Dispute Resolution Protocol

If a triager marks your report as Informative or downgrades the severity unfairly:

  • Do not argue semantics: Do not say “You don’t understand security, this is clearly Critical!”
  • Reference Program Policy & RFCs: Cite the specific scope rules:
    “According to the program brief under ‘Eligible Vulnerabilities’, Section 3 explicitly includes Broken Object Level Authorization on /api/v2/*. Here is the exact boundary crossover demonstrated…”
  • Provide a concrete attack chain: If they claim the leaked token has no impact, show where that token can be replayed to modify user email or billing state.
  • Request Platform Mediation calmly: If at an impasse on HackerOne or Bugcrowd, politely request platform mediation:
    “I respect the triage assessment; however, because this endpoint allows unrestricted exfiltration of customer PII across tenant boundaries, could we please request a platform mediator or program manager review for second-tier evaluation?”

Propose the Remediation (The ProdSec Delight)

Security engineers who review triaged tickets have to write the fix. When you supply the remediation logic in code, you cut their engineering cycle in half. Many programs award discretionary bonus bounties (+$250 to +$1,000) for clean fix proposals.

### Suggested Remediation

In `app/controllers/invoices_controller.py`, enforce an explicit ownership check using the authenticated session's tenant context rather than trusting the user-supplied `org_id`:

```python
# Vulnerable
invoice = Invoice.objects.filter(id=invoice_id, org_id=request_org_id).first()

# Secure
current_org = request.user.organization
invoice = Invoice.objects.filter(id=invoice_id, organization=current_org).first()
if not invoice:
    raise Http404("Invoice not found or access denied.")

---

## 5. The Battle-Tested Bug Bounty Report Template

Use this markdown template for your future submissions on HackerOne, Bugcrowd, Intigriti, or direct private disclosure programs.

```markdown
# [Vulnerability Type] in [Endpoint/Component] leads to [Business Impact]

## Summary
A [Vulnerability Class, e.g., Broken Object Level Authorization] vulnerability exists in the [Target Endpoint/Feature]. An authenticated attacker with [Attacker Privilege Level] can [Specific Exploitation Action], leading to [Direct Impact, e.g., unauthorized access to full customer financial records].

- **Vulnerable URL**: `https://api.target.com/v1/resource/{id}`
- **HTTP Method**: `GET` / `POST` / `PUT` / `DELETE`
- **CVSS v3.1 Score**: `8.5 (High)` - `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N`
- **CWE**: `CWE-639: Authorization Bypass Through User-Controlled Key`

---

## Testing Environment & Credentials
- **Target URL**: `https://app.target.com`
- **Attacker Account (User A)**: `attacker_test@pentest.com` (Org ID: `org_111`)
- **Victim Account (User B)**: `victim_test@pentest.com` (Org ID: `org_222`, Resource ID: `doc_999`)

---

## Steps to Reproduce
1. Log in to User B (Victim) account and navigate to `https://app.target.com/documents`.
2. Locate document `doc_999` and note that it contains sensitive personal data.
3. Open an Incognito window, log in as User A (Attacker).
4. Send the following cURL request using User A's authorization token, attempting to access User B's document:

```bash
curl -i -sS -X GET "https://api.target.com/v1/documents/doc_999" \
  -H "Host: api.target.com" \
  -H "Authorization: Bearer <USER_A_TOKEN>" \
  -H "Accept: application/json"
  1. Observe the response: The server responds with HTTP/2 200 OK and returns the complete document metadata and download URI belonging to User B.

Proof of Concept

Raw HTTP Request

GET /v1/documents/doc_999 HTTP/2
Host: api.target.com
Authorization: Bearer eyJhbGciOi...[User A Token]
Accept: application/json

Raw HTTP Response

HTTP/2 200 OK
Content-Type: application/json
Date: Wed, 02 Sep 2026 23:40:00 GMT

{
  "document_id": "doc_999",
  "owner_org_id": "org_222",
  "title": "Quarterly Financial Audit & PII Records",
  "file_url": "https://s3.us-east-1.amazonaws.com/target-vault/doc_999.pdf"
}

Business Impact

An attacker can enumerate sequential document IDs to exfiltrate private internal documents across all tenants in the multi-tenant architecture. This breaks multi-tenant isolation, exposing customer data in violation of GDPR and SOC2 compliance standards.


Suggested Remediation

Implement tenant-scoped database queries ensuring that requests for documents validate document.organization_id == current_user.organization_id before returning records.


---

## 6. Pre-Submission Checklist

Before hitting the **Submit Report** button, run through this 60-second flight check:

- [ ] **Scope Checked**: Have you verified that the domain/wildcard is explicitly in scope in the program brief?
- [ ] **Zero-State Test**: Can a third party follow your steps without requiring unmentioned prior configuration?
- [ ] **Dual-Account Proof**: Did you prove cross-tenant impact between two separate accounts rather than testing against yourself?
- [ ] **Noise Stripped**: Did you remove useless tracking cookies and user-agent noise from your cURL requests?
- [ ] **Accurate CVSS**: Is your CVSS score justifiable without theoretical speculation?
- [ ] **Clean Artifacts**: Did you sanitize or delete any temporary test data you created during testing?
- [ ] **Professional Tone**: Is the report phrased constructively, focusing on technical vulnerability and remediation?

By engineering your reports with the same precision you apply to exploiting targets, you transform your bug bounty workflow from a frustrating back-and-forth into a predictable, high-payout machine.