Java中动态加载字节码

这个顺序是跟着p神的文章走的,学这个也是因为cc3中会用到这部分的知识,所以从这接着往后面弄了,抓紧时间弄完去深入一点渗透了!!

什么是Java的“字节码”

它是程序的一种低级表示,可以运行于Java虚拟机上。将程序抽象成字节码可以保证Java程序在各种设备上的运行,严格来说,Java字节码(ByteCode)其实仅仅指的是Java虚拟机执行使用的一类指令,通常被存储在.class文件中。而.class文件可以运行于各个计算机的JVM虚拟机中。其实也就是实现了一次编译,各处使用的性质。甚至,开发者可以用类似Scala、Kotlin这样的语言编写代码,只要你的编译器能够将代码编译成.class文件,都可以在JVM虚拟机中运行,来看一张图:

在学习的过程中,把字节码的定义放大点:所有能够恢复成一个类并在JVM虚拟机里加
载的字节序列,都在我们的探讨范围内。

利用URLClassLoader加载远程class文件

ClassLoader 是什么呢?

与普通程序不同的是。Java程序(class文件)并不是本地的可执行程序。当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader。而Class Loader这个类的作用就是根据一个指定的类的全限定名,找到对应的Class字节码文件,然后加载它转化成一个java.lang.Class类的一个实例

之前学习反射的过程中,我们知道它就是一个“加载器”,告诉Java虚拟机如何加载这个类。Java默认的
ClassLoader 就是根据类名来加载类,这个类名是类完整路径,如 java.lang.Runtime

URLClassLoader

URLClassLoader 实际上是我们平时默认使用的 AppClassLoader 的父类,所以,我们解释
URLClassLoader 的工作过程实际上就是在解释默认的Java类加载器的工作流程。

正常情况下,Java会根据配置项 sun.boot.class.path java.class.path 中列举到的基础路径(这
些路径是经过处理后的 java.net.URL 类)来寻找.class文件来加载,而这个基础路径有分为三种情况:

URL未以斜杠 / 结尾,则认为是一个JAR文件,使用 JarLoader 来寻找类,即为在Jar包中寻
找.class文件
URL以斜杠 / 结尾,且协议名是 file ,则使用 FileLoader 来寻找类,即为在本地文件系统中寻
找.class文件
URL以斜杠 / 结尾,且协议名不是 file ,则使用最基础的 Loader 来寻找类

试试http协议,从远程http服务器上加载.class文件,先在本地编写个.class文件

public class Hello{
    public Hello(){
        System.out.println("Hello c111");
    }
}

然后编译成.class文件

javac Hello.java

.class文件目录本地开个服务

python -m http.server

接着编写远程加载代码:

import java.net.URL;
import java.net.URLClassLoader;
public class a
{
    public static void main( String[] args ) throws Exception
    {
        URL[] urls = {new URL("http://localhost:8000/")};          //创建URL数组,里面放了一个实例化后的url对象
        URLClassLoader loader = URLClassLoader.newInstance(urls); 
//这步用于创建类加载器,因为java中每个类都需要一个特定的类加载器,所以这里需要一个加载器的对象,不一定需要用newInstance(),URLClassLoader这个类用new来实例化也是可以的

  
        Class c = loader.loadClass("Hello");   //这步用于类加载器去加载相应的类对象
        c.newInstance();                    //最后实例化类对象,同时肯定会调用其默认的无参构造
    } 
}

这里还要注意一点:(也是补基础知识了-。-)

idea中运行的时候他会将该项目里面的所有java文件编译一次(也就是变成class文件),接着当类加载的时候会先去本地里面查找相应的class文件加载为相应类对象,如果找不到的话才会去远程加载。

所以想要直观的看到是远程加载的class文件而不是加载的本来的class文件的话,先编译生成一个class文件,然后删除原来的java文件,再在所在class文件开个服务就行了

所以作为攻击者,如果我们能够控制目标Java ClassLoader的基础路径为一个http服务器,则可以利用远程加载的方式执行任意代码了。

利用ClassLoader##defineClass直接加载字节码

URLClassLoader加载远程class文件(也就是字节码)的过程已经了解了。其实,不管是加
载远程class文件,还是本地的classjar文件,Java都经历的是下面这三个方法调用:

ClassLoader#loadClass=>ClassLoader#findClass=>ClassLoader#defineClass

