DOM破坏原理与漏洞复现

目录

Dom 破坏

1.获取标签

2.cookie值的覆盖

3.多层覆盖

4.字符串覆盖写入

漏洞复现

案例一

案例二

Dom 破坏


Dom Clobbering(Dom破坏) 就是⼀种将 HTML 代码注入页面中以操纵 DOM 并最终更改页面上 JavaScript 行为的技术。 在无法直接XSS的情况下,我们就可以往DOM Clobbering 这⽅向考虑了。

其实 Dom Clobbering 比较简单,我们看几个简单的例⼦就能知道它是⼲什么了的。

1.获取标签


通过打印<img>标签中的id或者name属性值,我们获取到了整个<img>标签

从中我们也发现了规律,直接打印x,y不管是id还是name都可以打印出来

而通过document来获取x,y只能打印出name属性的标签

window和直接打印的结果是一样的,都可以打印

打印的结果如图:

2.cookie值的覆盖


这个例子可以看出原本是没有cookie这个属性的,然后我创建了一个div,再创建一个img,里面包含一个name属性,值为cookie。

接着把img放入div,把div放入document.body下,再调用document.cookie发现获取了这个img标签,这就说明document.cookie已经被我们用img标签给覆盖了

3.多层覆盖


document.body.appendChild(div)我们上面也有,是追加div进body里面,我们看一下此时的输出

这个方法不存在了,取而代之的是img标签,这里其实就是实现了覆盖的作用,破坏了原有的方法。

4.字符串覆盖写入


既然我们可以通过这种方式去创建或覆盖document或者widow对象的某些值,但是看起来我们举的例子只是利用标签创建或覆盖最终得到的也是标签,是一个HTMLElment对象。但对于大多数情况来说,我们可能更需要将其转换成一个可控的字符串类型,以便于我们进行操作。

所以我们可以通过以下代码进行筛选得到可以通过toString方法将其转换成字符串类型的标签:
 

Object.getOwnPropertyNames(window)        //通过getOwnPropertyNames函数获取object自身的属性名称
        .filter(p => p.match(/Element$/))  //filter是一个过滤函数,过滤出含Element结尾的函数
        .map(p => window[p])               //使用map将过滤的函数所有的window元素拿出来
        .filter(p => p && p.prototype && p.prototype.toString
        !== Object.prototype.toString)     //通常Object上的tostring方法返回的值是一个对象,
                                             此句表示继续筛选使原型链上tostring方法与Object上的toString方法不相同,
                                             即需要保证筛选出的p.prototype.toString返回的值为非对象


执行结果:

这里执行代码后得到了两个object的值分别为HTMLAreaElement()和HTMLAnchorElement(),这两个标签其实分别为<area>及<a>这两个方法返回的值为非对象(字符串), 我们可以通过href属性利用这两个标签进行字符串的转换。 

漏洞复现


XSS Game - Learning XSS Made Simple! | Created by PwnFunction

案例一

<h2 id="boomer">Ok, Boomer.</h2>
<script>
    boomer.innerHTML = DOMPurify.sanitize(new URL(location).searchParams.get('boomer') || "Ok, Boomer")
    setTimeout(ok, 2000)
</script>
GET参数boomer被设置为boomer.innerHTML,通过get参数将内容写入h2标签内,而且有过滤框架DOMPurify

setTimeout可以接受函数或字符串作为参数并执行它。在这里,ok变量被执行,但它不存在。

这里的JS代码是没有任何关于ok参数的定义的,所以我们可以使用DOM破坏,通过DOM破坏,将HTML元素注入到DOM中。

创建JavaScript变量

构造ok参数,因为setTimeout函数执行字符串,所以需要用到<a>或者<textarea>标签

payload:

<a id=ok href="tel:alert(1)">a


案例二

<script>
    /* Helpers */
    const bootstrapAlert = (msg, type) => {
        return (`<div class="alert alert-${type}" role="alert">${DOMPurify.sanitize(msg)}</div>`)
    }
 
    document.getAlert = () => document.getElementById('alerts');
</script>
 
<script>
    /* Welcome */
    let name = (new URL(location).searchParams.get('name')) || "Pamela Landy";
    document.write(
        bootstrapAlert(`<b>Operation Treadstone</b>: Welcome <u>${name}</u>.`, 'info')
    )
</script>
 
<!-- alerts -->
<div id="alerts"></div>
 
<script>
    /* Handle to `#alert` */
    let alerts = document.getAlert();
 
    /* Treadstone Credentials */
    let identification = Math.random().toString(36).slice(2);
    let code = Math.floor(Math.random() * 89999 + 10000);
 
    /* Default Credentials */
    DEFAULTS = {};
    DEFAULTS[identification] = code;
</script>
 
<script>
    /* Optional Comment */
    if (location.hash) {
        let comment = document.createComment(decodeURI(location.hash).slice(1));
        document.querySelector('#alerts').appendChild(comment);
    }
</script>
 
<script>
    /* Use `DEFAULTS` to init `SECRETS` */
    SECRETS = DEFAULTS
 
    /* Increment the `code` before the check */
    let secretKey = new URL(location).searchParams.get('key') || "TREADSTONE_WEBB";
    SECRETS[secretKey] += 1;
 
    /* Authorization Check */
    if (SECRETS[secretKey] === SECRETS[identification]) {
        confirm(`Jesus Christ, it's Jason Bourne!`)
    } else {
        confirm(`You ain't David Webb!`)
    }
</script>

代码分析
第一个script块

首先定义了一个函数bootstrapAlert(msg,type),返回值是一个div标签里面包含着msg输入的东西,还使用了DOMPurify框架进行过滤

之后又定义了一个函数get.Alert,获取id为alerts的元素

第二个script块

首先通过GET传入name参数,如果没有传,默认Pamela Landy

然后调用bootstrapAlert函数,相当于这样
 

<div class="alert alert-info" role="alert">
     <b>Operation Treadstone</b>
     : Welcome <u>${name}</u>
</div>


然后一个id属性为alerts的div标签

第三个script块

首先将函数getAlert()的返回值赋给alerts

之后生成一个0,1的随机数,再将其转换为36进制的字符串,然后切片,去掉前两个字符

再使用Math.random()*89999+10000生成一个10000到89999的随机数赋给code

然后定义了一个DEFAULTS空对象

最后将code赋值给DEFAULTS对象里的identification属性

第四个script块

如果存在location.hash值

那就去掉前面的#号,将hash值取出来,然后以改内容创建注释赋值给comment

最后找到id属性为alerts的标签,将comment添加为它的子元素

第五个script块

首先把对象DEFAULTS赋给SECRETS

之后找到GET参数key,将它赋给secretKey,如果不存在默认为"TREADSTONE_WEBB"

并将secretKey值加1作为属性赋给SECRETS对象

然后if判断SECRETS[secretKey]与SECRETS[identification]是否相等

payload:

?name=<img name=getAlert><form id=alerts name=DEFAULTS>&key=innerHTML#--><img src onerror=alert(1337)>

  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值