cve-2016-7124
范围:
- PHP5 < 5.6.25
- PHP7 < 7.0.10
这种方法很简单,简而言之就是直接让序列化字符串中变量的值比实际上的变量值多就可以绕过了
就比如:
O:4:”xctf”:1:{s:4:”flag”;s:3:”111”;}
直接改成:
O:4:”xctf”:2:{s:4:”flag”;s:3:”111”;} 就绕过了
但是一定注意题目所处的php环境,环境不对是无法实现绕过的
引用赋值
简单来说就是让两个变量指向同一个内存地址,我们通过改变另外一个变量的值以实现同步修改
最基础的格式就是
$x=&$a;
这两个变量就指向了同一个内存地址了
fast-destruct
这种方法就是通过更改序列化数据结构,让序列化字符串失效,从而导致反序列化操作失败,在反序列化过程中,通过破坏序列化字符串的结构,迫使目标对象被立即标记为“垃圾”并触发 __destruct(),而在此过程中,__wakeup() 可能根本没机会执行。
在这种方向下,第一个方法就是直接删括号,比如:
a:2:{i:0;O:15:”VulnerableClass”:2:{s:3:”cmd”;s:6:”whoami”;s:8:”is_admin”;b:1;}i:1;s:13:”dummy_string”;}
直接把最后那个中括号给删了:
a:2:{i:0;O:15:”VulnerableClass”:2:{s:3:”cmd”;s:6:”whoami”;s:8:”is_admin”;b:1;}i:1;s:13:”dummy_string”;
那么php在进行反序列化操时就会无效,出错,进一步让php把它当做垃圾销毁,调用__destruct方法,在上述流程中wakeup
方法根本来不及调用
正常流程:
1️⃣ 解析字符串
2️⃣ 创建对象
3️⃣ 填充属性
4️⃣ 如果整个对象解析成功
→ 调用 __wakeup()
5️⃣ 返回对象
操作流程:
1️⃣ PHP 开始解析
2️⃣ 创建对象(对象已经存在)
3️⃣ 解析到一半出错
4️⃣ 反序列化失败
5️⃣ 这个对象失去引用
6️⃣ 触发垃圾回收
7️⃣ 调用 __destruct()
这样我们就实现了对wakeup的绕过了
还有第二种方法就是利用数字键冲突
a:2:{i:0;O:15:”VulnerableClass”:2:{s:3:”cmd”;s:6:”whoami”;s:8:”is_admin”;b:1;}i:1;s:13:”dummy_string”;}
这串代码就是典型的数组,可以看见i后面跟的就是数组的键,我们如果通过改变数组键的方式让两个键向相同,那么php在解析时同样会报错而导致无效化
a:2:{i:0;O:15:”VulnerableClass”:2:{s:3:”cmd”;s:6:”whoami”;s:8:”is_admin”;b:1;}i:0;s:13:”dummy_string”;}
在题目要求序列化字符串结构必须完整时这种方法就会派上用场
题目
【极客大挑战】php
进入题目没什么特别的东西,拿dirsearch扫了一下发现有个www.zip,把它下载下来
出现了以下文件,还有一个flag.php,那个不用想也知道是骗傻子的

通篇看下来其实这里的考点就是一个简单的__wakeup()绕过,整个题目的核心逻辑是要求password=100且username=admin,但是这里 由于wakeup函数会在执行反序列化之前就执行,这里wakeup函数会强制把username的值改为guest,那这里就必须要对其进行绕过

这里用wappalyzer来看php版本就看见版本为5.3.3,版本很低,利用cve-2016-7124进行绕过
写一下poc:

然后把二改成三传参给select就可以了


2022年中国工业互联网安全大赛北京市选拔赛暨全国线上预选赛-wakeup
查看源码:
1 |
|
这是一道使用引用赋值达到绕过wakeup的题目,这里运用的方法十分巧妙,同时也十分复杂;
通篇看下来,我们要利用的最核心的代码就是:
call_user_func_array($this->format[$name],$arguments);这个函数把第二个数据作为参数传入前一个数据,如果我们可以把$this->format[$name]传为例如system,把$arguments传为例如calc或者ls等,那这里我们就可以实现命令执行了;
但是在这个函数上面的if条件中有这么一行:
1 | if(!isset($this->wakeup)||!$this->wakeup) |
也就是说我们想要进入这个if,就必须要$this->wakeup是false或者空,而继续往下看会发现____wakeup()方法里面会强制把$this->wakeup设置为True,而这个方法在执行反序列化之前会被触发,也就是说我们这里需要想办法对wakeup()方法进行绕过,这里用到的方法就是引用赋值;
这里有反序列化操作,我们就首相想办法去找带有destruct方法的类作为切入口,有这种方法类只有Suncorpa和MoonCorpa,我们这里选用的SunCorpa类;
下面给出exp:
1 | class KeyPort { |
1 | class SunCorpa |
先看第一部分,第一部分的作用就是把$this->wakeup设置成false,往SunCorpa类里面看,执行des方法后:
1 | if ($this->process->finish()) { |
这里会调用finish函数,而它存在于keyport类里面:
1 | public function finish(){ |
可以看见这里先会把$this->format设置为一个空数组,然后返回$this->finish->iffinish的值给到if ($this->process->finish()),那么先看一下iffinish的位置,处于Arrayobj类中,并且是个private属性,类里面还有个一个get方法,而调用iffinish的地方又在keyport类里面,显然这里就是会触发到get方法的
1 | public function __get($name){ |
get方法会把$this->$name=$this->name[$name]的值返回到finish函数中,get方法里面的参数是调用的无法访问的对象,那么进入到get方法后就变成了:
$this->$iffinish=$this->name[iffinish];
到这应该就不存在继续往下执行的地方了,那么我们就尝试在这里赋值实现$this->wakeup=false
我这里是先从$this->$name=$this->name[$name]下手,可以看见这里是是从name数组里面的$name键的值赋值给等式左边的,我们直接:
$arr1->name=array(“iffinish”=>false);
那么等价一下:
$this->$iffinish=false,那么这里我们就实现了将iffinish的值改成了false,这时我们再进行引用赋值:
$this->$iffinish=&$x;
当然这里为了省事我们是在原本的基础上加了____construct,主要是为了省事,在实例化对象的时候自动就执行了,往exp里面看就是:
$arr1=new ArrayObj($key1->wakeup);在实例化时直接在括号里面传参只会给到construct,而我们的exp中直接写的:
public function __construct(&$x)
{
$this->iffinish=&$x;
也就是说,实际执行时是:
$this->iffinish=$key1->wakeup;
那么我们这里就成功做到了将wakeup绕过了
再来看到第二部分,第二部分就是进行传命令的一些操作了,往keyport类里面看,我们知道想进入call_user_fuc_array最前面的条件是进入call方法,也就是调用不存在的方法,往其他类里面看就会发现有个forward()方法,那么这个方法显然就是我们的切入点了
/image-20260312141303890.png)
这里我就用MoonCorpa类来做了,显然我们要进入call,那么我们就要把$this->options[‘new’]赋值成我们的Keyprot类,从而进入call
所以exp写到:
$moon=new MoonCorpa();
$this->moon=$moon;
$this->moon->options=array(“new”=>$this->key1);
这里我们再把_forward调换成我们想要的命令比如cacl或者ls;
接下来我们还要对forward想办法,在keyport的finish类里面,会将forward清空为一个空数组,这里用到的思路其实就和上面的思路是一模一样的,在这里再次使用引用赋值即可
1 | $key2=new KeyPort(); |
无非就是把第一部分重发一遍
这里加了个construct其实不加也可以,加了construct也不会造成影响,不加的话把复制这些操作写在类外面也一样可以,只不过可能稍微会麻烦一些
[UUCTF 2022 新生赛]ez_unser
进入题目,这里最核心的代码是eval($this->a);也就是说我们这里要想办法把a换成我们想要执行的命令,但是这里wakeup()会强制把我们传进去参数换成空,那么这里就需要对wakeup()进行绕过:
这里的方法还是用的引用变量,poc在最下面,简而言之就是考虑到des方法里面会有个赋值,我们就把c换成命令,然后a和b引用赋值就可以了