其中:
loadClass 的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机制),在前面没有找到的情况下,执行 findClass findClass 的作用是根据基础URL指定的方式来加载类的字节码,就像之前说到的,可能会在
本地文件系统、jar包或远程http服务器上读取字节码,然后交给 defineClass defineClass 的作用是处理前面传入的字节码,将其处理成真正的Java
所以可见,真正核心的部分其实是 defineClass ,他决定了如何将一段字节流转变成一个Java类,Java
默认的 ClassLoader#defineClass 是一个native方法,逻辑在JVM的C语言代码中。

什么是native方法:

native方法称为本地方法。在java源程序中以关键字“native”声明,不提供函数体。

其实现使用C/C++语言在另外的文件中编写,编写的规则遵循Java本地接口的规范(简称JNI)。

简而言就是Java中声明的可调用的使用C/C++实现的方法。

来看一个列子:

import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Base64;

public class a{
    public static void main(String[] args) throws Exception{
        Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        defineClass.setAccessible(true);
        byte[] code = Base64.getDecoder().decode("yv66vgAAADQAHgoABgAQCQARABIIABMKABQAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUB" +
                "AA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAHTEhlbGxvOwEA" +
                "ClNvdXJjZUZpbGUBAApIZWxsby5qYXZhDAAHAAgHABgMABkAGgEACkhlbGxvIGMxMTEHABsMABwA" +
                "HQEABUhlbGxvAQAQamF2YS9sYW5nL09iamVjdAEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVM" +
                "amF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShM" +
                "amF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAQABAAcACAABAAkAAAA/AAIAAQAAAA0qtwAB" +
                "sgACEgO2AASxAAAAAgAKAAAADgADAAAAAgAEAAMADAAEAAsAAAAMAAEAAAANAAwADQAAAAEADgAA" +
                "AAIADw==");
        Class c = (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(),"Hello",code,0,code.length);
        c.newInstance();
    }
}

//ClassLoader.getSystemClassLoader()返回系统的类加载器对象

里面的base64编码用linux命令完成

cat /home/kali/桌面/Hello.class | base64 

由于系统的 ClassLoader#defineClass 是一个保护属性,所以我们无法直接在外部访问,不得
不使用反射的形式来调用。

跟进一下defineClass具体实现

return里面套了一层,继续跟进看

返回了一个类对象,也就是加载后的类对象,最后newInstance()实现该Hello类就可以了

在实际场景中,因为defineClass方法作用域是不开放的,所以攻击者很少能直接利用到它,但它却是我们常用的一个攻击链 TemplatesImpl 的基石。

利用TemplatesImpl加载字节码

前面分析了defineClass方法并不好直接利用,但是Java底层还是有一些类用到了它,这就是 TemplatesImplcom.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl 这个类中定义了一个内部类
TransletClassLoader :

static final class TransletClassLoader extends ClassLoader {
    private final Map<String,Class> _loadedExternalExtensionFunctions;

     TransletClassLoader(ClassLoader parent) {
         super(parent);
        _loadedExternalExtensionFunctions = null;
    }

    TransletClassLoader(ClassLoader parent,Map<String, Class> mapEF) {
        super(parent);
        _loadedExternalExtensionFunctions = mapEF;
    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        Class<?> ret = null;
        // The _loadedExternalExtensionFunctions will be empty when the
        // SecurityManager is not set and the FSP is turned off
        if (_loadedExternalExtensionFunctions != null) {
            ret = _loadedExternalExtensionFunctions.get(name);
        }
        if (ret == null) {
            ret = super.loadClass(name);
        }
        return ret;
     }

    /**
     * Access to final protected superclass member from outer class.
     */
    Class defineClass(final byte[] b) {
        return defineClass(null, b, 0, b.length);
    }
}

这个类里重写了 defineClass 方法,并且这里没有显式地声明其定义域。Java中默认情况下,如果一个
方法没有显式声明作用域,其作用域为default。所以也就是说这里的 defineClass 由其父类的
protected类型变成了一个default类型的方法,可以被类外部调用。

但是因为TransletClassLoader是内部类,所以说只允许TemplatesImpl类中的方法调用,往上找找调用他的方法,看到TemplatesImpl类中的defineTransletClasses()方法:

