Slides from the Black Hat USA 2021 Arsenal presentation of remote-method-guesser.
Recording: https://youtu.be/t_aw1mDNhzI
remote-method-guesser (rmg) is a Java RMI vulnerability scanner that checks for common misconfigurations on Java RMI endpoints.
It combines well known techniques for RMI enumeration with detection capabilities for lesser known attack vectors that are often missed.
Apart from detecting RMI vulnerabilities, remote-method-guesser can perform attack operations for each supported vulnerability type.
The following list shows some of it's currently supported operations:
* List available bound names and their interface class names
* List codebase locations (if exposed by the remote server)
* Check for known vulnerabilities (enabled class loader, missing JEP290, JEP290 bypasses, localhost bypass (CVE-2019-2684))
* Identify existing remote methods by using a bruteforce (wordlist) approach
* Call remote methods with user specified arguments (no manual coding required)
* Call remote methods with ysoserial gadgets within the arguments
* Call remote methods with a client specified codebase (remote class loading attack)
* Perform DGC, registry and activator calls with ysoserial gadgets or a client specified codebase
* Perform bind, rebind and unbind operations against an RMI registry
* Bypass registry deserialization filters by using An Trinhs registry bypass
* Enumerate the unmarshalling behavior of java.lang.String
* Create Java code dynamically to invoke remote methods manually
3. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
$ whoami
Tobias Neitzel
• Penetration tester and security
researcher at
• Former physicist in the field of
Lattice QCD
• Interested in RPC technologieslike
Java RMI, .NET Remoting, WCF,
Windows RPC, DCOM, …
4. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
What I'm Going to Talk About
• Motivation and Etymology
• Java RMI in a Nutshell
• Java RMI Vulnerabilities
• Application Level Attacks
RMI Codebase Attacks
Remote Binding (Localhost Bypass - CVE-2019-2684)
JEP 290 and the Activation System
JEP 290 Bypasses
Remote Method Guessing and Method Invocation
Application Level Deserialization Attacks
… and how to detect them with remote-method-guesser
5. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Motivation and Etymology
• Project started with the only purpose of guessing
remote methods remote-method-guesser
• Learned a lot about RMI internals during
implementation. Noticed that several vulnerabilities are
missing tool support.
• Idea: Turn remote-method-guesser into a Java RMI
vulnerability scanner
Fig 1 - nmap scan identifying Java RMI endpoints
9. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
Instance
RemoteMath
new RemoteMath()
Listen: 0.0.0.0:4444
ObjID: 1234567890
Server
Fig 2 – Example RMI implementation
exportObject(instance, 4444)
10. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
Client
???
Server
Instance
RemoteMath
Listen: 0.0.0.0:4444
ObjID: 1234567890
11. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
magic version protocol opType objid opNum methodHash methodArgs
RMIMessage Structure:
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
Client
12. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
RMIMessage Structure:
Legacy
(nowadaysalways -1)
Constants
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
Client
version protocol opType objid opNum methodHash methodArgs
magic
13. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
sha(add(II)I)
(int)1
(int)2
RMIMessage Structure:
Legacy
(nowadaysalways -1)
Constants
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
Client
ArgumentTypes: int, int Return Type:int
magic version protocol opType objid opNum methodHash methodArgs
14. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
WhyObjIDs?
Instance
RemoteControl
Listen: 0.0.0.0:4444
ObjID: 0987654321
Allowsmultipleremote objects
to listen on the same port
RMIMessage Structure:
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
Client
version protocol opType objid opNum methodHash methodArgs
magic
15. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
RMIMessage Structure:
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Client Server
version protocol opType objid opNum methodHash methodArgs
magic
16. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
Instance
RMIRegistry
Listen: 0.0.0.0:1099
ObjID: 0
RMIMessage Structure:
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Client Server
version protocol opType objid opNum methodHash methodArgs
magic
17. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
HashMap<String,RemoteObj>
RMIMessage Structure:
Instance
RMIRegistry
Listen: 0.0.0.0:1099
ObjID: 0
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Client Server
version protocol opType objid opNum methodHash methodArgs
magic
18. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
IMath
0.0.0.0:4444:ObjID
math
bind("math", instance)
RMIMessage Structure:
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:1099
ObjID: 0
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Client Server
version protocol opType objid opNum methodHash methodArgs
magic
19. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
IMath 0.0.0.0:4444:ObjID
RMIMessage Structure:
IMath
0.0.0.0:4444:ObjID
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:1099
ObjID: 0
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Client Server
bind("math", instance)
math
IMath stub = lookup("math")
version protocol opType objid opNum methodHash methodArgs
magic
20. #BHUSA @BLACKHATEVENTS
Java RMI in a Nutshell
Tobias Neitzel @qtc_de
stub.add(1,1)
2
Fig 3 – Full RMI call example
RMIMessage Structure:
IMath
0.0.0.0:4444:ObjID
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:1099
ObjID: 0
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Client Server
bind("math", instance)
math
IMath stub = lookup("math")
IMath 0.0.0.0:4444:ObjID
version protocol opType objid opNum methodHash methodArgs
magic
22. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Fig 4 – Example run of remote-method-guesser
• Example run of remote-method-guesser
• Docker images of vulnerable example servers can be found here:
https://github.com/qtc-de/remote-method-guesser/packages/414459
31. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Codebase Attacks
• Accepting client specified codebases was default behaviorup to 2011
• Misconfigurationstill possible (useCodebaseOnly=false), but often not
detected (nmap and metasploit modules focus on 2011 endpoints)
• For fully up to date RMI servers you usually need to know the signature
of a valid remote method or to attackfrom localhost (Details:
https://github.com/qtc-de/remote-method-guesser/blob/master/docs/rmg/actions.md#codebase-action )
Fig 7 – Client side codebase illustration
Source: https://docs.oracle.com/javase/7/docs/technotes/guides/rmi/codebase.html
32. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Codebase Attacks
Fig 8 – useCodebaseOnly detection of remote-method-guesser
• Even with useCodebaseOnly=false, exploitability depends on additionalfactors (SecurityManager)
Status is Non Default instead of Vulnerable
35. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Localhost Bypass(CVE-2019-2684)
• Reminder on how RMI servers bind objects to the RMI registry
Fig 9 – RMI bind operation
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:1099
ObjID: 0
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
bind("math", instance)
math IMath
0.0.0.0:4444:ObjID
36. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Localhost Bypass(CVE-2019-2684)
• Reminder on how RMI servers bind objects to the RMI registry
• But who is actually allowed to bind?
Fig 9 – RMI bind operation
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
Listen: 0.0.0.0:1099
ObjID: 0
bind("math", instance)
math IMath
0.0.0.0:4444:ObjID
37. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Localhost Bypass(CVE-2019-2684)
• Reminder on how RMI servers bind objects to the RMI registry
• But who is actually allowed to bind?
localhost is!
• Each user on the same host as the registry can:
• Not a vulnerability, but by design
Create new bound names
Modify existing bound names
Remove bound names
Fig 9 – RMI bind operation
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
Listen: 0.0.0.0:1099
ObjID: 0
bind("math", instance)
math IMath
0.0.0.0:4444:ObjID
38. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Localhost Bypass(CVE-2019-2684)
• CVE-2019-2684let's you bypass the localhost restriction
39. #BHUSA @BLACKHATEVENTS
• CVE-2019-2684let's you bypass the localhost restriction
• Reminder on the RMI message structure:
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Localhost Bypass(CVE-2019-2684)
RMIMessage Structure:
Legacy
(nowadaysalways -1)
Constants
version protocol opType objid opNum methodHash methodArgs
magic
40. #BHUSA @BLACKHATEVENTS
• CVE-2019-2684let's you bypass the localhost restriction
• Reminder on the RMI message structure:
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Localhost Bypass(CVE-2019-2684)
Still used for remote objects that use Skeletons, e.g. rmi-registry
Fig 10 – Server side implementation of the bind operation.
Access checks are performed in the skeleton only.
https://github.com/openjdk/jdk/blob/master/src/java.rmi/share/classes/sun/rmi/registry/RegistryImpl.java#L247
RMIMessage Structure:
Legacy
(nowadaysalways -1)
Constants
version protocol opType objid opNum methodHash methodArgs
magic
41. #BHUSA @BLACKHATEVENTS
• CVE-2019-2684let's you bypass the localhost restriction
• Reminder on the RMI message structure:
• RMI registry can be called using both:The legacy andthe
moderncall technique
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Localhost Bypass(CVE-2019-2684)
Still used for remote objects that use Skeletons, e.g. rmi-registry
Fig 10 – Server side implementation of the bind operation.
Access checks are performed in the skeleton only.
https://github.com/openjdk/jdk/blob/master/src/java.rmi/share/classes/sun/rmi/registry/RegistryImpl.java#L247
RMIMessage Structure:
Legacy
(nowadaysalways -1)
Constants
version protocol opType objid opNum methodHash methodArgs
magic
42. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
Localhost Bypass(CVE-2019-2684)
Check for legacy call
Handle legacy call via Skeleton – RMI registry calls are
intended to be handled here
Continue with modern call (no skeleton – and no
accesscheckfor the RMI Registry :D)
How Java RMI decided when to use a Skeleton up to 2019
Fig 11 – How Java RMI decided when to use Skeletons
https://github.com/openjdk/jdk/blob/65db4f42d021444a7cff0f83a86a407a41b16da5/
src/java.rmi/share/classes/sun/rmi/server/UnicastServerRef.java#L279
47. #BHUSA @BLACKHATEVENTS
• Reminder on the RMI message structure:
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290
RMIMessage Structure:
magic version protocol opType objid opNum methodHash methodArgs
48. #BHUSA @BLACKHATEVENTS
• Reminder on the RMI message structure:
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290
RMIMessage Structure:
int short byte int
long - int - long - short
Compound:
int long
Serialized Java Objects
magic version protocol opType objid opNum methodHash methodArgs
49. #BHUSA @BLACKHATEVENTS
• Reminder on the RMI message structure:
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290
RMIMessage Structure:
int short byte int
long - int - long - short
Compound:
int long
Fig 14 - How RMI arguments are unmarshaled (before 2020)
read by the RMI server
https://github.com/openjdk/jdk/blob/3789983e89c9de252ef546a1b98a732a7d066650
/src/java.rmi/share/classes/sun/rmi/server/UnicastRef.java#L298
magic version protocol opType objid opNum methodHash methodArgs
Serialized Java Objects
50. #BHUSA @BLACKHATEVENTS
• Reminder on the RMI message structure:
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290
RMIMessage Structure:
int short byte int
long - int - long - short
Compound:
int long
Fig 14 - How RMI arguments are unmarshaled (before 2020)
read by the RMI server
Deserialization Attacks
https://github.com/openjdk/jdk/blob/3789983e89c9de252ef546a1b98a732a7d066650
/src/java.rmi/share/classes/sun/rmi/server/UnicastRef.java#L298
magic version protocol opType objid opNum methodHash methodArgs
Serialized Java Objects
51. #BHUSA @BLACKHATEVENTS
• Reminder on the RMI message structure:
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290
RMIMessage Structure:
int short byte int
long - int - long - short
Compound:
int long
read by the RMI server
Fig 15 - How RMI arguments are unmarshaled (after 2020)
No Deserialization Attacks on Strings after 2020
https://github.com/openjdk/jdk/blob/master/src/java.rmi/share/classes/sun/rmi/server/UnicastRef.java#L302
magic version protocol opType objid opNum methodHash methodArgs
Serialized Java Objects
52. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290
• Deserializationattack surface on default remote objects:
RMI Registry
publicvoid bind(Stringname, Remoteobj)
publicvoid rebind(Stringname, Remote obj)
publicvoid unbind(Stringname)
publicString[] list()
publicRemote lookup(Stringname)
DistributedGarbageCollector (DGC – Availableon each RMI endpoint)
publicLease dirty(ObjID[] ids, longsequenceNum, Leaselease))
publicvoid clean( ObjID[] ids, long sequenceNum, VMID vmid, boolean strong)
RMI Activator(ActivationSystem – Legacy Component)
publicMarshalledObject activate(ActivationID id, boolean force)
Usable for deserialization attacks
Usable for deserialization attacks before 2020
Only callable from localhost
Callable from everywhere
Argument Legend Method Legend
53. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290
• JEP 290 introduced deserializationfilters for the RMI Registry andthe DGC(not for the ActivationSystem)
• Corresponding remote-method-guesser enumeration:
Fig 16 – JEP290 and Activation System enumeration of remote-method-guesser
56. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
• The RMI Registry andthe DGCallow certain classes to get deserialized
Filter bypasses are possible
Java RMI Vulnerabilities
JEP 290 Bypasses
57. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290 Bypasses
• The RMI Registry andthe DGCallow certain classes to get deserialized
Filter bypasses are possible
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:1099
ObjID: 0
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
Client
bind("math", instance)
math IMath
0.0.0.0:4444:ObjID
IMath stub = lookup("math")
IMath 0.0.0.0:4444:ObjID
58. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290 Bypasses
lookup(ysoserial.Payload)
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
Client
• The RMI Registry andthe DGCallow certain classes to get deserialized
Filter bypasses are possible
Listen: 0.0.0.0:1099
ObjID: 0
bind("math", instance)
math
Exception
IMath
0.0.0.0:4444:ObjID
59. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290 Bypasses
Protected
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
Client
• The RMI Registry andthe DGCallow certain classes to get deserialized
Filter bypasses are possible
Listen: 0.0.0.0:1099
ObjID: 0
bind("math", instance)
math
lookup(ysoserial.Payload)
Filtered!
IMath
0.0.0.0:4444:ObjID
60. #BHUSA @BLACKHATEVENTS
Exception
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290 Bypasses
Protected
JRMP Request
ysoserial.payload
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Server
Client
• The RMI Registry andthe DGCallow certain classes to get deserialized
Filter bypasses are possible
Fig 17 - Outbound JRMP Bypass Schema
Listen: 0.0.0.0:1099
ObjID: 0
bind("math", instance)
math
lookup(bypass.Payload)
IMath
0.0.0.0:4444:ObjID
61. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Java RMI Vulnerabilities
JEP 290 Bypasses
• The RMI Registry andthe DGCallow certain classes to get deserialized
Filter bypasses are possible
• JEP290 bypass enumeration – Uses the most recent bypass technique (discovered by @_tint0 in 2019):
Fig 18 – JEP290 bypass enumeration of remote-method-guesser
64. #BHUSA @BLACKHATEVENTS
Remote Method Guessing
Tobias Neitzel @qtc_de
int add(intarg1, int arg2)
SupportedMethods:
• Reminder: Typical RMI call example
stub.add(1,1)
2
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:1099
ObjID: 0
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Client Server
bind("math", instance)
math IMath
0.0.0.0:4444:ObjID
IMath stub = lookup("math")
IMath 0.0.0.0:4444:ObjID
65. #BHUSA @BLACKHATEVENTS
Remote Method Guessing
Tobias Neitzel @qtc_de
• Reminder: Typical RMI call example
• During blackbox assessments, you don’t know supported methods
???
SupportedMethods:
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Client Server
bind("math", instance)
math
int add(intarg1, int arg2)
Listen: 0.0.0.0:1099
ObjID: 0
IMath
0.0.0.0:4444:ObjID
IMath stub = lookup("math")
IMath 0.0.0.0:4444:ObjID
66. #BHUSA @BLACKHATEVENTS
Remote Method Guessing
Tobias Neitzel @qtc_de
• Reminder: Typical RMI call example
• During blackbox assessments, you don’t know supported methods
• You may miss dangerous remote methods ???
int add(intarg1, int arg2)
String exec(String cmd)
SupportedMethods:
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Client Server
bind("math", instance)
math
Listen: 0.0.0.0:1099
ObjID: 0
IMath
0.0.0.0:4444:ObjID
IMath stub = lookup("math")
IMath 0.0.0.0:4444:ObjID
67. #BHUSA @BLACKHATEVENTS
Remote Method Guessing
Tobias Neitzel @qtc_de
Fig 19 – Method Guessing Schema
• Reminder: Typical RMI call example
• During blackbox assessments, you don’t know supported methods
• You may miss dangerous remote methods
• Method Guessing:
stub.exec(canary)
SupportedMethods:
HashMap<String,RemoteObj>
Instance
RMIRegistry
Listen: 0.0.0.0:4444
ObjID: 1234567890
Instance
RemoteMath
Client Server
Exception
exists not exists
bind("math", instance)
math
Listen: 0.0.0.0:1099
ObjID: 0
int add(intarg1, int arg2)
String exec(String cmd)
IMath
0.0.0.0:4444:ObjID
IMath stub = lookup("math")
IMath 0.0.0.0:4444:ObjID
68. #BHUSA @BLACKHATEVENTS
Tobias Neitzel @qtc_de
Fig 20 – Method guessing run of remote-method-guesser
https://github.com/qtc-de/remote-method-guesser/blob/master/docs/rmg/method-guessing.md
Remote Method Guessing
69. #BHUSA @BLACKHATEVENTS
• You may want to call methods in a regular way (e.g. String exec(String cmd) )
• remote-method-guessercando that:
• Server responses are ignored by default. Plugins can be used to process them:
https://github.com/qtc-de/remote-method-guesser/blob/master/docs/rmg/plugin-system.md
Tobias Neitzel @qtc_de
Fig 21 – Calling a method with remote-method-guesser
Remote Method Guessing