dom型XSS

dom型XSS(主要是和js语言进行交互)

  1. 实验环境

源码:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

</body>
<script>
    const data = decodeURIComponent(location.hash.substr(1));
    const root = document.createElement('div');
    root.innerHTML = data;                    
    // 这里模拟了XSS过滤的过程,方法是移除所有属性
    for (let el of root.querySelectorAll('*')) {
        for (let attr of el.attributes) {
            el.removeAttribute(attr.name);
        }
    }

    document.body.appendChild(root);   //将移除属性后的root加入到列表中

</script>

</html>

2.代码审计

2.1 decodeURIComponent(location.hash.substr(1))方法

location.hash的作用

当我们在网页中加入 #字符 时,location.hash提取的就是你输入的内容:#字符

location.hash.substr(1):提取下标在1及以后的location.hash内容

decodeURIComponent:函数对 URI 组件进行解码

decodeURIComponent(location.hash.substr(1)):解码date中的内容

2.2 document.createElement函数

document.createElement:用于创建标签,按钮,实例等。

document.createElement('div'):用于创建一个<div>标签。

2.3 root.innerHTML = data;

将接收到的data的值放到 root.innerHTML中,即放到root创建的<div>标签中

2.4 querySelectorAll函数

querySelectorAll函数:用于获取子元素。

root.querySelectorAll('*'):用于获取root中子元素

2.5attributes函数

attributes函数:用于获取元素的属性

el.attributes:用于获取root中子元素的属性

2.6 语句分析

for (let el of root.querySelectorAll('*')) { //用于获取root中的所有子元素

for (let attr of el.attributes) { //用于获取root中的所有子元素的属性

el.removeAttribute(name); //用于移除root中的所有子元素的属性

3.开始进行xss测试注入

这里我们使用弹窗注入进行测试

3.1使用script方式进行注入

由于root.innerHTML = data; :将接收到的data的值放到 root.innerHTML中,即放到root创建的<div>标签中

所以我们首先直接进行注入,使用注入语句

<script>alert(1)</script>

注入结果:

生成弹窗失败,但是<div>标签中确实存在弹窗语句,为什么会失败呢?

3.1.1失败原因

由于这里的root使用了innerHTML属性,从而引发了innerHTML的安全问题所以html5设置了innerHTML属性通过script方式无法进行注入。

这里是摘自官方文档的解释,虽然无法使用<script>alert(1)</script>直接进行弹窗注入,但依旧可以采用<img src='x' οnerrοr='alert(1)'>的方式进行注入。

3.2使用img方法进行注入

3.2.1直接注入

<img src='x' onerror='alert(1)'>

注入结果:

弹窗失败

3.2.1.1失败原因
   for (let el of root.querySelectorAll('*')) {
        for (let attr of el.attributes) {
            el.removeAttribute(attr.name);

由于上面的代码进行了xss过滤,导致输入的语句中的两个子元素的属性src和onerror被移除。

3.2.2发现问题

我们通过被执行后的代码可知子元素的属性移除的并不彻底,原本的语句中子元素的属性有两个src和onerror可是执行代码后发现原本应该被全部移除的属性却还剩下一个onerror,这可以作为注入的突破口。

产生原因:

解释:如上图由于src和onerror这两个属性被放在同一个数组内,在移除属性时当指针一开始放在第一个属性下(即放在src属性下),src属性被移除,然后onerror前进一位占用了原本为src属性的位置,此时指针依旧指在src被移除的位置上即onerror属性现在的位置上,然后指针后移,发现数组中已经没有其他属性,随后指针弹出,这时onerror属性未被移除,留在了数组中,随后被调用。

3.2.3利用代码问题进行注入

思路:我们现在已经知道了代码的问题,所以可以考虑是否可以通过加入属性的方法致使属性移除不完整,将弹窗语句的两个属性保存下来。

具体操作:

分别给src属性及onerror属性前分别插入两个属性“x”和“xx”,使其在移除时将“x"和 "xx"属性移除将弹窗语句属性保留。

payload:

<img x src='x' xx onerror='alert(1)'>

注意:这里插入的两个属性不能相同,如果相同过滤时二者会按一个属性的形式进行过滤,导致弹窗语句的属性也会被过滤。

执行结果:

弹窗语句存在,弹窗成功

3.2.4分析绕过过滤过程

首先在这个过滤数组中存在四个属性分别是“x”“src”“xx”“onerror”,过滤的方式与之前相同,首先指针先指到下标为0的第一个属性“x”处,随后将属性“x”移除,之后指针不动,src属性前移到下标为0处,之后的属性统统前移一个单位,这时指针后移到下标为1处(既此时的xx属性的位置),随即将“xx”属性删除,此时指针不动“onerror”属性前移一位,移动到下标为1处,随后指针后移弹出,这时的数组中刚好存在“src”和“onerror”这两个弹窗语句属性,调用后成功生成弹窗。

弹窗语句被保留在列表中。

4测试结果

xss测试注入成功。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值