private void defineTransletClasses()
        throws TransformerConfigurationException {

        if (_bytecodes == null) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR);
            throw new TransformerConfigurationException(err.toString());
        }

        TransletClassLoader loader = (TransletClassLoader)
            AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap());
                }
            });

        try {
            final int classCount = _bytecodes.length;
            _class = new Class[classCount];

            if (classCount > 1) {
                _auxClasses = new HashMap<>();
            }

            for (int i = 0; i < classCount; i++) {
                _class[i] = loader.defineClass(_bytecodes[i]);//在这里调用了defineClass
                final Class superClass = _class[i].getSuperclass();

                // Check if this is the main class
                if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                    _transletIndex = i;
                }
                else {
                    _auxClasses.put(_class[i].getName(), _class[i]);
                }
            }

            if (_transletIndex < 0) {
                ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
                throw new TransformerConfigurationException(err.toString());
            }
        }
        catch (ClassFormatError e) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_CLASS_ERR, _name);
            throw new TransformerConfigurationException(err.toString());
        }
        catch (LinkageError e) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
            throw new TransformerConfigurationException(err.toString());
        }
    }

但它是一个private方法,还是不能直接调用,继续往上看到getTransletInstance(),他是调用了defineTransletClasses()的:

private Translet getTransletInstance()
        throws TransformerConfigurationException {
        try {
            if (_name == null) return null;

            if (_class == null) defineTransletClasses();//此处调用defineTransletClasses方法

            // The translet needs to keep a reference to all its auxiliary class to prevent the GC from collecting them
            AbstractTranslet translet = (AbstractTranslet)
                    _class[_transletIndex].getConstructor().newInstance();
            translet.postInitialization();
            translet.setTemplates(this);
            translet.setOverrideDefaultParser(_overrideDefaultParser);
            translet.setAllowedProtocols(_accessExternalStylesheet);
            if (_auxClasses != null) {
                translet.setAuxiliaryClasses(_auxClasses);
            }

            return translet;
        }
        catch (InstantiationException | IllegalAccessException |
                NoSuchMethodException | InvocationTargetException e) {
            ErrorMsg err = new ErrorMsg(ErrorMsg.TRANSLET_OBJECT_ERR, _name);
            throw new TransformerConfigurationException(err.toString(), e);
        }
    }

不过还是private方法,继续找到newTransformer()方法:

public synchronized Transformer newTransformer()
        throws TransformerConfigurationException
    {
        TransformerImpl transformer;

        transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
            _indentNumber, _tfactory);//调用了getTransletInstance方法

        if (_uriResolver != null) {
            transformer.setURIResolver(_uriResolver);
        }

        if (_tfactory.getFeature(XMLConstants.FEATURE_SECURE_PROCESSING)) {
            transformer.setSecureProcessing(true);
        }
        return transformer;
    }

这里就是public方法了,可以直接调用,所以一条调用链就出来了

TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses()
-> TransletClassLoader#defineClass()

在调用的过程中需要满足一些条件,会用到一些值,所以我们还得设置TemplatesImpl对象的三个私有属性,这里我们用反射设置就行,三个属性: _bytecodes_name_tfactory

· _name:为任意字符串,只要不是null才可以进入defineTransletClasses()

·_bytecodes:由字节码组成的数组,用来存放恶意代码,其值不能为null,最后才可以进入到

·_tfactory:需要一个TransformerFactoryImpl对象(getExternalExtensionsMap是这个对象方法),不然调用其他对象会报错,这样才能继续执行下去

插一嘴

我最后调试的时候发现并不是从图上的这个地方进去就获取了类对象的,这里只是为了不报错,因为要达到传入恶意代码的的操作,才能达到目的,所以是后面这里才获取了类对象的

再往后面跟一点点接着才实例化了对象


但是,TemplatesImpl中对加载的字节码还有一定的要求,这个字节码对应的类必须要是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的子类,所以我们还得构造一个特殊的类,用这个类生成字节码:

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 Hello extends AbstractTranslet {
    public void transform(DOM document, SerializationHandler[] handlers)
            throws TransletException {}
    public void transform(DOM document, DTMAxisIterator iterator,
                          SerializationHandler handler) throws TransletException {}
    public Hello() throws Exception{
        super();
        System.out.println("Hello c111");
    }
}

简单解释一下为什么要多两个transform方法

这里是因为子类需要实现父类里面的抽象方法,同时因为父类是抽象类,可能没有将接口的方法全部实现,这时子类如果不是抽象的,那必须将其他接口方法都实现。

这里面 transform(DOM document, DTMAxisIterator iterator,SerializationHandler handler)是父类里面的抽象方法所以要重写

transform(DOM document, SerializationHandler[] handlers)是父类没有实现接口的方法所以要重写

同样将其编译为class文件,然后base64加密。

