kavklaw@llm ~ /guides/web-enumeration

kavklaw@llm $ cat web-enumeration-guide.md

Web Enumeration Methodology

๐ŸŸข Beginner

โฑ๏ธ 30 min read ยท Complete web testing methodology from first HTTP request to finding every attack surface

โ† Back to Guides
  • A web browser โ€” Firefox preferred (with Wappalyzer extension for tech fingerprinting)
  • curl โ€” command-line HTTP client (preinstalled on most Linux/macOS systems)
  • Browser developer tools โ€” F12 in any browser. The Network tab shows requests, Console shows JS errors, and Elements/Inspector shows the DOM
  • gobuster or ffuf โ€” directory/file brute forcing tools
  • whatweb โ€” CLI technology fingerprinting
  • SecLists โ€” wordlist collection for fuzzing (sudo apt install seclists on Kali/Debian)

Kali Linux has all of these preinstalled. On Debian/Ubuntu: sudo apt install curl gobuster whatweb seclists. For ffuf, grab the binary from GitHub releases.

โšก Quick Start

Got a web target and need to enumerate it right now? Here's the essential checklist:

# 1. First look โ€” what's on the page?
curl -I http://target.htb                     # Headers
curl -s http://target.htb | head -100         # First 100 lines of source

# 2. Technology fingerprinting
whatweb http://target.htb

# 3. Directory brute force
gobuster dir -u http://target.htb -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -t 50

# 4. Subdomain/vhost discovery
gobuster vhost -u http://target.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain

# 5. Check common files
curl -s http://target.htb/robots.txt
curl -s http://target.htb/sitemap.xml
curl -s http://target.htb/.git/HEAD
curl -s http://target.htb/.env

That's the speed run. Below is each technique broken down in detail.

๐ŸŒ What Is Web Enumeration?

Web enumeration is the systematic process of gathering information about a web application and its infrastructure. It sits between port scanning (which found the web service) and exploitation (where you attack it). Good enumeration is the difference between finding a vulnerability in 5 minutes and banging your head against the wall for hours.

How Web Servers Work

At its core, the web is a conversation between your browser (the client) and a remote computer (the server). Your browser sends an HTTP request โ€” essentially a structured text message saying "GET me the page at /login" โ€” and the server sends back an HTTP response containing the page content, a status code (200 = OK, 404 = not found, 500 = server error), and headers with metadata. Every tool in this guide โ€” curl, gobuster, ffuf, whatweb โ€” is just sending HTTP requests and analyzing the responses. Understanding this request-response cycle is fundamental because every web vulnerability comes from how the server processes your requests.

DNS and Subdomains

DNS (Domain Name System) is the internet's phone book โ€” it translates human-readable names like target.htb into IP addresses like 10.10.10.100. A subdomain is a prefix added to the main domain, like admin.target.htb or dev.target.htb, and it can point to a completely different application running on the same or different server. Subdomains are critical in pentesting because organizations often run development, staging, and internal applications on subdomains with weaker security than the main site. Many CTF challenges hide the real vulnerable application on a subdomain that's only discoverable through enumeration.

The goal is to map out the application's attack surface (every entry point where data you control gets processed by the server). The more entry points you find, the more chances you have to find a vulnerability:

  • Pages and endpoints โ€” visible and hidden
  • Parameters โ€” GET, POST, cookies, headers
  • Technologies โ€” server software, frameworks, libraries
  • Authentication mechanisms โ€” login forms, API keys, sessions
  • API endpoints โ€” REST, GraphQL, SOAP
  • File uploads โ€” potential for arbitrary file write
  • Configuration files โ€” accidentally exposed
  • Development artifacts โ€” .git, debug pages, test accounts

Web enumeration is iterative. Each finding can open new avenues. Finding a subdomain leads to a new application. Finding a JavaScript file reveals API endpoints. Finding an API endpoint reveals parameters. It's a tree you keep expanding until you've mapped everything.

๐Ÿš€ The First HTTP Request

Before any tools, start with a simple HTTP request and pay attention to everything:

# GET request with full headers
curl -v http://target.htb/ 2>&1

# What to observe:
# * Connected to target.htb (10.10.10.100) port 80
# > GET / HTTP/1.1
# > Host: target.htb
# > User-Agent: curl/7.88.1
# < HTTP/1.1 200 OK
# < Server: Apache/2.4.41 (Ubuntu)        โ† Server software + OS
# < X-Powered-By: PHP/7.4.3               โ† Backend language + version
# < Set-Cookie: PHPSESSID=abc123...        โ† Session management
# < Content-Type: text/html; charset=UTF-8

