prompt(1) to win 通关笔记
网址:http://prompt.ml
基于alert(1)做的xss闯关游戏
level 0
| 1 | function escape(input) { | 
代码是input标签的xss,没有过滤,直接闭合双引号插入常规paylaod即可
| 1 | a"><img src=x onerror=prompt(1)>// | 

level 1
| 1 | function escape(input) { | 
查看代码,发现是被正则过滤,只要有尖括号,且尖括号里有内容或有</> 均替换为空
以下均会过滤:
| 1 | <h1> | 
经过测试,发现只要有>就会被过滤,且此处input内容被包裹在<article>标签中,如果要解析,必须要插入标签才能执行。
那么这里<script>肯定用不了了,只能插入单标签
考虑到了js的自动补全特性(但实际上是开发者做的防止页面混乱的补全措施)只输入一半,用单行注释注释掉后面的双引号,即可成功
(经过测试,多行注释也行,只要注释掉就行)
| 1 | #onload事件会在页面或图像加载完成后立即发生 | 

level 2
| 1 | function escape(input) { | 
此题涉及到了浏览器渲染顺序
https://segmentfault.com/q/1010000002393260
先写一下我的理解
1.浏览器只负责url编码,url解码永远在服务器端。因为浏览器对于url永远是发起者,服务器是接受者,只需要服务器理解即可,所以url解码永远发生在服务器端。
PS:仅有在javascript:alert(1)这种情况下,点击后浏览器作为本地的服务器进行url解码,用于理解url内容。
2.浏览器先进行HTML解码,再进行js解码。
再来说这道题,因为是dom动态正则的input内容,所以使用html编码可以绕过正则检查,而在浏览器渲染dom树时,先解析了html的<svg> 和(,而后才解析了js,所以可以执行成功。
跟踪结果:
设置断点
运行,发现到escape函数时,html编码的(没有解析:

将代码写入sandbox(prompt(1)游戏是在sandbox里展示效果的,所以会有一次写入sandbox paylaod的操作):
将input的内容写入dom执行真正的解析操作(可参考浏览器解析流程):
然后浏览器解析svg标签为xml
代码中过滤了=和( 破坏了常规的paylaod,所以payload如下:
| 1 | html编码绕过正则匹配 | 
level 3
| 1 | function escape(input) { | 
使用–!>闭合注释即可
| 1 | --!><script>prompt(1)</script> | 

level 4
| 1 | function escape(input) { | 
根据过滤代码,判断是绕过正则,引入外部js
使用@符号,让浏览器取后面的域名引入js(chrome实现失败,firefox可以成功,chrome引入js提示blocked:origin)
本地启动web服务,并在根目录创建xss.js写入如下代码:
| 1 | prompt(1) | 
| 1 | http://prompt.ml%2f@prompt.ml/js/test.js | 

level 5
| 1 | function escape(input) { | 
分析代码,过滤了> on事件 focus方法 'onclick='这一类
但是是input标签,所以可以改变type属性,变成图片,然后利用换行绕过=号匹配,即可成功插入
| 1 | " type="image" src=x onerror | 

level 6
| 1 | function escape(input) { | 
根据代码,发现是url#post的格式提交表单。并且action过滤了script、data等伪协议。
所以尝试使用 javascript:alert(1)
但是被过滤了,之后了解到了。form表单后面的action会覆盖前面的,所以此处可以构造如下payload:
| 1 | javascript:alert(1)#{"action":"123"} | 

level 7
| 1 | function escape(input) { | 
代码限制了paylaod长度为12,所以需要注释绕过,经过测试需要使用/**/
js的注释有2中
| 1 | // 单行注释 | 
payload:
| 1 | "><script>/*#*/prompt(/*#*/1)/*#*/</script> | 

level 8 这题我也没搞明白
| 1 | function escape(input) { | 
思路是,根据编码绕过正则,续利用浏览器特性:
使用unicode绕过,chrome有效,火狐无效
| 1 | 浏览器console输入以下字符可获得paylaod | 
chrome下:
| 1 | alert(1) --> | 

IE下

level 9
| 1 | function escape(input) { | 
又是一个神奇的题目,编码绕过。这次是拉丁字母绕过正则匹配:
https://unicode-table.com/cn/017F/ſ是拉丁字母的s,可以绕过正则被解析。
paylaod:
| 1 | <ſcript ſrc=http://localhost/xss.js></ſcript> | 
level 10
| 1 | function escape(input) { | 
过滤单引号为空,prompt替换为alert
直接payload:prom'pt(1)
level 11
| 1 | function escape(input) { | 
js特性,后面的变量会覆盖前面的
发现如果名字相同,一定会输出后面,这也就是需要构造"message":prompt(1)
可是原先有引号,想闭合,发现正则表达式几乎全部过滤,所以只能另想办法
这里的技巧是利用字母操作符来绕过限制,例如in instanceof等等,在这里这两个可以执行成功,payload为"(prompt(1))instanceof"或者"(prompt(1))in"
level 12
| 1 | function escape(input) { | 
和第十题很像,但是顺序变了一下,可以用eval函数来执行js,eval((630038579).toString(30))(1)
因为单引号被过滤,尝试atob解码base64没成功
还有很多方法,参考链接
https://blog.csdn.net/Ni9htMar3/article/details/77938899

level 13
| 1 | function escape(input) { | 
大致看了一遍代码,逻辑应该是如下(偷看答案):
输入json,进入JSON.parse解析,不是json抛出异常、是json取其中的source的值,然后进入extend()把默认的属性替换为输入的,然后是正则判断source对应的值中是否有不属于url的符号,有则删去这个值,将source属性删除。
每个对象都会在其内部初始化一个属性,就是proto,当我们访问对象的属性时,如果对象内部不存在这个属性,那么就会去proto里面找这个属性。

那么基本上就是构造{"source":"'","__proto__":{"source":"onerror=prompt(1)"}},由于前面有非法字符’,则会删除,但是在替换的时候由于过滤了”,无法闭合,那么正好有一种特殊的替换方式
| Pattern | Inserts | 
|---|---|
| $$ | 匹配处删除并插入带$的字符串 | 
| $’ | 在匹配到字符后删除,并在字符结束后面增加内融容 | 
| $` | 匹配到之后将整串插入,删除匹配到的内容 | 
| $& | 匹配到之后插入内容 | 
| $n | Where n is a positive integer less than 100, inserts the nth parenthesized submatch string, provided the first argument was a RegExp object. Note that this is 1-indexed. | 
具体如下图:

所以payload如下:
| 1 | {"source":"'","__proto__":{"source":"$`onerror=prompt(1)>"}} | 

level 14(答案不对,思考中)
| 1 | function escape(input) { | 
level 15
| 1 | function escape(input) { | 
和level 7 一样,用html注释即可,payload如下:
| 1 | "><svg><!--#--><script><!--#-->prompt(1<!--#-->)</script> | 