最后就是写poc了,就新建一个TemplatesImpl对象,把属性设置进去然后执行newTransformer方法触发,主要是咱得先写一个利用反射给私有属性赋值的一个方法setFieldValue:

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import net.sf.cglib.proxy.Enhancer;

import java.lang.reflect.Field;
import java.util.Base64;

public class a {
    public static void setFieldValue(Object obj,String fieldName,Object Value) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj,Value);
    }
    public static void main(String[] args) throws Exception{
        byte[] codes = Base64.getDecoder().decode("yv66vgAAADQALgoABgAeCQAfACAIACEKACIAIwcAJAcAJQEACXRyYW5zZm9ybQEAcihMY29tL3N1" +
                "bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNo" +
                "ZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUB" +
                "AA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAHTEhlbGxvOwEA" +
                "CGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007" +
                "AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXpl" +
                "ci9TZXJpYWxpemF0aW9uSGFuZGxlcjsBAApFeGNlcHRpb25zBwAmAQCmKExjb20vc3VuL29yZy9h" +
                "cGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2lu" +
                "dGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJu" +
                "YWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGl0ZXJhdG9yAQA1TGNvbS9z" +
                "dW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjsBAAdoYW5kbGVy" +
                "AQBBTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0" +
                "aW9uSGFuZGxlcjsBAAY8aW5pdD4BAAMoKVYHACcBAApTb3VyY2VGaWxlAQAKSGVsbG8uamF2YQwA" +
                "GQAaBwAoDAApACoBAApIZWxsbyBjMTExBwArDAAsAC0BAAVIZWxsbwEAQGNvbS9zdW4vb3JnL2Fw" +
                "YWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20v" +
                "c3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNq" +
                "YXZhL2xhbmcvRXhjZXB0aW9uAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1By" +
                "aW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcv" +
                "U3RyaW5nOylWACEABQAGAAAAAAADAAEABwAIAAIACQAAAD8AAAADAAAAAbEAAAACAAoAAAAGAAEA" +
                "AAAJAAsAAAAgAAMAAAABAAwADQAAAAAAAQAOAA8AAQAAAAEAEAARAAIAEgAAAAQAAQATAAEABwAU" +
                "AAIACQAAAEkAAAAEAAAAAbEAAAACAAoAAAAGAAEAAAALAAsAAAAqAAQAAAABAAwADQAAAAAAAQAO" +
                "AA8AAQAAAAEAFQAWAAIAAAABABcAGAADABIAAAAEAAEAEwABABkAGgACAAkAAAA/AAIAAQAAAA0q" +
                "twABsgACEgO2AASxAAAAAgAKAAAADgADAAAADQAEAA4ADAAPAAsAAAAMAAEAAAANAAwADQAAABIA" +
                "AAAEAAEAGwABABwAAAACAB0=");
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates,"_bytecodes",new byte[][]{codes});
        setFieldValue(templates,"_name","c1");
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        templates.newTransformer();
    }
}

利用BCEL ClassLoader加载字节码

关于BCEL可以先看看p神的:https://www.leavesongs.com/PENETRATION/where-is-bcel-classloader.html#0x01-bcel

BCEL属于Apache Commons项目下的一个子项目,它提供了一系列用于分析、修改和创建Java Class文件的API,从库功能来看,使用性远不及其他库,但它比Commons Collection特殊一点的是它被包含在了JDK原生库里,位于com.sun.org.apache.bcel

BCEL包中有个com.sun.org.apache.bcel.internal.util.ClassLoader类,它是一个ClassLoader,但重写了Java内置的ClassLoader#LoadClass方法。

LoadClass中,会判断类名是否是$$BCEL$$开头,如果是的话,将会对这个字符串进行decode:

来看一下decode的具体算法:

private static class JavaWriter extends FilterWriter {
    public JavaWriter(Writer out) {
      super(out);
    }

    public void write(int b) throws IOException {
      if(isJavaIdentifierPart((char)b) && (b != ESCAPE_CHAR)) {
        out.write(b);
      } else {
        out.write(ESCAPE_CHAR); // Escape character

        // Special escape
        if(b >= 0 && b < FREE_CHARS) {
          out.write(CHAR_MAP[b]);
        } else { // Normal escape
          char[] tmp = Integer.toHexString(b).toCharArray();

          if(tmp.length == 1) {
            out.write('0');
            out.write(tmp[0]);
          } else {
            out.write(tmp[0]);
            out.write(tmp[1]);
          }
        }
      }
    }