# Headers-only request (faster)
curl -I http://target.htb/

# Follow redirects
curl -L -I http://target.htb/

# View the page source
curl -s http://target.htb/ | less

# Check different HTTP methods โ€” HTTP defines several "verbs" for different actions:
# GET = retrieve data, POST = submit data, PUT = upload/replace, DELETE = remove
# OPTIONS = ask what methods are allowed, TRACE = echo request back (debug)
curl -X OPTIONS http://target.htb/ -v
curl -X PUT http://target.htb/test.txt -d "test" -v   # โš ๏ธ Can write files if PUT is enabled โ€” authorized tests only
curl -X TRACE http://target.htb/ -v
๐Ÿ’ก Pro Tip: Always add the target hostname to /etc/hosts before testing. Many web applications behave differently based on the Host header. echo "10.10.10.100 target.htb" | sudo tee -a /etc/hosts. If you discover virtual hosts later, add those too.

๐Ÿ”ฌ Technology Fingerprinting

Knowing what technologies a web application uses tells you what vulnerabilities to look for, what default files to check, and what exploits might apply.

WhatWeb

# WhatWeb โ€” identifies technologies from HTTP responses
whatweb http://target.htb
# Output: http://target.htb [200 OK] Apache[2.4.41], Country[US],
#   HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.10.100],
#   JQuery, PHP[7.4.3], Title[My Website], WordPress[5.7.2],
#   X-Powered-By[PHP/7.4.3]

# Verbose output with all plugins
whatweb -v http://target.htb

# Aggressive mode (makes additional requests)
whatweb -a 3 http://target.htb

# Scan multiple targets
whatweb http://target.htb http://target.htb:8080

Wappalyzer

Wappalyzer is a browser extension that identifies technologies on web pages. It detects:

  • CMS (WordPress, Drupal, Joomla)
  • JavaScript frameworks (React, Angular, Vue)
  • Web servers (Apache, Nginx, IIS)
  • Programming languages (PHP, Python, Ruby)
  • Analytics tools (Google Analytics, Matomo)
  • CDNs (Content Delivery Networks โ€” Cloudflare, AWS CloudFront)
  • E-commerce platforms (WooCommerce, Shopify)

Install the Wappalyzer browser extension and just visit the page โ€” it automatically analyzes technologies and shows them in the toolbar icon.

BuiltWith & Netcraft

# BuiltWith โ€” technology lookup (web-based)
# https://builtwith.com/target.htb

# Netcraft โ€” site report
# https://sitereport.netcraft.com/?url=target.htb

# Command-line alternative: httpx
httpx -u http://target.htb -tech-detect -status-code -title -web-server

# Nuclei technology detection
nuclei -u http://target.htb -t technologies/

Manual Fingerprinting

# Check HTTP response headers for clues
curl -I http://target.htb/ 2>/dev/null | grep -iE "server|x-powered|x-aspnet|x-generator"

# Error page fingerprinting โ€” trigger a 404
curl http://target.htb/nonexistent_page_12345 -v
# Apache default 404 looks different from Nginx, IIS, Tomcat, etc.
# Custom error pages โ†’ the application may hide the server identity

# Check for framework-specific files
curl -s http://target.htb/wp-login.php       # WordPress
curl -s http://target.htb/administrator/     # Joomla
curl -s http://target.htb/user/login         # Drupal
curl -s http://target.htb/admin/             # Generic admin panel
curl -s http://target.htb/elmah.axd          # ASP.NET error log
curl -s http://target.htb/web.config         # IIS config
curl -s http://target.htb/server-status      # Apache server-status

# PHP info page (sometimes left exposed)
curl -s http://target.htb/phpinfo.php
curl -s http://target.htb/info.php

๐Ÿ“‹ HTTP Headers Analysis

HTTP headers leak a tremendous amount of information. Both response headers from the server and request headers you can manipulate are important.

Response Headers to Examine

curl -I http://target.htb/ 2>/dev/null

# Server identification
Server: Apache/2.4.41 (Ubuntu)     # Web server + OS
X-Powered-By: PHP/7.4.3            # Backend language
X-AspNet-Version: 4.0.30319         # ASP.NET version
X-Generator: WordPress 5.7.2        # CMS

