前言
这个前台注入漏洞存在已久,从 MetInfo 发布的6.0版本就存在,且后来官方的修复代码,也可以直接绕过。笔者于7月份提交了当时最新 MetInfo ** 6.1.0版本SQL注入漏洞,该漏洞直到 **6.1.3 版本才被修复。如下漏洞分析,权当思路分享。
漏洞分析
2018-1-29, *MetInfo *官方重写后台及前台代码,并发布了 *MetInfo6.0 * 。然而这一新版代码,却存在一个严重的前台SQL注入漏洞,我们来看一下具体详情。
首先漏洞存在于 app\system\message\web\message.class.php 文件中,变量 {$_M[form][id]} 直接拼接在 SQL 语句中,且验证码检测函数是在 SQL 语句查询之后,这也就造成了我们可以无视验证码检测函数,进行 SQL 注入。具体问题函数代码如下:
那么实际上,开发者是有考虑到 SQL 注入问题,并写了sqlinsert 来检测 SQL 注入攻击的,甚至还对 {$_M[form][id]} 进行了 is_numeric 判断处理,但是为什么最终还是存在 SQL 注入呢?下面我们具体来看看 {$_M[form][id]} 变量的获取过程。
想要触发这个 SQL 注入漏洞,我们可以通过前台的 在线留言 和 在线反馈 两个功能来触发。我们这里先分析 在线留言 处的程序逻辑。我们填写留言处的表单,并在最下方添加上 id 字段,其值对应我们的攻击 payload ,具体如下:(为了避免图片太长,我把一些参数的值给略去)
然后我们在 message\index.php 文件中打下断点,会发现在 app\system\include\class\common.class.php 的构造方法中,加载了表单,具体的调用链即代码如下:
此时 this 对象指的是 web 类,我们继续跟进 web 类的 load_form 方法。
我们会发现 common 基类的 load_form 方法将 GET 、POST 、COOKIE 获取的所有数据存放在 $_M[‘form’] 中,数据仅用 daddslashes 函数处理。而 web 类的 load_form 方法重写了 common 类的 load_form 方法,对 $_M[‘form’] 中的所有数据进行了 sqlinsert 处理,并且在后面判断 $_M[‘form’][‘id’] 是否为数字,如果不是数字就直接设置为空字符串。这些过滤函数具体代码如下:
可以看到 sqlinsert 函数对 sleep 关键字进行了处理,但是我们还是可以使用 MYSQL 中的 benchmark 函数轻松绕过(这两个函数都能利用在时间盲注中,具体可以参考:https://xz.aliyun.com/t/2288)。但是我们的 payload 却绕不过 is_numeric 函数,所以此时的 $_M[‘form’][‘id’] 会被设置成空字符串。那为什么还存在 SQL 攻击呢?我们接着看程序逻辑。
在跟进程序的过程中,我们会发现在执行完 load::sys_class(‘upfile’, ‘new’) 代码后, $_M[‘form’][‘id’] 的值又变成了我们传入的 payload ,这说明这里面一定是又重新进行了表单赋值。
果不其然,程序中加载的 upfile 类又重新载入了 $_M[‘form’] 的值,而且并没有进行除了基类中 daddslashes 方法以外的消毒处理,这也是造成 SQL 注入的原因。
我们可以根据上面的漏洞原理,轻松的写出攻击脚本:
接着,我们再来对比一下5月份的修复代码,此时 *MetInfo *官方并没有更新版本号,而是偷偷修复了代码,修复代码如下右图:
可以清晰的看见 daddslashes 方法开始对每个参数进行 sqlinsert 处理。而上面我们说过,这个消毒函数是可以绕过的,所以我们依然可以进行 SQL 注入。
虽然可以进行盲注,但是还是太耗费时间了,我们是否有办法将其转换成布尔盲注呢?答案是可以的,接下来我们就来看看,如何将这一处时间盲注变成布尔盲注。
在 app\system\message\web\message.class.php 文件中,我们会发现如果SQL语句查询结果中的 value 字段非空,则会进行验证码的检测,否则会直接返回 反馈已经被关闭 。
这样子,我们就可以根据不同的返回结果,进行布尔盲注。现在我们来看一下如何找到满足查询结果中的 value 字段非空的 columnid ,直接在 MYSQL 中做如下查询:
这样子我们就找到了两个可用的 columnid 最终我们同样可以写出攻击脚本,如下:
我们再来看另一处注入点,前面我们分析的是 在线留言 处的 SQL 注入问题,这次我们要分析 在线反馈 处的 SQL 注入,漏洞的成因都是类似的。不过在利用这个漏洞时,我们需要经过 feedback 类 check_field 方法的检测。
这里实际上可用的 id 有两个,如下:
例如这里我想利用 id=71 ,那么需要构造如下数据包,才会进入漏洞触发处。
现在绕过了 check_field 方法的检测,我们就顺利来到了触发 SQL 注入漏洞的 feedback 累的 add 方法,其位置在 app\system\feedback\web\feedback.class.php ,具体代码如下:
我们会发现这次的验证码校验,位置在注入点前面,所以利用起来比较麻烦。这里我们可以使用爆破验证码结合时间盲注的方式进行攻击,用以下程序生成全排列验证码字典:
1 | from itertools import product |
但是这样的攻击成本还是太高了,如果大家会机器学习,可以加入相关算法来自动识别验证码。
最后我们来看一下 MetInfo 6.1.3最新版的修复情况如何,具体情况如下 DIFF 图:
结语
文章中提到的这个漏洞点,实际上也可以从其他入口点进行触发,之前也已经有师傅放出分析文章,漏洞的成因都是一样的,具体可以参阅文末的相关文章,这里不再赘述。本文分析到此结束,如有不当还望各位斧正。另外,我们团队正在招收代码审计成员,如果您在这方面有丰富的经验,欢迎加入我们。(可以发邮件至:sec-redclub@qq.com)