Skip to content

Commit

Permalink
Merge pull request #24 from qtc-de/develop
Browse files Browse the repository at this point in the history
Prepare v4.2.0 release
  • Loading branch information
qtc-de authored Dec 30, 2021
2 parents 7f87697 + 0f2785d commit eb26533
Show file tree
Hide file tree
Showing 13 changed files with 636 additions and 27 deletions.
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

0 comments on commit eb26533

Please sign in to comment.