DEV Community

Bristie
Bristie

Posted on

WaspSting - Penetration Testing & Bug Bounty Tool

I Built a Penetration Testing Tool From Scratch - Here's What I Learned

By N00dleN00b | WaspSting v1.5 | Open Source | Apache 2.0

The Problem Nobody Talks About in Bug Bounty

Everyone talks about finding bugs. Nobody talks about the hours you spend after you find one.

I'm a cybersecurity student at The University of Tulsa, and like a lot of people in this field, I got hooked on ethical hacking through TryHackMe, HackTheBox, and Burp Suite Labs. The labs are great because it's controlled, structured, and satisfying. You find the vulnerability, you get the flag, you move on. Clean.

Then I tried my first real bug bounty hunt on Bugcrowd.

The actual discovery part? Exciting. Everything else? A grind. I was manually keeping track of endpoints in a notepad, copy-pasting evidence into a Word document, writing up findings one by one, trying to remember which OWASP category applied to what, and losing hours to documentation that had nothing to do with actually finding bugs.

I wanted to get straight to the meat: discovery, exploitation, and verification. Not writing up that yes, the X-Frame-Options header was missing, for the fifteenth time.

So I built WaspSting.


What is WaspSting?

WaspSting is a Python CLI tool for authorized penetration testing and bug bounty hunting. The core idea is simple: the documentation should write itself.

You run a scan. WaspSting makes the requests, identifies the issues, maps them to OWASP Top 10:2025, calculates CVSS v3.1 scores, and generates a structured Markdown and JSON report automatically. By the time you're done testing, your report is already 80% written.

It's built entirely in Python, uses zero paid APIs, and has only two dependencies: rich for terminal output and requests for HTTP. Everything else is either standard library or optional external tools.

git clone https://github.com/N00dleN00b/WaspSting.git
cd WaspSting
pip install -r requirements.txt
python3 waspsting.py --help
Enter fullscreen mode Exit fullscreen mode

How It Works: A Technical Deep Dive

WaspSting is built around a modular architecture. Each testing capability lives in its own module under modules/, and the main waspsting.py orchestrates them based on the mode you select.

Scan Modes

full     — All modules at once
sast     — Static analysis of a GitHub repo
recon    — Security headers, tech fingerprinting, CVE lookup
auth     — Login lockout testing, JWT attack documentation
bola     — BOLA/IDOR sequential ID walking
api      — Rate limiting, CORS, injection probes
enum     — Subdomain enumeration via crt.sh + DNS brute force
fuzz     — Payload fuzzer across 9 vulnerability categories
nuclei   — Nuclei template runner
bounty   — Scope ingestion → phased test plan
Enter fullscreen mode Exit fullscreen mode

The Findings Pipeline

Every module returns a standardised findings dict:

{
    "module":      "recon",
    "owasp_id":    "A02",
    "owasp_name":  "Security Misconfiguration",
    "severity":    "HIGH",
    "title":       "Missing Content-Security-Policy",
    "description": "CSP missing — XSS risk increased",
    "evidence":    "Header absent from response",
    "fix":         "Add Content-Security-Policy header",
    "cvss_score":  6.5,
    "cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
}
Enter fullscreen mode Exit fullscreen mode

This means every finding, regardless of which module produced it, flows through the same reporter, gets scored by the same CVSS engine, and ends up in the same structured report.

CVSS v3.1 Scoring: Pure Python, No Library

One of the features I'm most proud of is the CVSS calculator. Rather than pulling in an external library, I implemented the full CVSS v3.1 base score formula from scratch in modules/cvss.py.

The spec defines a specific formula with metric weights for Attack Vector, Attack Complexity, Privileges Required, User Interaction, Scope, and the CIA triad (Confidentiality, Integrity, Availability). The tricky part is the "round up" rule CVSS uses ceiling rounding to one decimal place, not standard rounding.

def calculate_score(vector: CVSSVector) -> float:
    iss = 1 - (1 - c) * (1 - i) * (1 - a)

    if vector.S == "U":
        impact = 6.42 * iss
    else:
        impact = 7.52 * (iss - 0.029) - 3.25 * ((iss - 0.02) ** 15)

    exploitability = 8.22 * av * ac * pr * ui

    raw = min(impact + exploitability, 10) if vector.S == "U" \
          else min(1.08 * (impact + exploitability), 10)

    return math.ceil(raw * 10) / 10  # CVSS ceiling rounding
