PHP反序列化初探
PHP反序列化初探
php序列化是什么?
在反序列化之前,先来看看php序列化是什么样的,php序列化和反序列化由serialize()和unserialize()这两个函数进行相互转化的,简单的说就是serialize()将一串字符串、数组、对象进行格式化处理,然后再有unserialize()函数将格式化处理后的字符串、数组进行还原;
代码:
<?php
$str = "this is php";
$arr = array("username"=>"xiaoming","password"=>"[email protected]#123");
class DemoSer{
public $name = "xiaoming";
public $sex = "man";
public $age = "14";
}
echo "string-".serialize($str)."</br>";
echo "array-".serialize($arr)."</br>";
$example = new DemoSer();
$example->name = "CC";
$example->sex = "woman";
$example->age = "12";
echo serialize($example);
echo "</br>";
$flag = serialize($example);
$unser = unserialize($flag);
?>
这样看着序列化之后的格式肯定不是人看的;所以对DemoSer序列化后的格式进行注释一下,标准格式如下:
O:7:"DemoSer":3:{s:4:"name";s:2:"CC";s:3:"sex";s:5:"woman";s:3:"age";s:2:"12";}
O:7: 这是一个object对象,长度为7
“DemoSer”:3: 代表名字为DemoSer,DemoSer有三个属性
{
s:4:“name”; string类型,属性名长度为4,属性名是name
s:2:“CC”; string类型,参数值长度2,参数值是CC
s:3:“sex”; string类型,属性名长度为3,属性名是sex
s:5:“woman”; string类型,参数值长度5,参数值是woman
s:3:“age”; string类型,属性名长度为3,属性名是age
s:2:“12”; string类型,参数值长度2,参数值是12
}
php反序列化
接下来使用unserialize()将序列化后的对象还原,代码:
<?php
$str = "this is php";
$arr = array("username"=>"xiaoming","password"=>"[email protected]#123");
class DemoSer{
public $name = "xiaoming";
public $sex = "man";
public $age = "14";
}
$flag1 = serialize($str);
echo "string-".unserialize($flag1)."</br>";
$flag2 = serialize($arr);
print_r(unserialize($flag2))."</br>";
echo "</br>";
$example = new DemoSer();
$example->name = "CC";
$example->sex = "woman";
$example->age = "12";
$flag3 = serialize($example);
print_r(unserialize($flag3));
?>
为什么出现反序列化漏洞
现在来看为什么这里反序列化会出现漏洞呢?主要是反序列化传递的参数可控,如果反序列化对象中存在魔术方法,而且魔术方法中的代码有能够被我们控制,漏洞就这样产生了,根据不同的代码可以导致各种攻击;php中的魔术方法在函数前会有"__",;这些魔术方法在类的创建或结束的时候会默认执行某些函数:__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString();比如下面的代码:
<?php
class TestDemo{
public $username = "admin";
public $password = "passwd";
public function out(){
echo "username:".$this->username.";password:".$this->password."</br>";
}
public function __destruct(){
echo "__destruct()"."</br>";
}
public function __construct(){
echo "__construct()"."</br>";
}
public function __sleep(){
echo "__sleep()"."</br>";
return array("username","password");
}
public function __wakeup(){
echo "__wakeup()"."</br>";
}
public function __toString(){
return "__toString()"."</br>";
}
}
#创建对象调用_construct()
$NewObject = new TestDemo();
$NewObject->username = "xiaoming";
$NewObject->password = "[email protected]#123";
#序列化对象调用__sleep()
$flag = serialize($NewObject);
#输出序列化后的字符串
print 'serialize:'.$flag."</br>";
#重建对象调用__wakeup()
$NewObject2 = unserialize($flag);
echo "unserialize:".$NewObject2."</br>";
#调用out()输出数据
$NewObject2->out();
#脚本结束调用__destruct
?>
现在我们来执行一下反序列化会产生的漏洞:
<?php
class Demo{
var $name = "xiaoming";
function __destruct(){
@eval($this->name);#在这里执行了phpinfo()函数;
}
}
$name = $_POST['name'];
$len = strlen($name)+1;
$str = "O:4:\"Demo\":1:{s:4:\"name\";s:".$len.":\"".$name.";\";}";
$test__unser = unserialize($str);
?>
执行效果:
从代码中可以知道phpinfo()函数实在程序结束时调用了__destruct()中执行了,同时里面还有@eval()函数,由此可以设想,直接在__destruct()函数中执行phpinfo()会怎么样?:
<?php
class Demo{
var $name = "xiaoming";
function __destruct(){
echo "hello";
@eval(phpinfo());
}
}
$name = $_POST['name'];
$len = strlen($name)+1;
$str = "O:4:\"Demo\":1:{s:4:\"name\";s:".$len.":\"".$name.";\";}";
$test__unser = unserialize($str);
?>
执行效果
直接在__destruct()中执行phpinfo():
在上面的代码中有@eval函数,也可以正常解析phpinfo(),这里还可以使用菜刀连接成为webshell,
最后基本知道php反序列化漏洞是怎么产生了~~
写的东西很基础,大佬勿喷啦……
这篇文章我先发布在圈子里面,过了一段时间后我再发布到自己的博客中……