GitBook: [master] 2 pages modified

This commit is contained in:
CPol 2021-06-18 17:11:21 +00:00 committed by gitbook-bot
parent a898ca5db7
commit cceaaecbd9
No known key found for this signature in database
GPG key ID: 07D2180C7B12D0FF
2 changed files with 162 additions and 11 deletions

View file

@ -287,7 +287,7 @@
* [873 - Pentesting Rsync](pentesting/873-pentesting-rsync.md)
* [1026 - Pentesting Rusersd](pentesting/1026-pentesting-rusersd.md)
* [1080 - Pentesting Socks](pentesting/1080-pentesting-socks.md)
* [1098/1099 - Pentesting Java RMI](pentesting/1099-pentesting-java-rmi.md)
* [1098/1099/1050 - Pentesting Java RMI - RMI-IIOP](pentesting/1099-pentesting-java-rmi.md)
* [1433 - Pentesting MSSQL - Microsoft SQL Server](pentesting/pentesting-mssql-microsoft-sql-server.md)
* [1521,1522-1529 - Pentesting Oracle TNS Listener](pentesting/1521-1522-1529-pentesting-oracle-listener/README.md)
* [Oracle Pentesting requirements installation](pentesting/1521-1522-1529-pentesting-oracle-listener/oracle-pentesting-requirements-installation.md)

View file

