目录
一、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值的覆盖 ![](https://i-blog.csdnimg.cn/direct/fcbc22d8ccbc4c4a95ccebe8799213dd.png)
这个例子可以看出原本是没有cookie这个属性的,然后我创建了一个div,再创建一个img,里面包含一个name属性,值为cookie。
接着把img放入div,把div放入document.body下,再调用document.cookie发现获取了这个img标签,这就说明document.cookie已经被我们用img标签给覆盖了
3.多层覆盖![](https://i-blog.csdnimg.cn/direct/fa1b799574614f1b802dce595fca1841.png)
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
1.案例一
第一关
源码:
<!-- Challenge -->
<h2 id="spaghet"></h2>
<script>
spaghet.innerHTML = (new URL(location).searchParams.get('somebody') || "Somebody") + " Toucha Ma Spaghet!"
</script>
分析源码:
在script标签里边,使用get传参的方式给somebody传一个值,如果没有传值,默认传Somebody+Toucha Ma Spaghet!,然后赋值给spaghet,放在h2标签中
这里的spaghet后边加了一个innerHTMl的属性,官方解释 element.innerHTML - Web API 接口 |MDN的 (mozilla.org)https://developer.mozilla.org/zh-CN/docs/Web/API/Element/innerHTML
明显插入到h2标签中了,但他并不会执行
那我们就改其他标签试试
?somebody=<img%20src=1%20onerror="alert(1337)">
防御:可以使用innerText进行防御,这样插入的话只插入文本,不会插入标签
2.案例二
第八关
<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
3.案例三![](https://i-blog.csdnimg.cn/direct/f76f111fc2eb421785a7a91ec6c97ef1.png)
<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)>