    public void write(char[] cbuf, int off, int len) throws IOException {
      for(int i=0; i < len; i++)
        write(cbuf[off + i]);
    }

    public void write(String str, int off, int len) throws IOException {
      write(str.toCharArray(), off, len);
    }
  }

可以理解为是传统字节码的16进制编码,然后将 \ 替换为 $ ,默认还会在最外层加上 GZip 压缩

尝试编写恶意类:

import java.io.IOException;

public class a{
    static  {
        try {
            Runtime.getRuntime().exec("calc.exe");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

然后通过BCEL提供的两个类Repositoryutility来利用:

Repository用于将一个Java Class先转换成原生字节码(也可以直接javac编译获得);

utility用于将原生字节码转换成BCEL格式的字节码
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;

public class POP {
    public static void main(String[] args) throws Exception{
        JavaClass javaClass = Repository.lookupClass(a.class);
        String code = Utility.encode(javaClass.getBytes(),true);
        System.out.println(code);
    }
}

最后用BCEL ClassLoader加载这串特殊的字节码,并执行里面的代码:

import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;

public class Hello {
    public static void main(String[] args) throws Exception{
        c();
    }
    private static void b() throws Exception{
        JavaClass javaClass = Repository.lookupClass(a.class);
        String code = Utility.encode(javaClass.getBytes(),true);
        System.out.println(code);
    }
    private static void c() throws Exception {
        new ClassLoader().loadClass("$$BCEL$$$l$8b$I$A$A$A$A$A$A$AmQ$cbN$c2P$Q$3d$X$K$z$b5$I$82$f8$7e$3f$c1$85l$dci$dc$YM$8c$f5$R$n$ba$be$5co$f0$o$b6$a4$5c$M$7f$e4$da$8d$g$X$7e$80$le$9c$5e$VM$b4Ig$3a$e7$cc93$93$be$bd$bf$bc$C$d8$c2$9a$L$H$e3$$$s0$e9$60$w$ce$d36f$5c$a40kc$ce$c6$3cCzG$FJ$ef2$q$cb$95$L$Gk$_$bc$92$M9_$F$f2$a4w$db$90Q$9d7$da$84$U$fcP$f0$f6$F$8fT$5c$7f$81$96$beV$5d$92$fa$7c$9b$c1$d9$R$ed$_$_F$5c$c9o$f1$3b$5eUa$f5$f0t$bf$_dG$ab0$a0$b6lMsqs$cc$3b$c6$83$d6apka$_$S$f2$40$c5$9ei$be$Z$L$3dd$e0$daX$f0$b0$88$r2$a7$e1bS$f6$a5$87e$ac0$U$ff1$f7$b0$K$97$86s$86$bc$a1$db$3chVO$h$z$v4$c3$c8$Pt$de$L$b4$ba$a5YnS$eaAQ$wW$fc$3f$3d$b4$b0ES$F$c3z$f9$X$5b$d3$91$K$9a$db$bf$FgQ$ud$b7K$82$5c$87Hm$ce$acG$5cH$ba$c0$a6$ff$Q$3f$J$b0$f8$$$8aCTU$v3$ca$a9$8d$t$b0$HC$7b$U$d3$GL$oK$d1$fbl$c00r$94$j$e4$Hbn$cc$80$c23$S$85$e4$p$ac$cb$7b8G$h$8fH$3f$Y$3cC$da$U$b9$c4$8ec$f4$V$fbf$Mj$93$b3$83$Rr$fa$9e$90$85Eu$81$aa$o$bd6$S$be$8dQ$8b$88$92Yj$ec$D$e5$e5$f0$NQ$C$A$A").newInstance();
    }
}

其实也可以简化一点代码

import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import com.sun.org.apache.bcel.internal.util.ClassLoader;

public class Hello {
    public static void main(String[] args) throws Exception {


        JavaClass javaClass = Repository.lookupClass(a.class);
        String code = Utility.encode(javaClass.getBytes(), true);
        System.out.println(code);


        new ClassLoader().loadClass("$$BCEL$$" + code).newInstance();

    }
}

一样是可以利用的

最后

BCEL ClassLoaderFastjson等漏洞的利用链构造时都有被用到,其实这个BCEL ClassLoader类和前面的
TemplatesImpl 都出自于同一个第三方库,Apache Xalan。所以才要导入,还要导入maven项目。还有一个重要的利用条件就是在Java 8u251的更新中,这个ClassLoader被移除了,所以之后只能在这个之前的版本才可以利用。

参考链接

BCEL ClassLoader去哪了