关于 staging-subdomain-hunt
用于授权渗透测试的子域名发现技能,通过证书透明度记录、DNS 暴力枚举和 Web 探测寻找暂存、开发及内部主机。还提供与生产环境对比安全控制及核实发现的步骤。
主要功能
- 01证书透明度枚举
- 02DNS 子域名枚举
- 03存活主机探测
- 04暂存环境检查
- 05生产与暂存对比
- 06发现结果验证
适用场景
- 01为已加固的生产站点寻找其他测试入口
- 02检查暂存主机是否暴露调试页面或内部 API
- 03比较生产与暂存环境的安全控制
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill staging-subdomain-hunt安装后在 Claude Code 中输入/staging-subdomain-hunt确认已加载。
详细介绍
预发布环境与子域名猎查技能
通过证书透明度(crt.sh)、DNS 暴力枚举和 Web 探测来发现预发布、开发和内部子域名。利用预发布环境中的安全缺口——与生产环境相比,预发布环境的安全性通常较弱(没有 WAF、启用了调试功能、安装页面可访问)。已在 7 个美国目标上验证:预发布子域名暴露了生产环境中不可见的 phpinfo、WordPress 安装页面和内部 API。
适用场景
- 在高价值目标上运行
deep-invade第 5 阶段。 - 生产目标防护完善(有 WAF、没有信息泄露)——转而探测预发布环境。
- 目标的攻击面较大(电子商务、SaaS、加盟模式)。
- 主站已加固,需要寻找其他入口点。
subdomain-enumeration生成子域名列表后。
前提条件
- 配备 curl、httpx、jq 的
terminal。 - 目标域名(例如
example.com)。 - DNS 暴力枚举所需的字典:
./tools/subdomains.txt。
运行方法
DOMAIN="example.com"
# crt.sh discovery
curl --max-time 30 --connect-timeout 10 -sk "https://crt.sh/?q=%25.$DOMAIN&output=json" | jq -r '.[].name_value' | sed 's/\*\.//g' | sort -u > subs.txt
# Probe for live hosts
httpx -silent -l subs.txt -threads 50 -status-code -tech-detect -o alive_subs.txt
# Check for staging indicators
grep -iE 'staging|stage|dev|test|uat|beta' alive_subs.txt
快速参考
| 指标 | 含义 | 操作 |
|---|---|---|
/wp-admin/install.php 返回 200 | 全新的 WordPress——尚未配置站点 | 接管安装 |
/wp-admin/upgrade.php 返回 200 | WP 需要升级数据库 | 数据库信息泄露 |
预发布环境中存在 info.php / phpinfo.php | 已启用调试 | 分析 PHPInfo(参见 phpinfo-to-rce) |
| 预发布环境没有 Cloudflare/WAF | 可直接访问源站 | 对源站 IP 运行完整的 deep-invade |
| 预发布环境启用了 CORS,生产环境没有 | 预发布环境的 CORS 策略较弱 | 从预发布环境上下文发起 CORS 攻击 |
预发布环境中存在 .env | 开发凭据暴露 | 窃取凭据,转向生产环境 |
操作步骤
第 1 步——证书透明度枚举
DOMAIN="$1"
OUTDIR="$OUTDIR/staging/$DOMAIN"
mkdir -p "$OUTDIR"
echo "[*] crt.sh enumeration for *.$DOMAIN"
# Primary crt.sh query
curl -sk --max-time 30 --connect-timeout 10 "https://crt.sh/?q=%25.$DOMAIN&output=json" 2>/dev/null | \
jq -r '.[].name_value' 2>/dev/null | sed 's/\*\.//g' | sed 's/^www\.//' | sort -u > "$OUTDIR/crtsh_subs.txt"
# Also try without wildcard prefix
curl -sk --max-time 30 --connect-timeout 10 "https://crt.sh/?q=$DOMAIN&output=json" 2>/dev/null | \
jq -r '.[].name_value' 2>/dev/null | sed 's/\*\.//g' | sed 's/^www\.//' | sort -u >> "$OUTDIR/crtsh_subs.txt"
sort -u "$OUTDIR/crtsh_subs.txt" -o "$OUTDIR/crtsh_subs.txt"
sub_count=$(wc -l < "$OUTDIR/crtsh_subs.txt")
echo "[+] crt.sh: $sub_count unique subdomains"
# Categorize by pattern
echo ""
echo "[*] Staging/dev subdomains:"
grep -iE 'staging|stage|dev\.|development|test|uat|beta|sandbox|demo|preview|qa' "$OUTDIR/crtsh_subs.txt"
echo ""
echo "[*] Admin/internal subdomains:"
grep -iE 'admin|portal|internal|dashboard|manage|cp\.|control|panel|cpanel|webmail|mail\.' "$OUTDIR/crtsh_subs.txt"
echo ""
echo "[*] API subdomains:"
grep -iE 'api|rest|graphql|ws\.|websocket' "$OUTDIR/crtsh_subs.txt"
echo ""
echo "[*] Infrastructure subdomains:"
grep -iE 'cdn|static|assets|media|img|images|files|download|origin|proxy' "$OUTDIR/crtsh_subs.txt"
echo ""
echo "[*] Franchise/location subdomains:"
grep -iE 'franchise|location|store|shop|branch|office' "$OUTDIR/crtsh_subs.txt"
第 2 步——发现在线主机
DOMAIN="$1"
OUTDIR="$OUTDIR/staging/$DOMAIN"
echo "[*] Probing $(wc -l < "$OUTDIR/crtsh_subs.txt") subdomains..."
httpx -silent -l "$OUTDIR/crtsh_subs.txt" -threads 50 -status-code -tech-detect -title \
-o "$OUTDIR/alive_subs.txt"
alive=$(wc -l < "$OUTDIR/alive_subs.txt")
echo "[+] $alive live hosts"
# Prioritize staging/dev
echo ""
echo "[*] Staging/dev LIVE:"
grep -iE 'staging|stage|dev\.|development|test|uat' "$OUTDIR/alive_subs.txt" | head -20
第 3 步——检查 WordPress 安装页面(预发布环境接管)
预发布站点经常已安装 WordPress,但尚未完成配置:
DOMAIN="$1"
OUTDIR="$OUTDIR/staging/$DOMAIN"
echo "[*] Checking for WordPress install pages on staging..."
for sub in $(grep -iE 'staging|stage|dev' "$OUTDIR/alive_subs.txt" | awk '{print $1}' | head -10); do
echo "--- $sub ---"
# Check install.php (fresh WP, no config)
install_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 10 --connect-timeout 10 "$sub/wp-admin/install.php")
if [[ "$install_code" == "200" ]]; then
echo " [TAKEOVER] /wp-admin/install.php — fresh WP install, can configure site!"
# Extract form fields
curl -sk --max-time 10 --connect-timeout 10 "$sub/wp-admin/install.php" | grep -Eo 'name="[^"]+"' | sort -u
fi
# Check upgrade.php (needs DB upgrade)
upgrade_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 10 --connect-timeout 10 "$sub/wp-admin/upgrade.php")
if [[ "$upgrade_code" == "200" ]]; then
echo " [INFO] /wp-admin/upgrade.php — DB upgrade page accessible"
fi
# Check setup-config.php (no wp-config)
config_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 10 --connect-timeout 10 "$sub/wp-admin/setup-config.php")
if [[ "$config_code" == "200" || "$config_code" == "409" ]]; then
echo " [INFO] /wp-admin/setup-config.php — wp-config missing or accessible"
fi
# Check for exposed info.php
info_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 "$sub/info.php")
[[ "$info_code" == "200" ]] && echo " [CRITICAL] /info.php exposed on staging!"
# Check for .env on staging
env_code=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 "$sub/.env")
[[ "$env_code" == "200" ]] && echo " [CRITICAL] /.env exposed on staging!"
done
第 4 步——分析生产环境与预发布环境的安全差异
DOMAIN="$1"
PROD="https://$DOMAIN"
STAGING=$(grep -iE 'staging|stage' "$OUTDIR/staging/$DOMAIN/alive_subs.txt" | head -1 | awk '{print $1}')
if [[ -n "$STAGING" ]]; then
echo "[*] Comparing $PROD vs $STAGING"
# Compare HTTP headers
echo "=== Production Headers ==="
curl --max-time 30 --connect-timeout 10 -skI "$PROD" 2>/dev/null | head -20
echo ""
echo "=== Staging Headers ==="
curl --max-time 30 --connect-timeout 10 -skI "$STAGING" 2>/dev/null | head -20
# Check for common staging weaknesses
echo ""
echo "[*] Staging-specific checks:"
# Directory listing
listing=$(curl -sk --max-time 5 --connect-timeout 5 "$STAGING/wp-content/uploads/" | grep -i "Index of")
[[ -n "$listing" ]] && echo " [WEAK] Directory listing enabled on uploads"
# Debug mode
debug=$(curl -sk --max-time 5 --connect-timeout 5 "$STAGING/" | grep -i "wp_debug\|debug mode\|error_reporting")
[[ -n "$debug" ]] && echo " [WEAK] Debug output visible"
# CORS on staging
cors=$(curl -skI --max-time 5 --connect-timeout 5 "$STAGING/wp-json/wp/v2/users" -H "Origin: https://evil.com" | grep -i "access-control-allow-credentials: true")
[[ -n "$cors" ]] && echo " [WEAK] CORS credential reflection on staging"
# XMLRPC on staging
xmlrpc=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 -X POST "$STAGING/xmlrpc.php" \
-d '<?xml version="1.0"?><methodCall><methodName>demo.sayHello</methodName></methodCall>')
[[ "$xmlrpc" == "200" ]] && echo " [WEAK] XMLRPC open on staging"
fi
第 5 步——加盟/多地点子域名枚举
适用于加盟业务或多地点企业:
DOMAIN="$1"
OUTDIR="$OUTDIR/staging/$DOMAIN"
echo "[*] Franchise/location subdomains:"
# Extract location-based subdomains
grep -iE 'franchise|location|store|shop|branch|office|city|state' "$OUTDIR/alive_subs.txt" | while read -r line; do
sub=$(echo "$line" | awk '{print $1}')
echo "--- $sub ---"
# Check if it's a WordPress site
wp=$(curl -sk -o /dev/null -w "%{http_code}" --max-time 5 --connect-timeout 5 "$sub/wp-login.php")
[[ "$wp" =~ ^(200|301|302)$ ]] && echo " WordPress detected"
# Check for WPSL (WP Store Locator) data
wpsl=$(curl --max-time 30 --connect-timeout 10 -sk "$sub/wp-json/wpsl/v1/" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d) if isinstance(d,list) else 'no')" 2>/dev/null)
[[ "$wpsl" != "no" && "$wpsl" != "0" ]] && echo " WPSL: $wpsl locations"
# Check for store-specific data
users=$(curl --max-time 30 --connect-timeout 10 -sk "$sub/wp-json/wp/v2/users" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d) if isinstance(d,list) else 0)" 2>/dev/null)
[[ "$users" -gt 0 ]] && echo " Users: $users"
done
注意事项
- crt.sh 速率限制。 达到速率限制时,crt.sh 可能返回空 JSON。查询之间间隔 2-3 秒,或直接在
crt.sh/?d=查询 PostgreSQL 转储。 - 通配符证书会隐藏子域名。 如果只有
*.example.com证书,crt.sh 中不会显示各个子域名。可使用 subfinder DNS 暴力枚举作为备用方法。 - 预发布环境可能需要 VPN。 有些预发布环境限制了 IP 地址。 仅从已获授权的测试源地址进行测试。
- 生产环境可能也能访问 WordPress install.php。 一些维护不善的生产站点也开放了此页面。这不一定是预发布环境独有的问题。检查页面标题文字中是否有 “Welcome to WordPress”,以确认这是全新安装。
- 生产环境和预发布环境的 CORS 配置可能不同。 在声称存在安全控制缺口之前,先在两个环境中测试相同的有界端点矩阵。
验证
- 必须使用 httpx 探测每个预发布子域名,确认其处于在线状态。
- WordPress install.php 必须返回 HTTP 200,且响应正文包含 “WordPress” 和 “installation”(而非重定向或 SPA)。
- 必须将预发布环境的弱点与生产环境进行比较,确认存在安全缺口(例如,生产环境有 WAF,而预发布环境没有)。
- 必须确认通过 crt.sh 发现的内部子域名确实属于目标的基础设施,而非同一证书中无关的域名。
- 必须将发现的凭据或配置值作为敏感证据处理。仅在获得明确授权且使用获批身份的情况下测试凭据;切勿假定可以在生产环境中尝试预发布环境的凭据。
评论
支持 Markdown