Enter fullscreen mode Exit fullscreen mode

Every finding is auto-mapped to a vector based on its OWASP category and title. A SQL injection maps to AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H (network reachable, no privileges, scope changed, full CIA impact) — a 10.0 CRITICAL. A missing header maps to something lower. You can also override any finding's vector interactively with --cvss-override.

The Fuzzer — And Why False Positives Were a Problem

The fuzzer (modules/fuzzer.py) sends payloads across the following 9 categories: SQLi, XSS, SSTI, path traversal, SSRF, command injection, NoSQL, prompt injection, and open redirect.

Early on I had a serious false-positive problem. When testing against OWASP Juice Shop (a React SPA), every single XSS and SSTI payload was flagging as a hit. The tool was reporting 60+ HIGH findings that were all fake.

The root cause: I was checking if the payload appeared anywhere in the response body, but React SPAs return the same index.html shell for every request regardless of query parameters. The payload appeared in the URL, which appeared in the response, which triggered the match.

The fix was a two-part SPA guard:

def _is_spa_shell(response_text: str) -> bool:
    indicators = ["<app-root>", "<div id=\"root\">", "data-beasties-container"]
    return any(ind.lower() in response_text.lower() for ind in indicators)

# If baseline is SPA and response is same size/status — skip it
same_response = (r.status_code == baseline_status and size_diff < 50)
if baseline_is_spa and same_response:
    continue
Enter fullscreen mode Exit fullscreen mode

And tighter detection signatures (SSTI) only triggers if 49 appears as a standalone token (the evaluated result of {{7*7}}), not just anywhere in the response. XSS only triggers if the tag appears unencoded in the body.

Custom OWASP Pattern Rules

v1.5 adds a YAML-based rules engine that lets you write your own detection patterns:

- id: aws-key-exposure
  name: AWS Access Key Exposed
  owasp: A02
  severity: CRITICAL
  match:
    response_body: "AKIA[0-9A-Z]{16}"
    source_code:   "(AKIA|ASIA|AROA)[0-9A-Z]{16}"
  fix: Rotate the key immediately and audit CloudTrail.
Enter fullscreen mode Exit fullscreen mode

Rules can match against HTTP response bodies, source code files (SAST mode), header presence/absence, header values, and status codes. Drop your YAML files in rules/ and they're picked up automatically.

Bugcrowd API Integration

Instead of manually copying scope from a Bugcrowd program page, WaspSting can pull it directly:

export BUGCROWD_API_TOKEN=your_token_here
python3 waspsting.py --bugcrowd-program acme-corp
Enter fullscreen mode Exit fullscreen mode

It fetches the target groups, filters to web-relevant targets, converts wildcards (*.example.com) to their apex domain with a clear warning, and saves a WaspSting-compatible scope JSON ready to pass to --mode bounty.


Finding Real Bugs — DVWA Walkthrough

Let's put it into practice against DVWA (Damn Vulnerable Web Application), a deliberately vulnerable app you can run locally for safe, legal testing.

Setup

# Run DVWA in Docker
docker run -d -p 8080:80 vulnerables/web-dvwa
Enter fullscreen mode Exit fullscreen mode

Open http://localhost:8080, log in with admin/password, click Setup/Reset DB, then set security level to Low in the DVWA Security tab.

Step 1 — Recon

python3 waspsting.py --target http://localhost:8080 --mode recon --rules --confirm
Enter fullscreen mode Exit fullscreen mode

What you'll see: Missing security headers (HSTS, CSP, X-Frame-Options), no HTTPS, CVSS scores auto-populated for each finding.

Step 2 — Auth Audit

python3 waspsting.py --target http://localhost:8080 --mode auth --confirm
Enter fullscreen mode Exit fullscreen mode

What you'll see: WaspSting finds the login endpoint, tests lockout policy (DVWA has none on Low security), documents JWT attack vectors for manual testing.

Step 3 — Fuzzer

python3 waspsting.py --target http://localhost:8080 --mode fuzz --confirm
Enter fullscreen mode Exit fullscreen mode