@ -1,4 +1,4 @@
# 1098/1099 - Pentesting Java RMI
# 1098/1099/1050 - Pentesting Java RMI - RMI-IIOP
## Basic Information
@ -84,12 +84,53 @@ public class RmiClient {
}
```
## Enumeration
## RMI Enumeration
Based on the fact that arbitrary java is being treated in a different Java VM, this may allow an attacker to **deserialize** a **payload** in this Java instance and **execute arbitrary code**.
The default configuration of `rmiregistry`allows loading classes from remote URLs, which can lead to remote code execution.
RMI registries do **not disclose a list of available method signatures**, but if you can guess it, you can invoke it. Therefore, a good approach to abuse this service is to **brute-force** the available method signatures.
RMI methods are usually interesting as a bunch of them will **deserialize the received data** making them vulnerable to [**Java Insecure Deserialization**](../pentesting-web/deserialization/#java-http) and granting RCE to an attacker.
**Basically this service could allow you to execute code.**
### RMI Method Signatures
\(Research taken from [https://labs.bishopfox.com/tech-blog/rmiscout](https://labs.bishopfox.com/tech-blog/rmiscout)\)
To execute remote methods, Java RMI clients submit a 64-bit hash of the method signature, which the server uses to identify the corresponding server-side method. These **hashes are computed** with the following logic:
1. **Source code** representation of the signature:
`void myRemoteMethod(int count, Object obj, boolean flag)`
2. **Bytecode** representation of signature:
`myRemoteMethod(ILjava/lang/Object;Z)V`
3. Method Hash: **big-endian representation of first 8 bytes of the SHA1 of the signature**:
`Hash = SHA1String(“myRemoteMethod(ILjava/lang/Object;Z)V”).substring(0,8).reverse()`
As shown above, the information that is used to compute a method hash are: **the method name, the return types, and an ordered list of the fully qualified names of the parameters types**. Instead of brute-forcing the 64-bit keyspace, we can use wordlists for each of these categories to guess common signatures. Using [GitGot](https://labs.bishopfox.com/blog/gitgot-tool-release), I scraped GitHub for RMI interfaces in open source projects and found interesting patterns across the 15,000+ method signatures:
![Distribution of return types of 15,000 functions sampled from RMI interfaces on GitHub](https://labs.bishopfox.com/hs-fs/hubfs/200508-content-image-pie-chart-RMIScout.png?width=617&name=200508-content-image-pie-chart-RMIScout.png)
_**Figure 2**: Distribution of return types of 15,000 functions sampled from RMI interfaces on GitHub_
As shown above, using `int`, `boolean`, and `void` as our guessed return types gives us a 61.4% chance of guessing correctly based off this data. If we add `java.lang.String`, we can add an extra 9.8% to our probable success, as it represents nearly a third of the non-primitive return types. That brings our final list of candidate return types \(`int`, `boolean`, `void`, `String`\) to a 71.22% probability in the observed dataset.
RMIScout includes a deduped wordlist of prototypes \(`prototypes.txt`\) found from this exploration and it also includes a list of most frequently occurring method names \(`methods.txt`\).
### Brute-Forcing Signatures
To **identify RMI functions without executing them**, RMIScout leverages low-level JRE RMI functions and uses dynamic class generation to send RMI invocations with **deliberately mismatched types** to trigger **`RemoteExceptions`**. These exceptions allow us to identify remote methods without actually invoking them.
To accomplish this, RMIScout **computes the method hash** using the original user-supplied types, but substitutes the values of all parameters for an instance of a **dynamically generated, serializable clas**s. The class is generated with a **random 255-character name** \(the underlying assumption being that this random name does not exist in the remote class path\). For example:
**Candidate Remote Interface:**
`void login(String user, String password)`
**RMIScout will invoke:**
`login((String) new QUjsdg83..255 chars..(), (String) new QUjsdg83..255 chars..())`
If the **RMI method is present**, it will attempt to **unmarshal** the parameters. This will result in a **remote exception** disclosed to the client. Specifically, itll be a `java.rmi.UnmarshalException` either caused by a `ClassNotFoundException` \(due to our non-existent random class\) or by other exceptions \(finding object-typed data when primitive-typed data was expected in the stream\) **without invoking the underlying method.**
I was **not able to discover a method for identifying parameter-less methods without invoking them**. As such, by default void argument prototypes are skipped by RMIScout unless the option `--allow-unsafe` is used. **Note: `--allow-unsafe`** will cause parameter-less methods to **be invoked on discovery**, which can lead to unexpected and possibly destructive behavior on the remote server.
### Automatic Enumeration
```bash
msf> use auxiliary/scanner/misc/java_rmi_server
@ -97,20 +138,130 @@ msf> use auxiliary/gather/java_rmi_registry
nmap -sV --script "rmi-dumpregistry or rmi-vuln-classloader" -p <PORT> <IP>
```
### RMI methods enumeration
[https://github.com/BishopFox/rmiscout](https://github.com/BishopFox/rmiscout) to explore and try to find RCE vulnerabilities.
[https://github.com/NickstaDB/BaRMIe](https://github.com/NickstaDB/BaRMIe) to enumerate and attack
[https://github.com/siberas/sjet](https://github.com/siberas/sjet) allows an easy exploitation of insecure configured JMX services \(I tried and It gave me `Error: Can't connect to remote service` let me know if you know how to fix this issue\).
## Reverse Shell
### MSF
### Reverse Shell
```bash
msf> use exploit/multi/misc/java_rmi_server
```
## RMI-IIOP
> **RMI-IIOP** \(read as "RMI over IIOP"\) denotes the [Java Remote Method Invocation](https://en.wikipedia.org/wiki/Java_Remote_Method_Invocation) \(RMI\) interface over the [Internet Inter-Orb Protocol](https://en.wikipedia.org/wiki/Internet_Inter-Orb_Protocol) \(IIOP\), which delivers [Common Object Request Broker Architecture](https://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture) \(CORBA\) [distributed computing](https://en.wikipedia.org/wiki/Distributed_computing) capabilities to the Java platform. It was initially based on two specifications: the Java Language Mapping to OMG IDL, and CORBA/IIOP 2.3.1
>
> With features inherited from CORBA, software components that work together can be written in multiple computer languages and run on multiple computers. In other words, it supports multiple platforms and can make remote procedure calls to execute, subroutines on another computer as defined by RMI.
> Description from [here](https://en.wikipedia.org/wiki/RMI-IIOP).
\(Research from [https://labs.bishopfox.com/tech-blog/lessons-learned-on-brute-forcing-rmi-iiop-with-rmiscout](https://labs.bishopfox.com/tech-blog/lessons-learned-on-brute-forcing-rmi-iiop-with-rmiscout)\)
Unlike standard Java RMI \(aka RMI-JRMP\) services that are identified by a method hash, Java Method invocation over the CORBA Internet Inter-Orb Protocol \(RMI-IIOP\) uses **two different algorithms to identify method signatures**:
1. For **non-overloaded methods**, the signature is just the **method name represented as a string**. Parameter types, the number of parameters, and return type are all disregarded.
2. For **overloaded methods** \(methods sharing the same name\), RMI-IIOP uses a concatenated string with the **method name and its respective ordered types** \(examples below\).
Lets take a look at an example interface and a decompiled RMI-IIOP stub. Here is an excerpt of the remote interface from the RMIScout demo:
```java
public int add(int paramInt1, int paramInt2) throws RemoteException;
public String sayTest19(int paramInt) throws RemoteException;
public String sayTest19(List paramList1, List paramList2) throws RemoteException;
public String sayTest19(List[] paramArrayOfList, int paramInt) throws RemoteException;
public Object sayTest20(String paramString) throws RemoteException;
```
First lets look at the **`add(int,int)`** method. Since its **method name is unique**, the **generated stub** is simply the **method** **name**. The server compares the clients requested method \(`paramString` in the figure below\) against a string literal.
Because this method only uses primitive parameter types, the compiled stub has no type safety. The server will perform two 8-byte reads and interpret the bytes as long integers. **For brute-forcing, the lack of type safety makes it impossible to know if we guessed the correct types**. Furthermore, any additional input from the client is disregarded, thus preventing safe identification via an error for too many supplied parameters:
```java
if (paramString.equals("add"))
{
int m = localInputStream.read_long();
i2 = localInputStream.read_long();
int i3 = localCorbaImpl.add(m, i2);
localObject9 = paramResponseHandler.createReply();
((org.omg.CORBA.portable.OutputStream)localObject9).write_long(i3);
return (org.omg.CORBA.portable.OutputStream)localObject9;
}
```
Now, lets look at the **overloaded `sayTest19` methods**. Here, the CORBA stub compiler **appends the signature with information about the types to differentiate between the overloaded method names**. Some naming schemes are more intuitive than others. In this case, we are provided type safety by the signature itself:
```java
if (paramString.equals("sayTest19__long"))
{
int n = localInputStream.read_long();
localObject6 = localCorbaImpl.sayTest19(n);
localObject8 = (org.omg.CORBA_2_3.portable.OutputStream)paramResponseHandler.createReply();
((org.omg.CORBA_2_3.portable.OutputStream)localObject8).write_value((Serializable)localObject6, String.class);
return (org.omg.CORBA.portable.OutputStream)localObject8;
}
if (paramString.equals("sayTest19__java_util_List__java_util_List"))
{
localObject3 = (List)localInputStream.read_value(List.class);
localObject6 = (List)localInputStream.read_value(List.class);
localObject8 = localCorbaImpl.sayTest19((List)localObject3, (List)localObject6);
localObject9 = (org.omg.CORBA_2_3.portable.OutputStream)paramResponseHandler.createReply();
((org.omg.CORBA_2_3.portable.OutputStream)localObject9).write_value((Serializable)localObject8, String.class);
return (org.omg.CORBA.portable.OutputStream)localObject9;
}
if (paramString.equals("sayTest19__org_omg_boxedRMI_java_util_seq1_List__long"))
{
localObject2 = (List[])localInputStream.read_value(new List[0].getClass());
i2 = localInputStream.read_long();
localObject7 = localCorbaImpl.sayTest19((List[])localObject2, i2);
localObject9 = (org.omg.CORBA_2_3.portable.OutputStream)paramResponseHandler.createReply();
((org.omg.CORBA_2_3.portable.OutputStream)localObject9).write_value((Serializable)localObject7, String.class);
return (org.omg.CORBA.portable.OutputStream)localObject9;
}
```
And for **`sayTest20(String)`**, we again have a **unique method nam**e, but here we are **deserializing** a **`String`** class. In this case, the complex parameter allows us to **force a `ClassCastException` to allow identification without invocation**.
```javascript
if (paramString.equals("sayTest20"))
{
localObject1 = (String)localInputStream.read_value(String.class);
localObject4 = localCorbaImpl.sayTest20((String)localObject1);
localObject7 = paramResponseHandler.createReply();
Util.writeAny((org.omg.CORBA.portable.OutputStream)localObject7, localObject4);
return (org.omg.CORBA.portable.OutputStream)localObject7;
}
```
So, what does this mean for safely brute-forcing RMI-IIOP stubs? Overall, its a significantly smaller keyspace; **most of the time we will only need to get the name of the method correct**. That said, we will likely **accidentally invoke methods that only use primitives**, and we wont always know the true method signature.
### RMI-IIOP Brute-forcing Limitations
**1. We can't identify methods solely using primitive typed parameters without invoking the method**
This is because there is no concept of type checking in the generated stubs, any values sent along will be deserialized and cast to the expected primitive \(as seen in the `add(int, int)` example above\). **Unlike RMI-JRMP, primitives are not up-cast to an `Object`-derived type, upcasting throws a `ClassCastException` instead of execution.**
**2. We can't identify the maximum number or types of parameters**
If a method is **not overloaded**, we will only have an **exception if there is a `ClassCastException` when deserializing a parameter or an unexpected `EOFException` because of insufficient parameters**. Extra parameters in the input stream will just be ignored.
**3. We can't identify the return types**
**Return types are not included** in any part of the signature matching, so theres no guaranteed way to identify the return type. If its an `Object`-derived type, we may get a local `ClassCastException` if RMIScout attempts to deserialize an incorrect typed response \(invoke mode\), but for primitives, we wont know.
**4. We have to send two requests for every check**
**RMIScout needs to test both possible signature formats** because the overloaded methods use a distinct alternative format.
**5. We need to use JRE8 to successfully use RMIScout's RMI-IIOP functionality**
JRE9 stripped out RMI-IIOP functionality, so to run these tests and take advantage of existing standard library code, **we need to use JRE8**.
**Overall, there is a risk of accidental invocation in brute-forcing these signatures**. As such, RMIScout displays a warning prior to running IIOP brute-forcing. However, it is also significantly easier to enumerate signatures for IIOP. Using **custom wordlists with method names least likely to cause harm is recommended** \(e.g., a method name like `deleteRecord` may match against `deleteRecord(int)` whereas `evaluateString` is less likely to match a primitive\).
We can still achieve arbitrary Java deserialization by replacing object or array types in a method signature. **Unlike RMI-JRMP, `String` types can still be exploited in RMI-IIOP servers compiled with the latest build of the JDK8.**
## Shodan
* `port:1099 java`