本篇详细分析了 PHPCMS 的部分历史漏洞。其中多是以获取到漏洞点为场景,反向挖掘至漏洞触发入口(假设自己发现了漏洞点,模拟如何找寻整个攻击链及其入口点),旨在提高自身代码审计能力。当中包含一些网络上未公开的触发点,以及补丁对比分析与绕过。

v9.6.1任意文件读取

这个版本的 任意文件读取 漏洞和上个版本的 SQL注入 漏洞原理是类似的,且出问题的文件均在 phpcms/modules/content/down.php 中。在该文件的 download 方法中最后一行调用了 file_down 文件下载函数,我们可以看到其第一个参数是要读取的文件路径。

我们再来看看 download 方法中有哪些限制条件。可以看到其开头部分的代码,和上一个版本的 SQL注入 类似,唯一不同的是这里加解密的 key 变成了 $pc_auth_key ,我们等下就要来找找使用 $pc_auth_key 进行加密的可控点。继续看 download 方法,里面对要下载的文件后缀进行了黑名单校验,但是末尾又对 >< 字符进行替换,这就导致后缀名正则可被绕过,例如: .ph<p 。(下图对应文件位置:phpcms/modules/content/down.php)

现在我们就要来找找使用 $pc_auth_key 作为加密 key 的可控点。通过搜索关键字,我们可以看到有三处地方。然而前两处地方是不可以利用的,因为都有登录检测。而第三个点就可以利用,我们看其中 $i、$d、$s 作为明文字符串被加密。(下图对应文件位置:phpcms/modules/content/down.php)

有了加密字符串,我们如何能够从前台获取呢,这里其实在最后一行包含模板文件时,将加密字符串 $downurl 输出了,这样也就解决了我们获取的问题。

$i、$d、$s 这三个变量从哪里来?我们往前看,代码有没有相当熟悉?这里只对 $i 进行了 intval 过滤,其他两个变量还是可以利用。而且加密字符串 $a_k 的获取,就和上个版本的 SQL注入 漏洞攻击链的前2步是一样的,这里不再赘述。(下图对应文件位置:phpcms/modules/content/down.php)

我们在构造 payload 的时候,我们要注意整个攻击过程会经过两次 safe_replace 、两次 parse_str 、一次 str_replace(array('<','>'), '',$fileurl) ,而程序对 ..php 字符进行了检测。所以我们要想访问 php 文件或进行路径穿越,后缀可以设置成 ph>p ,路径符可以变成 .>. 。但是 safe_replace 函数会 str_replace('>','>',$string) ,所以 > 字符需要编码两次,变成 %25253e

我们可以将整个漏洞的触发过程整理成下图:

最后来看一下官方发布的 PHPCMS v9.6.2 中是如何修复这个漏洞的,补丁如下:

可以看到补丁将后缀匹配规则放在离下载文件最近的地方,貌似能防止规则中的文件被读取,但是我们可以利用 windows 的特性,在 windows 下绕过这个正则,这也是网传的一种 PHPCMS v9.6.2任意文件下载 漏洞。

v9.6.2前台SQL注入

这个版本的的注入,是建立在任意文件读取漏洞存在的情况下才可利用。通过任意文件读取漏洞获得加解密的 key 值,我们可以用这个 key 加密我们的 SQL注入payload 。由于程序对解密后的数据并未过滤,最终导致漏洞发生。严格上来讲 v9.6.2 版本的注入只能在 windows 上利用,具体原因在上面的任意文件读取漏洞分析时也说了。下面我们来具体分析一下这个漏洞。

漏洞文件位于 phpcms/modules/member/classes/foreground.class.php ,代码如下图。我们可以明显看到下图第33行,程序直接将解密后的数据未经过滤直接带入查询。而待解密数据 $phpcms_auth 和解密秘钥 $auth_key 均可构造。

我们先来看一下待解密数据 $phpcms_auth 如何构造。从下图中,可以看出程序将从 cookie 中的 xxx_auth 字段经过 sys_auth 函数解密后,返回给了 $phpcms_auth ,而默认情况下使用 pc_base::load_config('system', 'auth_key') 作为加解密的 key 值。

pc_base::load_config('system', 'auth_key') 的值在网站搭建好后,会存储在 caches/configs/system.php 中,我们可以通过任意文件读取来获得这个值。

现在 $phpcms_auth 已经搞定了,我们再来看看 $auth_key = get_auth_key('login') 如何构造,跟进 get_auth_key 的代码。我们可以看到 $auth_key$prefix、pc_base::load_config('system','auth_key')、ip() 三个元素决定。前两个都是已知的,而第三个获取用户IP的函数存在IP伪造的问题,也可以是固定的。

所以 get_auth_key('login') 的值也是我们可以构造的,剩下的事情只要我们将 payload 传给加密函数加密两次即可。我们最后再来看一下 PHPCMS v9.6.3 中是如何修复这个漏洞的,补丁如下:

可以明显看到,补丁将解密后获得的 $userid 进行了强转。

结束语

分析历史漏洞好处在于,可以使自身对这个 CMS 更熟悉,摸清该 CMS 普遍存在的问题,甚至有机会通过 bypass 补丁来发现新的 0day 。有些补丁只是暂时修复了漏洞,安全隐患仍然存在。随着 CMS 功能越来越多,我们可以将新功能中的利用点,结合之前的风险点,打出一条漂亮的攻击链,期待下个 0day 的诞生。

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