What you'll see: On DVWA at Low security, the fuzzer should trigger on SQL injection endpoints because DVWA actually returns database errors and real signatures, not SPA noise.

Step 4 — Full Scan with HTML Report

python3 waspsting.py \
  --target http://localhost:8080 \
  --mode full \
  --html --rules --cvss-override \
  --confirm
Enter fullscreen mode Exit fullscreen mode

What you'll see: Every module runs sequentially, findings accumulate, CVSS table prints at the end, HTML report saved to output/.

Open the HTML report in your browser and that's your executive report with risk gauge, severity charts, and filterable findings table.


What I Learned as Building a Security Tool

1. Building tools forces you to understand the underlying concepts deeply

You can't write a CVSS calculator without understanding how Attack Vector, Scope, and the CIA triad interact. You can't write a fuzzer without understanding why SPAs behave differently from server-rendered apps. The act of building forced me to go deeper than any lab ever did.

2. False positives are a real problem and most write-ups skip it

Every security tool deals with false positives. The difference between a useful tool and a noisy one is how carefully you tune detection. I spent more time on the SPA guard than on the entire fuzzer payload list.

3. Good architecture matters more than features

The decision to standardize all findings into the same dict format early on meant that CVSS scoring, deduplication, notifications, and reporting all just worked when I added them and no refactoring needed. The modular design meant I could add Nuclei integration without touching any existing module.

4. Documentation is part of the product

The README, the example rules file, the clear error messages are as important as the code. A tool with great features and terrible docs gets ignored. A tool with decent features and great docs gets used and contributed to.

5. Real-world testing is humbling

The moment I ran WaspSting against Juice Shop and saw 60 false-positive XSS findings, I understood something that no CTF ever taught me: the difference between "payload sent" and "vulnerability confirmed" is everything. That lesson shapes how I think about every finding now.


How to Use WaspSting for Bug Bounty

Here's the workflow I'd recommend for a typical Bugcrowd engagement:

1. Import scope

export BUGCROWD_API_TOKEN=your_token
python3 waspsting.py --bugcrowd-program target-program
Enter fullscreen mode Exit fullscreen mode

2. Run the bounty planner

python3 waspsting.py --mode bounty --scope output/scope_target_SESSION.json
Enter fullscreen mode Exit fullscreen mode

This gives you a phased test plan prioritised by payout potential.

3. Recon each in-scope target

python3 waspsting.py --target https://app.target.com --mode recon --cve --rules --confirm
Enter fullscreen mode Exit fullscreen mode

4. Subdomain enumeration

python3 waspsting.py --target https://target.com --mode enum --screenshot --confirm
Enter fullscreen mode Exit fullscreen mode

5. Full scan on interesting subdomains

python3 waspsting.py --target https://app.target.com --mode full \
  --html --rules --burp --dedup --confirm
Enter fullscreen mode Exit fullscreen mode

6. Manual follow-up
Use the generated Burp config to load scope and payloads into Burp Suite Community for manual testing of anything the automated scan flagged.

7. Clear session when done

python3 clear_session.py
Enter fullscreen mode Exit fullscreen mode

Saves your reports, wipes the output directory, and you're ready for the next program.


What's Next

The roadmap still has some exciting items:

  • HackerOne API integration (same as Bugcrowd but H1's OAuth flow)
  • CVSS v4.0 support (the spec dropped in 2023, still catching up)
  • Custom OWASP pattern sharing — a community rules repository
  • CI/CD integration docs for teams running WaspSting in pipelines
  • General optimization, fine-tuning for accuracy, and I'm sure there's more things I haven't even considered yet.

If you find a bug, want to contribute a module, or just want to say the tool helped you find something on a real program, open an issue or a PR. I'm learning as I go and every contribution teaches me something.


Get Started

git clone https://github.com/N00dleN00b/WaspSting.git
cd WaspSting
pip install -r requirements.txt
python3 waspsting.py --help
Enter fullscreen mode Exit fullscreen mode

Licensed Apache 2.0. Free to use, fork, and build on.

If WaspSting helped you find a bug — star the repo. It means a lot as a student building in public.


N00dleN00b is a cybersecurity student at TU passionate about ethical hacking, bug bounty research, and building open-source security tooling.

GitHub: github.com/N00dleN00b/WaspSting

Top comments (0)