# Security headers (or lack thereof) โ€” missing ones are findings in a pentest!
X-Frame-Options: DENY               # Clickjacking protection (prevents embedding in iframes)
X-Content-Type-Options: nosniff     # MIME sniffing protection
X-XSS-Protection: 1; mode=block    # XSS (Cross-Site Scripting) filter
Content-Security-Policy: ...        # CSP โ€” restricts which resources the page can load
Strict-Transport-Security: ...      # HSTS โ€” forces browser to always use HTTPS
Access-Control-Allow-Origin: *      # CORS (Cross-Origin Resource Sharing) โ€” controls
                                    # which other websites can make requests to this one

# Missing security headers = findings in a pentest!

# Interesting response headers
Set-Cookie: session=abc; HttpOnly; Secure  # Cookie attributes
X-Request-ID: abc-123-def           # Request tracking
X-Debug: true                       # Debug mode!
X-Backend-Server: app-server-01     # Internal infrastructure

# Check all ports for different headers
for port in 80 443 8080 8443; do
  echo "=== Port $port ==="
  curl -I http://target.htb:$port/ 2>/dev/null | head -10
done

Manipulating Request Headers

# Test for virtual hosts via Host header
curl -H "Host: admin.target.htb" http://10.10.10.100/
curl -H "Host: dev.target.htb" http://10.10.10.100/

# X-Forwarded-For โ€” a header that proxy servers add to indicate the original
# client IP. If the app trusts this header, you can spoof your IP to bypass
# restrictions that only allow access from localhost or internal networks
curl -H "X-Forwarded-For: 127.0.0.1" http://target.htb/admin/
curl -H "X-Real-IP: 127.0.0.1" http://target.htb/admin/
curl -H "X-Originating-IP: 127.0.0.1" http://target.htb/admin/
curl -H "X-Custom-IP-Authorization: 127.0.0.1" http://target.htb/admin/

# User-Agent testing
curl -A "Mozilla/5.0" http://target.htb/         # Standard browser
curl -A "Googlebot/2.1" http://target.htb/        # Google crawler
curl -A "" http://target.htb/                      # Empty UA

# Accept header testing
curl -H "Accept: application/json" http://target.htb/api/
curl -H "Accept: application/xml" http://target.htb/api/

๐Ÿ“ Source Code Review

Always read the HTML source code. Don't just look at the rendered page โ€” view the raw source for hidden information.

What to Look For

# Download and examine the page source
curl -s http://target.htb/ > source.html

# HTML comments (developers leave notes, credentials, TODOs)
grep -n "<!--" source.html
# Common finds:
# <!-- TODO: remove before production -->
# <!-- admin:password123 -->
# <!-- Debug mode enabled -->
# <!-- Old login at /login_backup.php -->

# Hidden form fields
grep -i "hidden" source.html
# <input type="hidden" name="debug" value="0">
# <input type="hidden" name="role" value="user">
# โ†’ Try changing these: debug=1, role=admin

# Links and paths
grep -oP 'href="[^"]*"' source.html | sort -u
grep -oP 'src="[^"]*"' source.html | sort -u
grep -oP 'action="[^"]*"' source.html | sort -u

# Email addresses
grep -oP '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' source.html

# Embedded JavaScript with interesting data
grep -i "var \|const \|let \|api\|token\|key\|secret\|password\|endpoint" source.html

# Base64 encoded strings
grep -oP '[A-Za-z0-9+/]{20,}={0,2}' source.html | while read b64; do
  decoded=$(echo "$b64" | base64 -d 2>/dev/null)
  if [ $? -eq 0 ]; then echo "$b64 โ†’ $decoded"; fi
done

Checking Common Hidden Files

# robots.txt โ€” tells crawlers what NOT to index (= interesting paths)
curl -s http://target.htb/robots.txt
# Disallow: /admin/
# Disallow: /backup/
# Disallow: /internal/api/
# โ†‘ These are all worth investigating!

# sitemap.xml โ€” full site structure
curl -s http://target.htb/sitemap.xml

# .git directory โ€” exposed source code!
curl -s http://target.htb/.git/HEAD
# If this returns "ref: refs/heads/main" โ†’ source code is exposed
# Use git-dumper to extract:
git-dumper http://target.htb/.git/ ./dumped_repo

# .env file โ€” environment variables (credentials, API keys)
curl -s http://target.htb/.env
# DB_PASSWORD=supersecret
# API_KEY=sk-live-abc123
# JWT_SECRET=mysecretkey

# .htaccess โ€” Apache configuration
curl -s http://target.htb/.htaccess

