这是 酒仙桥六号部队 的第 30 篇文章。
全文共计2229个字,预计阅读时长8分钟。
大家在做代码审计或者学习代码审计的过程中,会有大量时间是对着代码的。有时候会觉得代码枯燥无聊,看代码看到怀疑自我。
这时候不妨通过其他思路,换个思维,看点有趣的相关事务。回过头来再看代码,也许会有意想不到的惊喜!
我在 GITHUB上找到了一个合适的开源 CMS项目。在其官网上可以看到该CMS的更新的日志。
查看更新日志(CHANGELOG):
我们可以看到 1.2.3 版本修复了一个 SQL 盲注 (Issue#19) 和 1.2.4版本修复了一个文件管理器上传漏洞 (Issue#20)。
查看该 CMS 的代码结构,发现是基于 CodeIgniter 框架(后文简称 CI )进行的二次开发。对 CI 的相关类进行了继承,并自己封装了函数,进行一些特殊处理。
我们先来看下这个 SQL 盲注,首先访问下 GITHUB 上 Issue#19 的页面看下详情。
这个是一个 1.2.2版本上报的 ISSUE 信息,从中我们可以看出这位漏洞上报者来自 ABT 实验室。漏洞信息非常全面,格式优美,图文并茂。
在 Issue 中不但给出了问题文件的路径是/core/MY_Security.php ,还给出了 Payload 细节:在前端登录时,User-Agent: '-( if( condition, sleep(5), 1))-'', '192.168.1.11','time')# 。并且将漏洞产生原因进行了详细阐述:由于参数缺失,该 CMS 会记录下该登录包的信息,以"无效的 CSRF 防护"为原因写入数据库。但是信息中的 HTTP_USER_AGENT 这个参数未作任何检测,就直接拼接到 SQ L语句中,故造成了漏洞可以被利用。
在最后漏洞上报者还给出了修复建议。
我们根据给出了文件名找到了 1.2.2 版本的 MY_Securtiy.php 文件。
MY_Securtiy 是继承 CI 框架的 CI_Security,并对其一些常用函数进行了封装扩展。
我们的目标函数 csrf_show_error就是其中之一,我们先来看下该函数的调用过程:
调用在CI框架中的 Security.php 文件:在 csrf 验证时,如果 csrf_token 不合法,则会调用csrf_show_error 函数。所以我们在构建请求报文时,需要将 csrf_token 参数不设置或者改动一下。
CI_Security中的csrf_show_error:只有错误显示。
My_Security中的csrf_show_error:增加了对错误登录的记录入库并细分了错误种类。
第45行,即漏洞上报者提到的condition,如果语句不会被执行,则count也不会增加,所以条件一直为true。
第46行,可以看出直接将 $_SERVER['HTTP_USER_AGENT'] 拼接到 SQL 的 INSERT 语句中,并没有任何过滤,所以产生了SQL 注入。
看起来简单明了,这个漏洞应该会被完美的修复掉。但事实上这个漏洞修复的过程并没有这么简单。在这个Issue#19中后续有一段很有趣的CMS开发者和漏洞上报者之间的对话记录,引起了我的兴趣:
漏洞上报的版本为 1.2.2,而这时开发者已经开发出了 1.2.3-rev1版本,并且尝试修复这个 SQL 注入的问题,并希望漏洞上报者使用 1.2.3-rev1 来看看是否修复了此漏洞。
这个漏洞上报者非常有耐心,他在实际尝试后发现问题并没有被修复的同时,还查看1.2.3-rev1 的 MY_Security.php 的代码。他告诉CMS开发者使用 xss_clean 函数并不能解决 SQL注入的问题,并且告诉他应该使用正确的函数为:escape-string 或者real_escape_string 这两个函数来防止 SQL 注入,并给出了这两个函数在PHP.NET 的官方链接和以及修复之后的代码以及测试结果。
再来看之后的对话:
这个CMS开发者很勤奋,第二天就放出了 1.2.3-rev2 版本尝试修复了这个问题,并且再次邀请漏洞上报者测试是否修复此问题。
漏洞上报者于当日确认已经修复,Payload 不再起作用,然后开发者关闭了此问题。至此一个开源 CMS 的安全问题被修复了。
看起来一切正常,但是我们能从这段对话中得到什么呢?
我们这个开发者是有基本安全意识的,该 CMS 包含 xxs_clean 函数,证明开发者在已经意识到 XSS 问题并且封装了相应的函数来做过滤处理。
但是该开发者对于安全问题的细节认识并不太清晰。在碰到 SQL 注入问题时企图用 xss_clean 来解决这个问题,这点说明他对于 SQL 注入产生的原因和修复方法并不太明确。
我们来看下GITHUB上 1.2.3-rev2 版本中开发者对于这个问题的修复方案:
开发者很听话,使用了 escape_string 来修复 SQL 注入的问题,做的很好。但是他并没有去掉 xss_clean 方法,而是在escape_string 调用之后仍然还继续调用xss_clean 方法。
这是为什么呢?我们来尝试猜测开发者的想法:在1.2.3-rev2版本中使用xss_clean后未修复,于是加上了escape_string来修复该漏洞。xss_clean 是处理危险字符的方法,escape_string也是处理危险字符的方法,两个过滤危险字符的方法叠加起来,理应是更安全的。就像两个 WAF 串联叠加,不应该是难上加难吗?
但是事实可能并非如此,也许正是这种情况给了我们绕过机会!
首先看一下 escape_string 到底转义了哪些字符:
下列字符受影响:
x00
n
r
'
"
x1a
由于这段 SQL 注入是字符型,我们需要 ' 来闭合语句,但是 escape_string 会将 ' 变为 ' 从而阻止 SQL 注入。
我们来看下 xss_clean 中的代码:
第 354-365 行是一个递归调用。
第 368行是一个移除不可见字符的方法,如:x00、x01 等等。
第 379-389 行是我们的关键代码,这段代码是判断 $str 参数中是否包含 % 字符,如果有的话就判定为需要 URL 解码并且调用 rawurldecode 来进行 URL 解码。
这个不就是我们要找的代码吗?!
escape_string 方法并不会转义 % ,所以我们将 Payload 进行 URL 编码后并不会被 escape_string 方法改变任何内容。而当到达 xss_clean 时 Payload 将被 URL 解码,从而绕过了对 SQL 注入的过滤。
我们先来测试下原来的 Payload:
**'-( if((1=1), sleep(5), 1) )-'', '192.168.1.11','time') #**
没有造成延时,原 Payload 失败。
新的 Payload 就是将原 Payload 进行 URL 编码,新 Payload:
**%27%2d%28%20%69%66%28%28%31%3d%31%29%2c%20%73%6c%65%65%70%28%35%29%2c%20%31%29%20%29%2d%27%27%2c%20%27%31%39%32%2e%31%36%38%2e%31%2e%31%31%27%2c%27%74%69%6d%65%27%29%20%23**
造成延时成功,绕过修复方案,触发 SQL 时间盲注漏洞。
此漏洞在GITHUB中上报给了作者团队。
本文作者:酒仙桥六号部队
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/139876.html