php反序列化字符逃逸

前言

这玩意真挺离谱的.jpg

知识点来源:GYCTF2020 Easyphp

弄完pop链被反序列化卡住,查了一圈才知道居然还有这种骚操作。

逃逸

先抽象出这段简单的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
function safe($str)
{
return str_replace('bb', 'ccc', $str);
}
class A
{
public $name = 'aaaa';
public $pass = '123456';
}
$AA = new A();
$res = safe(serialize($AA));
echo $res."\r\n";
$c = unserialize($res);
echo $c->pass;

image-20210722211114410

一句话能搞明白么

简单来说,就是通过在内容中,使用特殊字符闭合这段变量(结束符号 ";} ),让你后面的东西插进去成为这串反序列化字符串内的玩意,而且还可以把后面的挤掉。

细说

但是,和上图写的一样,php会按照这个长度进行解析,在长度内的就按照普通字符串解析了。

那既然有了长度规定,只要让你的payload在长度范围之外就好了!

但是一般来说,长度是根据内容计算出来的,怎么会有长度和内容不统一的问题?

这就要归功于上面那段代码的 safe 函数,某些特殊字符会被替换掉。一个bb->ccc,这就多出来1位!

那么我们只要构造相应个的bb,就可以注入相应长度的内容啦

比如说我们要注入这段 ";s:4:"pass";s:6:"hacker";} ,将 pass 的值改成了 hacker ,这段payload长度为27,我们只需要在前面加上27*bb就可以达到这个效果了

具体过程是:

aaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"hacker";} ,长度为4+27*2+27,

aaaa长度4,27个bb长度2*27,payload长度27。这段作为 $name 变量,交给php进行序列化。

php在序列化时候,会 strlen(name) ,得到4+27*2+27=85。于是就有了下面这个

1
O:1:"A":2:{s:4:"name";s:85:"aaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:6:"hacker";}";s:4:"pass";s:6:"123456";}

在经历 filter() 函数时,这串字符串的bb会被替换成ccc、

1
O:1:"A":2:{s:4:"name";s:85:"aaaaccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";s:4:"pass";s:6:"hacker";}";s:4:"pass";s:6:"123456";}

显然,这85可就出问题了。我们精心构造的长度起了作用,现在85正好是aaaaccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc,闭合成功。

image-20210722214649043

这破图做了我半天麻烦死了

后记

php 真可怕啊

远离 php 从我做起

参考:PHP反序列化字符逃逸详解


php反序列化字符逃逸
https://qiuye.ink/pages/8fc2ba/
作者
Akiba
发布于
2021年7月22日
许可协议