# Other sensitive files
curl -s http://target.htb/crossdomain.xml
curl -s http://target.htb/clientaccesspolicy.xml
curl -s http://target.htb/security.txt
curl -s http://target.htb/.well-known/security.txt
curl -s http://target.htb/composer.json
curl -s http://target.htb/package.json
curl -s http://target.htb/Gruntfile.js
curl -s http://target.htb/config.php.bak
curl -s http://target.htb/web.config
curl -s http://target.htb/WEB-INF/web.xml

๐Ÿง  Knowledge Check โ€” Web Enumeration Fundamentals

What tool identifies web technologies (CMS, frameworks, languages) from HTTP responses?
WhatWeb is the go-to CLI tool for technology fingerprinting. It identifies CMS (WordPress, Drupal, Joomla), web servers (Apache, Nginx, IIS), programming languages (PHP, Python), JavaScript frameworks, and more from a single HTTP response. Run whatweb http://target.htb as one of your first commands on any web target. Wappalyzer (browser extension) provides similar capability in the GUI.
Complete the command to discover virtual hosts on a web server:
$  -u http://target.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain
gobuster vhost fuzzes the Host header to discover virtual hosts sharing the same IP. This is crucial in CTFs where the main site is often a decoy and the real vulnerable app runs on a subdomain like dev.target.htb or admin.target.htb. Always fuzz for vhosts on every web target. ffuf can also do this with -H "Host: FUZZ.target.htb".
๐Ÿ“‹ Scenario
$ curl -s http://target.htb/.git/HEAD
ref: refs/heads/main

$ curl -I http://target.htb/.env
HTTP/1.1 200 OK
Content-Type: text/plain

$ curl -s http://target.htb/.env
DB_HOST=localhost
DB_USER=appuser
DB_PASS=S3cretDbP@ss!
JWT_SECRET=my-super-secret-jwt-key-2024
You've found an exposed .git directory and .env file. Which provides the higher-impact finding?
Both findings are extremely high-impact. The .env file gives immediate wins: database credentials (try them against the DB and for credential reuse) and a JWT secret (forge admin tokens). The .git directory is potentially even more powerful: use git-dumper to download the full source code, then analyze it for hardcoded credentials, hidden endpoints, SQL injection points, and application logic vulnerabilities. In a pentest, always exploit both.
๐Ÿ’ก Pro Tip: Exposed .git directories are one of the most impactful findings in real-world pentesting. With the full source code, you can find hardcoded credentials, understand application logic, find hidden endpoints, and discover vulnerabilities. Always check /.git/HEAD on every web target.

๐Ÿ“œ JavaScript Analysis

JavaScript files are a goldmine. They contain API routes, authentication logic, hidden endpoints, debug functions, and sometimes hardcoded credentials or API keys.

Finding JavaScript Files

# Extract all JS file references from the page
curl -s http://target.htb/ | grep -oP 'src="[^"]*\.js[^"]*"' | sort -u
# src="/static/js/main.js"
# src="/static/js/app.bundle.js"
# src="/assets/js/api-client.js"

# Download all JS files
for js in $(curl -s http://target.htb/ | grep -oP 'src="\K[^"]*\.js[^"]*'); do
  echo "Downloading: $js"
  curl -s "http://target.htb$js" -o "$(basename $js)"
done

# Check for sourcemaps (unminified source code!)
curl -s http://target.htb/static/js/main.js | tail -1
# //# sourceMappingURL=main.js.map
curl -s http://target.htb/static/js/main.js.map | python3 -m json.tool

Analyzing JavaScript Content

# Search for API endpoints
grep -oP '["'\'']/api/[^"'\'']*' *.js | sort -u
# "/api/v1/users"
# "/api/v1/admin/settings"  โ† Hidden admin API!
# "/api/internal/debug"     โ† Debug endpoint!

# Search for hardcoded credentials/keys
grep -iP '(api[_-]?key|token|secret|password|auth|bearer)\s*[:=]' *.js
# const API_KEY = "sk-live-abc123def456"
# let adminToken = "eyJhbGciOiJ..."

# Search for interesting URLs/endpoints
grep -oP 'https?://[^"'\''\\s]*' *.js | sort -u

