├── .gitignore ├── BappDescription.html ├── BappManifest.bmf ├── README.md ├── burp-send-to-extension ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ └── main │ ├── java │ ├── burp │ │ ├── BurpExtender.java │ │ ├── Cookie.java │ │ ├── IRequestInfoWrapper.java │ │ ├── IRequestResponseHolder.java │ │ ├── IResponseInfoWrapper.java │ │ ├── RequestInfoWrapper.java │ │ ├── RequestResponseHolder.java │ │ └── ResponseInfoWrapper.java │ ├── com │ │ └── google │ │ │ └── gson │ │ │ └── typeadapters │ │ │ └── RuntimeTypeAdapterFactory.java │ └── net │ │ └── bytebutcher │ │ └── burpsendtoextension │ │ ├── builder │ │ └── CommandBuilder.java │ │ ├── executioner │ │ └── CommandExecutioner.java │ │ ├── gui │ │ ├── CommandsChangeListener.java │ │ ├── SendToAddAdvancedDialog.form │ │ ├── SendToAddAdvancedDialog.java │ │ ├── SendToAddAdvancedPlaceholderBehaviourPanel.form │ │ ├── SendToAddAdvancedPlaceholderBehaviourPanel.java │ │ ├── SendToAddDialog.form │ │ ├── SendToAddDialog.java │ │ ├── SendToContextMenu.java │ │ ├── SendToContextMenuItem.java │ │ ├── SendToPreviewDialog.form │ │ ├── SendToPreviewDialog.java │ │ ├── SendToRunInTerminalBehaviourChoiceDialog.form │ │ ├── SendToRunInTerminalBehaviourChoiceDialog.java │ │ ├── SendToTab.form │ │ ├── SendToTab.java │ │ ├── SendToTabSettingsContextMenu.java │ │ ├── SendToTable.java │ │ ├── SendToTableListener.java │ │ ├── action │ │ │ └── SendToContextMenuItemAction.java │ │ ├── listener │ │ │ └── ToolTipActionListener.java │ │ └── util │ │ │ ├── DialogUtil.java │ │ │ ├── SelectionUtil.java │ │ │ └── WebUtil.java │ │ ├── models │ │ ├── CommandObject.java │ │ ├── Config.java │ │ ├── Context.java │ │ ├── ERunInTerminalBehaviour.java │ │ ├── ERuntimeBehaviour.java │ │ ├── Placeholders.java │ │ └── placeholder │ │ │ ├── AbstractPlaceholder.java │ │ │ ├── AbstractRequestInfoPlaceholder.java │ │ │ ├── AbstractRequestPlaceholder.java │ │ │ ├── AbstractRequestResponseInfoPlaceholder.java │ │ │ ├── AbstractRequestResponsePlaceholder.java │ │ │ ├── AbstractRequestResponsePlaceholderBase.java │ │ │ ├── CookiesPlaceholder.java │ │ │ ├── HostPlaceholder.java │ │ │ ├── HttpBodyToFilePlaceholder.java │ │ │ ├── HttpContentLengthPlaceholder.java │ │ │ ├── HttpHeadersToFilePlaceholder.java │ │ │ ├── HttpMethodPlaceholder.java │ │ │ ├── HttpRequestResponsePlaceholder.java │ │ │ ├── HttpStatusCodePlaceholder.java │ │ │ ├── IPlaceholder.java │ │ │ ├── IPlaceholderParser.java │ │ │ ├── IPlaceholderParserFactory.java │ │ │ ├── PortPlaceholder.java │ │ │ ├── ProtocolPlaceholder.java │ │ │ ├── SelectedTextPlaceholder.java │ │ │ ├── SelectedTextToFilePlaceholder.java │ │ │ ├── UrlPathPlaceholder.java │ │ │ ├── UrlPlaceholder.java │ │ │ ├── UrlQueryPlaceholder.java │ │ │ └── behaviour │ │ │ ├── CommandSeparatedPlaceholderBehaviour.java │ │ │ ├── FileSeparatedPlaceholderBehaviour.java │ │ │ ├── IPlaceholderBehaviour.java │ │ │ └── StringSeparatedPlaceholderBehaviour.java │ │ └── utils │ │ ├── OsUtils.java │ │ └── StringUtils.java │ └── resources │ ├── panel_help.png │ ├── panel_help_highlighted.png │ ├── panel_settings.png │ └── panel_settings_highlighted.png └── images ├── burp-send-to-extension-add-edit-dialog.png ├── burp-send-to-extension-advanced-dialog.png ├── burp-send-to-extension-context-menu-repeater.png ├── burp-send-to-extension-context-menu-target-sitemap.png ├── burp-send-to-extension-forkbomb.png ├── burp-send-to-extension-intro.png ├── burp-send-to-extension-options.png └── burp-send-to-extension-tab.png /.gitignore: -------------------------------------------------------------------------------- 1 | burp-send-to-extension/.idea 2 | burp-send-to-extension/.gradle 3 | burp-send-to-extension/build 4 | burp-send-to-extension/out 5 | -------------------------------------------------------------------------------- /BappDescription.html: -------------------------------------------------------------------------------- 1 |

Adds a customizable "Send to..."-context-menu to your BurpSuite.

2 | 3 |

Configuration

4 | 5 |

After loading the extension the "Send to"-Tab contains all necessary options to configure the "Send to"-context-menu.

6 | 7 |

New context-menu-entries can be added using the "Add"-button. Each entry consists of following fields:

8 | 9 | 48 | 49 |

50 | In addition it is possible to customize how placeholders behave when multiple HTTP messages are selected by clicking the "Advanced"-button. 51 | By default each selected HTTP message forms a separate command. However, it is also possible to join all values of a specific placeholder using a custom separator. 52 |

53 | 54 |

After creating new context-menu-entries using the "Add"-button they can be edited or deleted again using the "Edit"- and "Remove"-button. In addition the order in which they appear in the context-menu can be altered using the "Up"- and "Down"-button.

55 | 56 |

Terminal Options

57 | 58 |

59 | The "Terminal Options" allow to configure the graphical terminal to use. In addition it is possible to specify how multiple commands should be run in terminal. Multiple commands can either be run sequential in a single terminal or in parallel in separate terminals. While it's possible to choose a default behaviour, the exact behaviour can also be selected via a dialog, everytime a send-to context menu entry is selected. However, if you prefer one behaviour all the time, this dialog can also be disabled. 60 |

61 | 62 |

Context-Menu

63 | 64 |

The "Send to..." context-menu contains all entries which were added in the "Send to"-Tab. 65 | In addition you can add new entries via the "Custom command..."-context-menu-entry.

66 | 67 |

Save and load options

68 | 69 |

Usually the options of the "Send to"-Tab are saved automatically. However, if you switch computers you may save and load your current options. This can be done by clicking on the gear-symbol in the upper-left corner of the "Send to"-Tab and select the appropriate context-menu-entry.

70 | 71 |

Security Notes

72 | 73 |

Executing commands based on untrusted input always introduces the risk of command injection. This is especially true when using the %S placeholder. Thus it is recommended to always activate the Show preview option when using the %S placeholder and closely analyse commands in the preview window prior to execution.

