多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

DOM型XSS实战:pikachu靶场三关详解与绕过技巧

DOM型XSS实战:pikachu靶场三关详解与绕过技巧 做Web安全测试的人基本都绕不过XSS这一关。XSS全家桶里最容易让人犯迷糊的就是DOM型XSS——它不像存储型和反射型那样直观payload不经过服务端攻击逻辑全在浏览器端的DOM操作里完成。很多新手照着反射型XSS的思路去打发现payload发出去一丁点反应都没有问题就出在没搞懂DOM型XSS的数据流向。这几天我在Kali虚拟机上重新搭了pikachu靶场把Cross-Site Scripting模块里的DOM型XSS三个关卡完整过了一遍从源码分析到payload构造再到过滤器绕过每一步都记录下来了。这篇就围绕pikachu靶场的DOM型XSS关卡把原理、环境搭建、三关实战和踩坑记录全部整理出来适合正在刷pikachu靶场、准备面试或者刚学XSS想彻底弄懂DOM型到底是什么的读者。1. 先把DOM型XSS和另外两类XSS彻底分清1.1 三种XSS的触发链路差异很多人对DOM型XSS一脸懵本质原因是没把三类XSS的数据流向分清楚。按触发位置来分XSS通常有三类存储型、反射型、DOM型。存储型是把payload写到服务端数据库之后任何用户访问相关页面都会被动触发危害最持久反射型是服务端把请求参数原样拼接进响应HTML用户点一个精心构造的链接才会触发。这两类的共同点是恶意代码都经过了服务端服务端参与了“拼接”动作。DOM型则完全不同。服务端返回的HTML基本是固定不变的服务器根本没有把你的输入拼到响应里恶意代码的执行完全由浏览器端JavaScript完成——JS读取URL参数、document.referrer、window.name等数据再通过innerHTML、document.write这类接口把内容写进页面DOM。服务端在这个过程中“毫不知情”。这里我习惯用一个快递单的类比。存储型和反射型像是快递公司打单时就把收件人名字印在了面单上你拿到单子就能看到DOM型则是一张空白面单你把地址栏信息交给前台小姑娘相当于JavaScript她直接照着誊写上去问题就出在这位小姑娘从不检查内容笑话和命令她都照抄不误。1.2 触发链路上的两个关键角色source与sinkDOM型XSS的触发链路可以抽象成两个词source和sink。source是数据来源也叫输入源。常见的有这些window.locationdocument.URLdocument.referrerwindow.namelocation.searchlocation.hashsink是危险出口也就是把数据真正写入DOM或执行代码的位置。常见危险sink包括innerHTML/outerHTMLdocument.write()eval()setTimeout()/setInterval()当第一个参数是字符串时insertAdjacentHTML()window.location.assign()/location.href赋值DOM型XSS的本质就是攻击者能够控制source中的一部分内容而这一步数据又没有被安全处理最终流向了某个危险sink。举个最简单的例子script var name new URLSearchParams(window.location.search).get(name); document.getElementById(msg).innerHTML 欢迎, name; /script当URL是http://victim.com/index.html?nameimg srcx onerroralert(1)时浏览器解析URL后拿到nameinnerHTML直接拼入HTML图片加载失败触发onerror脚本执行。1.3 为什么DOM型XSS更隐蔽反射型和存储型XSS的payload都会出现在服务端的HTTP响应里因此WAF、入侵检测系统相对容易拦截。DOM型XSS有个独特的优势对攻击者来说或者说难点对防御者来说payload如果放在URL的#号后面也就是fragment部分那么整个payload根本不会发送到服务器。#之后的内容是纯粹给浏览器自己看的HTTP请求里完全没有。所以服务端日志里干净得很WAF也什么都拦不到。这个特性在实战中影响很大也是安全测试时容易漏报的一类漏洞。pikachu靶场把这个单独设关卡就是想让学习者体会到这条链路和传统XSS的区别。2. Kali系统下搭建pikachu靶场一次到位2.1 环境准备Kali自带的Web服务怎么启在Kali里跑pikachu是比较顺的因为Kali默认集成了Apache2、MariaDB或MySQL和PHP环境省掉了大半环境配置工作。我第一次搭的时候卡在“服务没启动”这种低级问题上后来养成习惯先检查再操作。打开终端按顺序执行sudo systemctl start apache2 sudo systemctl start mariadb确认两个服务已经监听端口sudo ss -tlnp | grep -E 80|3306如果看到类似0.0.0.0:80和127.0.0.1:3306的监听说明服务正常。顺便看一眼PHP版本php -vKali自带的Apache支持PHP模块一般是装好的如果遇到页面直接输出源代码而不是解析结果大概率是缺了libapache2-mod-php补一下再重启Apache即可。2.2 pikachu下载、安装与数据库初始化把pikachu放到Apache的网站根目录下通常是/var/www/html/。我习惯用git克隆cd /var/www/html/ sudo git clone https://github.com/zhuifengshaonianhanlu/pikachu.git如果网络拉取慢也可以直接在GitHub页面下载zip压缩包解压到同样位置重命名为pikachu。这一步完成后调整一下目录权限避免后面写入文件时遇到权限报错sudo chown -R www-data:www-data /var/www/html/pikachu然后修改数据库配置。pikachu的数据库连接信息在/var/www/html/pikachu/inc/config.inc.php用编辑器打开核心配置项长这样$dbhost 127.0.0.1; $dbuser root; $dbpass root; $dbname pikachu;Kali默认MariaDB的root密码通常是空或者root需要改成你机器上实际能用的账号密码。改完保存浏览器访问http://127.0.0.1/pikachu/首次访问会进入安装页面点击“初始化安装”按钮它会自动创建数据库、导入表结构并写入初始数据。初始化完成后重新访问看到左侧导航栏搭好了。2.3 宿主机访问不了虚拟机靶场按这个顺序排查在VMware里跑Kali宿主机浏览器访问http://虚拟机IP/pikachu打不开是pikachu靶场训练里被问得最多的问题。我自己的排查顺序如下照着走基本能解决。先看虚拟机的IP地址。Kali新版默认不带ifconfig用ip addr查看。拿到IP后在宿主机上ping这个IP能通的话说明网络层没问题。再检查Apache监听的是不是所有接口。执行sudo ss -tlnp | grep 80如果显示127.0.0.1:80说明Apache只监听本机回环地址外部访问自然被拒。需要修改Apache配置让它监听0.0.0.0:80然后重启服务。网络模式也要看。VMware默认NAT模式时宿主机访问虚拟机IP通常没问题如果改成了桥接模式要确保虚拟机和宿主机IP在同一网段。还有一个小概率但经常坑人的问题宿主机浏览器挂了代理插件访问虚拟机IP时请求被代理转发出去结果当然超时。临时关掉代理或者确认代理设置有no_proxy排除项。3. 实操DOM型XSS三关找入口、构造payload、绕过过滤3.1 第一关先闭合HTML再触发事件pikachu的DOM型XSS关卡入口在左侧栏“Cross-Site Scripting (XSS)”下点开“DOM型XSS”。页面提示输一个名字提交后会跳转并回显内容。我先在输入框填了个123提交观察地址栏内容出现在URL的查询参数里。按F12看DOM结构页面回显的链接被写进了innerHTML逻辑大概是下面这样var str document.getElementById(text).value; document.getElementById(dom).innerHTML a hrefstr点了试试/a;这里就是典型的sinkinnerHTML拼接了用户可控内容而且我们把控了整个字符串的后半段。构造payload的思路很直接先闭合掉前端拼好的单引号和a标签再补一个能触发事件的标签。我构造的第一个有效payload是这样的img srcx onerroralert(document.cookie)放在URL里提交之后相当于最终写入DOM的内容变成了a hrefimg srcx onerroralert(document.cookie)点了试试/aimg标签被解析出来src指定的x不存在加载失败onerror事件被触发cookie弹出来了。这里有个实操细节直接把这个payload粘贴到地址栏回车浏览器会自动把、编码成%3C、%3E服务端和JS拿到的是解码后的结果写入DOM时还原成HTML所以能正常触发。自己构造的时候不用刻意手动编码直接用原文提交即可。3.2 第二关过滤器拦了关键词怎么绕第二关页面上多了一层过滤逻辑。我第一次直接提交上一关的payload发现页面没有任何反应DOM里也没多出img标签。打开源码看关卡里对输入做了过滤把script这类明显特征给替换或删除了。这是XSS训练里最常见也最有价值的场景有过滤但不彻底。绕过的核心思路是换标签、换事件、改大小写。我在这一步把常见绕过payload逐个试了一遍svg/onloadalert(1) img srcx onerroralert(1) body onloadalert(1) a href# onclickalert(1)click/a其中svg/onloadalert(1)在这个关卡里成功率很高因为filter没有把svg和onload同时列进黑名单。如果你的靶场版本过滤规则略有差异还可以尝试大小写混写ImG sRcx oNeRrOralert(1)或者用标签名加空格、换行等畸形写法绕简单正则。重点不是背payload而是理解过滤规则通常是黑名单有限集合总有没覆盖到的标签和事件组合。3.3 第三关更严格过滤下的尝试顺序到了第三关过滤明显更严格。我测试了几个常见payload都被吞掉甚至、在某些位置被过滤。这一关需要冷静下来先认真读源码。关键的一点是过滤到底是做在服务端还是做在前端JS里如果过滤逻辑在服务端而页面JS读取的是URL的#号后面部分那么服务端压根看不到你的输入过滤就失去了防御意义。pikachu这一类关卡的设计意图正是在这里——让你看清DOM型XSS的特殊隐蔽性。我在这关的第一个有效尝试是把payload挪到#后面提交。由于#后面的内容不会被发送给服务器服务端过滤器形同虚设而JS通过location.hash读取数据并写入DOM攻击代码照样执行。这也解释了为什么我之前一直把payload放在查询参数里怎么改都被拦换到#后面一测就通。不同版本的pikachu在这个关卡的过滤方式会有差异有的限制了标签有的做了转义。实操中建议用浏览器开发者工具给JS代码打断点单步跟踪用户可控数据的走向确认它经过哪些处理函数、最终落到哪个sink。找到sink之后再回头构造payload比盲目尝试高效得多。3.4 三关跑完自己做个对比三关打完之后我建议做一个简单对比帮助把知识固化。第一关是完全没有防护考察最基本的闭合和事件注入第二关是黑名单不完整考察绕过思路第三关则是把DOM型XSS的隐蔽特性用起来利用fragment部分绕过服务端过滤。三个等级层层递进本质上是在同一个漏洞点上不断加防御视角。我在跑完三关后自己列了个表写清楚了每一关输入点、过滤方式、最终payload和触发路径。这种做法对面试准备特别有效因为面试官问DOM型XSS时往往不是要你背概念而是想听这类实操后的归纳。4. 踩坑记录、防御思路与延伸练习4.1 靶场实操典型问题速查表下面这个表格是我实操中遇到比较高频的问题以及对应解决思路现象可能原因处理方式payload提交后页面无任何反应输入被服务端或前端过滤打开开发者工具查看源码确认过滤范围和sink位置浏览器地址栏的变成%3C浏览器自动URL编码不用管浏览器解析后会还原直接观察触发结果弹窗没有出现控制台报错事件名称写错或者被CSP策略拦截先换一个常见事件如onload、onclick测试确认不是CSP拦截只有点击链接才触发代码是写入a标签的href属性而非直接拼接HTML尝试闭合引号后再构造完整标签初始化页面提示数据库连不上config.inc.php里的数据库账号密码不对修改配置文件中的$dbpass为实际密码后再刷新宿主机访问虚拟机IP超时Apache监听127.0.0.1、代理、网络模式问题按2.3节的顺序逐项排查4.2 从攻击视角切回防御视角练完攻击之后防御视角是一定要补上的不然这个实验只有一半价值。DOM型XSS的难防御之处在于服务端输出编码往往起不到作用因为恶意内容可能根本不出现在服务端响应里。正确姿势得从前端入手。第一条对可控input源的读取要谨慎读取之后做白名单校验而不是黑名单过滤。例如从URL参数里取值之前先判断它是否符合预期格式不符合就丢弃。第二条限制危险sink的使用。innerHTML、document.write这类接口能不用就不用改用textContent或者innerText。它们会按纯文本处理内容标签不会被解析。第三条如果业务上确实需要动态拼接HTML务必对拼接的变量做HTML实体编码和JavaScript转义再进行拼接。第四条配置CSP。一个简单的Content-Script-Src: self就能拦截大量内联脚本执行多数DOM型XSS的payload是内联的吧直接拦在门外。当然CSP不是万能但作为纵深防御的一层成本很低收益不小。4.3 加练一步自己改造靶场源码如果只按关卡做一遍就觉得掌握了其实还差一点点。我个人的建议是把pikachu靶场源码复制一份自己动手改造一下把原来用innerHTML拼接的地方改成textContent或者加一个基于白名单的过滤函数然后重新用之前的payload去试。这个过程的收获比单纯通关要大得多——你会亲眼看到同一个输入在不同的sink下产生完全不同的结果这种直观感受能让“为什么innerHTML危险”这个知识点真正刻进脑子。我在实际练习中还有一个习惯遇到不明白的过滤规则会直接在浏览器console里手动调用页面的处理函数输入各种测试payload观察返回值。这种交互式调试比改源码重新加载快得多也能更快定位是哪个环节吞掉了你的输入。DOM型XSS的排查和利用很大程度上比拼的就是对前端执行流程的熟悉程度多花点时间在中断、观察、构造这几步上性价比很高。另外多说一句靶场里的XSS关卡虽然是以“攻击”为导向的但所有操作都在本地授权的测试环境里完成目的始终是理解漏洞成因、提升防御能力。练完之后把防御文章读一读再把CSP配置尝试出来才算闭环。
返回列表