本文发表于先知社区:浅谈json参数解析对waf绕过的影响

0x00介绍

        编码问题一直以来是WAF防御体系中的薄弱环节,由于HTTP(S)协议支持多种格式数据传输,当网站存在SQL注入漏洞时,黑客往往会利用编码绕过WAF的防御。本文将简单介绍利用Fuzz技术探测对于json格式编码的数据中一些特殊字符的编码规律,提高WAF对于json格式数据的兼容性。
为了便于理解,我们先来看一个在URL中进行SQL注入的例子。
        假设服务器上的article.php是这样一段代码:

<?php
$id=$_GET['id'];
$sql="select * from article where id=$id";
mysql_query($sql,$conn);
?>


        那么当我们请求 article.php?id=1 and 1=1 时,mysql会执行
 

select * from article where id=1 and 1=1

        这样就可以通过修改and后面的语句进行查询,最终甚至可以拖下整个数据库数据。WAF(Web Application Firewall),中文名称网站应用防火墙,可以对用户提交的请求进行判断,拦截并阻断恶意的HTTP(S)请求,以达到保护网站安全的目的。对于部署了WAF的网站我们可以通过匹配关键字来阻断攻击。例如检测到HTTP(S)请求中含有and 1=1,我们可以将该请求阻断,当然这里仅仅是举个例子,真实情况下需要阻断请求的规则匹配十分复杂。黑客可以通过变形:”and/**/1=1”、”and%0a1=1”、”and+1=1”来绕过防御,这时就要求WAF能够加载这些变形的规则将有可能绕过WAF的请求全部拦截。

        在json格式的数据中,由于服务器会对json数据进行一次json解析,导致有些字符可能会漏防,所以本文主要研究有哪些字符在php+mysql执行环境中的json格式解码后容易出现遗漏。

        这张图描述了json格式传输的数据会被转义的几种情况,例如\”会被转义成”,\会被转义成\,\n会被转义成换行,\u(四位十六进制字符)会被转义成unicode解码后对应的字符。
        测试环境:php5.4.39  mysql 5.1.73。
        测试用例: 这里我们使用开源系统phpaacms中的show.php ,修改php标签内的内容为:

<?php
include_once 'global.php';
$id = json_decode($_GET['id'])->id;
for($i = 0; $i < strlen($id); $i ++)
{
printf("%x",ord($id[$i]));
echo ",";
}
$arc = getArticleInfo($id);
?>


        测试URL:

http://127.0.0.1/phpaacms/show.php?id={"id":"31 and 1=1"}

        测试说明:这里以\n为例,我们跟踪一下从提交请求到mysql后端的执行流程。

http://127.0.0.1/phpaacms/show.php?id={"id":"31 and\n1=1"}

        首先使用php代码跟踪php json_decode函数的执行结果,可以看到\n这里已经变为了0x0a也就是linefeed(换行符)。然后带入mysql执行,我们从mysqllog 跟踪:

        这里的 “and”和“1=1”之间确实多了一个换行符,说明\n被解析成0x0a后带入mysql中,为了验证mysql的执行我们在mysql中进行模拟:

        这里的换行符对于mysql语句来说仅仅是and和1=1之间的分隔符,对于执行没有影响。可见在json环境下传入的\n 字符会带入到mysql执行。

0x01 URL编码探测

        由于-%FF是经过url编码后的字符,对于%09、%0A、%0B、%0C、%0D这些字符都是在普通url中绕过waf常见的单词分隔符,所以我们想看一下这些特殊字符在json格式下是否能够被正常解析。
        利用工具:Burpsuite 中的Intruder模块。该模块可以通过加载字典将http请求包指定位置的内容进行替换,从而实现类似“暴力破解”的效果。

        测试用例:
 

