关注这个漏洞的其他相关笔记:XSS 漏洞 - 学习手册-CSDN博客
0x01:DOM 型 XSS —— 理论篇
DOM 全称 Document Object Model,使用 DOM 可以使程序和脚本能够动态访问和更新文档的内容、结构及样式。
DOM 型 XSS 是一种特殊类型的反射型 XSS,它是基于 DOM 文档对象模型的一种漏洞。
0x0101:知识前导 - DOM
HTML 的标签都是节点,而这些节点组成了 DOM 的整体结构 —— 节点树。通过 HTML DOM,树中的所有节点均可通过 JavaScript 进行访问。 所有 HTML 元素(节点)均可被修改,也可以创建或者删除节点。
比如下面这个 HTML 代码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>网页标题</title>
</head>
<body>
<h1>我的标题</h1>
<a href="">我的链接</a>
</body>
</html>
其对应的 HTML DOM 树的结构如下图所示:
在网站页面中有许多元素,当页面到达浏览器,浏览器会为页面创建一个顶级的 Document object 文档对象,接着生成各个子文档对象,每个页面元素对应一个文档对象,每个文档对象包含属性、方法和事件。
可以通过 JS 脚本对文档对象进行编辑,从而修改页面的元素。也就是说,客户端的脚本程序可以通过修改 DOM 动态修改页面内容,从客户端获取 DOM 中的数据并在本地执行。
由于 DOM 是在客户端修改节点的,所以基于 DOM 型的 XSS 漏洞不需要与服务器端交互,它只发生在客户端处理数据的阶段。
0x0102:DOM 型 XSS 攻击流程
常见的攻击方式: 用户请求一个经过专门设计的 URL,它由攻击者发布,而且其中包含 XSS 代码。服务器的响应不会以任何形式包含攻击者的脚本。当用户的浏览器处理这个响应时,DOM 对象就会处理 XSS 代码,导致存在 XSS 漏洞。
其攻击流程如下图所示:
其整体攻击流程与反射型 XSS 很相似,只不过是因为 DOM 型 XSS 形成的原因比较特别,发现它的安全专家专门提出了这种类型的 XSS。出于历史原因,就把它单独作为一个分类了。
0x02:DOM 型 XSS —— 实战篇
实验工具准备
PHP 运行环境:phpstudy_x64_8.1.1.3.zip(Apache2.4.39 + PHP 5.3.29nts)
实验源码包(附配置流程):DOMBasedXSSLab.zip
实验环境的搭建流程我放在了 DOMBasedXSSLab.zip 中了,这里就不多说了,下面直接开始演示。
0x0201:DOM 型 XSS 攻击
在浏览器的导航栏中输入下面的网址,访问实验环境:
http://127.0.0.1/DOMBasedXSSLab/
可以发现页面发生了跳转,跳转到了一个 404 Not Found 的页面。值得注意的是,该页面回显的 404 - Page XXX Not Found
中的 XXX
部分的内容似乎是通过浏览器 URL 中的 error
参数控制的。
我们可以修改一下导航栏中 error
字段的内容,并重新访问:
http://127.0.0.1/DOMBasedXSSLab/404.html?error=Blue17
可以发现,error
字段的内容果然会直接回显回页面中,我们右击页面查看网页源码,看看回显的位置:
发现我们传递的参数直接回显在了 <h1>
标签中,那么攻击者可以尝试构造下面的 Payload,来攻击用户:
http://127.0.0.1/DOMBasedXSSLab/404.html?error=<img src=1 onerror="alert(/You've Been Tricked/)" hidden/>
可以看到,页面成功加载了攻击者传入的 XSS 代码并运行。如果点击了 “确定” 按钮,可以看到,页面基本没有啥影响(弹窗只是为了演示脚本确实被执行了,真攻击没人给你弄这种弹窗):
至此,一个 DOM 型 XSS 攻击的全流程已经演示完毕了。整体流程和反射型其实是差不多的,主要是代码处理方面的问题,反射型 XSS 是后端语言处理用户提交的数据后,将 XSS 攻击代码携带进返回包中传递给客户端;而 DOM 型则是利用用户客户端的 JavaScript 脚本进行触发的,后端返回的数据包中并不包含 XSS 攻击代码,如下图所示:
可以发现,后端返回的数据包中只有页面标签的布局,<h1>
标签中是没有任何内容的,这符合了我们上面讲的 DOM 型 XSS,后端返回的数据包中不包含 XSS 攻击代码的特点。
0x0202:DOM 型 XSS 代码分析
下面是触发 DOM 型 XSS 漏洞的关键代码:
<h1 id="dom_output"></h1>
<script>
const urlParams = new URLSearchParams(window.location.search); // 获取链接中 ? 号后面参数的部分
if (urlParams.has('error')) { // 如果用户传递了 error 参数
const errorInfo = urlParams.get('error'); // 获取 error 参数的内容
document.getElementById("dom_output").innerHTML = "404 - Page " + errorInfo + " Not Found 未找到"; // 输出回标题
} else { // 如果用户没有传递 error 参数
document.getElementById("dom_output").innerHTML = "404 - Page Not Found 未找到"; // 输出回标题
}
</script>
DOM 型 XSS 程序只有 HTML 代码,并不存在服务器端代码,所以此程序并没有与服务器端进行交互,是一个纯粹的运行在客户端的 JavaScript 脚本代码触发的 XSS 漏洞。
用户访问实验环境后,会跳转到 404 页面,并向该页面传递参数 error=xxx
。urlParams
识别用户传递过来的参数并进行保存,如果用户传递了 ?error=xxx
,则获取 xxx
具体的内容,并通过 document.getElementById("dom_output").innerHTML
的方式,找到 id
为 dom_output
的标签(也就是 <h1>
标签),将用户传递过来的参数拼接并回显。如果用户没有传递 error
参数,则回显默认的内容:404 - Page Not Found 未找到
。
程序逻辑很简单,就是获取 error
字段的内容并回显,没有做任何过滤,导致了 XSS 漏洞。
当用户对 error
传入 <img src=1 onerror="alert(/You've Been Tricked/)" hidden/>
时, 输出到 <h1>
标签中的内容实际是:
<h1>404 - Page <img src=1 onerror="alert(/You've Been Tricked/)" hidden/> Not Found 未找到</h1>
此 HTML 代码中包含了一个 <img>
图片标签,链接到地址 1 的图片,这个图片必然不存在,所以导致图片加载错误触发了 onerror
部分的内容,该部分又是一个 JavaScript 代码,此代码会引起浏览器的弹窗,而末尾的 hidden
仅仅是为了隐藏 <img>
标签,让其在页面上不那么显眼而已。
0x03:DOM 型 XSS —— 后记
在构造攻击语句的时候,笔者一开始并没有想用 <img>
标签来触发,笔者一开始构造的链接如下:
http://127.0.0.1/DOMBasedXSSLab/404.html?error=<script>alert(/You've Been Tricked/)</script>
可以发现,JavaScript 脚本被成功内嵌进了页面,但是却并未被执行。笔者的思考是:由于浏览器解析网页的顺序是从上往下的,所以其执行顺序如下图所示(不知道对不对,反正笔者把自己说服了):
下面是文心一言给的参考中,我觉得最符合情况的一条:
浏览器 DOM 解析和渲染机制 当浏览器解析 HTML 文档时,它会识别
<script>
标签并尝试执行其中的脚本。但是,当脚本是通过 JavaScript 动态添加到 DOM 中时,浏览器会采取不同的策略。特别是,当脚本内容通过innerHTML
、document.write()
或其他类似方法动态添加到文档中的,浏览器不会立即执行这些脚本。这是因为浏览器需要区分 “正常的” HTML 解析过程中加载的脚本和后来通过脚本动态添加的脚本。
0x04:参考文献
-
《Web 安全攻防:渗透测试实战指南》 ISBN 978-7-121-34283-7