目录
DOM破坏的原理
例题
多层标签
HTMLCollection
一些常见的标签的关系
三层标签如何获取
例题
DOMClobber是一种攻击技术,它利用了DOM(文档对象模型)的特性来破坏或修改网页的结构和功能。
DOMClobber攻击通常发生在Web应用程序中,其中攻击者能够注入恶意代码或修改网页的DOM结构。DOMClobber攻击的目标是修改网页中的关键元素,例如修改表单的提交目标、修改链接的目标URL等。
攻击者可以通过修改网页的DOM结构来欺骗用户或实施其他恶意行为。例如,攻击者可以修改网页中的表单目标,使用户的输入数据被发送到攻击者控制的服务器,从而窃取用户的敏感信息。攻击者还可以修改网页中的链接,使用户被重定向到恶意网站。
为了防止DOMClobber攻击,开发人员应该对用户输入进行充分的过滤和验证,并使用安全的编码方式来处理和显示用户输入。此外,开发人员还应该使用安全的DOM操作方法,避免使用直接的innerHTML或eval等危险的操作。
我们来举一个例子
打印出来如下
通过打印标签中的id或者name属性值,我们获取到了整个标签,从中我们也发现了规律,直接打印x,y不管是id还是name都可以打印出来,而通过document来获取x,y只能打印出name属性的标签,window和直接打印的结果是一样的,都可以打印。
再看下面的例子
下面这个例子可以看到cookie开始是空值,然后创建了一个div元素,在div里面添加了标签,然后添加到body里面去,这时候再打印cookie,发现变成了,这个例子成功地让本来为空值的cookie有了值,而且是我们可以控制的。
然而得到一个标签对象并不是我们想要的,有些函数的参数并不是一个对象,而是字符串这就需要函数在调用自己时,自己本身有一个ToString函数能够转换为字符串,然后让函数执行,所以我们需要一个自身拥有ToString函数的标签,而不是继承父类Object的ToString函数
可以看到一个对象调用父类的toString函数就会返回[object object],所以我们需要一个本身有toString函数的标签,通过下面的脚本过滤出了自身拥有toString函数的标签,HTMLAreaElement()和HTMLAnchorElement(),也就是
所以我们可以通过以下代码来进⾏fuzz 得到可以通过toString ⽅法将其转换成字符串类型的标签
Object.getOwnPropertyNames(window)
.filter(p => p.match(/Element$/))
.map(p => window[p])
.filter(p => p && p.prototype && p.prototype.toString
!== Object.prototype.toString)
这道题通过get参数将内容写入h2标签内,而且有过滤框架DOMPurify,这个过滤框架由安全团队cure53开发,以我们的技术很难绕过,但是注意setTimeout函数内的ok参数,这里的JS代码是没有任何关于ok参数的定义的,所以我们可以使用DOM破坏。XSS Game - Ok, Boomer | PwnFunction
构造ok参数,因为setTimeout函数执行字符串,所以需要用到或者
因为DOMPurify框架过滤了javascript,所以我们用tel,tel也可以执行script脚本
如果我们需要获取一个标签下的子标签的内容,怎么获取呢?可以直接x.y吗?
很可惜,无法获取。
id = "x" >
form->button form->fieldset form->image form->img form->input form->object form->output
如果有三层标签,就需要要⽤到以上两种技巧来构建了,先分析x.y,x是一个集合,然后获取y,利用了第一种方法--集合方式,获取了第一个form标签然后x.y.z,因为form和output标签存在关系,可以直接调用y.z,利用了第二种方法--标签关系最后x.y.z.value就成功拿到output标签内的内容。
这个先⽤⼀个HTMLCollection 获取第⼆级,再在第⼀个表单中⽤output 标签。
以下是他的js代码。
const data = decodeURIComponent(location.hash.substr(1));
const root = document.createElement('div');
//data为URL后的hash值,然后创建了一个div标签,把我们输入的hash值放进了div这个标签里面.
root.innerHTML = data;
// 这里模拟了XSS过滤的过程,方法是移除所有属性
for (let el of root.querySelectorAll('*')) {
let attrs = [];//第一个for循环拿出了div元素的所有后代元素,用el表示,并且定义了一个空数组attrs.
for (let attr of el.attributes) {
attrs.push(attr.name);//第二个for循环拿出了后代元素的所有属性,用attr表示,然后将属性添加到attrs数组里.
}
for (let name of attrs) {
el.removeAttribute(name);
}//第三个for循环拿出了attrs数组里面的属性,用name表示,然后移除掉这个元素的该属性.
}
document.body.appendChild(root); //最后将div添加到body里面去.
这段代码的主要作用是创建一个带有两个输入字段的表单,并在表单元素的动画开始时触发一个警告框,但实际上由于CSS动画为空,所以没有实际的动画效果,即表单元素的动画开始时,将触发alert(1)。
这是chatgpt对于这段代码的解读,我们这样写
就是用来破坏el.attributes属性的,而上文我们的标签关系中有form和input这个关系属性的,可以直接调用x.y,所以代码中的el.attributes正好是我们的input标签,那为什么不用一个input标签,而是两个呢?
因为在for循环中el.attributes需要是可迭代的,而一个input标签只是一个对象,所以是不可迭代的
报错如下
所以我们需要两个input标签来组成一个集合,这时,集合就是可迭代的了根据代码,首先到style标签,没有属性可删,然后到form标签因为迭代对象变成了input标签集合,此时attr就变成了undefined,所以attr.name也就不存在,此时attrs数组获取不到form标签里的任何属性,自然下面的for循环也不会删除form表单的任何属性。
最后到我们的两个input标签,因为此时目的已经达到,删除了属性也无妨,最后成功绕过。