前言:前段时间热度挺高的一个系统存在SQL注入漏洞,升级后漏洞被修复了。
开始前先对比了一下两个版本的PHP代码,关键代码没进行修改,没有对单引号进行转义,也没有使用自动转义函数,但是GET、POST、REQUEST传参还是被转译了。
下面请允许我水一篇。
0x01 问题描述
测试环境如下:
172.28.113.153(2)
192.168.100.130(3)
测试代码 <?php echo $_GET[1];
在2中未对传入参数值做任何处理。
在3中的传入参数值,未进行手动转义、未加入自动转义函数,但是,参数值还是自动被传译了。
查看了7.0.3.1的PHP版本是5.3.5,想到magic_quotes_gpc还没被移除(从PHP5.4开始就已经被移除了),使用phpinfo查看发现是果然是开启状态。
然鹅7.0.2.1的PHP版本为5.6.40,升级到7.0.3.1后,PHP版本却降到了5.3.5🤣
其他猜测:如果代码中没有对单引号进行转义,也没有使用自动转义函数,magic_quotes_gpc等选项也已经关闭,GET、POST等传参还是被转译。
还有可能是其他扩展或模块(如:Suhosin扩展、mbstring模块)引起的,如果该环境的PHP程序是自己编译的,也可能是编译选项导致了单引号被转义。
0x02 magic_quotes_gpc
先确定一下magic_quotes_gpc的影响范围。
测试一手,毕竟要以实物为准嘛。
SESSION(无意义),SERVER、php://input($HTTP_RAW_POST_DATA)不受影响。
0x03 $HTTP_RAW_POST_DATA
上面做的事情是为了解决,升级后增加了magic_quotes_gpc限制,寻找有没有什么方法可以绕过转译。
找到了$HTTP_RAW_POST_DATA,发现Exxxxxxxx.XGI文件中出现过多次。
选择一个最近的137行,发现刚好下方有拼接的SQL语句,且语句中的可控点在$HTTP_RAW_POST_DATA中。
那么我们先让cmd等于"createUser"好通过if ($cmd == "createUser")判断,往上找发现103行$cmd通过$requestObj['command']赋值。
辣么,就是说。。。我们只需要让$requestObj['command'] == "createUser",继续往上看,89-95行是处理$requestObj的代码,可以看到其实$requestObj的键值是从$paramArr中得到的。
分析一下这段代码的逻辑,其实就是循环取$paramArr的键值,然后使用explode函数把$value通过"_"分割打散为数组,并赋值给$keyValue,然后把$keyValue[0]赋值给$requestObj当作键名,$keyValue[1]作为值赋给$requestObj[$keyValue[0]]。
explode函数实例:
回头看条件,其实只需要让$requestObj['command'] == "createUser"成立
就等于让$paramArr的value等于"command_createUser"就好了。(key在循环中没参与处理不需要管)
然后explode函数把"command_createUser"通过"_"分割打散为数组,就成了$keyValue = array('command' => 'createUser'),$keyValue[0]就等于"command",$keyValue[1]就等于"createUser",$requestObj[$keyValue[0]]就等于"createUser"了。
继续往上看$paramArr,在83行进行处理并赋值,通过$initparams处理后得到,与上方$requestObj同理。
继续往上看$initparams,在21行通过initParams传入。
条件都可控,尝试构造请求,提示参数非法。
原因是59行存在参数是否为空的判断。
加上参数,继续构造请求,还是提示参数非法。
往下看发现71行对$keyVal有限制,但是可以通过$key控制,就是需要$key == "wusuokey" || $key == "inner",这里就不看getfarminfo方法了(悄悄说一下这里上个版本可以getshell噢),这里直接给$key赋值为"inner",让$keyVal = "Realor"。
还有78行对sign的值有限制,就是需要让$sign == md5($initparams . $keyVal)
很简单,把传入的$initparams和$keyVal拼接起来进行md5加密就好了。
构造请求包,让$cmd等于createUser,通过了if判断,离注入就剩几行了。
这时$account还没传值所以上一步报错,接着看json_decode($HTTP_RAW_POST_DATA, true);给$account传值。
PHP json_decode() 函数用于对 JSON 格式的字符串进行解码,并转换为 PHP 变量。
好了直接构造数据包,记得把Content-Type改一下。
成功通过mysql_query,因为是通过$HTTP_RAW_POST_DATA传进来的,所以不会被magic_quotes_gpc限制,后面的话dddd,这里就不演示了(小提示:有32个字段;这里写shell是可以用相对路径的../../WebRoot/shell.xgi😅)。
0x04 下版本漏洞预测
毕竟json_decode($HTTP_RAW_POST_DATA, true);的地方不多,刚好又看到一个有意思的函数utf8ToGbk😅,注入经验丰富的师傅看到这可能就猜到下文了
就随便找个了利用点,让 $cmd=getUserDetailByAccount,与上方前半段手法一样不赘述了。
开个上帝模式先。
构造数据包,确认注入点。
单引号肯定不行啊,直接转译。
那咋办么,utf8ToGbk嚒,直接運一下,结束。
!提示:注意一下上面的md5加密,这里单引号传进入已经被转译了,所以要加密的是\'
说明:由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雁行及文章作者不为此承担任何责任。雁行拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雁行允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。