在 Windows环境、PHP版本<=5.3 的环境中, Metinfo 存在一个前台任意文件上传漏洞。攻击者可以通过该漏洞直接获取网站权限,漏洞影响至 Metinfo 最新版(目前最新版本为 6.2.0 )漏洞思路参考自 Metinfo6 Arbitrary File Upload Via Iconv Truncate ,原文中对 POC 打了码,我们可以自己来挖掘一下。
Metinfo 提供了一个强大的上传类,其中有一个实现了上传功能的方法 doupfile 。在下图 第5行 的代码中,设置了上传文件的一些模式信息,同时在 第11行 处,用 $info 变量来设置文件的属性信息。然而 $info 变量来自用户可控数据 $_M['form'] ,我们细看 set_upfile、set_upload 这两个方法中都设置了什么。
在 set_upfile、set_upload 这两个方法中,我们看到其都设置了 savepath 属性,该属性用于表示上传文件的保存路径。其中,在先调用的 set_upfile 函数中, savepath 属性默认值为 /website_root/upload/file/ 。而后调用的 set_upload 函数中,用可控数据重新赋值给 savepath 属性。之所以要关注这个属性,是因为该属性会影响我们上传文件的存储位置,且这个属性也是引发本次漏洞的关键之一。
我们继续看 doupfile 方法,在 $this->set_upload($info) 之后,紧接着执行 $this->upload($_M['form']['formname']) ,该方法则调用 upfile 类的 upload 方法。在 upfile 类的 upload 方法中,程序将 $_FILES 变量存在 $filear 变量中,然后对一些上传条件进行逐一判断。可以看到程序使用黑、白名单对文件后缀合法性进行检测。规则相对严格,暂时无法绕过,我们继续往下看。
当判断完文件后缀名,程序又对文件名中的非法字符进行替换。这里有一个 $this->is_rename 变量,该变量用于决定是否对上传文件进行重命名,其值可由用户控制(即前面提到的 $_M['form']['is_rename'] 变量)。
接着,只要 $this->savepath 以 /webroot/upload/ 开头且不包含 ./ ,就会将它和 $this->savename 拼接起来,作为文件的绝对路径(如果想绕过 ./ 的过滤,在 windows 下可以使用 ..\ 来进行路径穿越)。如果系统为 windows ,将会调用 iconv 函数对文件绝对路径进行编码转换,而问题就出在这里。
低版本的 PHP 中, iconv 函数存在字符转换截断问题。而我在尝试的过程中,发现了一些和漏洞发现者不一样的地方。原作者的 payload 中使用的是 %81 ,而这个字符在 windows 下作为文件夹名的一部分是不行的,所以在上图 第13行 创建目录会失败,导致程序提前终止,所以有了作者后续的一系列绕过操作。而我在测试的时候使用的是 %80 ,这个字符在 windows 下作为文件夹名的一部分是可以的,所以我们直接就完成了整个漏洞的分析。
下面,我们也来看看漏洞作者使用 %81 字符时会遇到的问题。首先,程序会根据路径逐级判断目录是否存在。如果不存在,则创建目录。而我们想用 %81 字符使 iconv 发生截断,但是 windows 下不允许这个字符存在文件名中,所以作者又利用了 windows 的特性,构造出 payload: a.php%80\..\1.jpg 。
如果 payload: a.php%80\..\1.jpg 能顺利拼接成文件名,那也就完成了整个漏洞复现。然而在 Metinfo 中,在未定义 define('IN_ADMIN', true); ,变量 $_M['form']['is_rename'] 中的数据就会经过 sqlinsert 函数处理,而这个函数恰恰把 \ 符号给替换成 / 了,那么就变成了 payload: a.php%80/../1.jpg ,这样就会触发上边的 ./ 匹配规则,所以我们得想办法绕过。
如果之前审计过 Metinfo 的 SQL 注入漏洞,就知道可以通过 admin/index.php 来绕过 sqlinsert 函数。我们可以利用该文件中的 define('IN_ADMIN', true) ,然后通过构造 Web 路由完成漏洞触发。
关于 EXP 这里不会具体给出,只给思路。我们可以登录后台,寻找上传文件的地方。抓包,然后再根据上文提到的一些必要参数,构造相应数据包即可。 EXP 运行结果如下: