*本文作者:Conan,本文属 CodeSec 原创奖励计划,未经许可禁止转载。
前言本文 是 以B站一个有趣的XSS(已修复)为引子(为什么说有趣后面再解释),作为实例分析其WAF的规则,方便大家加深对XSS WAF探测以及针对性bypass的理解。
进入主题一、一般waf是由多条正则配合使用,因而绕过也必须根据实际情况构造xss探针 '`";><aaa bbb=ccc>ddd<aaa/> 的方式逐步理清waf规则,对于没被wa的再在chrome浏览器的element和source看xss探针的解析,慢慢耐心尝试即可。
二、个人对于waf bypass的理解:本来应当被wa会生效的payload不在规则库里或用本来就不会生效的payload(这时候是大概率能通过waf的)经过服务器处理后payload最后生效了。
三、过程
1.首先是7师傅给了一个链接,让我们绕一绕b站的waf,callback参数存在xss。
2.上xss探针: ';`"><aaa bbb=ccc>ddd<aaa/>

可以看到探针成功被解析为标签和属性。
3. 上经典payload(这里由于可以控制标签,优先使用 <img><script> ):
对于 <img src=x onerror=alert(1)>

对于 <script>alert(1)<script>

可以看到均被waf拦截了。
4. 分析waf规则:基于从局部到整体的思想(这里是可以写成一个自动化的waf规则探测脚本的,xsstrike有简单的waf探测规则)
这里是xsstrike的简单探测截图:

于是可以率先得到一条waf规则: <script\s+[^>]*src=.* (注意实际的正则可能特别复杂,这里是简化版的,没有考虑一些特殊字符,比如字母和数字可能可以互换,还有一些特殊字符,空格等等,这里能大致描述清楚waf的正则表达式的主要结构就可以了,下面出现的正则表达式同理)。

验证一下确实如此:

5.开始我们的手工waf规则分析
(1) 局部探测
<img> 会wa么,没有wa:

alert(1)会wa么,没有wa:

单独的onerror会wa么,没有:

src=x会wa么,没有wa:

因此是某部分组合起来了才导致被wa了,单独的并没有。
(2) 尝试逐步组合
<img src=x> 会wa么,没有wa:

<img src=x onerror=xxxx> 会wa么,没有wa:

<img src=x onerror=alert(> 会wa么,没有wa:

<img src=x onerror=alert(xxxx> 会wa么,终于wa了哈哈:

(3) 回溯waf规则, <img src=x onerror=aaa(bbb> 会wa么,不wa:

这里我们就可以得知aaa处存在黑名单校验,alert在黑名单里,试试prompt/confirm呢:

均被wa了,单独提取出 onerror=alert(xxxx 呢,竟然不会:

再试试 onerror=alert(xxxx) ,终于wa了:

这里就要分析为什么 <img src=x onerror=alert(xxxx> 会wa,但 onerror=alert(xxxx 不会,只有补全了右括号才会的原因,我的猜测是前者触发了另一条waf规则(针对标签开头的waf规则 <[^>]*\s+on\w+=(?:prompt|alert|confirm){1}\(\w+ ,用 <..aaaa onbbbb=alert(ccc 成功触发waf(注意这里用\w的原因是比较懒,经过测试数字型on1111并不会被wa,描述清结构即可=。=)


而后者对应的waf规则直接是 on\w+=(?:prompt|alert|confirm){1}\(\w+\) 。
到这里我们明白我们的payload被wa的原因是触发了下列这两条(同时触发或者触发其中一条都会wa)。
on\w+=(?:prompt|alert|confirm){1}\(\w+\) <[^>]*\s+on\w+=(?:prompt|alert|confirm){1}\(\w+分析第一条规则和第二条规则,最主要的是对弹框函数的过滤,因此使用黑名单之外的函数可能bypass,测试发现console.log可以绕过:

但这里要求是弹框,对于函数的黑名单我们想到了可以用top对象绕过 top['alert'](1) 或者 top['al'+'ert'](1) 由于 [] 的存在不匹配字母数字或者下划线( \w )导致 <img src=x onerror=top['alert'](1)> 不匹配正则表达式也就不会被wa。


(4)对于7师傅的解法的分析:
a. 使用script标签利用响应包会拼接双写payload绕过(基于特殊情景构造不在waf规则里的无效payload,经过组合后又生效了,也就是上文之前对于bypass的第二种理解)
利用 </script><script> + </script></script> 拼接闭合中间的 <script>标签 ,然后浏览器解析的时候为我们补上了最后的 </script>
payload向量结构: aaa</script>bbb<script>ccc

可以看到最后aaa和ccc都是在 <script> 标签里了,并且aaa换成函数名+括号可以绕过了正则 <script>.*\(.*\) ,将a替换为 alert(document.cookie) ,将c替换为任意一个不被wa的变量或内置函数对象名即可 console.log
payload: alert(document.cookie)</script>bbb<script>console.log


b.7师傅的解法巧妙利用了前后双写拼接闭合中间的方式绕过 <script> 后的绝大部分正则检测,但对于正则的描述还不够具体, <script>aaa(bbb) 并不会被wa

因此联想到 <script>alert(1)</script> 被wa还是因为函数的原因,所以正则应该是 <script>.*\s(?:alert|prompt|confirm)\(.*\) ,因此简单的做法还是直接利用top对象绕过即可,payloads: <script>top['alert'](1)</script>

(5) 到这里发现了什么吗?
前面的截图中有的是弹1有的是弹document.cookie,因为7师傅的解法可以弹document.cookie,而其他两种解法的document.cookie被wa了,也就是说我们触发了某些waf规则了。
a.<script>top['alert'](document.cookie)</script>被wa对比 <script>aaa(document.cookie) 和 <bbb>aaa(document.cookie)

可以发现与 <script> 有关,经测试 <script>document.cookie 和 <script>doucment['cookie'] 均wa,再缩小,发现 <script>document[xxx] 和 <script>document.xxx 也wa了,但是 <script>documentxxx 不wa,于是乎可以判断又有两个waf规则很显然,即 <script>.*\s?document\.\w+ 和 <script>.*\s?document\[\w+\] 再对比 <script>aaa[](document.cookie) 与 <script>aaa[bbb](document.cookie)

可以发现后者被wa了,此时想到用反引号代替括号,但反引号内的 document.cookie 并不会被解析为对象,确实绕过了规则但并没有实现弹cookie,

到这里基本可以归纳补充加入反引号的规则的逃逸为 <script>[^`]*document\.\w+ 和 <script>[^`]*document\[\w+\]
缩小的过程的还发现 <script>\w+\.cookie 也会wa,到这里我就不想弹cookie了,打扰了。
尝试加载远程src,发现 <script\s(.*\s)?src(=\w+)?> 也wa了,到这里基本放弃对 <script> 后的规则的bypass了,打扰了。
b. <img src=x onerror=top['alert'](document.cookie)>经过a的分析,对于标签内的document.cookie的规则我觉得也是凶多吉少,这里不再尝试
c. 尝试了下a标签,发现 <a href=javascript:xxx 就会被wa,但是 <a href=ccc> 和 <a yyy=javascript:xxx> 和