http://127.0.0.1/phpaacms/show.php?id={"id":"31 and%(00-FF)1=1"}

        我们这里将url中 and与1=1之间的空格依次替换为~%FF。
        测试结果:

        测试结果中,我们可以看出不同的payload发送的请求http响应也不同,我们可以依据长度(Length)来进行筛选,按长度排序以后,返回长度2743的为”and 1=1”执行成功的请求,也就是说sql注入可以执行,而返回长度2664或小于2664的payload说明无法sql注入成功,这里我们看到仅有%20(space)和%2B(+) 可以在json格式中存活,在常见注入的url中的%0A %0B……这些字符并不能被php的json_decode所解析。

0x02转义探测

        这一次我们想探测经过’\’转义过的字符是否会被json decode并在mysql中执行。

        测试用例:

http://127.0.0.1/phpaacms/show.php?id={"id":"31 and\%(00-FF)1=1"}


        测试结果:

        %66(f)、%6E(n)、 %72(r)、%74(t)。也就是说 \f \n \r \t 这四个字符经过json_decode后,可以被当作mysql分隔符进行解析。
        测试用例:

http://127.0.0.1/phpaacms/show.php?id={"id":"31 and\u00(00-FF)1=1"}


        这一次主要测试\u0000-\u00ff 经过unicode编码的类型是否能够通过json_decode。
        测试结果:

        可以看出,\u0009、\u000A、\u000B、\u000C、\u000D、\u0020、\u002B均可以被json_decode并带入mysql作为mysql单词分隔符。

0x03单词中的探测

        为了探测\x这类字符是否会被json_decode解析后替换为空,这里我们采用单词间的探测,即在mysql单词中插入该用例进行探测这里采用的是”an\xd”。

        测试用例:

http://127.0.0.1/phpaacms/show.php?id={"id":"31 an\%(00-FF)d 1=1"}


        测试结果:

        无一幸免,可见在单词中插入这些转义字符并不会在json解析的时候丢失,多于的转义字符是破坏mysql语法的。

0x04十六进制探测

        对于常用的十六进制编码,有可能会采用\00-\ff和\x00-\xff。两种形式,这里我们分别fuzz一下。 


 1) \00-\FF

        测试用例:

http://127.0.0.1/phpaacms/show.php?id={"id":"31 and(00-FF) 1=1"}


        测试结果:

        可见\00-\FF 并不能如我们所愿被当作-%FF之类的解析。


2) \x00-\xFF


        测试用例:

http://127.0.0.1/phpaacms/show.php?id={"id":"31 and\x(00-FF)1=1"}


        测试结果:

        可以看出,\x00-FF与 \00-FF一样都不能被json直接解析。

0x05 小结

        经过以上几次fuzz,可以看出在json环境下:
        %20 %2B \f \n \r \t \u0009 \u000A \u000B \u000C \u000D \u0020 \u002B

        这些字符会作为mysql分隔符。下面我们来进行实战。


0x06 实战

        经过对目前国内主流的几大WAF产品测试,我们目前还没有发现能够防御该漏洞的产品。

        以国内某知名云安全厂商的waf产品为例:

        正常情况下:
        我们请求 http://www.xx.com/xx?id=1 会下图:

        而当我们请求http://www.xx.com/xx?id=1%20and%201=1
        被WAF阻断以后,会返回下图:

1)使用url编码用例进行探测


        测试结果:
 

        可以发现对于%20进行了防护,而对于%2b没有防护。

2)使用\f \n \r \t编码用例进行探测

        使用\r \f \n \t 测试结果均没有防护:

3)使用\/**\/代替注释符进行探测

        正常情况下,对于json格式数据的注入我们可以类似这样构造:

http://www.xx.com/xx?id={%22id%22:%221%20and/**/1=1%22}

        对于常用的绕过waf手段:替换空格为/**/,可能大多数网站都会有所防范。

        但是对于 json格式来说\/\/ 同样可以decode成为 //带入mysql成为分隔符。

http://www.xx.com/xx?id={%22id%22:%221%20and\/**\/1=1%22}


0x07总结

        在waf对抗技术中,针对编码(json、base64……)中的特殊字符处理上仍然是受到挑战的风险点,在设计和完善waf产品时应当充分考虑业务系统的数据交互流程,从而进一步将黑客对于漏洞的恶意利用遏制在萌芽中。