# Search for hidden routes (React/Angular/Vue router)
grep -oP 'path:\s*["'\'']/[^"'\'']*' *.js | sort -u
# path: "/admin/dashboard"
# path: "/debug/phpinfo"
# path: "/secret-page"

# Beautify minified JS
# Install js-beautify: npm install -g js-beautify
js-beautify main.js > main.beautified.js

# Use LinkFinder to extract endpoints automatically
python3 linkfinder.py -i http://target.htb/static/js/main.js -o cli

Source Map Exploitation

# Source maps contain the ORIGINAL unminified source code
# Check for .map files
curl -s http://target.htb/static/js/main.js.map -o main.js.map

# Extract source files from map
# Install: npm install -g sourcemapper
sourcemapper -url http://target.htb/static/js/main.js.map -output extracted/

# Now you have the full React/Angular/Vue source code!
find extracted/ -name "*.js" -exec grep -l "password\|secret\|admin" {} \;
๐Ÿ’ก Pro Tip: Modern web apps (React, Angular, Vue) compile everything into bundle files. These bundles contain ALL client-side logic, route definitions, and API calls. A single app.bundle.js can reveal dozens of hidden endpoints. Always download and analyze every JS file, and always check for source maps.

Cookies reveal session management details and can expose vulnerabilities:

# View cookies set by the server
curl -v http://target.htb/login 2>&1 | grep "Set-Cookie"
# Set-Cookie: PHPSESSID=abc123; path=/; HttpOnly
# Set-Cookie: user_role=dXNlcg==; path=/

# Check cookie attributes
# HttpOnly โ€” not accessible via JavaScript (XSS can't steal it)
# Secure   โ€” only sent over HTTPS
# SameSite โ€” CSRF protection (Strict/Lax/None)
# Path     โ€” which paths receive the cookie
# Domain   โ€” which domains receive the cookie

# Decode Base64 cookies
echo "dXNlcg==" | base64 -d
# "user" โ† Can we change this to "admin"?

# JWT token inspection
# If the cookie looks like: eyJhbGciOiJ... it's a JWT (three base64url parts separated by dots)
# Decode at jwt.io, or from the command line (JWT uses base64url, needs padding fix):
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | tr '_-' '/+' | base64 -d 2>/dev/null
# {"alg":"HS256","typ":"JWT"}

# Try modifying cookies
curl -b "user_role=YWRtaW4=" http://target.htb/dashboard
# "YWRtaW4=" = base64("admin") โ€” does it work?

# Flask session cookies (signed but not encrypted)
# Install: pip install flask-unsign
flask-unsign --decode --cookie 'eyJ1c2VyIjoiZ3Vlc3QifQ.Xt0...'
# {'user': 'guest'}

flask-unsign --unsign --cookie 'eyJ1c2VyIjoiZ3Vlc3QifQ.Xt0...' \
  --wordlist /usr/share/wordlists/rockyou.txt
# Found secret key: 'supersecret'

flask-unsign --sign --cookie '{"user":"admin"}' --secret 'supersecret'
# Forged admin cookie!

๐Ÿ“‚ Directory & File Enumeration

Directory brute forcing discovers hidden pages, backup files, admin panels, and configuration files that aren't linked from the main site.

# Gobuster โ€” directory mode
gobuster dir -u http://target.htb \
  -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
  -t 50 -o gobuster_dirs.txt

# With file extensions
gobuster dir -u http://target.htb \
  -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt \
  -x php,html,txt,bak,old,zip,config \
  -t 50 -o gobuster_files.txt

# ffuf โ€” faster and more flexible
ffuf -u http://target.htb/FUZZ \
  -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
  -t 50 -o ffuf_results.json -of json

# With extensions
ffuf -u http://target.htb/FUZZ \
  -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt \
  -e .php,.html,.txt,.bak,.zip,.config,.env \
  -t 50

# Gobuster does NOT support recursive scanning in dir mode
# Use feroxbuster instead โ€” it's recursive by default
feroxbuster -u http://target.htb \
  -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
  -t 50 -x php,html,txt
# feroxbuster will automatically scan discovered directories like /admin/, /api/, etc.

# Filter by response size (exclude common 404 pages)
ffuf -u http://target.htb/FUZZ \
  -w /usr/share/wordlists/dirb/big.txt \
  -fs 1234  # Filter out responses of size 1234 bytes (the 404 page)

Essential Wordlists for Directory Enumeration

Small/fast (start here):

  • /usr/share/seclists/Discovery/Web-Content/common.txt โ€” 4,700 entries
  • /usr/share/wordlists/dirb/common.txt โ€” 4,614 entries

Medium (good balance):

  • /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt โ€” 30,000 entries
  • /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt โ€” 17,000 entries

Large (thorough):

  • /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt โ€” 62,000 entries
  • /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-big.txt โ€” 1.2M entries

Technology-specific:

/usr/share/seclists/Discovery/Web-Content/apache.txt
/usr/share/seclists/Discovery/Web-Content/nginx.txt
/usr/share/seclists/Discovery/Web-Content/iis.txt
/usr/share/seclists/Discovery/Web-Content/tomcat.txt
/usr/share/seclists/Discovery/Web-Content/CGIs.txt

๐Ÿ—๏ธ CMS Identification & Testing

A CMS (Content Management System) is software for building websites without writing code from scratch, like WordPress, Joomla, or Drupal. They're common targets because they have well-known vulnerabilities (especially through plugins), default files, and specialized scanning tools that automate finding weaknesses.

WordPress

# Detection
curl -s http://target.htb/ | grep -i "wp-content\|wordpress"
curl -s http://target.htb/wp-login.php
curl -s http://target.htb/wp-json/wp/v2/users  # Enumerate users!

# WPScan โ€” thorough WordPress scanner
wpscan --url http://target.htb/ --enumerate u,p,t,tt
# u = users, p = plugins, t = themes, tt = timthumbs

# Aggressive plugin detection
wpscan --url http://target.htb/ --enumerate ap --plugins-detection aggressive

# With API token (for vulnerability data)
wpscan --url http://target.htb/ --enumerate vp,vt \
  --api-token YOUR_TOKEN_HERE

# Brute force WordPress login
wpscan --url http://target.htb/ -U admin -P /usr/share/wordlists/rockyou.txt

# Manual enumeration
curl -s http://target.htb/wp-json/wp/v2/users | python3 -m json.tool
# Returns user IDs, names, slugs
curl -s "http://target.htb/?author=1"  # User enumeration via author IDs

# Check WordPress version
curl -s http://target.htb/readme.html | grep -i version
curl -s http://target.htb/feed/ | grep generator

Joomla

# Detection
curl -s http://target.htb/ | grep -i "joomla"
curl -s http://target.htb/administrator/
curl -s http://target.htb/configuration.php~  # Backup config?

# JoomScan โ€” Joomla scanner
joomscan -u http://target.htb/

# Version detection
curl -s http://target.htb/administrator/manifests/files/joomla.xml | grep version
curl -s http://target.htb/language/en-GB/en-GB.xml | grep version

Drupal

# Detection
curl -s http://target.htb/ | grep -i "drupal"
curl -s http://target.htb/CHANGELOG.txt | head -5   # Version!
curl -s http://target.htb/core/CHANGELOG.txt | head -5

# Droopescan
droopescan scan drupal -u http://target.htb/

# Known Drupal vulns
# Drupalgeddon (CVE-2014-3704) โ€” SQL injection
# Drupalgeddon2 (CVE-2018-7600) โ€” RCE
# Drupalgeddon3 (CVE-2018-7602) โ€” RCE

๐Ÿ”Œ API Enumeration

An API (Application Programming Interface) is a set of URLs that the website's frontend uses to talk to the backend. For example, /api/v1/users might return a list of users in JSON format. Modern web apps are API-driven, meaning the real logic lives in API endpoints rather than traditional HTML pages. Discovering these endpoints reveals attack surfaces that directory brute forcing often misses.

Finding API Endpoints

# Check for API documentation
curl -s http://target.htb/api/
curl -s http://target.htb/api/docs
curl -s http://target.htb/api/v1/
curl -s http://target.htb/swagger.json
curl -s http://target.htb/swagger/
curl -s http://target.htb/openapi.json
curl -s http://target.htb/api-docs/
curl -s http://target.htb/v2/api-docs
curl -s http://target.htb/graphql          # GraphQL endpoint
curl -s http://target.htb/graphiql         # GraphQL IDE

# Fuzz API endpoints
ffuf -u http://target.htb/api/v1/FUZZ \
  -w /usr/share/seclists/Discovery/Web-Content/api/objects.txt \
  -t 50

# Fuzz API versions
ffuf -u http://target.htb/api/FUZZ/users \
  -w <(seq 1 20 | sed 's/^/v/') \
  -t 10

# Test HTTP methods on discovered endpoints
for method in GET POST PUT DELETE PATCH OPTIONS; do
  echo "=== $method ==="
  curl -X $method http://target.htb/api/v1/users -s -o /dev/null -w "%{http_code}\n"
done

GraphQL Enumeration

GraphQL is an alternative to REST APIs where you send a query describing exactly what data you want. The powerful thing for attackers: GraphQL has an "introspection" feature that lets you ask the API to describe its entire structure, including all available data types and fields.

# Introspection query โ€” get the full schema
curl -s -X POST http://target.htb/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{__schema{types{name,fields{name,type{name}}}}}"}'

# Simplified introspection
curl -s -X POST http://target.htb/graphql \
  -H "Content-Type: application/json" \
  -d '{"query":"{__schema{queryType{fields{name}}}}"}'

# Tools for GraphQL
# graphql-voyager โ€” visual schema explorer
# graphql-path-enum โ€” find sensitive paths
# InQL โ€” Burp extension for GraphQL

Parameter Discovery

# Arjun โ€” parameter discovery tool
arjun -u http://target.htb/api/endpoint

# ffuf parameter fuzzing  
ffuf -u "http://target.htb/page?FUZZ=test" \
  -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
  -fs 1234  # Filter default response size

# POST parameter fuzzing
ffuf -u http://target.htb/api/endpoint \
  -X POST -H "Content-Type: application/json" \
  -d '{"FUZZ":"test"}' \
  -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt \
  -fs 1234

๐Ÿ  Virtual Host Discovery

Virtual hosts (vhosts) allow multiple websites to run on a single IP address โ€” this is how most web servers in the real world operate, since IP addresses are expensive and limited. The web server looks at the Host header in your HTTP request to decide which site to show you. This means admin.target.htb and dev.target.htb could both point to the same IP but show completely different websites with entirely different codebases and security postures. Discovering vhosts reveals hidden applications the main site never links to โ€” and in CTFs, this is often where the actual vulnerability lives.

# Gobuster vhost mode
gobuster vhost -u http://target.htb \
  -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
  --append-domain

# ffuf vhost discovery
ffuf -u http://target.htb/ \
  -H "Host: FUZZ.target.htb" \
  -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
  -fs 1234  # Filter the default vhost response size

# Manual vhost testing
curl -H "Host: admin.target.htb" http://10.10.10.100/ -s | head -20
curl -H "Host: dev.target.htb" http://10.10.10.100/ -s | head -20
curl -H "Host: staging.target.htb" http://10.10.10.100/ -s | head -20
curl -H "Host: internal.target.htb" http://10.10.10.100/ -s | head -20
curl -H "Host: api.target.htb" http://10.10.10.100/ -s | head -20

# DNS subdomain enumeration
# If the target has real DNS (not just /etc/hosts)
gobuster dns -d target.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt

# Add discovered vhosts to /etc/hosts
echo "10.10.10.100 admin.target.htb dev.target.htb" | sudo tee -a /etc/hosts

๐Ÿง  Knowledge Check โ€” Advanced Enumeration

Complete the command to scan a WordPress site for users, plugins, and themes:
$ 
wpscan --url http://target.htb/ --enumerate u,p,t scans for users (u), plugins (p), and themes (t). WPScan is the gold standard for WordPress testing. For vulnerable plugins/themes, add --enumerate vp,vt with an API token (--api-token). WordPress plugins are the most common attack vector โ€” many have known RCE vulnerabilities. Also try /wp-json/wp/v2/users for user enumeration.
Why is JavaScript analysis critical for modern web application enumeration?
Modern SPAs (React, Angular, Vue) compile all client-side logic into JavaScript bundles. These bundles contain: API endpoint URLs (including hidden/internal ones), route definitions (including admin routes), authentication logic, debug/development functions, and sometimes hardcoded API keys, tokens, or credentials. Always download JS files, beautify them, and search for 'api', 'endpoint', 'token', 'secret', 'password', 'admin'. Source maps (.js.map) can reveal the entire unminified source code.
You're testing a web app and the X-Forwarded-For header lets you bypass IP-based admin restrictions. What value should you try?
127.0.0.1 (localhost) is the most commonly effective value for X-Forwarded-For bypass. Many web applications restrict admin panels to "local only" access and trust the X-Forwarded-For header to determine the client's IP. Setting X-Forwarded-For: 127.0.0.1 tricks the app into thinking the request comes from localhost. Also try X-Real-IP, X-Originating-IP, and X-Custom-IP-Authorization headers.
What should you always do FIRST before running any web enumeration tools?
Always add the target hostname to /etc/hosts first: echo "10.10.10.100 target.htb" | sudo tee -a /etc/hosts. Many web applications use virtual hosting and return different content based on the Host header. Browsing by IP might show a default page, while using the hostname shows the actual application. This is especially important in HTB/CTF environments where vhosts are common.
You ran gobuster but found nothing. What should you try next?
Different wordlists find different things. If common.txt or raft-medium didn't hit, try: (1) Technology-specific lists (apache.txt, nginx.txt, iis.txt), (2) Different extensions matching the stack (-x php,asp,aspx,jsp,bak,old,conf,env), (3) Larger wordlists (directory-list-2.3-big.txt), (4) Recursive scanning with feroxbuster, (5) Virtual host fuzzing you may have skipped. Also review the source code manually โ€” sometimes endpoints are referenced in HTML/JS but not in wordlists.
๐Ÿ’ก Pro Tip: Virtual host discovery is important in CTFs and HTB machines. When you get a box with a web server, the main site might be a dead end. The real vulnerable application is often on a subdomain. Always fuzz for vhosts. If ffuf shows many results with the same size, use -fs SIZE to filter out the default response.

๐Ÿ›ก๏ธ WAF Detection & Fingerprinting

A WAF (Web Application Firewall) is a security layer that sits between you and the web server. It inspects your requests and blocks anything that looks malicious (like SQL injection attempts or directory brute forcing). Detecting a WAF early helps you adjust your approach, for example by slowing down scans or encoding your payloads differently.

# wafw00f โ€” WAF detection tool
wafw00f http://target.htb

# Manual WAF detection โ€” send a malicious request and observe:
curl "http://target.htb/?id=1' OR 1=1--" -v

# Nmap WAF detection scripts
nmap -p 80 --script=http-waf-detect,http-waf-fingerprint target.htb

WAF responses often include: 403 Forbidden with custom error page, different response headers, modified response body ("Access Denied"), or redirect to a CAPTCHA challenge page.

Common WAF signatures in headers (useful heuristics, but not guarantees โ€” headers can be stripped, spoofed, or hidden behind CDNs):

  • cf-ray, cf-cache-status โ†’ Cloudflare
  • x-sucuri-id โ†’ Sucuri
  • x-akamai-transformed โ†’ Akamai
  • x-cdn: Served-By-Zenedge โ†’ Zenedge
  • x-protected-by: Sqreen โ†’ Sqreen

Working Around WAFs

# If there's a WAF, consider:
# 1. Finding the origin IP (bypass Cloudflare/CDN)
#    - Check DNS history: https://securitytrails.com
#    - Check Censys/Shodan for the origin IP
#    - Check mail headers (MX records reveal origin)

# 2. Adjusting your requests
#    - Slow down scanning (reduce threads)
#    - Rotate User-Agent strings
#    - Use different encoding for payloads
#    - Try HTTP parameter pollution

# 3. Using WAF bypass techniques (varies by WAF)
#    - URL encoding, double encoding
#    - Case variation
#    - Comment injection in SQL
#    - Alternative syntax for commands

๐ŸŽฏ Real-World Methodology

Here's the complete web enumeration methodology I use on every engagement. Follow this order โ€” each step feeds into the next.

  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
  โ”‚          WEB ENUMERATION METHODOLOGY            โ”‚
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ–ผ
  Phase 1: FIRST CONTACT โ”€โ”€โ”€ curl headers, whatweb,
           (5 min)           add to /etc/hosts
                         โ”‚
                         โ–ผ
  Phase 2: QUICK WINS โ”€โ”€โ”€โ”€โ”€ robots.txt, .git/HEAD,
           (5 min)          .env, source code review
                         โ”‚
                         โ–ผ
  Phase 3: DIRECTORY โ”€โ”€โ”€โ”€โ”€โ”€โ”€ gobuster/ffuf + vhost
           ENUM (parallel)   discovery (background)
                         โ”‚
                         โ–ผ
  Phase 4: DEEP ANALYSIS โ”€โ”€ JS analysis, cookies,
           (while scans run) CMS scanning, API discovery
                         โ”‚
                         โ–ผ
  Phase 5: ITERATE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ New finding? Go back to
                              Phase 2 for that target!

Phase 1: First Contact (5 minutes)

# Add hostname to /etc/hosts
echo "10.10.10.100 target.htb" | sudo tee -a /etc/hosts

# Quick look at the target
curl -I http://target.htb/
curl -s http://target.htb/ | head -200
whatweb http://target.htb

# Check all web ports
for port in 80 443 8080 8443 3000 5000 8000 9090; do
  code=$(curl -s -o /dev/null -w "%{http_code}" http://target.htb:$port/ 2>/dev/null)
  [ "$code" != "000" ] && echo "Port $port: HTTP $code"
done

Phase 2: Quick Wins (5 minutes)

# Check common files
for file in robots.txt sitemap.xml .git/HEAD .env .htaccess crossdomain.xml security.txt; do
  code=$(curl -s -o /dev/null -w "%{http_code}" http://target.htb/$file)
  [ "$code" == "200" ] && echo "FOUND: /$file"
done

# View source โ€” look for comments, hidden fields, JS files
curl -s http://target.htb/ | grep -i "