74 | 75 | -------------------------------------------------------------------------------- /BappManifest.bmf: -------------------------------------------------------------------------------- 1 | Uuid: f089f1ad056545489139cb9f32900f8e 2 | ExtensionType: 1 3 | Name: Custom Send To 4 | RepoName: custom-send-to 5 | ScreenVersion: 1.4 6 | SerialVersion: 1 7 | MinPlatformVersion: 0 8 | ProOnly: False 9 | Author: Thomas Engel 10 | ShortDescription: Add a customizable "Send to..." menu to the context menu 11 | EntryPoint: burp-send-to-extension/build/libs/burp-send-to-extension-1.4.jar 12 | BuildCommand: cd burp-send-to-extension; ./gradlew fatJar 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Burp-Send-To-Extension 2 | 3 | Adds a customizable "Send to..."-context-menu to your BurpSuite. 4 | 5 | ![Burp-Send-To-Extension Tab](images/burp-send-to-extension-intro.png) 6 | 7 | ## Configuration 8 | 9 | After loading the extension the "Send to"-Tab contains all necessary options to configure the "Send to"-context-menu. 10 | 11 | New context-menu-entries can be added using the "Add"-button. Each entry consists of following fields: 12 | * **Name:** the name of the context-menu-entry. 13 | * **Command:** the command to be executed. You can use following placeholders: 14 | * **%H:** will be replaced with the host 15 | * **%P:** will be replaced with the port 16 | * **%T:** will be replaced with the protocol 17 | * **%U:** will be replaced with the url 18 | * **%A:** will be replaced with the url path 19 | * **%Q:** will be replaced with the url query 20 | * **%C:** will be replaced with the cookies 21 | * **%L:** will be replaced with the HTTP-content-length 22 | * **%M:** will be replaced with the HTTP-method 23 | * **%O:** will be replaced with the HTTP-status-code 24 | * **%S:** will be replaced with the selected text 25 | * **%F:** will be replaced with the path to a temporary file containing the selected text 26 | * **%R:** will be replaced with the path to a temporary file containing the content of the focused request/response 27 | * **%E:** will be replaced with the path to a temporary file containing the header of the focused request/response 28 | * **%B:** will be replaced with the path to a temporary file containing the body of the focused request/response 29 | * **Group:** the name of the sub-menu in which this entry will be shown. Can be left blank. 30 | * **Run in terminal:** defines whether a terminal-window should appear in which the configured command is executed. By default "xterm" is used as terminal-emulator. You can change the terminal-emulator in the "Miscellaneous Options" to your liking. 31 | * **Show preview:** gives you the chance to preview and change the command before executing it. 32 | * **Output should replace selection:** will replace the selection with the output of the to be executed command. 33 | 34 | ![Burp-Send-To-Extension Add-/Edit-Dialog](images/burp-send-to-extension-add-edit-dialog.png) 35 | 36 | In addition it is possible to customize how placeholders behave when multiple HTTP messages are selected by clicking the "Advanced"-button. 37 | By default each selected HTTP message forms a separate command. However, it is also possible to join all values of a specific placeholder using a custom separator, or to store all values of a specific placeholder within a file. 38 | 39 | ![Burp-Send-To-Extension Advanced-Dialog](images/burp-send-to-extension-advanced-dialog.png) 40 | 41 | After creating new context-menu-entries using the "Add"-button they can be edited or deleted again using the "Edit"- and "Remove"-button. In addition the order in which they appear in the context-menu can be altered using the "Up"- and "Down"-button. 42 | 43 | ![Burp-Send-To-Extension Tab](images/burp-send-to-extension-tab.png) 44 | 45 | ## Terminal Options 46 | 47 | The "Terminal Options" allow to configure the graphical terminal to use. In addition it is possible to specify how multiple commands should be run in terminal. Multiple commands can either be run sequential in a single terminal or in parallel in separate terminals. While it's possible to choose a default behaviour, the exact behaviour can also be selected via a dialog, everytime a send-to context menu entry is selected. However, if you prefer one behaviour all the time, this dialog can also be disabled. 48 | 49 | ## Context-Menu 50 | 51 | The "Send to..." context-menu contains all entries which were added in the "Send to"-Tab. 52 | In addition you can add new entries via the "Custom command..."-context-menu-entry. 53 | 54 | #### Request Field 55 | 56 | ![Burp-Send-To-Extension Context-Menu](images/burp-send-to-extension-context-menu-repeater.png) 57 | 58 | #### Proxy History 59 | 60 | ![Burp-Send-To-Extension Context-Menu](images/burp-send-to-extension-context-menu-target-sitemap.png) 61 | 62 | ## Save and load options 63 | 64 | Usually the options of the "Send to"-Tab are saved automatically. However, if you switch computers you may save and load your current options. This can be done by clicking on the gear-symbol in the upper-left corner of the "Send to"-Tab and select the appropriate context-menu-entry. 65 | 66 | ![Burp-Send-To-Extension Options](images/burp-send-to-extension-options.png) 67 | 68 | ## Extending the Extension 69 | 70 | Not satisfied yet? The [Wiki Page](https://github.com/bytebutcher/burp-send-to/wiki/Examples) lists some additional context-menu entries which might come in handy. 71 | 72 | ## Security Notes 73 | 74 | Executing commands based on untrusted input always introduces the risk of command injection. This is especially true when using the **%S** placeholder. Thus it is recommended to always activate the **Show preview** option when using the **%S** placeholder and closely analyse commands in the preview window prior to execution. 75 | 76 | ![Burp-Send-To-Extension Options](images/burp-send-to-extension-forkbomb.png) 77 | 78 | ## Build 79 | 80 | This project was built using IntelliJ and Gradle. When you make changes to the source (and especially the GUI) you should apply following settings within Intellij to make sure that everything builds successfully: 81 | * File -> Settings -> Editor -> GUI Designer -> Generate GUI into: Java source 82 | * File -> Settings -> Build, Execution, Deployment -> Compiler -> Build project automatically 83 | * File -> Settings -> Build, Execution, Deployment -> Build Tools -> Gradle -> Build and run using: IntelliJ IDEA 84 | 85 | When the GUI is not updated correctly you may rebuild the project manually: 86 | * Build -> Rebuild Project 87 | 88 | After that you can execute the "fatJar"-task within the "build.gradle"-file. This will produce a jar in the "build/libs/" directory called "burp-send-to-extension-{version}.jar". 89 | -------------------------------------------------------------------------------- /burp-send-to-extension/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group 'net.bytebutcher' 4 | version '1.6' 5 | 6 | sourceCompatibility = 1.8 7 | 8 | repositories { 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | testCompile group: 'junit', name: 'junit', version: '4.12' 14 | compile 'com.intellij:forms_rt:7.0.3' 15 | compile 'net.portswigger.burp.extender:burp-extender-api:1.7.22' 16 | compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6' 17 | compile group: 'com.google.guava', name: 'guava', version: '27.0.1-jre' 18 | 19 | } 20 | 21 | task fatJar(type: Jar) { 22 | baseName = project.name 23 | from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } 24 | with jar 25 | } 26 | -------------------------------------------------------------------------------- /burp-send-to-extension/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/burp-send-to-extension/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /burp-send-to-extension/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 17 11:46:47 CET 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip 7 | -------------------------------------------------------------------------------- /burp-send-to-extension/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /burp-send-to-extension/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /burp-send-to-extension/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'burp-send-to-extension' 2 | 3 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/burp/BurpExtender.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import net.bytebutcher.burpsendtoextension.gui.SendToContextMenu; 4 | import net.bytebutcher.burpsendtoextension.gui.SendToTab; 5 | import net.bytebutcher.burpsendtoextension.gui.SendToTable; 6 | import net.bytebutcher.burpsendtoextension.models.Config; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | import java.io.PrintWriter; 11 | 12 | public class BurpExtender implements IBurpExtender, ITab { 13 | 14 | private JPanel tab = null; 15 | private SendToContextMenu sendToContextMenu; 16 | private SendToTable sendToTable; 17 | 18 | private static IBurpExtenderCallbacks callbacks; 19 | private static Config config; 20 | private static SendToTab sendToTab = null; 21 | 22 | private static PrintWriter stdout; 23 | private static PrintWriter stderr; 24 | 25 | @Override 26 | public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { 27 | BurpExtender.callbacks = callbacks; 28 | initLogHandler(callbacks); 29 | BurpExtender.printOut("Initializing Send to Extension..."); 30 | BurpExtender.callbacks.setExtensionName("Send to"); 31 | BurpExtender.printOut("Initializing config..."); 32 | BurpExtender.config = new Config(this); 33 | BurpExtender.printOut("Initializing tab..."); 34 | BurpExtender.sendToTab = new SendToTab(this); 35 | BurpExtender.printOut("Registering context menu..."); 36 | this.sendToContextMenu = new SendToContextMenu(this, sendToTab.getSendToTableListener()); 37 | BurpExtender.callbacks.registerContextMenuFactory(sendToContextMenu); 38 | this.tab = sendToTab.getRootPanel(); 39 | BurpExtender.printOut("Loading table data..."); 40 | this.sendToTable = sendToTab.getSendToTable(); 41 | this.sendToTable.addCommandObjects(config.getSendToTableData()); 42 | callbacks.addSuiteTab(this); 43 | BurpExtender.printOut("----------------------------------------------------------------------"); 44 | } 45 | 46 | private void initLogHandler(IBurpExtenderCallbacks callbacks) { 47 | stderr = new PrintWriter(callbacks.getStderr(), true); 48 | stdout = new PrintWriter(callbacks.getStdout(), true); 49 | } 50 | 51 | @Override 52 | public String getTabCaption() { 53 | return "Send to"; 54 | } 55 | 56 | @Override 57 | public Component getUiComponent() { 58 | return this.tab; 59 | } 60 | 61 | public ImageIcon createImageIcon(String path, String description, int width, int height) { 62 | java.net.URL imgURL = getClass().getResource(path); 63 | if (imgURL != null) { 64 | ImageIcon icon = new ImageIcon(imgURL); 65 | Image image = icon.getImage().getScaledInstance(width, height, Image.SCALE_SMOOTH); 66 | return new ImageIcon(image, description); 67 | } else { 68 | BurpExtender.printErr("Couldn't find file: " + path); 69 | return null; 70 | } 71 | } 72 | 73 | public static JFrame getParent() { 74 | return sendToTab.getParent(); 75 | } 76 | 77 | public static IBurpExtenderCallbacks getCallbacks() { 78 | return callbacks; 79 | } 80 | 81 | public static Config getConfig() { 82 | return config; 83 | } 84 | 85 | public static void printOut(String s) { 86 | stdout.println(s); 87 | } 88 | 89 | public static void printErr(String s) { 90 | stderr.println(s); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/burp/Cookie.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import com.google.common.collect.Lists; 4 | 5 | import java.text.SimpleDateFormat; 6 | import java.util.*; 7 | import java.util.stream.Collectors; 8 | 9 | /** 10 | * Implements the ICookie interface which holds details about an HTTP cookie. 11 | * @author Thomas Engel 12 | * @author August Detlefsen [augustd at codemagi dot com] 13 | */ 14 | public class Cookie implements ICookie { 15 | 16 | private String name; 17 | private String value; 18 | private String domain; 19 | private String path; 20 | private Date expiration; 21 | private Long maxAge; 22 | private Boolean secure = false; 23 | private Boolean httpOnly = false; 24 | 25 | public Cookie(String name, String value) { 26 | this.name = name; 27 | this.value = value; 28 | } 29 | 30 | public Cookie(ICookie cookie) { 31 | this.name = cookie.getName(); 32 | this.value = cookie.getValue(); 33 | this.domain = cookie.getDomain(); 34 | this.path = cookie.getPath(); 35 | this.expiration = cookie.getExpiration(); 36 | } 37 | 38 | /** 39 | * Parses a list of HTTP response header fields containing the raw cookie 40 | * _value_ (Minus "Set-Cookie:"). 41 | * 42 | * @param rawCookies A list of string containing the raw cookie 43 | * @return A list of ICookie objects parsed from a list of raw cookie strings 44 | */ 45 | public static List parseResponseCookies(List rawCookies) { 46 | return rawCookies.stream().map(Cookie::parseResponseCookie).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toList()); 47 | } 48 | 49 | /** 50 | * Parses a cookie from a String containing the raw HTTP response header 51 | * _value_ (Minus "Set-Cookie:"). 52 | * 53 | * @param rawCookie A String containing the raw cookie 54 | * @return A Cookie object parsed from the raw cookie string 55 | */ 56 | private static Optional parseResponseCookie(String rawCookie) { 57 | String[] rawCookieParams = rawCookie.split(";"); 58 | 59 | //get the cookie name, check for valid cookie 60 | String[] rawCookieNameAndValue = rawCookieParams[0].split("="); 61 | String cookieName = rawCookieNameAndValue[0].trim(); 62 | if (cookieName.isEmpty()) { 63 | BurpExtender.printErr("Invalid cookie: missing name"); 64 | return Optional.empty(); 65 | } 66 | 67 | //get the cookie value 68 | String cookieValue = rawCookieNameAndValue[1].trim(); 69 | 70 | //construct output 71 | Cookie output = new Cookie(cookieName, cookieValue); 72 | 73 | //parse other cookie params 74 | for (int i = 1; i < rawCookieParams.length; i++) { 75 | String[] rawCookieParam = rawCookieParams[i].trim().split("="); 76 | 77 | String paramName = rawCookieParam[0].trim(); 78 | 79 | if ("secure".equalsIgnoreCase(paramName)) { 80 | output.setSecure(true); 81 | 82 | } else if ("HttpOnly".equalsIgnoreCase(paramName)) { 83 | output.setHttpOnly(true); 84 | 85 | } else { 86 | if (rawCookieParam.length != 2) { 87 | //attribute not a flag or missing value 88 | continue; 89 | } 90 | String paramValue = rawCookieParam[1].trim(); 91 | 92 | if ("expires".equalsIgnoreCase(paramName)) { 93 | try { 94 | SimpleDateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss zzz"); 95 | Date expiryDate = format.parse(paramValue); 96 | output.setExpiration(expiryDate); 97 | } catch (Exception e) { 98 | //couldn't parse date, ignore 99 | BurpExtender.printErr("WARNING: unable to parse cookie expiration: " + paramValue); 100 | } 101 | } else if ("max-age".equalsIgnoreCase(paramName)) { 102 | long maxAge = Long.parseLong(paramValue); 103 | output.setMaxAge(maxAge); 104 | } else if ("domain".equalsIgnoreCase(paramName)) { 105 | output.setDomain(paramValue); 106 | } else if ("path".equalsIgnoreCase(paramName)) { 107 | output.setPath(paramValue); 108 | } 109 | } 110 | } 111 | 112 | return Optional.of(output); 113 | } 114 | 115 | /** 116 | * Parses cookies from a list of raw HTTP request headers 117 | * _value_ (Minus "Cookie:"). 118 | * 119 | * @param rawCookies A list of strings containing the raw cookie 120 | * @return A list of ICookie objects parsed from a list of raw cookie strings 121 | */ 122 | public static List parseRequestCookies(List rawCookies) { 123 | return rawCookies.stream().map(Cookie::parseRequestCookies).flatMap(Collection::stream).collect(Collectors.toList()); 124 | } 125 | 126 | /** 127 | * Parses a cookie from a String containing the raw HTTP request header 128 | * _value_ (Minus "Cookie:"). 129 | * 130 | * @param rawCookie A String containing the raw cookie 131 | * @return A list of Cookie objects parsed from the raw cookie string 132 | */ 133 | public static List parseRequestCookies(String rawCookie) { 134 | List cookies = Lists.newArrayList(); 135 | String[] rawCookieParams = rawCookie.split(";"); 136 | for (String rawCookieParam : rawCookieParams) { 137 | //get the cookie name, check for valid cookie 138 | String[] rawCookieNameAndValue = rawCookieParam.split("="); 139 | String cookieName = rawCookieNameAndValue[0].trim(); 140 | if (cookieName.isEmpty() || !rawCookieParam.contains("=")) { 141 | BurpExtender.printErr("Invalid cookie: missing name"); 142 | continue; 143 | } 144 | 145 | //get the cookie value 146 | String cookieValue = ""; 147 | if (rawCookieNameAndValue.length != 1) { 148 | cookieValue = rawCookieNameAndValue[1].trim(); 149 | } 150 | 151 | //construct output 152 | cookies.add(new Cookie(cookieName, cookieValue)); 153 | } 154 | 155 | return cookies; 156 | } 157 | 158 | @Override 159 | public String getName() { 160 | return name; 161 | } 162 | 163 | public void setName(String name) { 164 | this.name = name; 165 | } 166 | 167 | @Override 168 | public String getValue() { 169 | return value; 170 | } 171 | 172 | public void setValue(String value) { 173 | this.value = value; 174 | } 175 | 176 | @Override 177 | public String getDomain() { 178 | return domain; 179 | } 180 | 181 | public void setDomain(String domain) { 182 | this.domain = domain; 183 | } 184 | 185 | @Override 186 | public String getPath() { 187 | return path; 188 | } 189 | 190 | public void setPath(String path) { 191 | this.path = path; 192 | } 193 | 194 | @Override 195 | public Date getExpiration() { 196 | return expiration; 197 | } 198 | 199 | public void setExpiration(Date expiration) { 200 | this.expiration = expiration; 201 | } 202 | 203 | public Long getMaxAge() { 204 | return maxAge; 205 | } 206 | 207 | public void setMaxAge(Long maxAge) { 208 | this.maxAge = maxAge; 209 | } 210 | 211 | public Boolean getSecure() { 212 | return secure; 213 | } 214 | 215 | public void setSecure(Boolean secure) { 216 | this.secure = secure; 217 | } 218 | 219 | public Boolean getHttpOnly() { 220 | return httpOnly; 221 | } 222 | 223 | public void setHttpOnly(Boolean httpOnly) { 224 | this.httpOnly = httpOnly; 225 | } 226 | 227 | @Override 228 | public boolean equals(Object o) { 229 | if (this == o) return true; 230 | if (o == null || getClass() != o.getClass()) return false; 231 | Cookie cookie = (Cookie) o; 232 | return Objects.equals(name, cookie.name) && 233 | Objects.equals(value, cookie.value) && 234 | Objects.equals(domain, cookie.domain) && 235 | Objects.equals(path, cookie.path) && 236 | Objects.equals(expiration, cookie.expiration) && 237 | Objects.equals(maxAge, cookie.maxAge) && 238 | Objects.equals(secure, cookie.secure) && 239 | Objects.equals(httpOnly, cookie.httpOnly); 240 | } 241 | 242 | @Override 243 | public int hashCode() { 244 | return Objects.hash(name, value, domain, path, expiration, maxAge, secure, httpOnly); 245 | } 246 | } -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/burp/IRequestInfoWrapper.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.util.List; 4 | 5 | public interface IRequestInfoWrapper extends IRequestInfo { 6 | 7 | List getCookies(); 8 | String getBody(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/burp/IRequestResponseHolder.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | public interface IRequestResponseHolder { 4 | IRequestInfo getRequestInfo(); 5 | 6 | IResponseInfo getResponseInfo(); 7 | 8 | IBurpExtenderCallbacks getBurpExtenderCallbacks(); 9 | 10 | IHttpRequestResponse getHttpRequestResponse(); 11 | } 12 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/burp/IResponseInfoWrapper.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.util.List; 4 | 5 | public interface IResponseInfoWrapper extends IResponseInfo { 6 | 7 | List getCookies(); 8 | String getBody(); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/burp/RequestInfoWrapper.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.net.URL; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.stream.Collectors; 7 | 8 | public class RequestInfoWrapper implements IRequestInfoWrapper { 9 | 10 | private IHttpRequestResponse httpRequestResponse; 11 | private IRequestInfo requestInfo; 12 | private List cookies; 13 | private String requestBody; 14 | 15 | public RequestInfoWrapper(IHttpRequestResponse httpRequestResponse, IRequestInfo requestInfo) { 16 | this.httpRequestResponse = httpRequestResponse; 17 | this.requestInfo = requestInfo; 18 | } 19 | 20 | @Override 21 | public List getCookies() { 22 | if (cookies == null) { 23 | String cookieHeaderPrefix = "cookie: "; 24 | cookies = Cookie.parseRequestCookies(requestInfo.getHeaders().stream().filter(s -> s.toLowerCase().startsWith(cookieHeaderPrefix)).map(s -> s.substring(cookieHeaderPrefix.length() - 1)).collect(Collectors.toList())); 25 | } 26 | return cookies; 27 | } 28 | 29 | @Override 30 | public String getBody() { 31 | if (requestBody == null) { 32 | byte[] request = httpRequestResponse.getRequest(); 33 | int bodyOffset = this.getBodyOffset(); 34 | requestBody = new String(Arrays.copyOfRange(request, bodyOffset, request.length)); 35 | } 36 | return requestBody; 37 | } 38 | 39 | @Override 40 | public String getMethod() { 41 | return requestInfo.getMethod(); 42 | } 43 | 44 | @Override 45 | public URL getUrl() { 46 | return requestInfo.getUrl(); 47 | } 48 | 49 | @Override 50 | public List getHeaders() { 51 | return requestInfo.getHeaders(); 52 | } 53 | 54 | @Override 55 | public List getParameters() { 56 | return requestInfo.getParameters(); 57 | } 58 | 59 | @Override 60 | public int getBodyOffset() { 61 | return requestInfo.getBodyOffset(); 62 | } 63 | 64 | @Override 65 | public byte getContentType() { 66 | return requestInfo.getContentType(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/burp/RequestResponseHolder.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | public class RequestResponseHolder implements IRequestResponseHolder { 4 | 5 | private final IBurpExtenderCallbacks burpExtenderCallbacks; 6 | private final IHttpRequestResponse httpRequestResponse; 7 | private IRequestInfoWrapper requestInfo; 8 | private IResponseInfoWrapper responseInfo; 9 | 10 | public RequestResponseHolder(IBurpExtenderCallbacks burpExtenderCallbacks, IHttpRequestResponse httpRequestResponse) { 11 | this.burpExtenderCallbacks = burpExtenderCallbacks; 12 | this.httpRequestResponse = httpRequestResponse; 13 | } 14 | 15 | @Override 16 | public IRequestInfoWrapper getRequestInfo() { 17 | if (requestInfo == null) { 18 | requestInfo = new RequestInfoWrapper(httpRequestResponse, burpExtenderCallbacks.getHelpers().analyzeRequest(httpRequestResponse.getHttpService(), httpRequestResponse.getRequest())); 19 | } 20 | return requestInfo; 21 | } 22 | 23 | @Override 24 | public IResponseInfoWrapper getResponseInfo() { 25 | if (responseInfo == null) { 26 | responseInfo = new ResponseInfoWrapper(httpRequestResponse, burpExtenderCallbacks.getHelpers().analyzeResponse(httpRequestResponse.getResponse())); 27 | } 28 | return responseInfo; 29 | } 30 | 31 | @Override 32 | public IBurpExtenderCallbacks getBurpExtenderCallbacks() { 33 | return burpExtenderCallbacks; 34 | } 35 | 36 | @Override 37 | public IHttpRequestResponse getHttpRequestResponse() { 38 | return httpRequestResponse; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/burp/ResponseInfoWrapper.java: -------------------------------------------------------------------------------- 1 | package burp; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | public class ResponseInfoWrapper implements IResponseInfoWrapper { 8 | 9 | private IHttpRequestResponse httpRequestResponse; 10 | private IResponseInfo responseInfo; 11 | private List cookies; 12 | private String responseBody; 13 | 14 | public ResponseInfoWrapper(IHttpRequestResponse httpRequestResponse, IResponseInfo responseInfo) { 15 | this.httpRequestResponse = httpRequestResponse; 16 | this.responseInfo = responseInfo; 17 | } 18 | 19 | @Override 20 | public List getHeaders() { 21 | return responseInfo.getHeaders(); 22 | } 23 | 24 | @Override 25 | public int getBodyOffset() { 26 | return responseInfo.getBodyOffset(); 27 | } 28 | 29 | @Override 30 | public short getStatusCode() { 31 | return responseInfo.getStatusCode(); 32 | } 33 | 34 | @Override 35 | public List getCookies() { 36 | if (cookies == null) { 37 | String cookieHeaderPrefix = "set-cookie: "; 38 | cookies = Cookie.parseResponseCookies(responseInfo.getHeaders().stream().filter(s -> s.toLowerCase().startsWith(cookieHeaderPrefix)).map(s -> s.substring(cookieHeaderPrefix.length() - 1)).collect(Collectors.toList())); 39 | } 40 | return cookies; 41 | } 42 | 43 | @Override 44 | public String getStatedMimeType() { 45 | return responseInfo.getStatedMimeType(); 46 | } 47 | 48 | @Override 49 | public String getInferredMimeType() { 50 | return responseInfo.getInferredMimeType(); 51 | } 52 | 53 | @Override 54 | public String getBody() { 55 | if (responseBody == null) { 56 | byte[] response = httpRequestResponse.getResponse(); 57 | int bodyOffset = this.getBodyOffset(); 58 | responseBody = new String(Arrays.copyOfRange(response, bodyOffset, response.length)); 59 | } 60 | return responseBody; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.google.gson.typeadapters; 18 | 19 | import com.google.gson.*; 20 | import com.google.gson.internal.Streams; 21 | import com.google.gson.reflect.TypeToken; 22 | import com.google.gson.stream.JsonReader; 23 | import com.google.gson.stream.JsonWriter; 24 | 25 | import java.io.IOException; 26 | import java.util.LinkedHashMap; 27 | import java.util.Map; 28 | 29 | /** 30 | * Adapts values whose runtime type may differ from their declaration type. This 31 | * is necessary when a field's type is not the same type that GSON should create 32 | * when deserializing that field. For example, consider these types: 33 | *
   {@code
 34 |  *   abstract class Shape {
 35 |  *     int x;
 36 |  *     int y;
 37 |  *   }
 38 |  *   class Circle extends Shape {
 39 |  *     int radius;
 40 |  *   }
 41 |  *   class Rectangle extends Shape {
 42 |  *     int width;
 43 |  *     int height;
 44 |  *   }
 45 |  *   class Diamond extends Shape {
 46 |  *     int width;
 47 |  *     int height;
 48 |  *   }
 49 |  *   class Drawing {
 50 |  *     Shape bottomShape;
 51 |  *     Shape topShape;
 52 |  *   }
 53 |  * }
54 | *

Without additional type information, the serialized JSON is ambiguous. Is 55 | * the bottom shape in this drawing a rectangle or a diamond?

   {@code
 56 |  *   {
 57 |  *     "bottomShape": {
 58 |  *       "width": 10,
 59 |  *       "height": 5,
 60 |  *       "x": 0,
 61 |  *       "y": 0
 62 |  *     },
 63 |  *     "topShape": {
 64 |  *       "radius": 2,
 65 |  *       "x": 4,
 66 |  *       "y": 1
 67 |  *     }
 68 |  *   }}
69 | * This class addresses this problem by adding type information to the 70 | * serialized JSON and honoring that type information when the JSON is 71 | * deserialized:
   {@code
 72 |  *   {
 73 |  *     "bottomShape": {
 74 |  *       "type": "Diamond",
 75 |  *       "width": 10,
 76 |  *       "height": 5,
 77 |  *       "x": 0,
 78 |  *       "y": 0
 79 |  *     },
 80 |  *     "topShape": {
 81 |  *       "type": "Circle",
 82 |  *       "radius": 2,
 83 |  *       "x": 4,
 84 |  *       "y": 1
 85 |  *     }
 86 |  *   }}
87 | * Both the type field name ({@code "type"}) and the type labels ({@code 88 | * "Rectangle"}) are configurable. 89 | * 90 | *

Registering Types

