Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare v4.2.0 release #24

Merged
merged 5 commits into from
Dec 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [4.2.0] - Dec 30, 2021

### Changed

* *SSRF* payloads are now created using the *SingleOpProtocol* by default.
The ``--stream-protocol`` option can be used to create *SSRF* payloads using
the *Stream Protocol*.
* Updated test cases.


## [4.1.0] - Dec 23, 2021

### Added
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<artifactId>remote-method-guesser</artifactId>
<name>remote-method-guesser</name>
<packaging>jar</packaging>
<version>4.1.0</version>
<version>4.2.0</version>
<description>Identify common misconfigurations on Java RMI endpoints</description>

<properties>
Expand Down
6 changes: 6 additions & 0 deletions resources/bash_completion.d/rmg
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ function _rmg() {
opts="$opts --gopher"
opts="$opts --ssrf"
opts="$opts --ssrf-response"
opts="$opts --stream-protocol"
opts="$opts --encode"
opts="$opts --raw"
opts="$opts --bind-objid"
Expand Down Expand Up @@ -89,6 +90,7 @@ function _rmg() {
opts="$opts --gopher"
opts="$opts --ssrf"
opts="$opts --ssrf-response"
opts="$opts --stream-protocol"
opts="$opts --encode"
opts="$opts --raw"
opts="$opts --config"
Expand Down Expand Up @@ -125,6 +127,7 @@ function _rmg() {
opts="$opts --gopher"
opts="$opts --ssrf"
opts="$opts --ssrf-response"
opts="$opts --stream-protocol"
opts="$opts --encode"
opts="$opts --raw"
opts="$opts --position"
Expand Down Expand Up @@ -173,6 +176,7 @@ function _rmg() {
opts="$opts --gopher"
opts="$opts --ssrf"
opts="$opts --ssrf-response"
opts="$opts --stream-protocol"
opts="$opts --encode"
opts="$opts --raw"
opts="$opts --localhost-bypass"
Expand Down Expand Up @@ -363,6 +367,7 @@ function _rmg() {
opts="$opts --gopher"
opts="$opts --ssrf"
opts="$opts --ssrf-response"
opts="$opts --stream-protocol"
opts="$opts --encode"
opts="$opts --raw"
opts="$opts --position"
Expand Down Expand Up @@ -400,6 +405,7 @@ function _rmg() {
opts="$opts --gopher"
opts="$opts --ssrf"
opts="$opts --ssrf-response"
opts="$opts --stream-protocol"
opts="$opts --encode"
opts="$opts --raw"
opts="$opts --localhost-bypass"
Expand Down
1 change: 1 addition & 0 deletions src/config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ ssrf = false
srfresponse =
ssrf_encode = false
ssrf_raw = false
ssrf_stream_protocol = false

bind_objid = [6633018:17cb5d1bb57:-7ff8, -8114172517417646722]
bind_bypass = false
Expand Down
1 change: 1 addition & 0 deletions src/de/qtc/rmg/internal/RMGOption.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public enum RMGOption {
SSRFRESPONSE("--ssrf-response", "evaluate ssrf response from the server", Arguments.store(), RMGOptionGroup.SSRF, "hex"),
SSRF_ENCODE("--encode", "double URL encode the SSRF payload", Arguments.storeTrue(), RMGOptionGroup.SSRF),
SSRF_RAW("--raw", "print payload without color and without additional text", Arguments.storeTrue(), RMGOptionGroup.SSRF),
SSRF_STREAM_PROTOCOL("--stream-protocol", "use the stream protocol instead of single operation", Arguments.storeTrue(), RMGOptionGroup.SSRF),

BIND_OBJID("--bind-objid", "ObjID of the bound object.", Arguments.store(), RMGOptionGroup.ACTION, "objid"),
BIND_ADDRESS("bind-host", "host specifications the bound remote object should point to", Arguments.store(), RMGOptionGroup.ACTION, "host:port"),
Expand Down
59 changes: 59 additions & 0 deletions src/de/qtc/rmg/io/SingleOpOutputStream.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package de.qtc.rmg.io;

import java.io.ByteArrayOutputStream;

import de.qtc.rmg.internal.ExceptionHandler;

/**
* The SingleOpOutputStream class is used during SSRF operations. When the SSRF option is used,
* remote-method-guesser collects output data into an byte array instead of sending it to a remote
* server. The corresponding RMI calls always use the stream protocol, which is not ideal for SSRF
* attacks. The SingleOpOutputStream abuses the fact that Java RMI calls the flush method on the
* stream directly before and after the handshake that is performed within the stream protocol.
* This allows to cleanly cutoff the handshake and to switch the contents of the resulting byte
* array to the single operation protocol.
*
* @author Tobias Neitzel (@qtc_de)
*/
public class SingleOpOutputStream extends ByteArrayOutputStream {

private int flushCount;

public SingleOpOutputStream() {
super();
flushCount = 0;
}

/**
* Java RMI calls the flush method before and after the handshake. During the first call, only the
* RMI magic, the protocol version and the protocol type are contained in the stream. After the
* second call, the client host and client port are contained. Afterwards, the handshake has completed
* and the RMI communication starts.
*/
public synchronized void write(byte[] b, int off, int len)
{
switch( flushCount++ ) {

case 0:

if( b[len - 1] != 0x4b )
ExceptionHandler.internalError("SingleOpOutputStream.write", "invalid protocol type");

b[len - 1] = 0x4c;
break;

case 1:

return;

case 2:

if( b[0] != 0x50 )
ExceptionHandler.internalError("SingleOpOutputStream.write", "invalid operation type");

break;
}

super.write(b, off, len);
}
}
30 changes: 28 additions & 2 deletions src/de/qtc/rmg/networking/SSRFResponseSocket.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package de.qtc.rmg.networking;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import de.qtc.rmg.io.DevNullOutputStream;
import sun.rmi.transport.TransportConstants;

/**
* Socket implementation that prevents outputs from being send anywhere and that simulates input
Expand Down Expand Up @@ -34,18 +37,41 @@
*/
public class SSRFResponseSocket extends Socket {

private int port;
private String host;
private byte[] content;

private int count = 0;

public SSRFResponseSocket(byte[] response)
public SSRFResponseSocket(String host, int port, byte[] response)
{
this.host = host;
this.port = port;
this.content = response;
}

/**
* Before the input stream is returned, we compare the first byte of the response
* to the TransportConstants.Return value. If it matches, the response was created by a
* single operation protocol request. In this case we need to prefix the response with
* a fake-handshake to simulate the response from a stream protocol request.
*/
@SuppressWarnings("restriction")
public InputStream getInputStream() throws IOException
{
return new ByteArrayInputStream(content);
ByteArrayOutputStream ibos = new ByteArrayOutputStream();

if( content[0] == TransportConstants.Return ) {

ibos.write(TransportConstants.ProtocolAck);

DataOutputStream dos = new DataOutputStream(ibos);
dos.writeUTF(host);
dos.writeInt(port);
}

ibos.write(content);
return new ByteArrayInputStream(ibos.toByteArray());
}

public OutputStream getOutputStream()
Expand Down
2 changes: 1 addition & 1 deletion src/de/qtc/rmg/networking/SSRFResponseSocketFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public SSRFResponseSocketFactory(byte[] content)
@Override
public Socket createSocket(String host, int port) throws IOException
{
return new SSRFResponseSocket(content);
return new SSRFResponseSocket(host, port, content);
}

@Override
Expand Down
15 changes: 12 additions & 3 deletions src/de/qtc/rmg/networking/SSRFSocket.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import de.qtc.rmg.internal.ExceptionHandler;
import de.qtc.rmg.internal.RMGOption;
import de.qtc.rmg.io.Logger;
import de.qtc.rmg.io.SingleOpOutputStream;
import de.qtc.rmg.utils.RMGUtils;
import sun.rmi.server.MarshalOutputStream;
import sun.rmi.transport.TransportConstants;
Expand Down Expand Up @@ -78,12 +79,20 @@ public InputStream getInputStream() throws IOException

/**
* Simulate an OutputStream that is connected to an RMI server. Instead of sending
* anything, collect all data in a byte array.
* anything, collect all data in a byte array. If the SSRF_SINGLEOP option was used,
* we choose an SingleOpOutputStream. This stream inspects data written to it and
* modifies stream protocol messages to single operation protocol messages.
*/
public OutputStream getOutputStream()
{
if( bos == null )
bos = new ByteArrayOutputStream();
if( bos == null ) {

if( RMGOption.SSRF_STREAM_PROTOCOL.getBool() )
bos = new ByteArrayOutputStream();

else
bos = new SingleOpOutputStream();
}

return bos;
}
Expand Down
7 changes: 7 additions & 0 deletions src/de/qtc/rmg/operations/Operation.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public enum Operation {
RMGOption.SSRFRESPONSE,
RMGOption.SSRF_ENCODE,
RMGOption.SSRF_RAW,
RMGOption.SSRF_STREAM_PROTOCOL,
RMGOption.BIND_BOUND_NAME,
RMGOption.BIND_BYPASS,
RMGOption.BIND_OBJID,
Expand All @@ -61,6 +62,7 @@ public enum Operation {
RMGOption.SSRFRESPONSE,
RMGOption.SSRF_ENCODE,
RMGOption.SSRF_RAW,
RMGOption.SSRF_STREAM_PROTOCOL,
RMGOption.CALL_ARGUMENTS,
}),

Expand All @@ -82,6 +84,7 @@ public enum Operation {
RMGOption.SSRFRESPONSE,
RMGOption.SSRF_ENCODE,
RMGOption.SSRF_RAW,
RMGOption.SSRF_STREAM_PROTOCOL,
RMGOption.CODEBASE_URL,
RMGOption.CODEBASS_CLASS,
RMGOption.ARGUMENT_POS,
Expand All @@ -104,6 +107,7 @@ public enum Operation {
RMGOption.SSRFRESPONSE,
RMGOption.SSRF_ENCODE,
RMGOption.SSRF_RAW,
RMGOption.SSRF_STREAM_PROTOCOL,
RMGOption.DGC_METHOD,
RMGOption.REG_METHOD,
}),
Expand Down Expand Up @@ -174,6 +178,7 @@ public enum Operation {
RMGOption.SSRFRESPONSE,
RMGOption.SSRF_ENCODE,
RMGOption.SSRF_RAW,
RMGOption.SSRF_STREAM_PROTOCOL,
RMGOption.BIND_BOUND_NAME,
RMGOption.BIND_BYPASS,
RMGOption.BIND_OBJID,
Expand Down Expand Up @@ -227,6 +232,7 @@ public enum Operation {
RMGOption.SSRFRESPONSE,
RMGOption.SSRF_ENCODE,
RMGOption.SSRF_RAW,
RMGOption.SSRF_STREAM_PROTOCOL,
RMGOption.ARGUMENT_POS,
RMGOption.GADGET_NAME,
RMGOption.GADGET_CMD,
Expand All @@ -245,6 +251,7 @@ public enum Operation {
RMGOption.SSRFRESPONSE,
RMGOption.SSRF_ENCODE,
RMGOption.SSRF_RAW,
RMGOption.SSRF_STREAM_PROTOCOL,
RMGOption.BIND_BOUND_NAME,
RMGOption.BIND_BYPASS,
});
Expand Down
Loading