当前位置: 当前位置:首页 > 焦点 > java rmi 服务远程方法调用漏洞正文

java rmi 服务远程方法调用漏洞

作者:热点 来源:百科 浏览: 【大 中 小】 发布时间:2026-10-17 06:08:53 评论数:

2015年,服法调Java反序列化漏洞集中爆发的程方那段时间,我正好在做一个银行内部系统的用漏渗透测试。目标环境里跑着一个RMI服务,服法调端口1099敞开着,程方没做任何访问控制。用漏用ysoserial随便打了一个CommonsCollections链,服法调直接弹出了计算器。程方那一刻我意识到,用漏RMI这个东西远比大多数人想象的服法调危险。

RMI,程方Remote Method Invocation,用漏Java原生的服法调一套远程调用机制。它的程方设计初衷很朴素——让一台JVM上的对象能调用另一台JVM上的方法,像调本地方法一样自然。用漏底层靠JRMP协议通信,默认监听1099端口。问题恰恰出在这个"像调本地方法一样自然"上。

JRMP:一个没有身份验证的协议

JRMP协议本身不包含任何认证机制。客户端发起调用时,服务端不会验证"你是谁"。这意味着只要网络可达,任何人都能向RMI Registry注册、查找、调用远程对象。很多开发者在内网环境里图省事,直接把RMI Registry绑定在0.0.0.0上,连防火墙规则都不加。一旦攻击者通过SSRF或VPN进入内网,这就是一条直通RCE的路。

更麻烦的是RMI的动态类加载机制。当客户端调用远程方法时,如果本地classpath里没有对应的类,RMI会尝试从服务端指定的URL去加载。这个URL可以是远程HTTP地址。攻击者完全可以搭一个恶意HTTP服务,让目标JVM从上面加载恶意class文件。这种攻击方式不需要依赖任何第三方库的反序列化链,纯靠RMI自身的特性就能完成。

反序列化:RMI的致命放大器

RMI的通信过程中大量使用了Java序列化。方法参数、返回值,全部走序列化通道。这就把RMI变成了反序列化攻击的完美入口。攻击者不需要认证,不需要任何前置条件,只要能连上1099端口,就能把恶意序列化数据直接喂给服务端。

ysoserial里的JRMPClient和JRMPListener两个payload就是专门针对RMI场景设计的。前者用于攻击RMI Registry,后者用于在目标发起反序列化时回连攻击者控制的JRMP服务端,形成二次利用。实际操作中,如果目标classpath里有commons-collections、groovy、spring等常见库,打成功的概率非常高。

我见过一个案例:某企业的Dubbo服务暴露在公网,Dubbo默认使用RMI协议作为传输层之一。攻击者没有直接打Dubbo的反序列化点,而是通过Dubbo的RMI端口间接攻击了底层RMI Registry,最终拿到了服务器权限。这种链路在实战中并不罕见。

防御这件事,说起来简单做起来难

最直接的办法是给RMI Registry加上java.rmi.server.hostname绑定到127.0.0.1,只允许本机访问。如果必须对外提供服务,至少要在网络层做白名单。但现实是,很多遗留系统根本不敢动配置,一动就崩。

JDK在后续版本里加了com.sun.jndi.rmi.object.trustURLCodebase和com.sun.jndi.cosnaming.object.trustURLCodebase这两个开关,默认设为false,用来阻断远程类加载。但反序列化那条路依然通畅,除非升级JDK或者引入JEP 290的序列化过滤器。

我的建议很直接:能不用RMI就不用。如果非要用,至少做到三点——绑定本地回环、升级到最新JDK、在JVM参数里配置序列化白名单。别指望WAF能拦住JRMP流量,那玩意儿根本不走HTTP。

RMI是Java生态里一块被遗忘的暗礁。它太老了,老到很多人觉得它已经过时、不值得关注。但正是这种忽视,让它成了内网渗透中最稳定的突破口之一。