91 | * Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field 92 | * name to the {@link #of} factory method. If you don't supply an explicit type 93 | * field name, {@code "type"} will be used.
   {@code
 94 |  *   RuntimeTypeAdapterFactory shapeAdapterFactory
 95 |  *       = RuntimeTypeAdapterFactory.of(Shape.class, "type");
 96 |  * }
97 | * Next register all of your subtypes. Every subtype must be explicitly 98 | * registered. This protects your application from injection attacks. If you 99 | * don't supply an explicit type label, the type's simple name will be used. 100 | *
   {@code
101 |  *   shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
102 |  *   shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
103 |  *   shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
104 |  * }
105 | * Finally, register the type adapter factory in your application's GSON builder: 106 | *
   {@code
107 |  *   Gson gson = new GsonBuilder()
108 |  *       .registerTypeAdapterFactory(shapeAdapterFactory)
109 |  *       .create();
110 |  * }
111 | * Like {@code GsonBuilder}, this API supports chaining:
   {@code
112 |  *   RuntimeTypeAdapterFactory shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
113 |  *       .registerSubtype(Rectangle.class)
114 |  *       .registerSubtype(Circle.class)
115 |  *       .registerSubtype(Diamond.class);
116 |  * }
117 | * 118 | *

Serialization and deserialization

119 | * In order to serialize and deserialize a polymorphic object, 120 | * you must specify the base type explicitly. 121 | *
   {@code
122 |  *   Diamond diamond = new Diamond();
123 |  *   String json = gson.toJson(diamond, Shape.class);
124 |  * }
125 | * And then: 126 | *
   {@code
127 |  *   Shape shape = gson.fromJson(json, Shape.class);
128 |  * }
129 | */ 130 | public final class RuntimeTypeAdapterFactory implements TypeAdapterFactory { 131 | private final Class baseType; 132 | private final String typeFieldName; 133 | private final Map> labelToSubtype = new LinkedHashMap>(); 134 | private final Map, String> subtypeToLabel = new LinkedHashMap, String>(); 135 | private final boolean maintainType; 136 | 137 | private RuntimeTypeAdapterFactory(Class baseType, String typeFieldName, boolean maintainType) { 138 | if (typeFieldName == null || baseType == null) { 139 | throw new NullPointerException(); 140 | } 141 | this.baseType = baseType; 142 | this.typeFieldName = typeFieldName; 143 | this.maintainType = maintainType; 144 | } 145 | 146 | /** 147 | * Creates a new runtime type adapter using for {@code baseType} using {@code 148 | * typeFieldName} as the type field name. Type field names are case sensitive. 149 | * {@code maintainType} flag decide if the type will be stored in pojo or not. 150 | */ 151 | public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName, boolean maintainType) { 152 | return new RuntimeTypeAdapterFactory(baseType, typeFieldName, maintainType); 153 | } 154 | 155 | /** 156 | * Creates a new runtime type adapter using for {@code baseType} using {@code 157 | * typeFieldName} as the type field name. Type field names are case sensitive. 158 | */ 159 | public static RuntimeTypeAdapterFactory of(Class baseType, String typeFieldName) { 160 | return new RuntimeTypeAdapterFactory(baseType, typeFieldName, false); 161 | } 162 | 163 | /** 164 | * Creates a new runtime type adapter for {@code baseType} using {@code "type"} as 165 | * the type field name. 166 | */ 167 | public static RuntimeTypeAdapterFactory of(Class baseType) { 168 | return new RuntimeTypeAdapterFactory(baseType, "type", false); 169 | } 170 | 171 | /** 172 | * Registers {@code type} identified by {@code label}. Labels are case 173 | * sensitive. 174 | * 175 | * @throws IllegalArgumentException if either {@code type} or {@code label} 176 | * have already been registered on this type adapter. 177 | */ 178 | public RuntimeTypeAdapterFactory registerSubtype(Class type, String label) { 179 | if (type == null || label == null) { 180 | throw new NullPointerException(); 181 | } 182 | if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { 183 | throw new IllegalArgumentException("types and labels must be unique"); 184 | } 185 | labelToSubtype.put(label, type); 186 | subtypeToLabel.put(type, label); 187 | return this; 188 | } 189 | 190 | /** 191 | * Registers {@code type} identified by its {@link Class#getSimpleName simple 192 | * name}. Labels are case sensitive. 193 | * 194 | * @throws IllegalArgumentException if either {@code type} or its simple name 195 | * have already been registered on this type adapter. 196 | */ 197 | public RuntimeTypeAdapterFactory registerSubtype(Class type) { 198 | return registerSubtype(type, type.getSimpleName()); 199 | } 200 | 201 | public TypeAdapter create(Gson gson, TypeToken type) { 202 | if (type.getRawType() != baseType) { 203 | return null; 204 | } 205 | 206 | final Map> labelToDelegate 207 | = new LinkedHashMap>(); 208 | final Map, TypeAdapter> subtypeToDelegate 209 | = new LinkedHashMap, TypeAdapter>(); 210 | for (Map.Entry> entry : labelToSubtype.entrySet()) { 211 | TypeAdapter delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); 212 | labelToDelegate.put(entry.getKey(), delegate); 213 | subtypeToDelegate.put(entry.getValue(), delegate); 214 | } 215 | 216 | return new TypeAdapter() { 217 | @Override public R read(JsonReader in) throws IOException { 218 | JsonElement jsonElement = Streams.parse(in); 219 | JsonElement labelJsonElement; 220 | if (maintainType) { 221 | labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName); 222 | } else { 223 | labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); 224 | } 225 | 226 | if (labelJsonElement == null) { 227 | throw new JsonParseException("cannot deserialize " + baseType 228 | + " because it does not define a field named " + typeFieldName); 229 | } 230 | String label = labelJsonElement.getAsString(); 231 | @SuppressWarnings("unchecked") // registration requires that subtype extends T 232 | TypeAdapter delegate = (TypeAdapter) labelToDelegate.get(label); 233 | if (delegate == null) { 234 | throw new JsonParseException("cannot deserialize " + baseType + " subtype named " 235 | + label + "; did you forget to register a subtype?"); 236 | } 237 | return delegate.fromJsonTree(jsonElement); 238 | } 239 | 240 | @Override public void write(JsonWriter out, R value) throws IOException { 241 | Class srcType = value.getClass(); 242 | String label = subtypeToLabel.get(srcType); 243 | @SuppressWarnings("unchecked") // registration requires that subtype extends T 244 | TypeAdapter delegate = (TypeAdapter) subtypeToDelegate.get(srcType); 245 | if (delegate == null) { 246 | throw new JsonParseException("cannot serialize " + srcType.getName() 247 | + "; did you forget to register a subtype?"); 248 | } 249 | JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); 250 | 251 | if (maintainType) { 252 | Streams.write(jsonObject, out); 253 | return; 254 | } 255 | 256 | JsonObject clone = new JsonObject(); 257 | 258 | if (jsonObject.has(typeFieldName)) { 259 | throw new JsonParseException("cannot serialize " + srcType.getName() 260 | + " because it already defines a field named " + typeFieldName); 261 | } 262 | clone.add(typeFieldName, new JsonPrimitive(label)); 263 | 264 | for (Map.Entry e : jsonObject.entrySet()) { 265 | clone.add(e.getKey(), e.getValue()); 266 | } 267 | Streams.write(clone, out); 268 | } 269 | }.nullSafe(); 270 | } 271 | } -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/builder/CommandBuilder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.builder; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.google.common.collect.Maps; 5 | import net.bytebutcher.burpsendtoextension.models.CommandObject; 6 | import net.bytebutcher.burpsendtoextension.models.Context; 7 | import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser; 8 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour; 9 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour; 10 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour; 11 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour; 12 | 13 | import java.io.File; 14 | import java.io.PrintWriter; 15 | import java.util.Comparator; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.Objects; 19 | import java.util.stream.Collectors; 20 | 21 | public class CommandBuilder { 22 | 23 | // The model of the context menu entry with the format string and various options. 24 | private final CommandObject commandObject; 25 | 26 | // List of selected messages containing the placeholders and their values. 27 | private final List> placeholderMap; 28 | 29 | // List of placeholders and merged values. 30 | private final Map placeholderValues; 31 | 32 | private final Context context; 33 | 34 | private static class Placeholder { 35 | 36 | private final String name; 37 | private final IPlaceholderBehaviour behaviour; 38 | 39 | public Placeholder(CommandObject.Placeholder placeholder) { 40 | this.name = placeholder.getName(); 41 | this.behaviour = placeholder.getBehaviour(); 42 | } 43 | 44 | @Override 45 | public boolean equals(Object o) { 46 | if (this == o) return true; 47 | if (o == null || getClass() != o.getClass()) return false; 48 | Placeholder that = (Placeholder) o; 49 | return Objects.equals(name, that.name) && Objects.equals(behaviour, that.behaviour); 50 | } 51 | 52 | @Override 53 | public int hashCode() { 54 | return Objects.hash(name, behaviour); 55 | } 56 | } 57 | 58 | public CommandBuilder(CommandObject commandObject, List> placeholderMap, Context context) throws Exception { 59 | this.commandObject = commandObject; 60 | this.placeholderMap = placeholderMap; 61 | this.context = context; 62 | this.placeholderValues = initPlaceholderValues(); 63 | } 64 | 65 | public Map initPlaceholderValues() throws Exception { 66 | Map placeholderValues = Maps.newHashMap(); 67 | for (CommandObject.Placeholder coPlaceholder : commandObject.getPlaceholders()) { 68 | Placeholder cbPlaceholder = new Placeholder(coPlaceholder); 69 | if (!placeholderValues.containsKey(cbPlaceholder)) { 70 | if (coPlaceholder.getBehaviour() instanceof StringSeparatedPlaceholderBehaviour){ 71 | // combine the values of all messages using the defined placeholder separator 72 | placeholderValues.put(cbPlaceholder, commandObject.getValid(placeholderMap, context).stream() 73 | .map(m -> m.get(coPlaceholder.getName())) 74 | .map(iPlaceholder -> iPlaceholder.getValue(context)) 75 | .collect(Collectors.joining(((StringSeparatedPlaceholderBehaviour) coPlaceholder.getBehaviour()).getSeparator()))); 76 | } else if (coPlaceholder.getBehaviour() instanceof FileSeparatedPlaceholderBehaviour){ 77 | // combine the values of all messages and write them into a file. 78 | placeholderValues.put(cbPlaceholder, writeToFile(commandObject.getValid(placeholderMap, context).stream() 79 | .map(m -> m.get(coPlaceholder.getName())) 80 | .map(iPlaceholder -> iPlaceholder.getValue(context)) 81 | .collect(Collectors.joining("\n")))); 82 | } 83 | } 84 | } 85 | return placeholderValues; 86 | } 87 | 88 | /** 89 | * Returns the command while all placeholders are replaced with their associated value as String. 90 | * @throws Exception when retrieving/replacing a placeholder failed. 91 | */ 92 | public String build() throws Exception { 93 | try { 94 | List result = Lists.newArrayList(); 95 | boolean containsCommandSeparatedPlaceholderBehaviour = commandObject.getPlaceholders().stream() 96 | .map(CommandObject.Placeholder::getBehaviour) 97 | .anyMatch(c -> c instanceof CommandSeparatedPlaceholderBehaviour); 98 | if (containsCommandSeparatedPlaceholderBehaviour) { 99 | for (int messageIndex = 0; messageIndex < placeholderMap.size(); messageIndex++) { 100 | result.add(buildByMessage(messageIndex)); 101 | } 102 | } else { 103 | result.add(buildByMessage(0)); 104 | } 105 | return String.join("\n", result); 106 | } catch (RuntimeException e) { 107 | // Rethrow from unchecked to checked exception. We only deal with RuntimeException here, since streams 108 | // (here: placeholderMap.stream()) does not handle checked exceptions well. 109 | throw new Exception(e); 110 | } 111 | } 112 | 113 | private String buildByMessage(int messageIndex) throws Exception { 114 | StringBuffer format = new StringBuffer(commandObject.getFormat()); 115 | // For each placeholder, starting from the placeholder at the very end, replace it with the value from the message. 116 | List placeholders = commandObject.getPlaceholders().stream().sorted( 117 | Comparator.comparing(CommandObject.Placeholder::getEnd).reversed() 118 | ).collect(Collectors.toList()); 119 | for (CommandObject.Placeholder placeholder : placeholders) { 120 | replaceCommandPlaceholder(placeholder, placeholderMap, messageIndex, format, context); 121 | } 122 | return format.toString(); 123 | } 124 | 125 | private void replaceCommandPlaceholder(CommandObject.Placeholder placeholder, List> placeholderMap, int messageIndex, StringBuffer command, Context context) throws Exception { 126 | String value; 127 | if (placeholderValues.containsKey(new Placeholder(placeholder))) { 128 | // merged values 129 | value = placeholderValues.get(new Placeholder(placeholder)); 130 | } else { 131 | // command separated - use the value from the actual message 132 | value = placeholderMap.get(messageIndex).get(placeholder.getName()).getValue(context); 133 | } 134 | command.replace(placeholder.getStart(), placeholder.getEnd(), value); 135 | } 136 | 137 | private String writeToFile(String value) throws Exception { 138 | try { 139 | File tmp = File.createTempFile("burp_", ".snd"); 140 | PrintWriter out = new PrintWriter(tmp.getPath()); 141 | out.write(value); 142 | out.flush(); 143 | return tmp.getAbsolutePath(); 144 | } catch (RuntimeException e) { 145 | throw new Exception(this.getClass().getSimpleName() + ": Error writing to temporary file!", e); 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/executioner/CommandExecutioner.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.executioner; 2 | 3 | import burp.BurpExtender; 4 | import burp.IHttpRequestResponse; 5 | import com.google.common.collect.Lists; 6 | import net.bytebutcher.burpsendtoextension.gui.util.SelectionUtil; 7 | import net.bytebutcher.burpsendtoextension.models.Context; 8 | import net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour; 9 | import net.bytebutcher.burpsendtoextension.utils.OsUtils; 10 | import net.bytebutcher.burpsendtoextension.utils.StringUtils; 11 | 12 | import java.io.IOException; 13 | import java.time.ZonedDateTime; 14 | import java.time.format.DateTimeFormatter; 15 | import java.util.Arrays; 16 | import java.util.List; 17 | import java.util.stream.Collectors; 18 | 19 | public class CommandExecutioner { 20 | 21 | private final ERunInTerminalBehaviour runInTerminalBehaviour; 22 | private final boolean shouldOutputReplaceSelection; 23 | private final Context context; 24 | 25 | public CommandExecutioner(boolean shouldOutputReplaceSelection, Context context) { 26 | this.runInTerminalBehaviour = null; 27 | this.shouldOutputReplaceSelection = shouldOutputReplaceSelection; 28 | this.context = context; 29 | } 30 | 31 | public CommandExecutioner(ERunInTerminalBehaviour runInTerminalBehaviour, boolean shouldOutputReplaceSelection, Context context) { 32 | this.runInTerminalBehaviour = runInTerminalBehaviour; 33 | this.shouldOutputReplaceSelection = shouldOutputReplaceSelection; 34 | this.context = context; 35 | } 36 | 37 | public void execute(String commands) throws Exception { 38 | if (commands != null) { 39 | List commandOutput = Lists.newArrayList(); 40 | if (runInTerminalBehaviour != null && runInTerminalBehaviour == ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL) { 41 | // Run commands sequential within terminal 42 | String command = Arrays.stream(commands.split("\n")).collect(Collectors.joining(" ; ")); 43 | execute(command, commandOutput); 44 | } else { 45 | // Run commands in parallel (within separate terminals or in background) 46 | for (String command : commands.split("\n")) { 47 | execute(command, commandOutput); 48 | } 49 | } 50 | if (!commandOutput.isEmpty()) { 51 | replaceSelectedText(context, commandOutput.stream().collect(Collectors.joining("\n"))); 52 | } 53 | } 54 | } 55 | 56 | private void execute(String command, List commandOutput) throws IOException { 57 | ProcessBuilder commandProcessBuilder = getProcessBuilder(command); 58 | logCommandToBeExecuted(commandProcessBuilder.command().toArray(new String[commandProcessBuilder.command().size()])); 59 | Process process = commandProcessBuilder.start(); 60 | if (shouldOutputReplaceSelection) { 61 | commandOutput.add(StringUtils.fromInputStream(process.getInputStream())); 62 | } 63 | } 64 | 65 | private ProcessBuilder getProcessBuilder(String command) { 66 | if (runInTerminalBehaviour != null) { 67 | return new ProcessBuilder(formatCommandForRunningInTerminal(command)); 68 | } else { 69 | return new ProcessBuilder(formatCommandForRunningOnOperatingSystem(command)); 70 | } 71 | } 72 | 73 | private String[] formatCommandForRunningOnOperatingSystem(String command) { 74 | String[] commandToBeExecuted; 75 | if (OsUtils.isWindows()) { 76 | commandToBeExecuted = new String[]{"cmd", "/c", command}; 77 | } else { 78 | commandToBeExecuted = new String[]{"/bin/bash", "-c", command}; 79 | } 80 | return commandToBeExecuted; 81 | } 82 | 83 | private String[] formatCommandForRunningInTerminal(String command) { 84 | String[] commandToBeExecuted = BurpExtender.getConfig().getRunInTerminalCommand().split(" "); 85 | for (int i = 0; i < commandToBeExecuted.length; i++) { 86 | String commandPart = commandToBeExecuted[i]; 87 | if ("%C".equals(commandPart)) { 88 | commandToBeExecuted[i] = command; 89 | } 90 | } 91 | return commandToBeExecuted; 92 | } 93 | 94 | private void replaceSelectedText(Context context, String replaceText) throws Exception { 95 | if (context.getSelectedMessages() != null && context.getSelectedMessages().length > 0) { 96 | IHttpRequestResponse message = context.getSelectedMessages()[0]; 97 | switch (context.getOrigin()) { 98 | case HTTP_REQUEST: 99 | message.setRequest(SelectionUtil.replaceSelectedText(message.getRequest(), context.getSelectionBounds(), replaceText)); 100 | break; 101 | case HTTP_RESPONSE: 102 | message.setResponse(SelectionUtil.replaceSelectedText(message.getResponse(), context.getSelectionBounds(), replaceText)); 103 | break; 104 | } 105 | } 106 | } 107 | 108 | private void logCommandToBeExecuted(String[] commandToBeExecuted) { 109 | String commandToBeExecutedWithoutControlCharacters = String.join(" ", commandToBeExecuted).replaceAll("[\u0000-\u001f]", ""); 110 | String dateTime = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("uuuu/MM/dd HH:mm:ss")); 111 | BurpExtender.printOut("[" + dateTime + "] " + commandToBeExecutedWithoutControlCharacters); 112 | BurpExtender.printOut("----------------------------------------------------------------------"); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/CommandsChangeListener.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui; 2 | 3 | import net.bytebutcher.burpsendtoextension.models.CommandObject; 4 | 5 | import java.util.List; 6 | 7 | public interface CommandsChangeListener { 8 | 9 | void commandsChanged(List commandObjects); 10 | } 11 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedDialog.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
96 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedDialog.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.intellij.uiDesigner.core.GridConstraints; 5 | import com.intellij.uiDesigner.core.GridLayoutManager; 6 | import com.intellij.uiDesigner.core.Spacer; 7 | import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; 8 | import net.bytebutcher.burpsendtoextension.models.CommandObject; 9 | 10 | import javax.swing.*; 11 | import java.awt.*; 12 | import java.awt.event.*; 13 | import java.util.List; 14 | 15 | public class SendToAddAdvancedDialog extends JDialog { 16 | private final Component parent; 17 | private final List placeholders; 18 | private JPanel contentPane; 19 | private JButton buttonOK; 20 | private JButton buttonCancel; 21 | private JPanel pnlPlaceholderBehaviour; 22 | 23 | private boolean success = false; 24 | 25 | public SendToAddAdvancedDialog(Component parent, List placeholders) { 26 | this.parent = parent; 27 | this.placeholders = placeholders; 28 | $$$setupUI$$$(); 29 | 30 | setContentPane(contentPane); 31 | setTitle("Customize Placeholder Behaviour"); 32 | setModal(true); 33 | getRootPane().setDefaultButton(buttonOK); 34 | 35 | buttonOK.addActionListener(new ActionListener() { 36 | public void actionPerformed(ActionEvent e) { 37 | onOK(); 38 | } 39 | }); 40 | 41 | buttonCancel.addActionListener(new ActionListener() { 42 | public void actionPerformed(ActionEvent e) { 43 | onCancel(); 44 | } 45 | }); 46 | 47 | // call onCancel() when cross is clicked 48 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 49 | addWindowListener(new WindowAdapter() { 50 | public void windowClosing(WindowEvent e) { 51 | onCancel(); 52 | } 53 | }); 54 | 55 | // call onCancel() on ESCAPE 56 | contentPane.registerKeyboardAction(new ActionListener() { 57 | public void actionPerformed(ActionEvent e) { 58 | onCancel(); 59 | } 60 | }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 61 | } 62 | 63 | public List getPlaceholders() { 64 | List placeholders = Lists.newArrayList(); 65 | for (Component component : pnlPlaceholderBehaviour.getComponents()) { 66 | placeholders.add(((SendToAddAdvancedPlaceholderBehaviourPanel) component).getPlaceholder()); 67 | } 68 | return placeholders; 69 | } 70 | 71 | private void onOK() { 72 | success = true; 73 | dispose(); 74 | } 75 | 76 | private void onCancel() { 77 | success = false; 78 | dispose(); 79 | } 80 | 81 | public boolean run() { 82 | for (CommandObject.Placeholder placeholder : placeholders) { 83 | pnlPlaceholderBehaviour.add(new SendToAddAdvancedPlaceholderBehaviourPanel(placeholder)); 84 | } 85 | this.setSize(450, 250); 86 | int x = DialogUtil.getX(parent, this); 87 | int y = DialogUtil.getY(parent, this); 88 | this.setLocation(x, y); 89 | this.pack(); 90 | this.setVisible(true); 91 | return success; 92 | } 93 | 94 | /** 95 | * Method generated by IntelliJ IDEA GUI Designer 96 | * >>> IMPORTANT!! <<< 97 | * DO NOT edit this method OR call it in your code! 98 | * 99 | * @noinspection ALL 100 | */ 101 | private void $$$setupUI$$$() { 102 | createUIComponents(); 103 | contentPane = new JPanel(); 104 | contentPane.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1)); 105 | final JPanel panel1 = new JPanel(); 106 | panel1.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1)); 107 | contentPane.add(panel1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false)); 108 | final Spacer spacer1 = new Spacer(); 109 | panel1.add(spacer1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 110 | final JPanel panel2 = new JPanel(); 111 | panel2.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1, true, false)); 112 | panel1.add(panel2, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 113 | buttonOK = new JButton(); 114 | buttonOK.setText("OK"); 115 | panel2.add(buttonOK, new GridConstraints(0, 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)); 116 | buttonCancel = new JButton(); 117 | buttonCancel.setText("Cancel"); 118 | panel2.add(buttonCancel, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 119 | final JPanel panel3 = new JPanel(); 120 | panel3.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1)); 121 | contentPane.add(panel3, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 122 | final JLabel label1 = new JLabel(); 123 | label1.setText("Customize the behaviour of each placeholder when multiple HTTP messages are selected:"); 124 | panel3.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)); 125 | final JPanel panel4 = new JPanel(); 126 | panel4.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1)); 127 | panel3.add(panel4, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 128 | final Spacer spacer2 = new Spacer(); 129 | panel4.add(spacer2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_VERTICAL, 1, GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 130 | panel4.add(pnlPlaceholderBehaviour, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 131 | } 132 | 133 | /** 134 | * @noinspection ALL 135 | */ 136 | public JComponent $$$getRootComponent$$$() { 137 | return contentPane; 138 | } 139 | 140 | private void createUIComponents() { 141 | pnlPlaceholderBehaviour = new JPanel(); 142 | pnlPlaceholderBehaviour.setLayout(new BoxLayout(this.pnlPlaceholderBehaviour, BoxLayout.Y_AXIS)); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedPlaceholderBehaviourPanel.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 |
42 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddAdvancedPlaceholderBehaviourPanel.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui; 2 | 3 | import burp.BurpExtender; 4 | import com.intellij.uiDesigner.core.GridConstraints; 5 | import com.intellij.uiDesigner.core.GridLayoutManager; 6 | import net.bytebutcher.burpsendtoextension.models.CommandObject; 7 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour; 8 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour; 9 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour; 10 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour; 11 | 12 | import javax.swing.*; 13 | import java.awt.*; 14 | import java.awt.event.ActionEvent; 15 | import java.awt.event.ActionListener; 16 | 17 | public class SendToAddAdvancedPlaceholderBehaviourPanel extends JPanel { 18 | 19 | private final CommandObject.Placeholder placeholder; 20 | 21 | private JTextField txtSeparator; 22 | private JPanel pnlMain; 23 | private JComboBox cmbSeperator; 24 | private JLabel lblPlaceholder; 25 | 26 | public SendToAddAdvancedPlaceholderBehaviourPanel(CommandObject.Placeholder placeholder) { 27 | this.placeholder = placeholder; 28 | this.add(pnlMain); 29 | this.lblPlaceholder.setText(placeholder.getName()); 30 | initFields(); 31 | initEventListener(); 32 | } 33 | 34 | private void initFields() { 35 | IPlaceholderBehaviour placeholderBehaviour = placeholder.getBehaviour(); 36 | if (placeholderBehaviour instanceof StringSeparatedPlaceholderBehaviour) { 37 | this.txtSeparator.setText(((StringSeparatedPlaceholderBehaviour) placeholderBehaviour).getSeparator()); 38 | this.txtSeparator.setEnabled(true); 39 | this.cmbSeperator.setSelectedIndex(1); 40 | } else if (placeholderBehaviour instanceof FileSeparatedPlaceholderBehaviour) { 41 | this.txtSeparator.setText(""); 42 | this.txtSeparator.setEnabled(false); 43 | this.cmbSeperator.setSelectedIndex(2); 44 | } else { 45 | this.txtSeparator.setText(""); 46 | this.txtSeparator.setEnabled(false); 47 | this.cmbSeperator.setSelectedIndex(0); 48 | } 49 | } 50 | 51 | private void initEventListener() { 52 | this.cmbSeperator.addActionListener(new ActionListener() { 53 | public void actionPerformed(ActionEvent e) { 54 | if (cmbSeperator.getSelectedIndex() == 1) { 55 | txtSeparator.setEnabled(true); 56 | txtSeparator.setText(""); 57 | } else { 58 | txtSeparator.setEnabled(false); 59 | txtSeparator.setText(""); 60 | } 61 | } 62 | }); 63 | } 64 | 65 | public CommandObject.Placeholder getPlaceholder() { 66 | switch (cmbSeperator.getSelectedIndex()) { 67 | case 1: 68 | placeholder.setBehaviour(new StringSeparatedPlaceholderBehaviour(txtSeparator.getText())); 69 | break; 70 | case 2: 71 | placeholder.setBehaviour(new FileSeparatedPlaceholderBehaviour()); 72 | break; 73 | default: 74 | placeholder.setBehaviour(new CommandSeparatedPlaceholderBehaviour()); 75 | break; 76 | } 77 | return placeholder; 78 | } 79 | 80 | { 81 | // GUI initializer generated by IntelliJ IDEA GUI Designer 82 | // >>> IMPORTANT!! <<< 83 | // DO NOT EDIT OR ADD ANY CODE HERE! 84 | $$$setupUI$$$(); 85 | } 86 | 87 | /** 88 | * Method generated by IntelliJ IDEA GUI Designer 89 | * >>> IMPORTANT!! <<< 90 | * DO NOT edit this method OR call it in your code! 91 | * 92 | * @noinspection ALL 93 | */ 94 | private void $$$setupUI$$$() { 95 | pnlMain = new JPanel(); 96 | pnlMain.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1)); 97 | txtSeparator = new JTextField(); 98 | pnlMain.add(txtSeparator, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false)); 99 | cmbSeperator = new JComboBox(); 100 | final DefaultComboBoxModel defaultComboBoxModel1 = new DefaultComboBoxModel(); 101 | defaultComboBoxModel1.addElement("split into separate commands"); 102 | defaultComboBoxModel1.addElement("separate by string"); 103 | defaultComboBoxModel1.addElement("merge into file"); 104 | cmbSeperator.setModel(defaultComboBoxModel1); 105 | pnlMain.add(cmbSeperator, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 106 | lblPlaceholder = new JLabel(); 107 | lblPlaceholder.setText("..."); 108 | pnlMain.add(lblPlaceholder, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 109 | } 110 | 111 | /** 112 | * @noinspection ALL 113 | */ 114 | public JComponent $$$getRootComponent$$$() { 115 | return pnlMain; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToAddDialog.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 |
192 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToContextMenu.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui; 2 | 3 | import burp.BurpExtender; 4 | import burp.IContextMenuFactory; 5 | import burp.IContextMenuInvocation; 6 | import com.google.common.collect.Lists; 7 | import com.google.common.collect.Maps; 8 | import net.bytebutcher.burpsendtoextension.models.CommandObject; 9 | import net.bytebutcher.burpsendtoextension.models.Context; 10 | import net.bytebutcher.burpsendtoextension.models.Placeholders; 11 | import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser; 12 | 13 | import javax.swing.*; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class SendToContextMenu implements IContextMenuFactory { 19 | 20 | private BurpExtender burpExtender; 21 | private SendToTableListener sendToTableListener; 22 | 23 | public SendToContextMenu(BurpExtender burpExtender, SendToTableListener sendToTableListener) { 24 | this.burpExtender = burpExtender; 25 | this.sendToTableListener = sendToTableListener; 26 | } 27 | 28 | @Override 29 | public List createMenuItems(IContextMenuInvocation invocation) { 30 | List> placeholders = Placeholders.get(BurpExtender.getCallbacks(), invocation.getSelectedMessages()); 31 | List commandObjects = BurpExtender.getConfig().getSendToTableData(); 32 | if (commandObjects.isEmpty()) { 33 | return Lists.newArrayList(); 34 | } 35 | 36 | JMenu sendToMenu = new JMenu("Send to..."); 37 | HashMap> groupedCommandObjects = Maps.newLinkedHashMap(); 38 | boolean hasEmptyGroup = false; 39 | for (final CommandObject commandObject : commandObjects) { 40 | String group = commandObject.getGroup(); 41 | if (group.isEmpty()) { 42 | addMenuItem(sendToMenu, commandObject, placeholders, invocation); 43 | hasEmptyGroup = true; 44 | continue; 45 | } 46 | if (!groupedCommandObjects.containsKey(group)) { 47 | groupedCommandObjects.put(group, Lists.newArrayList()); 48 | } 49 | groupedCommandObjects.get(group).add(commandObject); 50 | } 51 | if (hasEmptyGroup && !groupedCommandObjects.isEmpty()) { 52 | sendToMenu.addSeparator(); 53 | } 54 | for (String group : groupedCommandObjects.keySet()) { 55 | JMenu menuItem = new JMenu(group); 56 | for (CommandObject commandObject : groupedCommandObjects.get(group)) { 57 | addMenuItem(menuItem, commandObject, placeholders, invocation); 58 | } 59 | sendToMenu.add(menuItem); 60 | } 61 | return Lists.newArrayList(sendToMenu); 62 | } 63 | 64 | private void addMenuItem(JMenu menu, CommandObject commandObject, List> placeholders, IContextMenuInvocation invocation) { 65 | 66 | JMenuItem item; 67 | Context context = new Context(invocation); 68 | if (commandObject.doesRequireRequestResponse(placeholders.get(0)) && context.getOrigin() == Context.Origin.UNKNOWN) { 69 | item = new JMenu(commandObject.getName()); 70 | SendToContextMenuItem request = new SendToContextMenuItem("request", commandObject, placeholders, new Context(Context.Origin.HTTP_REQUEST, invocation), sendToTableListener); 71 | SendToContextMenuItem response = new SendToContextMenuItem("response", commandObject, placeholders, new Context(Context.Origin.HTTP_RESPONSE, invocation), sendToTableListener); 72 | item.add(request); 73 | item.add(response); 74 | item.setEnabled(request.isEnabled() || response.isEnabled()); 75 | } else { 76 | item = new SendToContextMenuItem(commandObject.getName(), commandObject, placeholders, context, sendToTableListener); 77 | } 78 | menu.add(item); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToContextMenuItem.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui; 2 | 3 | import net.bytebutcher.burpsendtoextension.gui.action.SendToContextMenuItemAction; 4 | import net.bytebutcher.burpsendtoextension.models.CommandObject; 5 | import net.bytebutcher.burpsendtoextension.models.Context; 6 | import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser; 7 | 8 | import javax.swing.*; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | public class SendToContextMenuItem extends JMenuItem { 13 | 14 | public SendToContextMenuItem(String title, CommandObject commandObject, List> placeholders, Context context, SendToTableListener sendToTableListener) { 15 | String text = ""; 16 | List> validEntries = commandObject.getValid(placeholders, context); 17 | if (placeholders.size() > 1) { 18 | text = title + " (" + validEntries.size() + "/" + placeholders.size() + ")"; 19 | } else { 20 | text = title; 21 | } 22 | this.setAction(new SendToContextMenuItemAction(text, commandObject, placeholders, sendToTableListener, context)); 23 | if (commandObject.shouldOutputReplaceSelection() && context.getSelectionBounds() == null) { 24 | // Always disable context menu item, when command should replace selection but no selection was made. 25 | this.setEnabled(false); 26 | } else { 27 | // Do only enable context menu item, when at least one HTTP-message can be used to construct the command. 28 | this.setEnabled(validEntries.size() > 0); 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToPreviewDialog.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
76 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToPreviewDialog.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui; 2 | 3 | import com.intellij.uiDesigner.core.GridConstraints; 4 | import com.intellij.uiDesigner.core.GridLayoutManager; 5 | import com.intellij.uiDesigner.core.Spacer; 6 | import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | import java.awt.event.ActionEvent; 11 | import java.awt.event.ActionListener; 12 | 13 | public class SendToPreviewDialog { 14 | 15 | private JTextArea txtCommandPreview; 16 | private JButton cancelButton; 17 | private JButton okButton; 18 | private JCheckBox alwaysShowThisDialogCheckBox; 19 | private JPanel formPanel; 20 | 21 | private SendToTableListener sendToTableListener; 22 | private AbstractAction onOkAction; 23 | private AbstractAction onCancelAction; 24 | private final JDialog dialog; 25 | private boolean success = false; 26 | 27 | public SendToPreviewDialog(JFrame parent, String title, final String command) { 28 | this.dialog = initDialog(parent, title); 29 | this.txtCommandPreview.setText(command); 30 | initEventListener(); 31 | initKeyboardShortcuts(); 32 | } 33 | 34 | public SendToPreviewDialog(JFrame parent, String title, final String command, final String commandId, final SendToTableListener sendToTableListener) { 35 | this(parent, title, command); 36 | this.sendToTableListener = sendToTableListener; 37 | initEventListener(commandId, sendToTableListener); 38 | } 39 | 40 | private void initEventListener() { 41 | onOkAction = new AbstractAction() { 42 | @Override 43 | public void actionPerformed(ActionEvent e) { 44 | success = true; 45 | dialog.dispose(); 46 | } 47 | }; 48 | okButton.addActionListener(onOkAction); 49 | onCancelAction = new AbstractAction() { 50 | @Override 51 | public void actionPerformed(ActionEvent e) { 52 | success = false; 53 | dialog.dispose(); 54 | } 55 | }; 56 | cancelButton.addActionListener(onCancelAction); 57 | } 58 | 59 | private void initEventListener(final String commandId, final SendToTableListener sendToTableListener) { 60 | alwaysShowThisDialogCheckBox.addActionListener(new ActionListener() { 61 | @Override 62 | public void actionPerformed(ActionEvent e) { 63 | sendToTableListener.onShowPreviewChange(e, commandId, alwaysShowThisDialogCheckBox.isSelected()); 64 | } 65 | }); 66 | alwaysShowThisDialogCheckBox.setVisible(true); 67 | } 68 | 69 | private JDialog initDialog(JFrame parent, String title) { 70 | JDialog dialog = new JDialog(parent, title, true); 71 | dialog.getContentPane().add(this.getRootPanel()); 72 | dialog.pack(); 73 | dialog.setSize(540, 200); 74 | int x = DialogUtil.getX(parent, dialog); 75 | int y = DialogUtil.getY(parent, dialog); 76 | dialog.setLocation(x, y); 77 | return dialog; 78 | } 79 | 80 | private void initKeyboardShortcuts() { 81 | bindKeyStrokeToAction("ESCAPE", onCancelAction); 82 | bindKeyStrokeToAction("ENTER", onOkAction); 83 | } 84 | 85 | private void bindKeyStrokeToAction(String keyStroke, Action action) { 86 | KeyStroke stroke = KeyStroke.getKeyStroke(keyStroke); 87 | InputMap inputMap = formPanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); 88 | inputMap.put(stroke, keyStroke); 89 | formPanel.getActionMap().put(keyStroke, action); 90 | } 91 | 92 | public boolean run() { 93 | this.dialog.setVisible(true); 94 | return this.success; 95 | } 96 | 97 | public String getCommand() { 98 | return this.txtCommandPreview.getText().replace("\r", ""); 99 | } 100 | 101 | private Component getRootPanel() { 102 | return formPanel; 103 | } 104 | 105 | { 106 | // GUI initializer generated by IntelliJ IDEA GUI Designer 107 | // >>> IMPORTANT!! <<< 108 | // DO NOT EDIT OR ADD ANY CODE HERE! 109 | $$$setupUI$$$(); 110 | } 111 | 112 | /** 113 | * Method generated by IntelliJ IDEA GUI Designer 114 | * >>> IMPORTANT!! <<< 115 | * DO NOT edit this method OR call it in your code! 116 | * 117 | * @noinspection ALL 118 | */ 119 | private void $$$setupUI$$$() { 120 | formPanel = new JPanel(); 121 | formPanel.setLayout(new GridLayoutManager(3, 1, new Insets(10, 10, 10, 10), -1, -1)); 122 | final JLabel label1 = new JLabel(); 123 | label1.setText("Do you really want to execute the following command(s)?"); 124 | formPanel.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)); 125 | final JPanel panel1 = new JPanel(); 126 | panel1.setLayout(new GridLayoutManager(1, 4, new Insets(0, 0, 0, 0), -1, -1)); 127 | formPanel.add(panel1, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 128 | final Spacer spacer1 = new Spacer(); 129 | panel1.add(spacer1, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 130 | cancelButton = new JButton(); 131 | cancelButton.setText("Cancel"); 132 | cancelButton.setMnemonic('C'); 133 | cancelButton.setDisplayedMnemonicIndex(0); 134 | panel1.add(cancelButton, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 135 | okButton = new JButton(); 136 | okButton.setText("Ok"); 137 | okButton.setMnemonic('O'); 138 | okButton.setDisplayedMnemonicIndex(0); 139 | panel1.add(okButton, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 140 | alwaysShowThisDialogCheckBox = new JCheckBox(); 141 | alwaysShowThisDialogCheckBox.setSelected(true); 142 | alwaysShowThisDialogCheckBox.setText("Always show this dialog"); 143 | alwaysShowThisDialogCheckBox.setVisible(false); 144 | panel1.add(alwaysShowThisDialogCheckBox, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 145 | final JScrollPane scrollPane1 = new JScrollPane(); 146 | formPanel.add(scrollPane1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false)); 147 | txtCommandPreview = new JTextArea(); 148 | scrollPane1.setViewportView(txtCommandPreview); 149 | } 150 | 151 | /** 152 | * @noinspection ALL 153 | */ 154 | public JComponent $$$getRootComponent$$$() { 155 | return formPanel; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToRunInTerminalBehaviourChoiceDialog.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 |
113 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToRunInTerminalBehaviourChoiceDialog.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui; 2 | 3 | import com.intellij.uiDesigner.core.GridConstraints; 4 | import com.intellij.uiDesigner.core.GridLayoutManager; 5 | import com.intellij.uiDesigner.core.Spacer; 6 | import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; 7 | import net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour; 8 | 9 | import javax.swing.*; 10 | import java.awt.*; 11 | import java.awt.event.*; 12 | 13 | public class SendToRunInTerminalBehaviourChoiceDialog extends JDialog { 14 | 15 | private EChoice choice; 16 | 17 | public enum EChoice { 18 | RUN_IN_SINGLE_TERMINAL, 19 | RUN_IN_SEPARATE_TERMINALS, 20 | REVIEW_COMMANDS, 21 | CANCEL 22 | } 23 | 24 | private final JFrame parent; 25 | private JPanel contentPane; 26 | private JButton btnRunInSingleTerminal; 27 | private JButton btnCancel; 28 | private JButton btnRunInSeparateTerminals; 29 | private JLabel lblCommandCount; 30 | private JButton btnReviewCommands; 31 | 32 | public SendToRunInTerminalBehaviourChoiceDialog(JFrame parent, ERunInTerminalBehaviour defaultChoice, int nrOfCommands) { 33 | this.parent = parent; 34 | this.choice = getDefaultChoice(defaultChoice); 35 | this.lblCommandCount.setText(String.valueOf(nrOfCommands)); 36 | setContentPane(contentPane); 37 | setTitle("Select execution behaviour"); 38 | setModal(true); 39 | 40 | btnRunInSeparateTerminals.addActionListener(e -> onButtonPress(EChoice.RUN_IN_SEPARATE_TERMINALS)); 41 | btnRunInSingleTerminal.addActionListener(e -> onButtonPress(EChoice.RUN_IN_SINGLE_TERMINAL)); 42 | btnReviewCommands.addActionListener(e -> onButtonPress(EChoice.REVIEW_COMMANDS)); 43 | btnCancel.addActionListener(e -> onButtonPress(EChoice.CANCEL)); 44 | 45 | // call onCancel() when cross is clicked 46 | setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); 47 | addWindowListener(new WindowAdapter() { 48 | public void windowClosing(WindowEvent e) { 49 | onCancel(); 50 | } 51 | }); 52 | 53 | // call onCancel() on ESCAPE 54 | contentPane.registerKeyboardAction(new ActionListener() { 55 | public void actionPerformed(ActionEvent e) { 56 | onCancel(); 57 | } 58 | }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 59 | this.pack(); 60 | initButtonState(defaultChoice); 61 | } 62 | 63 | private void initButtonState(ERunInTerminalBehaviour defaultChoice) { 64 | switch (defaultChoice) { 65 | case RUN_IN_SEPARATE_TERMINALS: 66 | SwingUtilities.getRootPane(btnRunInSeparateTerminals).setDefaultButton(btnRunInSeparateTerminals); 67 | btnRunInSeparateTerminals.grabFocus(); 68 | break; 69 | case RUN_IN_SINGLE_TERMINAL: 70 | // fall through 71 | default: 72 | SwingUtilities.getRootPane(btnRunInSingleTerminal).setDefaultButton(btnRunInSingleTerminal); 73 | btnRunInSingleTerminal.grabFocus(); 74 | } 75 | } 76 | 77 | private EChoice getDefaultChoice(ERunInTerminalBehaviour defaultChoice) { 78 | switch (defaultChoice) { 79 | case RUN_IN_SEPARATE_TERMINALS: 80 | return EChoice.RUN_IN_SEPARATE_TERMINALS; 81 | case RUN_IN_SINGLE_TERMINAL: 82 | // fall through 83 | default: 84 | return EChoice.RUN_IN_SINGLE_TERMINAL; 85 | } 86 | } 87 | 88 | public EChoice run() { 89 | int x = DialogUtil.getX(parent, this); 90 | int y = DialogUtil.getY(parent, this); 91 | this.setLocation(x, y); 92 | this.setVisible(true); 93 | return choice; 94 | } 95 | 96 | private void onButtonPress(EChoice choice) { 97 | this.choice = choice; 98 | dispose(); 99 | } 100 | 101 | private void onCancel() { 102 | this.choice = EChoice.CANCEL; 103 | dispose(); 104 | } 105 | 106 | { 107 | // GUI initializer generated by IntelliJ IDEA GUI Designer 108 | // >>> IMPORTANT!! <<< 109 | // DO NOT EDIT OR ADD ANY CODE HERE! 110 | $$$setupUI$$$(); 111 | } 112 | 113 | /** 114 | * Method generated by IntelliJ IDEA GUI Designer 115 | * >>> IMPORTANT!! <<< 116 | * DO NOT edit this method OR call it in your code! 117 | * 118 | * @noinspection ALL 119 | */ 120 | private void $$$setupUI$$$() { 121 | contentPane = new JPanel(); 122 | contentPane.setLayout(new GridLayoutManager(2, 1, new Insets(10, 10, 10, 10), -1, -1)); 123 | final JPanel panel1 = new JPanel(); 124 | panel1.setLayout(new GridLayoutManager(1, 6, new Insets(0, 0, 0, 0), -1, -1)); 125 | contentPane.add(panel1, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, 1, null, null, null, 0, false)); 126 | final Spacer spacer1 = new Spacer(); 127 | panel1.add(spacer1, new GridConstraints(0, 5, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 128 | btnRunInSeparateTerminals = new JButton(); 129 | btnRunInSeparateTerminals.setText("Run in separate terminals"); 130 | btnRunInSeparateTerminals.setMnemonic('S'); 131 | btnRunInSeparateTerminals.setDisplayedMnemonicIndex(7); 132 | panel1.add(btnRunInSeparateTerminals, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 133 | btnCancel = new JButton(); 134 | btnCancel.setText("Cancel"); 135 | panel1.add(btnCancel, new GridConstraints(0, 4, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 136 | final Spacer spacer2 = new Spacer(); 137 | panel1.add(spacer2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 138 | btnReviewCommands = new JButton(); 139 | btnReviewCommands.setText("Review commands"); 140 | btnReviewCommands.setMnemonic('O'); 141 | btnReviewCommands.setDisplayedMnemonicIndex(8); 142 | panel1.add(btnReviewCommands, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 143 | btnRunInSingleTerminal = new JButton(); 144 | btnRunInSingleTerminal.setText("Run in single terminal"); 145 | btnRunInSingleTerminal.setMnemonic('I'); 146 | btnRunInSingleTerminal.setDisplayedMnemonicIndex(8); 147 | panel1.add(btnRunInSingleTerminal, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 148 | final JPanel panel2 = new JPanel(); 149 | panel2.setLayout(new GridLayoutManager(2, 4, new Insets(0, 0, 0, 0), -1, -1)); 150 | contentPane.add(panel2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false)); 151 | final JLabel label1 = new JLabel(); 152 | label1.setText("Please select how the commands should be executed."); 153 | panel2.add(label1, new GridConstraints(1, 0, 1, 4, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 154 | final JLabel label2 = new JLabel(); 155 | label2.setText("You are going to execute"); 156 | panel2.add(label2, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 157 | lblCommandCount = new JLabel(); 158 | lblCommandCount.setText("0"); 159 | panel2.add(lblCommandCount, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 160 | final JLabel label3 = new JLabel(); 161 | label3.setText("commands."); 162 | panel2.add(label3, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false)); 163 | final Spacer spacer3 = new Spacer(); 164 | panel2.add(spacer3, new GridConstraints(0, 3, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, 1, null, null, null, 0, false)); 165 | } 166 | 167 | /** 168 | * @noinspection ALL 169 | */ 170 | public JComponent $$$getRootComponent$$$() { 171 | return contentPane; 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTabSettingsContextMenu.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui; 2 | 3 | import burp.BurpExtender; 4 | import com.google.gson.Gson; 5 | import com.google.gson.reflect.TypeToken; 6 | import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; 7 | import net.bytebutcher.burpsendtoextension.models.CommandObject; 8 | 9 | import javax.swing.*; 10 | import java.awt.event.ActionEvent; 11 | import java.awt.event.ActionListener; 12 | import java.io.File; 13 | import java.io.FileNotFoundException; 14 | import java.io.FileReader; 15 | import java.io.PrintWriter; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | class SendToTabSettingsContextMenu extends JPopupMenu { 20 | 21 | private BurpExtender burpExtender; 22 | private final JMenuItem restoreDefaults; 23 | private final JMenuItem loadOptions; 24 | private final JMenuItem saveOptions; 25 | 26 | private SendToTable sendToTable; 27 | private SendToTab sendToTab; 28 | 29 | public SendToTabSettingsContextMenu(final BurpExtender burpExtender, final SendToTab sendToTab) { 30 | this.burpExtender = burpExtender; 31 | this.sendToTab = sendToTab; 32 | this.sendToTable = sendToTab.getSendToTable(); 33 | restoreDefaults = new JMenuItem("Restore defaults"); 34 | restoreDefaults.addActionListener(new ActionListener() { 35 | @Override 36 | public void actionPerformed(ActionEvent e) { 37 | boolean result = DialogUtil.showConfirmationDialog(sendToTab.getParent(), "Reset \"Send to\"-options", 38 | "Do you really want to reset the \"Send to\"-options?"); 39 | if (result) { 40 | sendToTab.resetOptions(); 41 | } 42 | } 43 | }); 44 | add(restoreDefaults); 45 | loadOptions = new JMenuItem("Load options"); 46 | loadOptions.addActionListener(new ActionListener() { 47 | @Override 48 | public void actionPerformed(ActionEvent e) { 49 | burpExtender.getCallbacks().printOutput("Loading options..."); 50 | JFileChooser fileChooser = new JFileChooser(); 51 | fileChooser.setDialogTitle("Load \"Send to\" options from file..."); 52 | fileChooser.setCurrentDirectory(new File(System.getProperty("user.home"))); 53 | int result = fileChooser.showOpenDialog(getParent()); 54 | if (result == JFileChooser.APPROVE_OPTION) { 55 | File selectedFile = fileChooser.getSelectedFile(); 56 | try { 57 | burpExtender.getCallbacks().printOutput("Reading selected file: " + selectedFile.getAbsolutePath()); 58 | List commandObjectList = new Gson().fromJson(new FileReader(selectedFile), new TypeToken>(){}.getType()); 59 | burpExtender.getCallbacks().printOutput("Adding " + commandObjectList.size() + " items to table..."); 60 | sendToTable.removeAll(); 61 | sendToTable.addCommandObjects(commandObjectList); 62 | burpExtender.getCallbacks().printOutput("Successfully loaded options into table!"); 63 | } catch (FileNotFoundException e1) { 64 | DialogUtil.showErrorDialog( 65 | sendToTab.getParent(), 66 | "Error while loading options!", 67 | "

There was an unknown error while loading the options!

" + 68 | "

For more information check out the \"Send to\" extension error log!

" 69 | ); 70 | burpExtender.getCallbacks().printError("Error while loading options: " + e1); 71 | return; 72 | } catch (Exception e2) { 73 | burpExtender.getCallbacks().printError("Error while loading options: " + e2); 74 | return; 75 | } 76 | } 77 | } 78 | }); 79 | add(loadOptions); 80 | saveOptions = new JMenuItem("Save options"); 81 | saveOptions.addActionListener(new ActionListener() { 82 | @Override 83 | public void actionPerformed(ActionEvent e) { 84 | JFileChooser fileChooser = new JFileChooser(); 85 | fileChooser.setDialogTitle("Save \"Send to\" options to file..."); 86 | 87 | int userSelection = fileChooser.showSaveDialog(getParent()); 88 | if (userSelection == JFileChooser.APPROVE_OPTION) { 89 | File fileToSave = fileChooser.getSelectedFile(); 90 | String json = new Gson().toJson(sendToTable.getCommandObjects()); 91 | try (PrintWriter out = new PrintWriter(fileToSave)) { 92 | out.write(json); 93 | } catch (FileNotFoundException e1) { 94 | DialogUtil.showErrorDialog( 95 | sendToTab.getParent(), 96 | "Error while saving options!", 97 | "

There was an unknown error while saving the options!

" + 98 | "

For more information check out the \"Send to\" extension error log!

" 99 | ); 100 | burpExtender.getCallbacks().printError("Error while saving options: " + e1); 101 | return; 102 | } 103 | burpExtender.getCallbacks().printOutput("Successfully saved options in '" + fileToSave.getAbsolutePath() + "'!"); 104 | } 105 | } 106 | }); 107 | add(saveOptions); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTable.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui; 2 | 3 | import burp.BurpExtender; 4 | import com.google.common.collect.Lists; 5 | import com.google.common.primitives.Ints; 6 | import net.bytebutcher.burpsendtoextension.models.CommandObject; 7 | import net.bytebutcher.burpsendtoextension.models.ERuntimeBehaviour; 8 | 9 | import javax.swing.*; 10 | import javax.swing.event.TableModelEvent; 11 | import javax.swing.table.DefaultTableModel; 12 | import java.util.*; 13 | import java.util.stream.Collectors; 14 | 15 | public class SendToTable extends JTable { 16 | 17 | private final DefaultTableModel defaultModel; 18 | private BurpExtender burpExtender; 19 | 20 | private enum Column { 21 | ID(0), 22 | NAME(1), 23 | COMMAND(2), 24 | GROUP(3), 25 | RUNTIME_BEHAVIOUR(4), 26 | SHOW_PREVIEW(5), 27 | PLACEHOLDERS(6); 28 | 29 | private final int index; 30 | 31 | Column(int id) { 32 | this.index = id; 33 | } 34 | 35 | public int getIndex() { 36 | return index; 37 | } 38 | } 39 | 40 | private class SendToTableModel extends DefaultTableModel { 41 | 42 | private boolean areEventsBlocked; 43 | 44 | @Override 45 | public boolean isCellEditable(int row, int column) { 46 | return false; 47 | } 48 | 49 | @Override 50 | public void fireTableChanged(TableModelEvent e) { 51 | if (!areEventsBlocked) { 52 | super.fireTableChanged(e); 53 | } 54 | } 55 | 56 | public void setBlockEvents(boolean areEventsBlocked) { 57 | this.areEventsBlocked = areEventsBlocked; 58 | } 59 | } 60 | 61 | public SendToTable(BurpExtender burpExtender) { 62 | this.burpExtender = burpExtender; 63 | this.defaultModel = new SendToTableModel(); 64 | this.defaultModel.addColumn("Id"); 65 | this.defaultModel.addColumn("Name"); 66 | this.defaultModel.addColumn("Command"); 67 | this.defaultModel.addColumn("Group name"); 68 | this.defaultModel.addColumn("Runtime behaviour"); 69 | this.defaultModel.addColumn("Show preview"); 70 | this.defaultModel.addColumn("Placeholder behaviour"); 71 | setModel(this.defaultModel); 72 | hideColumns(Column.ID, Column.COMMAND, Column.PLACEHOLDERS); 73 | } 74 | 75 | private void hideColumns(Column ... c) { 76 | List collect = Arrays.stream(c).sorted(Comparator.comparingInt(Column::getIndex).reversed()).collect(Collectors.toList()); 77 | for (Column column : collect) { 78 | this.removeColumn(this.getColumnModel().getColumn(column.getIndex())); 79 | } 80 | } 81 | 82 | public CommandObject getSelectedCommandObject() { 83 | int[] selectedRows = this.getSelectedRows(); 84 | if (selectedRows.length > 0) { 85 | int selectedRow = selectedRows[0]; 86 | return getCommandObjectByRowIndex(selectedRow); 87 | } 88 | throw new IllegalStateException("No row selected!"); 89 | } 90 | 91 | public DefaultTableModel getDefaultModel() { 92 | return defaultModel; 93 | } 94 | 95 | public String getSelectedNames() { 96 | int[] selectedRows = this.getSelectedRows(); 97 | if (selectedRows.length > 0) { 98 | int selectedRow = selectedRows[0]; 99 | return getNameByRowIndex(selectedRow); 100 | } 101 | throw new IllegalStateException("No row selected!"); 102 | } 103 | 104 | private String getNameByRowIndex(int rowIndex) { 105 | return Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.NAME.getIndex())).orElse("").toString(); 106 | } 107 | 108 | private boolean getShowPreviewByRowIndex(int rowIndex) { 109 | return Boolean.parseBoolean(Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.SHOW_PREVIEW.getIndex())).orElse("").toString()); 110 | } 111 | 112 | private ERuntimeBehaviour getRuntimeBehaviourByRowIndex(int rowIndex) { 113 | return ERuntimeBehaviour.getEnum(Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.RUNTIME_BEHAVIOUR.getIndex())).orElse("").toString()); 114 | } 115 | 116 | private String getGroupByRowIndex(int rowIndex) { 117 | return Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.GROUP.getIndex())).orElse("").toString(); 118 | } 119 | 120 | private String getCommandByRowIndex(int rowIndex) { 121 | return Optional.ofNullable(this.getModel().getValueAt(rowIndex, Column.COMMAND.getIndex())).orElse("").toString(); 122 | } 123 | 124 | private List getPlaceholders(int rowIndex) { 125 | try { 126 | return Lists.newArrayList((List) this.getModel().getValueAt(rowIndex, Column.PLACEHOLDERS.getIndex())); 127 | } catch (Exception e) { 128 | BurpExtender.printErr("Casting of placeholder behaviour failed for row " + rowIndex); 129 | return Lists.newArrayList(); 130 | } 131 | } 132 | 133 | public List getCommandObjects() { 134 | List commandObjects = Lists.newArrayList(); 135 | for (int i = 0; i < this.getDefaultModel().getRowCount(); i++) { 136 | commandObjects.add(getCommandObjectByRowIndex(i)); 137 | } 138 | return commandObjects; 139 | } 140 | 141 | private CommandObject getCommandObjectByRowIndex(int rowIndex) { 142 | String id = this.getModel().getValueAt(rowIndex, Column.ID.getIndex()).toString(); 143 | String name = getNameByRowIndex(rowIndex); 144 | String command = getCommandByRowIndex(rowIndex); 145 | String group = getGroupByRowIndex(rowIndex); 146 | ERuntimeBehaviour runtimeBehaviour = getRuntimeBehaviourByRowIndex(rowIndex); 147 | boolean showPreview = getShowPreviewByRowIndex(rowIndex); 148 | List placeholders = getPlaceholders(rowIndex); 149 | return new CommandObject(id, name, command, group, runtimeBehaviour, showPreview, placeholders); 150 | } 151 | 152 | public CommandObject getCommandObjectById(String commandId) { 153 | if (commandId == null) { 154 | BurpExtender.printErr("CommandObject id should not be null!"); 155 | throw new IllegalArgumentException("CommandObject id should not be null!"); 156 | } 157 | for (int i = 0; i < this.getDefaultModel().getRowCount(); i++) { 158 | CommandObject commandObject = getCommandObjectByRowIndex(i); 159 | if (commandId.equals(commandObject.getId())) { 160 | return commandObject; 161 | } 162 | } 163 | BurpExtender.printErr("No command found with the specified id!"); 164 | throw new IllegalStateException("No command found with the specified id!"); 165 | } 166 | 167 | public void addCommandObjects(List commandObjectList) { 168 | for (CommandObject commandObject : commandObjectList) { 169 | addCommandObject(commandObject); 170 | } 171 | } 172 | 173 | public void addCommandObject(CommandObject commandObject) { 174 | getDefaultModel().addRow(new Object[]{ 175 | commandObject.getId(), 176 | commandObject.getName(), 177 | commandObject.getFormat(), 178 | commandObject.getGroup(), 179 | commandObject.getRuntimeBehaviour().alternateName(), 180 | commandObject.shouldShowPreview(), 181 | commandObject.getPlaceholders() 182 | }); 183 | } 184 | 185 | public void editSelectedCommandObject(CommandObject commandObject) { 186 | int selectedRowIndex = this.getSelectedRow(); 187 | if (selectedRowIndex >= 0) { 188 | editRow(selectedRowIndex, commandObject); 189 | } 190 | } 191 | 192 | private void editRow(int rowIndex, CommandObject commandObject) { 193 | DefaultTableModel model = getDefaultModel(); 194 | ((SendToTableModel) getDefaultModel()).setBlockEvents(true); 195 | model.setValueAt(commandObject.getId(), rowIndex, Column.ID.getIndex()); 196 | model.setValueAt(commandObject.getName(), rowIndex, Column.NAME.getIndex()); 197 | model.setValueAt(commandObject.getGroup(), rowIndex, Column.GROUP.getIndex()); 198 | model.setValueAt(commandObject.getFormat(), rowIndex, Column.COMMAND.getIndex()); 199 | model.setValueAt(commandObject.getRuntimeBehaviour().alternateName(), rowIndex, Column.RUNTIME_BEHAVIOUR.getIndex()); 200 | model.setValueAt(commandObject.shouldShowPreview(), rowIndex, Column.SHOW_PREVIEW.getIndex()); 201 | model.setValueAt(commandObject.getPlaceholders(), rowIndex, Column.PLACEHOLDERS.getIndex()); 202 | ((SendToTableModel) getDefaultModel()).setBlockEvents(false); 203 | getDefaultModel().fireTableDataChanged(); 204 | } 205 | 206 | public void editCommandObject(CommandObject commandObject) { 207 | for (int i = 0; i < this.getDefaultModel().getRowCount(); i++) { 208 | CommandObject commandObjectFromRow = getCommandObjectByRowIndex(i); 209 | if (commandObjectFromRow.getId().equals(commandObject.getId())) { 210 | editRow(i, commandObject); 211 | return; 212 | } 213 | } 214 | } 215 | 216 | public void removeSelectedRow() { 217 | List rows = Ints.asList(this.getSelectedRows()); 218 | Collections.sort(rows, Collections.reverseOrder()); 219 | for (Integer row : rows) { 220 | getDefaultModel().removeRow(row); 221 | } 222 | } 223 | 224 | public void clearTable() { 225 | for (int row = this.getRowCount() - 1; row >= 0; row--) { 226 | getDefaultModel().removeRow(row); 227 | } 228 | } 229 | 230 | public void moveSelectedRowUp() { 231 | moveRowBy(-1); 232 | } 233 | 234 | public void moveSelectedRowDown() { 235 | moveRowBy(1); 236 | } 237 | 238 | private void moveRowBy(int index) { 239 | DefaultTableModel model = (DefaultTableModel) this.getModel(); 240 | int[] rows = this.getSelectedRows(); 241 | int destination = rows[0] + index; 242 | int rowCount = model.getRowCount(); 243 | 244 | if (destination < 0 || destination >= rowCount) { 245 | return; 246 | } 247 | 248 | model.moveRow(rows[0], rows[rows.length - 1], destination); 249 | this.setRowSelectionInterval(rows[0] + index, rows[rows.length - 1] + index); 250 | } 251 | 252 | @Override 253 | public void removeAll() { 254 | DefaultTableModel model = (DefaultTableModel) this.getModel(); 255 | for (int i = 0; i < model.getRowCount(); i++) { 256 | model.removeRow(i); 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/SendToTableListener.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui; 2 | 3 | import burp.BurpExtender; 4 | import net.bytebutcher.burpsendtoextension.models.CommandObject; 5 | 6 | import javax.swing.*; 7 | import javax.swing.event.TableModelEvent; 8 | import javax.swing.event.TableModelListener; 9 | import java.awt.event.ActionEvent; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class SendToTableListener implements TableModelListener { 14 | 15 | private SendToTable sendToTable; 16 | 17 | public SendToTableListener(SendToTable sendToTable) { 18 | this.sendToTable = sendToTable; 19 | } 20 | 21 | @Override 22 | public void tableChanged(TableModelEvent e) { 23 | BurpExtender.getConfig().saveSendToTableData(sendToTable.getCommandObjects()); 24 | } 25 | 26 | public void onAddButtonClick(ActionEvent e, CommandObject commandObject) { 27 | sendToTable.addCommandObject(commandObject); 28 | } 29 | 30 | public void onEditButtonClick(ActionEvent e, CommandObject commandObject) { 31 | sendToTable.editSelectedCommandObject(commandObject); 32 | } 33 | 34 | public void onRemoveButtonClick(ActionEvent e) { 35 | sendToTable.removeSelectedRow(); 36 | } 37 | 38 | public void onUpButtonClick(ActionEvent e) { 39 | sendToTable.moveSelectedRowUp(); 40 | } 41 | 42 | public void onDownButtonClick(ActionEvent e) { 43 | sendToTable.moveSelectedRowDown(); 44 | } 45 | 46 | public void onShowPreviewChange(ActionEvent e, String commandId, boolean showPreview) { 47 | CommandObject commandObject = sendToTable.getCommandObjectById(commandId); 48 | commandObject.setShowPreview(showPreview); 49 | sendToTable.editCommandObject(commandObject); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/action/SendToContextMenuItemAction.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui.action; 2 | 3 | import burp.BurpExtender; 4 | import net.bytebutcher.burpsendtoextension.builder.CommandBuilder; 5 | import net.bytebutcher.burpsendtoextension.executioner.CommandExecutioner; 6 | import net.bytebutcher.burpsendtoextension.gui.SendToPreviewDialog; 7 | import net.bytebutcher.burpsendtoextension.gui.SendToRunInTerminalBehaviourChoiceDialog; 8 | import net.bytebutcher.burpsendtoextension.gui.SendToTableListener; 9 | import net.bytebutcher.burpsendtoextension.gui.util.DialogUtil; 10 | import net.bytebutcher.burpsendtoextension.models.CommandObject; 11 | import net.bytebutcher.burpsendtoextension.models.Context; 12 | import net.bytebutcher.burpsendtoextension.models.ERunInTerminalBehaviour; 13 | import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser; 14 | 15 | import javax.swing.*; 16 | import java.awt.event.ActionEvent; 17 | import java.io.PrintWriter; 18 | import java.io.StringWriter; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | public class SendToContextMenuItemAction extends AbstractAction { 23 | 24 | private final CommandObject commandObject; 25 | private final List> placeholders; 26 | private final SendToTableListener sendToTableListener; 27 | private final Context context; 28 | 29 | public SendToContextMenuItemAction(String title, CommandObject commandObject, List> placeholders, SendToTableListener sendToTableListener, Context context) { 30 | super(title); 31 | this.commandObject = commandObject; 32 | this.placeholders = placeholders; 33 | this.sendToTableListener = sendToTableListener; 34 | this.context = context; 35 | } 36 | @Override 37 | public void actionPerformed(ActionEvent e) { 38 | try { 39 | String command = new CommandBuilder(commandObject, placeholders, context).build(); 40 | if (commandObject.shouldShowPreview()) { 41 | command = showSendToPreviewDialog(commandObject.getId(), command); 42 | } 43 | if (command == null) { 44 | return; 45 | } 46 | if (commandObject.shouldRunInTerminal()) { 47 | runCommandInTerminal(command); 48 | } else { 49 | runCommandInBackground(command); 50 | } 51 | } catch (Exception e1) { 52 | DialogUtil.showErrorDialog( 53 | BurpExtender.getParent(), 54 | "Error during command execution!", 55 | "

There was an unknown error during command execution!

" + 56 | "

For more information check out the \"Send to\" extension error log!

" 57 | ); 58 | BurpExtender.printErr("Error during command execution: " + e1); 59 | BurpExtender.printErr(stackTraceToString(e1)); 60 | } 61 | } 62 | 63 | private void runCommandInBackground(String command) throws Exception { 64 | new CommandExecutioner(commandObject.shouldOutputReplaceSelection(), context).execute(command); 65 | } 66 | 67 | private void runCommandInTerminal(String command) throws Exception { 68 | ERunInTerminalBehaviour runInTerminalBehaviour = BurpExtender.getConfig().getRunInTerminalBehaviour(); 69 | boolean containsMultipleCommands = command.contains("\n"); 70 | if (containsMultipleCommands && BurpExtender.getConfig().shouldShowRunInTerminalBehaviourChoiceDialog()) { 71 | SendToRunInTerminalBehaviourChoiceDialog.EChoice choice = null; 72 | while (choice != SendToRunInTerminalBehaviourChoiceDialog.EChoice.RUN_IN_SEPARATE_TERMINALS && choice != SendToRunInTerminalBehaviourChoiceDialog.EChoice.RUN_IN_SINGLE_TERMINAL) { 73 | choice = new SendToRunInTerminalBehaviourChoiceDialog(BurpExtender.getParent(), runInTerminalBehaviour, command.split("\n").length).run(); 74 | switch (choice) { 75 | case RUN_IN_SINGLE_TERMINAL: 76 | runInTerminalBehaviour = ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL; 77 | break; 78 | case RUN_IN_SEPARATE_TERMINALS: 79 | runInTerminalBehaviour = ERunInTerminalBehaviour.RUN_IN_SEPARATE_TERMINALS; 80 | break; 81 | case REVIEW_COMMANDS: 82 | command = showSendToPreviewDialog(command); 83 | if (command == null) { 84 | return; 85 | } 86 | case CANCEL: 87 | return; 88 | } 89 | } 90 | } 91 | new CommandExecutioner(runInTerminalBehaviour, commandObject.shouldOutputReplaceSelection(), context).execute(command); 92 | } 93 | 94 | private String showSendToPreviewDialog(String command) { 95 | SendToPreviewDialog previewDialog = new SendToPreviewDialog(BurpExtender.getParent(), "Review commands", command); 96 | return previewDialog.run() ? previewDialog.getCommand() : null; 97 | } 98 | 99 | private String showSendToPreviewDialog(String id, String command) throws Exception { 100 | SendToPreviewDialog previewDialog = new SendToPreviewDialog( 101 | BurpExtender.getParent(), 102 | "Execute command?", 103 | command, id, 104 | sendToTableListener 105 | ); 106 | if (!previewDialog.run()) { 107 | return null; 108 | } 109 | return previewDialog.getCommand(); 110 | } 111 | 112 | private String stackTraceToString(Exception e) { 113 | StringWriter sw = new StringWriter(); 114 | PrintWriter pw = new PrintWriter(sw); 115 | e.printStackTrace(pw); 116 | return sw.toString(); 117 | } 118 | } -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/listener/ToolTipActionListener.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui.listener; 2 | 3 | import javax.swing.*; 4 | import java.awt.event.ActionEvent; 5 | import java.awt.event.ActionListener; 6 | 7 | public class ToolTipActionListener implements ActionListener { 8 | 9 | private JComponent component; 10 | private String toolTipText; 11 | 12 | public ToolTipActionListener(JComponent component, String toolTipText) { 13 | this.component = component; 14 | this.toolTipText = toolTipText; 15 | } 16 | 17 | @Override 18 | public void actionPerformed(ActionEvent e) { 19 | JToolTip toolTip = this.component.createToolTip(); 20 | toolTip.setTipText(this.toolTipText); 21 | PopupFactory popupFactory = PopupFactory.getSharedInstance(); 22 | int x = this.component.getLocationOnScreen().x; 23 | int y = this.component.getLocationOnScreen().y; 24 | x += this.component.getWidth() / 2; 25 | y += this.component.getHeight(); 26 | final Popup tooltipContainer = popupFactory.getPopup(this.component, toolTip, x, y); 27 | tooltipContainer.show(); 28 | (new Thread(new Runnable() { 29 | public void run() { 30 | try { 31 | Thread.sleep(10000); 32 | } catch (InterruptedException ex) { 33 | // Nothing to do here. 34 | } 35 | tooltipContainer.hide(); 36 | } 37 | 38 | })).start(); 39 | } 40 | 41 | public String getToolTipText() { 42 | return toolTipText; 43 | } 44 | 45 | public void setToolTipText(String toolTipText) { 46 | this.toolTipText = toolTipText; 47 | } 48 | 49 | public JComponent getComponent() { 50 | return component; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/util/DialogUtil.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui.util; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | 6 | public final class DialogUtil { 7 | 8 | public static void showErrorDialog(Component parent, String title, String message) { 9 | final JDialog dlgError = new JOptionPane(message, JOptionPane.ERROR_MESSAGE).createDialog(parent, title); 10 | dlgError.setLocation(getX(parent, dlgError),getY(parent, dlgError)); 11 | dlgError.setVisible(true); 12 | } 13 | 14 | public static boolean showConfirmationDialog(Component parent, String title, String message) { 15 | JOptionPane jOptionPane = new JOptionPane(message, JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION); 16 | final JDialog dlgError = jOptionPane.createDialog(parent, title); 17 | dlgError.setLocation(getX(parent, dlgError),getY(parent, dlgError)); 18 | dlgError.setVisible(true); 19 | return ((Integer) jOptionPane.getValue()).intValue() == JOptionPane.YES_OPTION; 20 | } 21 | 22 | public static int getX(Component parent, Component child) { 23 | try { 24 | return parent.getX() + (parent.getWidth() / 2) - (child.getWidth() / 2); 25 | } catch (NullPointerException e) { 26 | return 0; 27 | } 28 | } 29 | 30 | public static int getY(Component parent, Component child) { 31 | try { 32 | return parent.getY() + (parent.getHeight() / 2) - (child.getHeight() / 2); 33 | } catch (NullPointerException e) { 34 | return 0; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/util/SelectionUtil.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui.util; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.util.Arrays; 6 | 7 | public class SelectionUtil { 8 | 9 | public static byte[] replaceSelectedText(byte[] message, int[] bounds, String replaceText) throws IOException { 10 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 11 | outputStream.write(Arrays.copyOfRange(message, 0, bounds[0])); 12 | outputStream.write(replaceText.getBytes()); 13 | outputStream.write(Arrays.copyOfRange(message, bounds[1], message.length)); 14 | outputStream.flush(); 15 | return outputStream.toByteArray(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/gui/util/WebUtil.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.gui.util; 2 | 3 | import java.awt.*; 4 | import java.net.URI; 5 | import java.net.URISyntaxException; 6 | import java.net.URL; 7 | 8 | public class WebUtil { 9 | 10 | public static boolean openWebpage(URI uri) { 11 | Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; 12 | if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { 13 | try { 14 | desktop.browse(uri); 15 | return true; 16 | } catch (Exception e) { 17 | // Nothing to do here... 18 | } 19 | } 20 | return false; 21 | } 22 | 23 | public static boolean openWebpage(URL url) { 24 | try { 25 | return openWebpage(url.toURI()); 26 | } catch (URISyntaxException e) { 27 | // Nothing to do here... 28 | } 29 | return false; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/CommandObject.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models; 2 | 3 | import burp.BurpExtender; 4 | import com.google.common.collect.Lists; 5 | import com.google.gson.annotations.SerializedName; 6 | import net.bytebutcher.burpsendtoextension.models.placeholder.AbstractRequestResponseInfoPlaceholder; 7 | import net.bytebutcher.burpsendtoextension.models.placeholder.AbstractRequestResponsePlaceholder; 8 | import net.bytebutcher.burpsendtoextension.models.placeholder.IPlaceholderParser; 9 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour; 10 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour; 11 | 12 | import java.util.*; 13 | 14 | public class CommandObject { 15 | 16 | private String id = UUID.randomUUID().toString(); 17 | private String name; 18 | @SerializedName(value="format", alternate={"command"}) // Changed field name from "command" to "format" in version 1.1 19 | private String format; 20 | private String group; 21 | private ERuntimeBehaviour runtimeBehaviour; 22 | private boolean showPreview; 23 | private List placeholders; 24 | 25 | public static class Placeholder { 26 | 27 | // The name of the placeholder (e.g. "%U"). 28 | private final String name; 29 | 30 | // Defines at which position the placeholder is found in the format string. 31 | private final int start; 32 | private final int end; 33 | 34 | // The behavior of the placeholder. 35 | private IPlaceholderBehaviour behaviour; 36 | 37 | public Placeholder(String placeholder, IPlaceholderBehaviour behaviour, int start, int end) { 38 | this.name = placeholder; 39 | this.behaviour = behaviour; 40 | this.start = start; 41 | this.end = end; 42 | } 43 | 44 | public String getName() { 45 | return name; 46 | } 47 | 48 | public int getStart() { 49 | return start; 50 | } 51 | 52 | public int getEnd() { 53 | return end; 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "Placeholder{" + 59 | "name='" + name + '\'' + 60 | ", start=" + start + 61 | ", end=" + end + 62 | ", behaviour=" + behaviour + 63 | '}'; 64 | } 65 | 66 | @Override 67 | public boolean equals(Object o) { 68 | if (this == o) return true; 69 | if (o == null || getClass() != o.getClass()) return false; 70 | Placeholder that = (Placeholder) o; 71 | return Objects.equals(name, that.name); 72 | } 73 | 74 | @Override 75 | public int hashCode() { 76 | return Objects.hash(name); 77 | } 78 | 79 | public IPlaceholderBehaviour getBehaviour() { 80 | return behaviour; 81 | } 82 | 83 | public void setBehaviour(IPlaceholderBehaviour behaviour) { 84 | this.behaviour = behaviour; 85 | } 86 | 87 | } 88 | 89 | public CommandObject(String name, String format, String group, ERuntimeBehaviour runtimeBehaviour, boolean showPreview, List placeholders) { 90 | this.name = name; 91 | this.format = format; 92 | this.group = group; 93 | this.runtimeBehaviour = runtimeBehaviour; 94 | this.showPreview = showPreview; 95 | this.placeholders = initPlaceholders(Lists.newArrayList(placeholders)); 96 | } 97 | 98 | public CommandObject(String id, String name, String format, String group, ERuntimeBehaviour runtimeBehaviour, boolean showPreview, List placeholders) { 99 | this(name, format, group, runtimeBehaviour, showPreview, placeholders); 100 | this.id = id; 101 | } 102 | 103 | public CommandObject(String name, String format, String group, ERuntimeBehaviour runtimeBehaviour, boolean showPreview) { 104 | this(name, format, group, runtimeBehaviour, showPreview, Lists.newArrayList()); 105 | } 106 | 107 | /** 108 | * Each context menu entry has a string format. The string format may contain placeholders. 109 | * The user can specify the behaviour of the placeholder which is stored in configuration. 110 | * This method links the placeholders and it's behavior. 111 | * 112 | * This method addresses the problem that the string format might have been changed (e.g. added/removed placeholder) 113 | * while the behavior was not. As a result this method need to autodetect whether the stored behavior can still be 114 | * applied or whether a default behaviour needs to be set. 115 | * 116 | * @param storedPlaceholders a (incomplete) list of placeholders. if this list is 117 | * empty (e.g. when no placeholder was defined by the user), each 118 | * placeholder found in the command is associated with a default placeholder behaviour. 119 | * Otherwise the placeholder behaviour in the given list is used. 120 | * @return the behaviour of each placeholder. 121 | */ 122 | private List initPlaceholders(List storedPlaceholders) { 123 | // Get the placeholders defined in the format string 124 | List actualPlaceholders = Placeholders.get(this.format); 125 | for (CommandObject.Placeholder actualPlaceholder : actualPlaceholders) { 126 | // Check whether a placeholder behavior exists for this placeholder. 127 | // We pick the first one we find which might not always be correct - but we can't do it any other way. 128 | Placeholder match = storedPlaceholders.stream().filter(x -> x.getName().equals(actualPlaceholder.getName())).findFirst().orElse(null); 129 | if (match != null && match.getBehaviour() != null) { 130 | actualPlaceholder.setBehaviour(match.getBehaviour()); 131 | } else { 132 | actualPlaceholder.setBehaviour(new CommandSeparatedPlaceholderBehaviour()); 133 | } 134 | storedPlaceholders.remove(match); 135 | } 136 | return actualPlaceholders; 137 | } 138 | 139 | public String getId() { return this.id; } 140 | 141 | public String getName() { 142 | return this.name; 143 | } 144 | 145 | public String getFormat() { 146 | return this.format; 147 | } 148 | 149 | public String getGroup() { 150 | return group; 151 | } 152 | 153 | public ERuntimeBehaviour getRuntimeBehaviour() { 154 | return this.runtimeBehaviour != null ? this.runtimeBehaviour : ERuntimeBehaviour.RUN_IN_TERMINAL; 155 | } 156 | 157 | public boolean shouldRunInTerminal() { 158 | return getRuntimeBehaviour() == ERuntimeBehaviour.RUN_IN_TERMINAL; 159 | } 160 | 161 | public boolean shouldOutputReplaceSelection() { 162 | return getRuntimeBehaviour() == ERuntimeBehaviour.OUTPUT_SHOULD_REPLACE_SELECTION; 163 | } 164 | 165 | public boolean shouldRunInBackground() { 166 | return getRuntimeBehaviour() == ERuntimeBehaviour.RUN_IN_BACKGROUND; 167 | } 168 | 169 | public void setShowPreview(boolean showPreview) { 170 | this.showPreview = showPreview; 171 | } 172 | 173 | public boolean shouldShowPreview() { 174 | return this.showPreview; 175 | } 176 | 177 | @Override 178 | public boolean equals(Object o) { 179 | if (this == o) return true; 180 | if (o == null || getClass() != o.getClass()) return false; 181 | CommandObject that = (CommandObject) o; 182 | return id.equals(that.id); 183 | } 184 | 185 | @Override 186 | public int hashCode() { 187 | return Objects.hash(id); 188 | } 189 | 190 | public List> getValid(List> placeholders, Context context) { 191 | List> validItems = Lists.newArrayList(); 192 | for (Map placeholderMap : placeholders) { 193 | if (isValid(placeholderMap, context)) { 194 | validItems.add(placeholderMap); 195 | } 196 | } 197 | return validItems; 198 | } 199 | 200 | private boolean isValid(Map placeholderMap, Context context) { 201 | for (CommandObject.Placeholder placeholder : getPlaceholders()) { 202 | if (placeholderMap.containsKey(placeholder.getName())) { 203 | if (!placeholderMap.get(placeholder.getName()).isValid(context)) { 204 | return false; 205 | } 206 | } 207 | } 208 | return true; 209 | } 210 | 211 | public boolean doesRequireRequestResponse(Map placeholderMap) { 212 | for (Placeholder placeholder : getPlaceholders()) { 213 | if (placeholderMap.containsKey(placeholder.getName())) { 214 | if (placeholderMap.get(placeholder.getName()) instanceof AbstractRequestResponseInfoPlaceholder || placeholderMap.get(placeholder) instanceof AbstractRequestResponsePlaceholder) { 215 | return true; 216 | } 217 | } 218 | } 219 | return false; 220 | } 221 | 222 | public List getPlaceholders() { 223 | if (placeholders == null) { 224 | // placeholder list might be null, when ContextMenuEntry was loaded from config 225 | // and no placeholders were defined. 226 | placeholders = initPlaceholders(Lists.newArrayList()); 227 | } 228 | return Lists.newArrayList(placeholders); 229 | } 230 | 231 | @Override 232 | public String toString() { 233 | return "CommandObject{" + 234 | "id='" + id + '\'' + 235 | ", name='" + name + '\'' + 236 | ", format='" + format + '\'' + 237 | ", group='" + group + '\'' + 238 | ", runtimeBehaviour=" + runtimeBehaviour + 239 | ", showPreview=" + showPreview + 240 | ", placeholders=" + placeholders + 241 | '}'; 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/Config.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models; 2 | 3 | import burp.BurpExtender; 4 | import burp.IBurpExtenderCallbacks; 5 | import com.google.common.collect.Lists; 6 | import com.google.gson.Gson; 7 | import com.google.gson.GsonBuilder; 8 | import com.google.gson.reflect.TypeToken; 9 | import com.google.gson.typeadapters.RuntimeTypeAdapterFactory; 10 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.CommandSeparatedPlaceholderBehaviour; 11 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.FileSeparatedPlaceholderBehaviour; 12 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.IPlaceholderBehaviour; 13 | import net.bytebutcher.burpsendtoextension.models.placeholder.behaviour.StringSeparatedPlaceholderBehaviour; 14 | import net.bytebutcher.burpsendtoextension.utils.OsUtils; 15 | 16 | import java.util.List; 17 | 18 | public class Config { 19 | 20 | private final IBurpExtenderCallbacks callbacks; 21 | private final Gson gson; 22 | private BurpExtender burpExtender; 23 | private String version = "1.6"; 24 | 25 | public Config(BurpExtender burpExtender) { 26 | this.burpExtender = burpExtender; 27 | this.callbacks = BurpExtender.getCallbacks(); 28 | this.gson = initGson(); 29 | refreshVersion(); 30 | } 31 | 32 | private Gson initGson() { 33 | RuntimeTypeAdapterFactory placeholderBehaviourAdapterFactory = RuntimeTypeAdapterFactory.of(IPlaceholderBehaviour.class, "type") 34 | .registerSubtype(StringSeparatedPlaceholderBehaviour.class, "StringSeparated") 35 | .registerSubtype(CommandSeparatedPlaceholderBehaviour.class, "CommandSeparated") 36 | .registerSubtype(FileSeparatedPlaceholderBehaviour.class, "FileSeparated"); 37 | return new GsonBuilder().registerTypeAdapterFactory(placeholderBehaviourAdapterFactory).create(); 38 | } 39 | 40 | public void saveSendToTableData(List sendToTableData) { 41 | this.callbacks.saveExtensionSetting("SendToTableData", 42 | gson.toJson(sendToTableData)); 43 | } 44 | 45 | public List getSendToTableData() { 46 | List commandObjectList = Lists.newArrayList(); 47 | try { 48 | String sendToTableData = this.callbacks.loadExtensionSetting("SendToTableData"); 49 | if (sendToTableData == null || sendToTableData.isEmpty() || "[]".equals(sendToTableData)) { 50 | if (isFirstStart()) { 51 | BurpExtender.printOut("Initializing default table data..."); 52 | commandObjectList = initializeDefaultSendToTableData(); 53 | } 54 | return commandObjectList; 55 | } 56 | return gson.fromJson(sendToTableData, new TypeToken>() {}.getType()); 57 | } catch (Exception e) { 58 | BurpExtender.printErr("Error retrieving table data!"); 59 | BurpExtender.printErr(e.toString()); 60 | return commandObjectList; 61 | } 62 | } 63 | 64 | private List initializeDefaultSendToTableData() { 65 | List commandObjectList = getDefaultSendToTableData(); 66 | saveSendToTableData(commandObjectList); 67 | unsetFirstStart(); 68 | return commandObjectList; 69 | } 70 | 71 | public List getDefaultSendToTableData() { 72 | String groupFuzz = "fuzz"; 73 | String groupCMS = "cms"; 74 | String groupSQL = "sql"; 75 | String groupSSL = "ssl"; 76 | String groupOther = "other"; 77 | return Lists.newArrayList( 78 | // cms 79 | new CommandObject("droopescan", "droopescan scan drupal -u %U -t 10", groupCMS, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 80 | new CommandObject("mooscan", "mooscan -v --url %U", groupCMS, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 81 | new CommandObject("wpscan", "wpscan --url %U --threads 10", groupCMS, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 82 | // fuzz 83 | new CommandObject("bfac", "bfac --url %U", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 84 | new CommandObject("gobuster", "gobuster -u %U -s 403,404 -w /usr/share/wfuzz/wordlist/general/common.txt", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 85 | new CommandObject("nikto", "nikto %U", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 86 | new CommandObject("wfuzz", "wfuzz -c -w /usr/share/wfuzz/wordlist/general/common.txt --hc 404,403 %U", groupFuzz, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 87 | // sql 88 | new CommandObject("sqlmap (GET)", "sqlmap -o -u %U --level=5 --risk=3", groupSQL, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 89 | new CommandObject("sqlmap (POST)", "sqlmap -r %R --level=5 --risk=3", groupSQL, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 90 | // ssl 91 | new CommandObject("sslscan", "sslscan %H:%P", groupSSL, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 92 | new CommandObject("sslyze", "sslyze --regular %H:%P", groupSSL, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 93 | new CommandObject("testssl", "testssl.sh %H:%P", groupSSL, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 94 | // other 95 | new CommandObject("Host (%H)", "echo %H", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 96 | new CommandObject("Port (%P)", "echo %P", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 97 | new CommandObject("Protocol (%T)", "echo %T", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 98 | new CommandObject("URL (%U)", "echo %U", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 99 | new CommandObject("URL-Path (%A)", "echo %A", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 100 | new CommandObject("URL-Query (%Q)", "echo %Q", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 101 | new CommandObject("Cookies (%C)", "echo %C", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 102 | new CommandObject("HTTP-Method (%M)", "echo %M", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 103 | new CommandObject("Selected text (%S)", "echo %S", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 104 | new CommandObject("Selected text as file (%F)", "echo %F && cat %F", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 105 | new CommandObject("HTTP-Request/-Response as file (%R)", "echo %R && cat %R", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 106 | new CommandObject("HTTP-Headers as file (%E)", "echo %E && cat %E", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true), 107 | new CommandObject("HTTP-Body as file (%B)", "echo %B && cat %B", groupOther, ERuntimeBehaviour.RUN_IN_TERMINAL, true) 108 | ); 109 | } 110 | 111 | private void refreshVersion() { 112 | if (!this.version.equals(this.callbacks.loadExtensionSetting("version"))) { 113 | this.callbacks.saveExtensionSetting("version", version); 114 | setFirstStart(); 115 | } 116 | } 117 | 118 | private boolean isFirstStart() { 119 | String isFirstStart = this.callbacks.loadExtensionSetting("isFirstStart"); 120 | return isFirstStart == null || "true".equals(isFirstStart); 121 | } 122 | 123 | private void setFirstStart() { 124 | this.callbacks.saveExtensionSetting("isFirstStart", null); 125 | } 126 | 127 | private void unsetFirstStart() { 128 | this.callbacks.saveExtensionSetting("isFirstStart", "false"); 129 | } 130 | 131 | public void setRunInTerminalBehaviour(ERunInTerminalBehaviour runInTerminalBehaviour) { 132 | this.callbacks.saveExtensionSetting("runInTerminalBehaviour", runInTerminalBehaviour.name()); 133 | } 134 | 135 | public ERunInTerminalBehaviour getRunInTerminalBehaviour() { 136 | String runInTerminalBehaviour = this.callbacks.loadExtensionSetting("runInTerminalBehaviour"); 137 | if (runInTerminalBehaviour == null || runInTerminalBehaviour.isEmpty()) { 138 | return ERunInTerminalBehaviour.RUN_IN_SINGLE_TERMINAL; 139 | } 140 | return ERunInTerminalBehaviour.valueOf(runInTerminalBehaviour); 141 | } 142 | 143 | public boolean shouldShowRunInTerminalBehaviourChoiceDialog() { 144 | String showDialog = this.callbacks.loadExtensionSetting("showRunInTerminalBehaviourDialog"); 145 | if (showDialog == null || showDialog.isEmpty()) { 146 | return true; // Default 147 | } 148 | return Boolean.parseBoolean(showDialog); 149 | } 150 | 151 | public void shouldShowRunInTerminalBehaviourChoiceDialog(boolean status) { 152 | this.callbacks.saveExtensionSetting("showRunInTerminalBehaviourDialog", String.valueOf(status)); 153 | } 154 | 155 | public String getRunInTerminalCommand() { 156 | if (OsUtils.isWindows()) { 157 | return getRunInTerminalCommand("runInTerminalSettingWindows", "cmd /c start cmd /K %C"); 158 | } else { 159 | return getRunInTerminalCommand("runInTerminalSettingUnix", "xterm -hold -e %C"); 160 | } 161 | } 162 | 163 | private String getRunInTerminalCommand(String label, String defaultValue) { 164 | String runInTerminalSetting = this.callbacks.loadExtensionSetting(label); 165 | if (runInTerminalSetting == null || runInTerminalSetting.isEmpty()) { 166 | runInTerminalSetting = defaultValue; 167 | this.callbacks.saveExtensionSetting(label, runInTerminalSetting); 168 | } 169 | return runInTerminalSetting; 170 | } 171 | 172 | public void setRunInTerminalCommand(String command) { 173 | if (OsUtils.isWindows()) { 174 | this.callbacks.saveExtensionSetting("runInTerminalSettingWindows", command); 175 | } else { 176 | this.callbacks.saveExtensionSetting("runInTerminalSettingUnix", command); 177 | } 178 | } 179 | 180 | public void resetRunInTerminalCommand() { 181 | this.callbacks.saveExtensionSetting("runInTerminalSettingWindows", "cmd /c start cmd /K %C"); 182 | this.callbacks.saveExtensionSetting("runInTerminalSettingUnix", "xterm -hold -e %C"); 183 | 184 | } 185 | 186 | public void setSafeMode(boolean status) { 187 | this.callbacks.saveExtensionSetting("safeMode", String.valueOf(status)); 188 | } 189 | 190 | public boolean isSafeModeActivated() { 191 | String safeMode = this.callbacks.loadExtensionSetting("safeMode"); 192 | if (safeMode == null || safeMode.isEmpty()) 193 | return true; 194 | return Boolean.parseBoolean(safeMode); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/Context.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models; 2 | 3 | import burp.IContextMenuInvocation; 4 | import burp.IHttpRequestResponse; 5 | import com.google.common.collect.Lists; 6 | 7 | import java.util.ArrayList; 8 | 9 | public class Context { 10 | private final Origin origin; 11 | private final IContextMenuInvocation invocation; 12 | 13 | public enum Origin { 14 | HTTP_RESPONSE, HTTP_REQUEST, UNKNOWN; 15 | 16 | public static Origin getTarget(IContextMenuInvocation contextMenuInvocation) { 17 | ArrayList requestContext = Lists.newArrayList(IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST, IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST); 18 | ArrayList responseContext = Lists.newArrayList(IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_RESPONSE, IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_RESPONSE); 19 | if (requestContext.contains(contextMenuInvocation.getInvocationContext())) { 20 | return Origin.HTTP_REQUEST; 21 | } else if (responseContext.contains(contextMenuInvocation.getInvocationContext())) { 22 | return Origin.HTTP_RESPONSE; 23 | } else { 24 | return Origin.UNKNOWN; 25 | } 26 | } 27 | 28 | } 29 | 30 | public Context(Origin origin, IContextMenuInvocation invocation) { 31 | this.origin = origin; 32 | this.invocation = invocation; 33 | } 34 | 35 | public Context(IContextMenuInvocation invocation) { 36 | this(Origin.getTarget(invocation), invocation); 37 | } 38 | 39 | public Origin getOrigin() { 40 | return origin; 41 | } 42 | 43 | /** 44 | * NOTE: Access to getSelectedMessages should be restricted, since messages should always be accessed via 45 | * RequestResponseHolder. Notably the definition and usage of this function here is a minor design issue. 46 | */ 47 | public IHttpRequestResponse[] getSelectedMessages() { 48 | return invocation.getSelectedMessages(); 49 | } 50 | 51 | public int[] getSelectionBounds() { 52 | return invocation.getSelectionBounds(); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/ERunInTerminalBehaviour.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models; 2 | 3 | public enum ERunInTerminalBehaviour { 4 | RUN_IN_SEPARATE_TERMINALS, 5 | RUN_IN_SINGLE_TERMINAL 6 | } 7 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/ERuntimeBehaviour.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models; 2 | 3 | public enum ERuntimeBehaviour { 4 | 5 | RUN_IN_BACKGROUND("Run in Background"), 6 | RUN_IN_TERMINAL("Run in Terminal"), 7 | OUTPUT_SHOULD_REPLACE_SELECTION("Output should replace Selection"); 8 | 9 | private String alternateName; 10 | 11 | ERuntimeBehaviour(String alternateName) { 12 | this.alternateName = alternateName; 13 | } 14 | 15 | public String alternateName() { 16 | return this.alternateName; 17 | } 18 | 19 | public static ERuntimeBehaviour getEnum(String name) { 20 | for (ERuntimeBehaviour value : ERuntimeBehaviour.values()) { 21 | if (value.name().equals(name) || value.alternateName().equals(name)) { 22 | return value; 23 | } 24 | } 25 | throw new IllegalArgumentException(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/Placeholders.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models; 2 | 3 | import burp.IBurpExtenderCallbacks; 4 | import burp.IHttpRequestResponse; 5 | import burp.RequestResponseHolder; 6 | import com.google.common.collect.Lists; 7 | import com.google.common.collect.Maps; 8 | import net.bytebutcher.burpsendtoextension.models.placeholder.*; 9 | 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | import java.util.stream.Collectors; 15 | 16 | public class Placeholders { 17 | 18 | public static List get() { 19 | return Lists.newArrayList( 20 | new CookiesPlaceholder(), 21 | new HostPlaceholder(), 22 | new HttpBodyToFilePlaceholder(), 23 | new HttpContentLengthPlaceholder(), 24 | new HttpHeadersToFilePlaceholder(), 25 | new HttpMethodPlaceholder(), 26 | new HttpRequestResponsePlaceholder(), 27 | new HttpStatusCodePlaceholder(), 28 | new PortPlaceholder(), 29 | new ProtocolPlaceholder(), 30 | new SelectedTextPlaceholder(), 31 | new SelectedTextToFilePlaceholder(), 32 | new UrlPathPlaceholder(), 33 | new UrlPlaceholder(), 34 | new UrlQueryPlaceholder()); 35 | } 36 | 37 | /** 38 | * Initializes the placeholders for each selected message and returns them in a list. 39 | */ 40 | public static List> get(IBurpExtenderCallbacks burpExtenderCallbacks, IHttpRequestResponse[] selectedMessages) { 41 | List> result = Lists.newArrayList(); 42 | if (selectedMessages == null) { 43 | return result; 44 | } 45 | List placeholderList = Placeholders.get(); 46 | for (IHttpRequestResponse selectedMessage : selectedMessages) { 47 | RequestResponseHolder requestResponseHolder = new RequestResponseHolder(burpExtenderCallbacks, selectedMessage); 48 | Map placeholderMap = Maps.newHashMap(); 49 | for (AbstractPlaceholder placeholder : placeholderList) { 50 | placeholderMap.put(placeholder.getPlaceholder(), placeholder.createParser(requestResponseHolder)); 51 | } 52 | result.add(placeholderMap); 53 | } 54 | return result; 55 | } 56 | 57 | public static List get(String format) { 58 | List validPlaceholders = Placeholders.get().stream().map(AbstractPlaceholder::getPlaceholder).collect(Collectors.toList()); 59 | List placeholders = Lists.newArrayList(); 60 | if (format != null && !format.isEmpty()) { 61 | Matcher m = Pattern.compile("(\\%[A-Z])").matcher(format); 62 | while (m.find()) { 63 | String placeholder = m.group(1); 64 | if (validPlaceholders.contains(placeholder)) { 65 | placeholders.add(new CommandObject.Placeholder(placeholder, null, m.start(), m.end())); 66 | } 67 | } 68 | } 69 | return placeholders; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | 5 | public abstract class AbstractPlaceholder implements IPlaceholder, IPlaceholderParserFactory { 6 | 7 | private final String placeholder; 8 | private final boolean doesRequireShellEscape; 9 | private final boolean shouldWriteToFile; 10 | 11 | public AbstractPlaceholder(String placeholder, boolean doesRequireShellEscape, boolean shouldWriteToFile) { 12 | this.placeholder = placeholder; 13 | this.doesRequireShellEscape = doesRequireShellEscape; 14 | this.shouldWriteToFile = shouldWriteToFile; 15 | } 16 | 17 | public String getPlaceholder() { 18 | return placeholder; 19 | } 20 | 21 | /** 22 | * Returns whether the placeholder requires shell-escaping. 23 | */ 24 | public boolean doesRequireShellEscape() { 25 | return doesRequireShellEscape; 26 | } 27 | 28 | 29 | public boolean shouldWriteToFile() { 30 | return shouldWriteToFile; 31 | } 32 | 33 | public abstract IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder); 34 | } 35 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestInfoPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.IRequestInfoWrapper; 4 | import burp.RequestResponseHolder; 5 | 6 | /** 7 | * Placeholders which do require additional information from a request. 8 | */ 9 | public abstract class AbstractRequestInfoPlaceholder extends AbstractRequestResponsePlaceholderBase { 10 | 11 | private final RequestResponseHolder requestResponseHolder; 12 | 13 | public AbstractRequestInfoPlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) { 14 | super(placeholder, requestResponseHolder); 15 | this.requestResponseHolder = requestResponseHolder; 16 | } 17 | 18 | protected IRequestInfoWrapper getRequestInfo() { 19 | return requestResponseHolder.getRequestInfo(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.IHttpRequestResponse; 4 | import burp.RequestResponseHolder; 5 | 6 | public abstract class AbstractRequestPlaceholder extends AbstractRequestResponsePlaceholderBase { 7 | 8 | private final RequestResponseHolder requestResponseHolder; 9 | 10 | public AbstractRequestPlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) { 11 | super(placeholder, requestResponseHolder); 12 | this.requestResponseHolder = requestResponseHolder; 13 | } 14 | 15 | protected IHttpRequestResponse getHttpRequestResponse() { 16 | return requestResponseHolder.getHttpRequestResponse(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestResponseInfoPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.IRequestInfoWrapper; 4 | import burp.IResponseInfoWrapper; 5 | import burp.RequestResponseHolder; 6 | 7 | /** 8 | * Placeholder which depends on a request/response is selected/focused. 9 | */ 10 | public abstract class AbstractRequestResponseInfoPlaceholder extends AbstractRequestResponsePlaceholderBase { 11 | 12 | private final RequestResponseHolder requestResponseHolder; 13 | 14 | public AbstractRequestResponseInfoPlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) { 15 | super(placeholder, requestResponseHolder); 16 | this.requestResponseHolder = requestResponseHolder; 17 | } 18 | 19 | protected IRequestInfoWrapper getRequestInfo() { 20 | return requestResponseHolder.getRequestInfo(); 21 | } 22 | 23 | protected IResponseInfoWrapper getResponseInfo() { 24 | return requestResponseHolder.getResponseInfo(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestResponsePlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | public abstract class AbstractRequestResponsePlaceholder extends AbstractRequestResponsePlaceholderBase { 7 | 8 | private final RequestResponseHolder requestResponseHolder; 9 | 10 | public AbstractRequestResponsePlaceholder(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) { 11 | super(placeholder, requestResponseHolder); 12 | this.requestResponseHolder = requestResponseHolder; 13 | } 14 | 15 | protected byte[] getRequestResponseAsByteArray(Context context) { 16 | switch(context.getOrigin()) { 17 | case HTTP_REQUEST: 18 | return requestResponseHolder.getHttpRequestResponse().getRequest(); 19 | case HTTP_RESPONSE: 20 | return requestResponseHolder.getHttpRequestResponse().getResponse(); 21 | } 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/AbstractRequestResponsePlaceholderBase.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | import java.io.File; 8 | import java.io.PrintWriter; 9 | import java.util.Optional; 10 | 11 | public abstract class AbstractRequestResponsePlaceholderBase implements IPlaceholderParser { 12 | 13 | private final IPlaceholder placeholder; 14 | private final RequestResponseHolder requestResponseHolder; 15 | 16 | public AbstractRequestResponsePlaceholderBase(IPlaceholder placeholder, RequestResponseHolder requestResponseHolder) { 17 | this.placeholder = placeholder; 18 | this.requestResponseHolder = requestResponseHolder; 19 | } 20 | 21 | public String getPlaceholder() { 22 | return placeholder.getPlaceholder(); 23 | } 24 | 25 | public boolean doesRequireShellEscape() { 26 | return placeholder.doesRequireShellEscape(); 27 | } 28 | 29 | public boolean shouldWriteToFile() { return placeholder.shouldWriteToFile(); } 30 | 31 | /** 32 | * Returns the value associated with the placeholder. 33 | * 34 | * @return the value associated with the placeholder. 35 | */ 36 | @Nullable 37 | protected abstract String getInternalValue(Context context) throws Exception; 38 | 39 | @Override 40 | public String getValue(Context context) throws RuntimeException { 41 | try { 42 | String value = Optional.ofNullable(getInternalValue(context)).orElse(""); 43 | if (placeholder.shouldWriteToFile()) { 44 | value = writeToFile(value); 45 | } 46 | return value; 47 | } catch (Exception e) { 48 | // This exception is thrown when the placeholder can not be constructed (e.g. no text selected, 49 | // no url-query-parameter present, etc.). This is done in favor of returning empty text. 50 | // In practice this exception should not be thrown anyway since menu items which contain this placeholder 51 | // are disabled and can not be selected anyway (see isValid()). 52 | throw new RuntimeException("Error replacing placeholder " + placeholder.getPlaceholder() + " !", e); 53 | } 54 | } 55 | 56 | private String writeToFile(String value) throws Exception { 57 | try { 58 | File tmp = File.createTempFile("burp_", ".snd"); 59 | PrintWriter out = new PrintWriter(tmp.getPath()); 60 | out.write(value); 61 | out.flush(); 62 | return tmp.getAbsolutePath(); 63 | } catch (RuntimeException e) { 64 | throw new Exception(this.getClass().getSimpleName() + ": Error writing to temporary file!", e); 65 | } 66 | } 67 | 68 | @Override 69 | public boolean isValid(Context context) { 70 | try { 71 | return getInternalValue(context) != null; 72 | } catch (Exception e) { 73 | return false; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/CookiesPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.ICookie; 4 | import burp.RequestResponseHolder; 5 | import com.google.common.collect.Lists; 6 | import net.bytebutcher.burpsendtoextension.models.Context; 7 | 8 | import javax.annotation.Nullable; 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | public class CookiesPlaceholder extends AbstractPlaceholder { 13 | 14 | public CookiesPlaceholder() { 15 | super("%C", true, false); 16 | } 17 | 18 | @Override 19 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 20 | return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) { 21 | 22 | @Nullable 23 | @Override 24 | protected String getInternalValue(Context context) { 25 | List cookies = Lists.newArrayList(); 26 | switch (context.getOrigin()) { 27 | case HTTP_REQUEST: 28 | cookies = getRequestInfo().getCookies(); 29 | break; 30 | case HTTP_RESPONSE: 31 | cookies = getResponseInfo().getCookies(); 32 | break; 33 | } 34 | return cookies.isEmpty() ? null : cookies.stream().map(iCookie -> iCookie.getName() + "=" + iCookie.getValue()).collect(Collectors.joining(",")); 35 | } 36 | }; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HostPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | public class HostPlaceholder extends AbstractPlaceholder { 9 | 10 | public HostPlaceholder() { 11 | super("%H", true, false); 12 | } 13 | 14 | @Override 15 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 16 | return new AbstractRequestPlaceholder(this, requestResponseHolder) { 17 | 18 | @Nullable 19 | @Override 20 | protected String getInternalValue(Context context) { 21 | return getHttpRequestResponse().getHttpService().getHost(); 22 | } 23 | 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpBodyToFilePlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | public class HttpBodyToFilePlaceholder extends AbstractPlaceholder { 9 | 10 | public HttpBodyToFilePlaceholder() { 11 | super("%B", false, true); 12 | } 13 | 14 | @Override 15 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 16 | return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) { 17 | @Nullable 18 | @Override 19 | protected String getInternalValue(Context context) { 20 | switch(context.getOrigin()) { 21 | case HTTP_REQUEST: 22 | return getRequestInfo().getBody(); 23 | case HTTP_RESPONSE: 24 | return getResponseInfo().getBody(); 25 | } 26 | return null; 27 | } 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpContentLengthPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | public class HttpContentLengthPlaceholder extends AbstractPlaceholder { 9 | 10 | public HttpContentLengthPlaceholder() { 11 | super("%L", false, false); 12 | } 13 | 14 | @Override 15 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 16 | return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) { 17 | @Nullable 18 | @Override 19 | protected String getInternalValue(Context context) { 20 | switch(context.getOrigin()) { 21 | case HTTP_REQUEST: 22 | return String.valueOf(getRequestInfo().getBody() == null ? 0 : getRequestInfo().getBody().length()); 23 | case HTTP_RESPONSE: 24 | return String.valueOf(getResponseInfo().getBody() == null ? 0 : getResponseInfo().getBody().length()); 25 | } 26 | return null; 27 | } 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpHeadersToFilePlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import com.google.common.collect.Lists; 5 | import net.bytebutcher.burpsendtoextension.models.Context; 6 | 7 | import javax.annotation.Nullable; 8 | import java.util.List; 9 | 10 | public class HttpHeadersToFilePlaceholder extends AbstractPlaceholder { 11 | 12 | public HttpHeadersToFilePlaceholder() { 13 | super("%E", false, true); 14 | } 15 | 16 | @Override 17 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 18 | return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) { 19 | @Nullable 20 | @Override 21 | protected String getInternalValue(Context context) { 22 | List headers = Lists.newArrayList(); 23 | switch(context.getOrigin()) { 24 | case HTTP_REQUEST: 25 | headers = getRequestInfo().getHeaders(); 26 | break; 27 | case HTTP_RESPONSE: 28 | headers = getResponseInfo().getHeaders(); 29 | break; 30 | } 31 | return headers.isEmpty() ? null : String.join(System.lineSeparator(), headers); 32 | } 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpMethodPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | public class HttpMethodPlaceholder extends AbstractPlaceholder { 9 | 10 | public HttpMethodPlaceholder() { 11 | super("%M", true, false); 12 | } 13 | 14 | @Override 15 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 16 | return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) { 17 | @Nullable 18 | @Override 19 | protected String getInternalValue(Context context) { 20 | return getRequestInfo().getMethod(); 21 | } 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpRequestResponsePlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | public class HttpRequestResponsePlaceholder extends AbstractPlaceholder { 9 | 10 | public HttpRequestResponsePlaceholder() { 11 | super("%R", false, true); 12 | } 13 | 14 | @Override 15 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 16 | return new AbstractRequestResponsePlaceholder(this, requestResponseHolder) { 17 | @Nullable 18 | @Override 19 | protected String getInternalValue(Context context) { 20 | byte[] requestResponse = getRequestResponseAsByteArray(context); 21 | return requestResponse != null ? new String(requestResponse) : null; 22 | } 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/HttpStatusCodePlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | public class HttpStatusCodePlaceholder extends AbstractPlaceholder { 9 | 10 | public HttpStatusCodePlaceholder() { 11 | super("%O", false, false); 12 | } 13 | 14 | @Override 15 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 16 | return new AbstractRequestResponseInfoPlaceholder(this, requestResponseHolder) { 17 | @Nullable 18 | @Override 19 | protected String getInternalValue(Context context) { 20 | short statusCode = -1; 21 | if (context.getOrigin() == Context.Origin.HTTP_RESPONSE) { 22 | statusCode = getResponseInfo().getStatusCode(); 23 | } 24 | return statusCode == -1 ? null : String.valueOf(statusCode); 25 | } 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/IPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | public interface IPlaceholder { 4 | 5 | String getPlaceholder(); 6 | 7 | boolean doesRequireShellEscape(); 8 | 9 | boolean shouldWriteToFile(); 10 | } 11 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/IPlaceholderParser.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import net.bytebutcher.burpsendtoextension.models.Context; 4 | 5 | public interface IPlaceholderParser extends IPlaceholder { 6 | 7 | String getValue(Context context) throws RuntimeException; 8 | 9 | boolean isValid(Context context); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/IPlaceholderParserFactory.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | 5 | public interface IPlaceholderParserFactory { 6 | 7 | IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/PortPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | public class PortPlaceholder extends AbstractPlaceholder { 9 | 10 | public PortPlaceholder() { 11 | super("%P", false, false); 12 | } 13 | 14 | @Override 15 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 16 | return new AbstractRequestPlaceholder(this, requestResponseHolder) { 17 | @Nullable 18 | @Override 19 | protected String getInternalValue(Context context) { 20 | return String.valueOf(getHttpRequestResponse().getHttpService().getPort()); 21 | } 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/ProtocolPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | public class ProtocolPlaceholder extends AbstractPlaceholder { 9 | 10 | public ProtocolPlaceholder() { 11 | super("%T", true, false); 12 | } 13 | 14 | @Override 15 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 16 | return new AbstractRequestPlaceholder(this, requestResponseHolder) { 17 | @Nullable 18 | @Override 19 | protected String getInternalValue(Context context) { 20 | return getHttpRequestResponse().getHttpService().getProtocol(); 21 | } 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/SelectedTextPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | 8 | public class SelectedTextPlaceholder extends AbstractPlaceholder { 9 | 10 | public SelectedTextPlaceholder() { 11 | super("%S", true, false); 12 | } 13 | 14 | protected SelectedTextPlaceholder(String placeholder, boolean doShellEscape, boolean doWriteToFile) { 15 | super(placeholder, doShellEscape, doWriteToFile); 16 | } 17 | 18 | @Override 19 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 20 | return new AbstractRequestResponsePlaceholder(this, requestResponseHolder) { 21 | @Nullable 22 | @Override 23 | protected String getInternalValue(Context context) { 24 | String value = null; 25 | int[] selectionBounds = context.getSelectionBounds(); 26 | if (selectionBounds != null) { 27 | byte[] requestResponse = getRequestResponseAsByteArray(context); 28 | if (requestResponse != null) { 29 | boolean isSelectionEmpty = selectionBounds[0] == selectionBounds[1]; 30 | if (!isSelectionEmpty) { 31 | value = new String(requestResponse).substring(selectionBounds[0], selectionBounds[1]); 32 | } 33 | } 34 | } 35 | return value; 36 | } 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/SelectedTextToFilePlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | public class SelectedTextToFilePlaceholder extends SelectedTextPlaceholder { 4 | 5 | public SelectedTextToFilePlaceholder() { 6 | super("%F", false, true); 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/UrlPathPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | import java.net.URL; 8 | import java.util.Optional; 9 | 10 | public class UrlPathPlaceholder extends AbstractPlaceholder { 11 | 12 | public UrlPathPlaceholder() { 13 | super("%A", true, false); 14 | } 15 | 16 | @Override 17 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 18 | return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) { 19 | @Nullable 20 | @Override 21 | protected String getInternalValue(Context context) { 22 | return Optional.ofNullable(getRequestInfo().getUrl()).map(URL::getPath).orElse(null); 23 | } 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/UrlPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | import java.util.Objects; 8 | 9 | public class UrlPlaceholder extends AbstractPlaceholder { 10 | 11 | public UrlPlaceholder() { 12 | super("%U", true, false); 13 | } 14 | 15 | @Override 16 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 17 | return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) { 18 | @Nullable 19 | @Override 20 | protected String getInternalValue(Context context) { 21 | return Objects.toString(getRequestInfo().getUrl()); 22 | } 23 | }; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/UrlQueryPlaceholder.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder; 2 | 3 | import burp.RequestResponseHolder; 4 | import net.bytebutcher.burpsendtoextension.models.Context; 5 | 6 | import javax.annotation.Nullable; 7 | import java.net.URL; 8 | import java.util.Optional; 9 | 10 | public class UrlQueryPlaceholder extends AbstractPlaceholder { 11 | 12 | public UrlQueryPlaceholder() { 13 | super("%Q", true, false); 14 | } 15 | 16 | @Override 17 | public IPlaceholderParser createParser(RequestResponseHolder requestResponseHolder) { 18 | return new AbstractRequestInfoPlaceholder(this, requestResponseHolder) { 19 | @Nullable 20 | @Override 21 | protected String getInternalValue(Context context) { 22 | return Optional.ofNullable(getRequestInfo().getUrl()).map(URL::getQuery).orElse(null); 23 | } 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/CommandSeparatedPlaceholderBehaviour.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour; 2 | 3 | public class CommandSeparatedPlaceholderBehaviour implements IPlaceholderBehaviour { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/FileSeparatedPlaceholderBehaviour.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour; 2 | 3 | public class FileSeparatedPlaceholderBehaviour implements IPlaceholderBehaviour { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/IPlaceholderBehaviour.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour; 2 | 3 | public interface IPlaceholderBehaviour { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/models/placeholder/behaviour/StringSeparatedPlaceholderBehaviour.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.models.placeholder.behaviour; 2 | 3 | public class StringSeparatedPlaceholderBehaviour implements IPlaceholderBehaviour { 4 | 5 | private final String separator; 6 | 7 | public StringSeparatedPlaceholderBehaviour(String separator) { 8 | this.separator = separator; 9 | } 10 | 11 | public String getSeparator() { 12 | return separator; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/utils/OsUtils.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.utils; 2 | 3 | public final class OsUtils { 4 | 5 | private static String OS = null; 6 | 7 | public static String getOsName() { 8 | if(OS == null) { 9 | OS = System.getProperty("os.name"); 10 | } 11 | return OS; 12 | } 13 | 14 | public static boolean isWindows() { 15 | return getOsName().startsWith("Windows"); 16 | } 17 | 18 | public static boolean isUnix() { 19 | return !isWindows(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/java/net/bytebutcher/burpsendtoextension/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package net.bytebutcher.burpsendtoextension.utils; 2 | 3 | import com.google.common.escape.Escaper; 4 | import com.google.common.escape.Escapers; 5 | import com.google.common.io.CharStreams; 6 | 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.io.InputStreamReader; 10 | 11 | public class StringUtils { 12 | 13 | public static final Escaper shellEscaper; 14 | static { 15 | final Escapers.Builder builder = Escapers.builder(); 16 | builder.addEscape('\'', "'\"'\"'"); 17 | shellEscaper = builder.build(); 18 | } 19 | 20 | public static String shellEscape(String command) { 21 | return shellEscaper.escape(command); 22 | } 23 | 24 | public static String fromInputStream(InputStream inputStream) throws IOException { 25 | return CharStreams.toString(new InputStreamReader(inputStream)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/resources/panel_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/burp-send-to-extension/src/main/resources/panel_help.png -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/resources/panel_help_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/burp-send-to-extension/src/main/resources/panel_help_highlighted.png -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/resources/panel_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/burp-send-to-extension/src/main/resources/panel_settings.png -------------------------------------------------------------------------------- /burp-send-to-extension/src/main/resources/panel_settings_highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/burp-send-to-extension/src/main/resources/panel_settings_highlighted.png -------------------------------------------------------------------------------- /images/burp-send-to-extension-add-edit-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/images/burp-send-to-extension-add-edit-dialog.png -------------------------------------------------------------------------------- /images/burp-send-to-extension-advanced-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/images/burp-send-to-extension-advanced-dialog.png -------------------------------------------------------------------------------- /images/burp-send-to-extension-context-menu-repeater.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/images/burp-send-to-extension-context-menu-repeater.png -------------------------------------------------------------------------------- /images/burp-send-to-extension-context-menu-target-sitemap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/images/burp-send-to-extension-context-menu-target-sitemap.png -------------------------------------------------------------------------------- /images/burp-send-to-extension-forkbomb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/images/burp-send-to-extension-forkbomb.png -------------------------------------------------------------------------------- /images/burp-send-to-extension-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/images/burp-send-to-extension-intro.png -------------------------------------------------------------------------------- /images/burp-send-to-extension-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/images/burp-send-to-extension-options.png -------------------------------------------------------------------------------- /images/burp-send-to-extension-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytebutcher/burp-send-to/db1e378bbc5f636d75eb1f02cef070c7be11399a/images/burp-send-to-extension-tab.png --------------------------------------------------------------------------------