├── .gitignore
├── AutoRepeater.jar
├── BappDescription.html
├── BappManifest.bmf
├── LICENSE
├── README.md
├── ar.png
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
└── burp
├── AutoRepeater.java
├── BurpExtender.java
├── Conditions
├── Condition.java
├── ConditionTableModel.java
└── Conditions.java
├── Filter
├── Filter.java
├── FilterTableModel.java
└── Filters.java
├── Highlighter
├── Highlighter.java
├── HighlighterTableModel.java
├── HighlighterUITableModel.java
└── Highlighters.java
├── Logs
├── LogEntry.java
├── LogEntryMenu.java
├── LogManager.java
└── LogTableModel.java
├── Replacements
├── Replacement.java
├── ReplacementTableModel.java
└── Replacements.java
└── Utils
├── AutoRepeaterMenu.java
├── DiffViewerPane.java
├── HttpComparer.java
├── MessageEditorController.java
├── ResponseStore.java
├── Utils.java
└── diff_match_patch.java
/.gitignore:
--------------------------------------------------------------------------------
1 | out/**
2 | .gradle/**
3 | .idea/**
4 | build/**
5 |
--------------------------------------------------------------------------------
/AutoRepeater.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/AutoRepeater/f22b01723ac46605ed08ec42b0e10e674b7d0f90/AutoRepeater.jar
--------------------------------------------------------------------------------
/BappDescription.html:
--------------------------------------------------------------------------------
1 |
This extension automatically repeats requests, with replacement rules and response diffing. It provides a general-purpose solution for streamlining authorization testing within web applications.
2 |
3 | AutoRepeater provides the following features:
4 |
5 |
6 | - Automatically duplicate, modify, and resend any request
7 | - Conditional replacements
8 | - Quick header, cookie, and parameter value replacements
9 | - Split request/response viewer
10 | - Original vs. modified request/response diff viewer
11 | - Highilight logs
12 | - Filter logs
13 | - Base replacements for values that break requests like CSRF tokens and session cookies
14 | - Renamable tabs
15 | - Logging
16 | - Exporting
17 | - Toggled activation
18 | - "Send to AutoRepeater" from other Burp Suite tools
19 |
20 |
--------------------------------------------------------------------------------
/BappManifest.bmf:
--------------------------------------------------------------------------------
1 | Uuid: f89f2837c22c4ab4b772f31522647ed8
2 | ExtensionType: 1
3 | Name: AutoRepeater
4 | RepoName: auto-repeater
5 | ScreenVersion: 1.0
6 | SerialVersion: 2
7 | MinPlatformVersion: 0
8 | ProOnly: False
9 | Author: Justin Moore, NCC Group
10 | ShortDescription: Automatically repeat requests, with replacement rules and response diffing.
11 | EntryPoint: build/libs/AutoRepeater-all.jar
12 | BuildCommand: gradle shadowJar
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 NCC Group Plc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AutoRepeater: Automated HTTP Request Repeating With Burp Suite
2 |
3 | ## tl;dr
4 | Within extender import AutoRepeater.jar
5 |
6 | ## Some Brief Instructions
7 | AutoRepeater will only resend requests which are changed by a defined replacement. When AutoRepeater receives a request that matches the conditions set for a given tab, AutoRepeater will first apply every defined base replacement to the request, then will copy the request with the base replacements performed for each defined replacement and apply the given replacement to the request.
8 |
9 | ## Introduction
10 | Burp Suite is an intercepting HTTP Proxy, and it is the defacto tool for performing web application security testing. While Burp Suite is a very useful tool, using it to perform authorization testing is often a tedious effort involving a "change request and resend" loop, which can miss vulnerabilities and slow down testing. AutoRepeater, an open source Burp Suite extension, was developed to alleviate this effort. AutoRepeater automates and streamlines web application authorization testing, and provides security researchers with an easy-to-use tool for automatically duplicating, modifying, and resending requests within Burp Suite while quickly evaluating the differences in responses.
11 |
12 | 
13 |
14 | ## AutoRepeater
15 | Without AutoRepeater, the basic Burp Suite web application testing flow is as follows:
16 |
17 | 1. User noodles around a web application until they find an interesting request
18 | 2. User sends the request to Burp Suite's "Repeater" tool
19 | 3. User modifies the request within "Repeater" and resends it to the server
20 | 4. Repeat step 3 until a sweet vulnerability is found
21 | 5. Start again from step 1, until the user runs out of testing time or can retire from bug bounty earnings
22 |
23 | While this testing flow works, it is particularly tedious for testing issues that could exist within any request. For example, changing email addresses, account identities, roles, URLs, and CSRF tokens can all lead to vulnerabilities. Currently, Burp Suite does not quickly test for these types of vulnerabilities within a web application.
24 |
25 | There are some existing Burp Suite plugins (AuthMatrix, Authz, and Autorize) which exist to make authorization testing easier but each has issues that limit their usefulness. AuthMatrix and Authz require users to send specific requests to the plugins and set up rules for how the authorization testing is performed, which introduces the risk of missing important requests and slows down testing. Autorize does not provide the users with the ability to perform general-purpose text replacements and has a confusing user interface. AutoRepeater takes all the best ideas from these plugins, along with the Burp Suite's familiar user interface, and combines them to create the most streamlined authorization testing plugin.
26 |
27 | AutoRepeater provides a general-purpose solution for streamlining authorization testing within web applications. AutoRepeater provides the following features:
28 |
29 | + Automatically duplicate, modify, and resend any request
30 | + Conditional replacements
31 | + Quick header, cookie, and parameter value replacements
32 | + Split request/response viewer
33 | + Original vs. modified request/response diff viewer
34 | + Base replacements for values that break requests like CSRF tokens and session cookies
35 | + Renamable tabs
36 | + Logging
37 | + Exporting
38 | + Toggled activation
39 | + "Send to AutoRepeater" from other Burp Suite tools
40 |
41 | ## Sample Usage
42 | Following are some common use cases for AutoRepeater. Some helpful tips when using the tool are:
43 |
44 | + Don't activate autorepeater until you're ready to start browsing.
45 | + Ensure **Extender** is not using cookies from Burp's cookie jar (**Project Options > Session**).
46 | + Check early to ensure your replacements are working as expected.
47 | + Tabs and configuration are preserved after a restart, but data is lost.
48 |
49 | ### Testing Unauthenticated User Access
50 | To test whether an unauthenticated user can access the application, configure one rule under Base Replacements to **Remove Header By Name** and then match "Cookie".
51 |
52 | ### Testing Authenticated User Access
53 | To test access between authenticated users (e.g. low privilege to higher privilege), you'll need to define replacements for each of the session cookies used.
54 |
55 | 1. Make note of the cookie names and values for the lower-privileged session.
56 | 2. Configure a rule under Base Replacements for each cookie to **Match Cookie Name, Replace Value**. Match the cookie name, replace with the lower-privileged user's cookie.
57 | 3. Repeat for as many roles as you'd like to test.
58 | 4. Browse the application as the highest-privileged user.
59 | 5. Review the results.
60 |
61 | ### Reviewing User Access Results
62 | To review the results of access testing, first ensure you're using the latest version of the tool (Git, not BApp store).
63 |
64 | 1. Sort by **URL**, then by **Resp. Len. Diff.**. Items with a difference of 0 and identical status codes are strong indicators of successful access.
65 | 2. Using **Logs > Log Filter** configure exclusions for irrelevant data (e.g. File Extension = (png|gif|css|ico), Modified Status Code = (403|404)).
66 | 3. Review the results and manually investigate anything that looks out of place.
67 |
68 | ## References
69 |
70 | + [BSides Rochester 2018 - AutoRepeater: Automated HTTP Request Repeating With Burp Suite](https://www.youtube.com/watch?v=IYFLp_4ccrw)
71 | + [AutoRepeater: Automated HTTP Request Repeating With Burp Suite](https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2018/january/autorepeater-automated-http-request-repeating-with-burp-suite/)
72 |
--------------------------------------------------------------------------------
/ar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/AutoRepeater/f22b01723ac46605ed08ec42b0e10e674b7d0f90/ar.png
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | jcenter()
4 | }
5 |
6 | dependencies {
7 | classpath 'com.github.jengelman.gradle.plugins:shadow:+'
8 | }
9 | }
10 |
11 | apply plugin: 'java'
12 | apply plugin: 'idea'
13 | apply plugin: 'com.github.johnrengelman.shadow'
14 |
15 | sourceCompatibility = 1.8
16 |
17 | repositories {
18 | mavenCentral()
19 | }
20 |
21 | compileJava.options.encoding = 'UTF-8'
22 |
23 | dependencies {
24 | compile group: 'com.google.code.gson', name: 'gson', version: '2.7'
25 | compile 'net.portswigger.burp.extender:burp-extender-api:1.7.13'
26 | compile 'com.google.guava:guava:23.4-jre'
27 | }
28 |
29 | //noinspection GroovyAssignabilityCheck
30 | sourceSets {
31 | main {
32 | java {
33 | srcDirs = ["./src"]
34 | }
35 | }
36 | }
37 |
38 | defaultTasks 'shadowJar'
39 |
40 | //noinspection GroovyAssignabilityCheck
41 | jar {
42 | manifest {
43 | attributes (
44 | "Manifest-Version": "1.0",
45 | )
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/AutoRepeater/f22b01723ac46605ed08ec42b0e10e674b7d0f90/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # http://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 | # Determine the Java command to use to start the JVM.
86 | if [ -n "$JAVA_HOME" ] ; then
87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88 | # IBM's JDK on AIX uses strange locations for the executables
89 | JAVACMD="$JAVA_HOME/jre/sh/java"
90 | else
91 | JAVACMD="$JAVA_HOME/bin/java"
92 | fi
93 | if [ ! -x "$JAVACMD" ] ; then
94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95 |
96 | Please set the JAVA_HOME variable in your environment to match the
97 | location of your Java installation."
98 | fi
99 | else
100 | JAVACMD="java"
101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102 |
103 | Please set the JAVA_HOME variable in your environment to match the
104 | location of your Java installation."
105 | fi
106 |
107 | # Increase the maximum file descriptors if we can.
108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109 | MAX_FD_LIMIT=`ulimit -H -n`
110 | if [ $? -eq 0 ] ; then
111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112 | MAX_FD="$MAX_FD_LIMIT"
113 | fi
114 | ulimit -n $MAX_FD
115 | if [ $? -ne 0 ] ; then
116 | warn "Could not set maximum file descriptor limit: $MAX_FD"
117 | fi
118 | else
119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120 | fi
121 | fi
122 |
123 | # For Darwin, add options to specify how the application appears in the dock
124 | if $darwin; then
125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126 | fi
127 |
128 | # For Cygwin, switch paths to Windows format before running java
129 | if $cygwin ; then
130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132 | JAVACMD=`cygpath --unix "$JAVACMD"`
133 |
134 | # We build the pattern for arguments to be converted via cygpath
135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136 | SEP=""
137 | for dir in $ROOTDIRSRAW ; do
138 | ROOTDIRS="$ROOTDIRS$SEP$dir"
139 | SEP="|"
140 | done
141 | OURCYGPATTERN="(^($ROOTDIRS))"
142 | # Add a user-defined pattern to the cygpath arguments
143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145 | fi
146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
147 | i=0
148 | for arg in "$@" ; do
149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
151 |
152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154 | else
155 | eval `echo args$i`="\"$arg\""
156 | fi
157 | i=$((i+1))
158 | done
159 | case $i in
160 | (0) set -- ;;
161 | (1) set -- "$args0" ;;
162 | (2) set -- "$args0" "$args1" ;;
163 | (3) set -- "$args0" "$args1" "$args2" ;;
164 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170 | esac
171 | fi
172 |
173 | # Escape application args
174 | save () {
175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176 | echo " "
177 | }
178 | APP_ARGS=$(save "$@")
179 |
180 | # Collect all arguments for the java command, following the shell quoting and substitution rules
181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182 |
183 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185 | cd "$(dirname "$0")"
186 | fi
187 |
188 | exec "$JAVACMD" "$@"
189 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem http://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34 |
35 | @rem Find java.exe
36 | if defined JAVA_HOME goto findJavaFromJavaHome
37 |
38 | set JAVA_EXE=java.exe
39 | %JAVA_EXE% -version >NUL 2>&1
40 | if "%ERRORLEVEL%" == "0" goto init
41 |
42 | echo.
43 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44 | echo.
45 | echo Please set the JAVA_HOME variable in your environment to match the
46 | echo location of your Java installation.
47 |
48 | goto fail
49 |
50 | :findJavaFromJavaHome
51 | set JAVA_HOME=%JAVA_HOME:"=%
52 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53 |
54 | if exist "%JAVA_EXE%" goto init
55 |
56 | echo.
57 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58 | echo.
59 | echo Please set the JAVA_HOME variable in your environment to match the
60 | echo location of your Java installation.
61 |
62 | goto fail
63 |
64 | :init
65 | @rem Get command-line arguments, handling Windows variants
66 |
67 | if not "%OS%" == "Windows_NT" goto win9xME_args
68 |
69 | :win9xME_args
70 | @rem Slurp the command line arguments.
71 | set CMD_LINE_ARGS=
72 | set _SKIP=2
73 |
74 | :win9xME_args_slurp
75 | if "x%~1" == "x" goto execute
76 |
77 | set CMD_LINE_ARGS=%*
78 |
79 | :execute
80 | @rem Setup the command line
81 |
82 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83 |
84 | @rem Execute Gradle
85 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86 |
87 | :end
88 | @rem End local scope for the variables with windows NT shell
89 | if "%ERRORLEVEL%"=="0" goto mainEnd
90 |
91 | :fail
92 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93 | rem the _cmd.exe /c_ return code!
94 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95 | exit /b 1
96 |
97 | :mainEnd
98 | if "%OS%"=="Windows_NT" endlocal
99 |
100 | :omega
101 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'AutoRepeater'
2 |
--------------------------------------------------------------------------------
/src/burp/BurpExtender.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | /**
4 | * Created by j on 8/7/17.
5 | */
6 |
7 | import burp.Utils.AutoRepeaterMenu;
8 | import burp.Utils.Utils;
9 | import com.google.gson.*;
10 |
11 | import java.awt.*;
12 | import java.awt.event.*;
13 | import java.util.*;
14 | import java.util.List;
15 | import java.util.concurrent.ExecutorService;
16 | import java.util.concurrent.Executors;
17 | import javax.swing.*;
18 | import javax.swing.Timer;
19 | import javax.swing.event.DocumentEvent;
20 | import javax.swing.event.DocumentListener;
21 |
22 | public class BurpExtender implements IBurpExtender, ITab, IHttpListener, IContextMenuFactory {
23 |
24 | // burp stuff
25 | private static IBurpExtenderCallbacks callbacks;
26 | private static IExtensionHelpers helpers;
27 | private static Gson gson;
28 |
29 | private static JTabbedPane mainTabbedPane;
30 | private static JTabbedPane parentTabbedPane;
31 | private static JPanel newTabButton;
32 | private static AutoRepeaterMenu autoRepeaterMenu;
33 | private ExecutorService executor;
34 | //private static ResponseStore responseStore;
35 |
36 | // Global state variables
37 | private static int tabCounter = 0;
38 | private static boolean tabChangeListenerLock = false;
39 | private static ArrayList autoRepeaters;
40 |
41 | @Override
42 | public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) {
43 | // keep a reference to our callbacks object
44 | BurpExtender.callbacks = callbacks;
45 | // obtain an extension helpers object
46 | BurpExtender.helpers = callbacks.getHelpers();
47 | // Gson for serialization
48 | BurpExtender.gson = new Gson();
49 | //BurpExtender.gson = new GsonBuilder().setPrettyPrinting().create();
50 | autoRepeaters = new ArrayList<>();
51 | executor = Executors.newFixedThreadPool(25);
52 | // create our UI
53 | SwingUtilities.invokeLater(() -> {
54 | mainTabbedPane = new JTabbedPane();
55 | newTabButton = new JPanel();
56 | newTabButton.setName("...");
57 | mainTabbedPane = new JTabbedPane();
58 | mainTabbedPane.add(newTabButton);
59 | // If there is a saved extensionSetting load it.
60 | String b64ConfigurationJson = callbacks.loadExtensionSetting(getTabCaption());
61 | if (b64ConfigurationJson != null) {
62 | initializeFromSave(b64ConfigurationJson, true);
63 | } else {
64 | addNewTab();
65 | }
66 | mainTabbedPane.addChangeListener(e -> {
67 | // Make all tabname not editable whenever the tab is changed
68 | if (!tabChangeListenerLock) {
69 | if (mainTabbedPane.getSelectedIndex() == mainTabbedPane.getTabCount() - 1) {
70 | if (!tabChangeListenerLock) {
71 | addNewTab();
72 | }
73 | }
74 | }
75 | for (int i = 0; i < mainTabbedPane.getTabCount() - 1; i++) {
76 | AutoRepeaterTabHandle arth = (AutoRepeaterTabHandle) mainTabbedPane.getTabComponentAt(i);
77 | arth.tabName.setEditable(false);
78 | }
79 | });
80 |
81 | // Set Extension Name
82 | callbacks.setExtensionName("AutoRepeater");
83 | // register As An HTTP Listener
84 | callbacks.registerHttpListener(BurpExtender.this);
85 | // Add To Right Click Menu
86 | callbacks.registerContextMenuFactory(BurpExtender.this);
87 | // Add response store
88 | //Save State
89 | callbacks.registerExtensionStateListener(
90 | () -> callbacks.saveExtensionSetting(getTabCaption(), exportSave())
91 | );
92 | // Add A Custom Tab To Burp
93 | callbacks.addSuiteTab(BurpExtender.this);
94 | // set parent component
95 | parentTabbedPane = (JTabbedPane) getUiComponent().getParent();
96 | autoRepeaterMenu = new AutoRepeaterMenu(parentTabbedPane.getRootPane());
97 | SwingUtilities.invokeLater(autoRepeaterMenu);
98 | });
99 | }
100 |
101 | public static AutoRepeaterMenu getAutoRepeaterMenu() {
102 | return autoRepeaterMenu;
103 | }
104 |
105 | public static JTabbedPane getParentTabbedPane() {
106 | return parentTabbedPane;
107 | }
108 |
109 | public static String exportSave() {
110 | JsonArray BurpExtenderJson = new JsonArray();
111 | // Don't count the "..." tab
112 | for (int i = 0; i < mainTabbedPane.getTabCount() - 1; i++) {
113 | AutoRepeaterTabHandle autoRepeaterTabHandle
114 | = (AutoRepeaterTabHandle) mainTabbedPane.getTabComponentAt(i);
115 | AutoRepeater ar = autoRepeaterTabHandle.autoRepeater;
116 | JsonObject AutoRepeaterJson = ar.toJson();
117 | AutoRepeaterJson.addProperty("tabName", autoRepeaterTabHandle.tabName.getText());
118 | BurpExtenderJson.add(AutoRepeaterJson);
119 | }
120 | return BurpExtenderJson.toString();
121 | }
122 |
123 | public static String exportSave(AutoRepeater ar) {
124 | return exportSaveAsJson(ar).toString();
125 | }
126 |
127 | public static JsonArray exportSaveAsJson(AutoRepeater ar) {
128 | JsonArray BurpExtenderJson = new JsonArray();
129 | // Don't count the "..." tab
130 | for (int i = 0; i < mainTabbedPane.getTabCount() - 1; i++) {
131 | AutoRepeaterTabHandle autoRepeaterTabHandle
132 | = (AutoRepeaterTabHandle) mainTabbedPane.getTabComponentAt(i);
133 | AutoRepeater tempAR = autoRepeaterTabHandle.autoRepeater;
134 | if (ar.equals(tempAR)) {
135 | JsonObject AutoRepeaterJson = tempAR.toJson();
136 | AutoRepeaterJson.addProperty("tabName", autoRepeaterTabHandle.tabName.getText());
137 | BurpExtenderJson.add(AutoRepeaterJson);
138 | }
139 | }
140 | return BurpExtenderJson;
141 | }
142 |
143 | public static void initializeFromSave(String configuration, boolean replaceTabs) {
144 | getCallbacks().printOutput("Loading Stored AutoRepeater Configuration");
145 | String configurationJson;
146 | // Check if the configuration is B64 encoded for legacy.
147 | try {
148 | configurationJson = new String(Base64.getDecoder().decode(configuration));
149 | } catch (IllegalArgumentException e) {
150 | configurationJson = configuration;
151 | }
152 | JsonParser jsonParser = new JsonParser();
153 | JsonArray tabConfigurations = jsonParser.parse(configurationJson).getAsJsonArray();
154 | if (replaceTabs) {
155 | closeAllTabs();
156 | }
157 | for (JsonElement tabConfiguration : tabConfigurations) {
158 | addNewTab(tabConfiguration.getAsJsonObject());
159 | }
160 | }
161 |
162 | public static void addNewTab(JsonObject tabContents) {
163 | String tabName = tabContents.get("tabName").getAsString();
164 | tabChangeListenerLock = true;
165 | tabCounter += 1;
166 | AutoRepeater autoRepeater = new AutoRepeater(tabContents);
167 | autoRepeaters.add(autoRepeater);
168 | mainTabbedPane.add(autoRepeater.getUI());
169 | AutoRepeaterTabHandle autoRepeaterTabHandle = new AutoRepeaterTabHandle(tabName, autoRepeater);
170 | mainTabbedPane.setTabComponentAt(mainTabbedPane.indexOfComponent(autoRepeater.getUI()),
171 | autoRepeaterTabHandle);
172 | // Hack to steal and remove focus
173 | mainTabbedPane.remove(newTabButton);
174 | mainTabbedPane.add(newTabButton);
175 | tabChangeListenerLock = false;
176 | }
177 |
178 | private static void addNewTab() {
179 | tabChangeListenerLock = true;
180 | tabCounter += 1;
181 | AutoRepeater autoRepeater = new AutoRepeater();
182 | autoRepeaters.add(autoRepeater);
183 | mainTabbedPane.add(autoRepeater.getUI());
184 | AutoRepeaterTabHandle autoRepeaterTabHandle = new AutoRepeaterTabHandle(
185 | Integer.toString(tabCounter), autoRepeater);
186 | mainTabbedPane.setTabComponentAt(mainTabbedPane.indexOfComponent(autoRepeater.getUI()),
187 | autoRepeaterTabHandle);
188 | // Hack to steal and remove focus
189 | mainTabbedPane.remove(newTabButton);
190 | mainTabbedPane.add(newTabButton);
191 | tabChangeListenerLock = false;
192 | }
193 |
194 | public static void highlightTab() {
195 | if (parentTabbedPane != null) {
196 | for (int i = 0; i < parentTabbedPane.getTabCount(); i++) {
197 | if (parentTabbedPane.getComponentAt(i).equals(mainTabbedPane)) {
198 | parentTabbedPane.setBackgroundAt(i, Utils.getBurpOrange());
199 | Timer timer = new Timer(3000, e -> {
200 | for (int j = 0; j < parentTabbedPane.getTabCount(); j++) {
201 | if (parentTabbedPane.getComponentAt(j).equals(mainTabbedPane)) {
202 | parentTabbedPane.setBackgroundAt(j, Color.BLACK);
203 | break;
204 | }
205 | }
206 | });
207 | timer.setRepeats(false);
208 | timer.start();
209 | break;
210 | }
211 | }
212 | }
213 | }
214 |
215 | // implement ITab
216 | @Override
217 | public String getTabCaption() {
218 | return "AutoRepeater";
219 | }
220 |
221 | @Override
222 | public Component getUiComponent() {
223 | return mainTabbedPane;
224 | }
225 |
226 | // implement IHttpListener
227 | @Override
228 | public void processHttpMessage(
229 | int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
230 | if (!messageIsRequest) {
231 | for (AutoRepeater autoRepeater : autoRepeaters) {
232 | executor.submit(
233 | () -> autoRepeater.modifyAndSendRequestAndLog(
234 | toolFlag,
235 | messageInfo)
236 | );
237 | }
238 | }
239 | }
240 |
241 | public static void closeAllTabs() {
242 | tabChangeListenerLock = true;
243 | int tabCount = mainTabbedPane.getTabCount() - 1;
244 | for (int i = 0; i < tabCount; i++) {
245 | if (mainTabbedPane.getTabComponentAt(0).getClass().equals(AutoRepeaterTabHandle.class)) {
246 | try {
247 | AutoRepeaterTabHandle arth = (AutoRepeaterTabHandle) mainTabbedPane.getTabComponentAt(0);
248 | autoRepeaters.remove(arth.autoRepeater);
249 | mainTabbedPane.remove(0);
250 | } catch (Exception e) {
251 | getCallbacks().printOutput(e.getMessage());
252 | }
253 | }
254 | }
255 | tabChangeListenerLock = false;
256 | }
257 |
258 | private static class AutoRepeaterTabHandle extends JPanel {
259 |
260 | AutoRepeater autoRepeater;
261 | JTextField tabName;
262 |
263 | public AutoRepeaterTabHandle(String title, AutoRepeater autoRepeater) {
264 | this.autoRepeater = autoRepeater;
265 | this.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
266 | this.setOpaque(false);
267 | JLabel label = new JLabel(title);
268 | label.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 2));
269 | tabName = new JTextField(title);
270 | tabName.setOpaque(false);
271 | tabName.setBorder(null);
272 | tabName.setBackground(new Color(0, 0, 0, 0));
273 | tabName.setEditable(false);
274 | tabName.setCaretColor(Color.BLACK);
275 |
276 | this.add(tabName);
277 | JButton closeButton = new JButton("✕");
278 | closeButton.setFont(new Font("monospaced", Font.PLAIN, 10));
279 | closeButton.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 2));
280 | closeButton.setForeground(Color.GRAY);
281 |
282 | closeButton.setBorderPainted(false);
283 | closeButton.setContentAreaFilled(false);
284 | closeButton.setOpaque(false);
285 |
286 | // Fix tabname redraw lag.
287 | JPanel parent = this;
288 | tabName.getDocument().addDocumentListener(
289 | new DocumentListener() {
290 | @Override
291 | public void insertUpdate(DocumentEvent e) {
292 | tabName.repaint();
293 | parent.validate();
294 | }
295 |
296 | @Override
297 | public void removeUpdate(DocumentEvent e) {
298 | tabName.repaint();
299 | parent.validate();
300 | }
301 |
302 | @Override
303 | public void changedUpdate(DocumentEvent e) {
304 | tabName.repaint();
305 | parent.validate();
306 | }
307 | });
308 |
309 | tabName.addMouseListener(new MouseAdapter() {
310 | @Override
311 | public void mouseClicked(MouseEvent e) {
312 | if (!mainTabbedPane.getSelectedComponent().equals(autoRepeater.getUI())) {
313 | mainTabbedPane.setSelectedComponent(autoRepeater.getUI());
314 | for (int i = 0; i < mainTabbedPane.getTabCount() - 2; i++) {
315 | if (!mainTabbedPane.getComponentAt(i).equals(autoRepeater.getUI())) {
316 | AutoRepeaterTabHandle autoRepeaterTabHandle =
317 | (AutoRepeaterTabHandle) mainTabbedPane.getTabComponentAt(i);
318 | autoRepeaterTabHandle.tabName.setEditable(false);
319 | }
320 | }
321 | } else {
322 | mainTabbedPane.setSelectedComponent(autoRepeater.getUI());
323 | tabName.setEditable(true);
324 | }
325 | }
326 |
327 | @Override
328 | public void mousePressed(MouseEvent e) {
329 | if (!mainTabbedPane.getSelectedComponent().equals(autoRepeater.getUI())) {
330 | mainTabbedPane.setSelectedComponent(autoRepeater.getUI());
331 | for (int i = 0; i < mainTabbedPane.getTabCount() - 2; i++) {
332 | if (!mainTabbedPane.getComponentAt(i).equals(autoRepeater.getUI())) {
333 | AutoRepeaterTabHandle arth = (AutoRepeaterTabHandle) mainTabbedPane
334 | .getTabComponentAt(i);
335 | arth.tabName.setEditable(false);
336 | }
337 | }
338 | } else {
339 | mainTabbedPane.setSelectedComponent(autoRepeater.getUI());
340 | }
341 | }
342 | });
343 |
344 | closeButton.addActionListener(e -> {
345 | tabChangeListenerLock = true;
346 | if (mainTabbedPane.getSelectedComponent().equals(autoRepeater.getUI())) {
347 | if (mainTabbedPane.getTabCount() == 2) {
348 | mainTabbedPane.remove(autoRepeater.getUI());
349 | autoRepeaters.remove(autoRepeater);
350 | addNewTab();
351 | tabChangeListenerLock = true;
352 | } else if (mainTabbedPane.getTabCount() > 2) {
353 | mainTabbedPane.remove(autoRepeater.getUI());
354 | autoRepeaters.remove(autoRepeater);
355 | }
356 | if (mainTabbedPane.getSelectedIndex() == mainTabbedPane.getTabCount() - 1) {
357 | mainTabbedPane.setSelectedIndex(mainTabbedPane.getTabCount() - 2);
358 | }
359 | } else {
360 | mainTabbedPane.setSelectedComponent(autoRepeater.getUI());
361 | }
362 | tabChangeListenerLock = false;
363 | });
364 | this.add(closeButton);
365 | }
366 |
367 | }
368 |
369 | public static AutoRepeater getSelectedAutoRepeater() {
370 | AutoRepeaterTabHandle autoRepeaterTabHandle = (AutoRepeaterTabHandle) mainTabbedPane.getTabComponentAt(mainTabbedPane.getSelectedIndex());
371 | return autoRepeaterTabHandle.autoRepeater;
372 | }
373 |
374 | public static IBurpExtenderCallbacks getCallbacks() {
375 | return callbacks;
376 | }
377 |
378 | public static IExtensionHelpers getHelpers() {
379 | return helpers;
380 | }
381 |
382 | public static ArrayList getAutoRepeaters() {
383 | return autoRepeaters;
384 | }
385 |
386 | public static Gson getGson() {
387 | return gson;
388 | }
389 |
390 | @Override
391 | public List createMenuItems(IContextMenuInvocation invocation) {
392 | ArrayList menu = new ArrayList<>();
393 | ActionListener listener;
394 | final int toolFlag = invocation.getToolFlag();
395 | IHttpRequestResponse[] requestResponses = invocation.getSelectedMessages();
396 |
397 | listener = event -> new Thread(() -> {
398 | if (toolFlag != -1) {
399 | BurpExtender.highlightTab();
400 | for (IHttpRequestResponse requestResponse : requestResponses) {
401 | final IHttpRequestResponse tempRequestResponse;
402 | if (requestResponse.getResponse() == null) {
403 | tempRequestResponse = BurpExtender.getCallbacks().makeHttpRequest(
404 | requestResponse.getHttpService(), requestResponse.getRequest());
405 | } else {
406 | tempRequestResponse = requestResponse;
407 | }
408 | executor.submit(() -> {
409 | for (AutoRepeater autoRepeater : autoRepeaters) {
410 | autoRepeater.modifyAndSendRequestAndLog(
411 | toolFlag,
412 | tempRequestResponse);
413 | }
414 | });
415 | }
416 | }
417 | }).start();
418 |
419 | JMenuItem item = new JMenuItem("Send to AutoRepeater", null);
420 | item.addActionListener(listener);
421 | menu.add(item);
422 | return menu;
423 | }
424 | }
425 |
--------------------------------------------------------------------------------
/src/burp/Conditions/Condition.java:
--------------------------------------------------------------------------------
1 | package burp.Conditions;
2 |
3 | import burp.BurpExtender;
4 | import burp.IHttpRequestResponse;
5 | import burp.IParameter;
6 | import burp.IRequestInfo;
7 | import burp.IResponseInfo;
8 | import com.google.common.io.Files;
9 |
10 | import java.util.Arrays;
11 | import java.util.List;
12 | import java.util.regex.Pattern;
13 | import java.util.stream.Collectors;
14 |
15 | public class Condition {
16 |
17 | private String booleanOperator;
18 | private String matchType;
19 | private String matchRelationship;
20 | private String matchCondition;
21 | private boolean isEnabled;
22 |
23 | public Condition(
24 | String booleanOperator,
25 | String matchType,
26 | String matchRelationship,
27 | String matchCondition) {
28 | this(booleanOperator,
29 | matchType,
30 | matchRelationship,
31 | matchCondition,
32 | true);
33 | }
34 |
35 | public Condition(
36 | String booleanOperator,
37 | String matchType,
38 | String matchRelationship,
39 | String matchCondition,
40 | boolean isEnabled) {
41 | setEnabled(isEnabled);
42 | setBooleanOperator(booleanOperator);
43 | setMatchType(matchType);
44 | setMatchRelationship(matchRelationship);
45 | setMatchCondition(matchCondition);
46 | }
47 |
48 | public Condition(Condition condition) {
49 | this(condition.getBooleanOperator(),
50 | condition.getMatchType(),
51 | condition.getMatchRelationship(),
52 | condition.getMatchCondition(),
53 | condition.isEnabled());
54 | if (getBooleanOperator().equals("")) {
55 | setBooleanOperator("And");
56 | }
57 | }
58 |
59 | public static final String[] BOOLEAN_OPERATOR_OPTIONS = {
60 | "And",
61 | "Or"
62 | };
63 |
64 | public static final String[] MATCH_TYPE_OPTIONS = {
65 | "String In Request",
66 | "String In Response",
67 | "Request Length",
68 | "Response Length",
69 | "URL",
70 | "Status Code",
71 | "Domain Name",
72 | //"IP Address",
73 | "Protocol",
74 | "HTTP Method",
75 | "File Extension",
76 | "Request",
77 | "Cookie Name",
78 | "Cookie Value",
79 | "Any Header",
80 | "Request Body",
81 | "Param Name",
82 | "Param Value",
83 | "Sent From Tool",
84 | "Listener Port"
85 | };
86 |
87 | public boolean checkCondition(int toolFlag, IHttpRequestResponse messageInfo) {
88 | switch (this.matchType) {
89 | case "Domain Name": return checkDomainName(messageInfo);
90 | case "Protocol": return checkProtocol(messageInfo);
91 | case "HTTP Method": return checkHttpMethod(messageInfo);
92 | case "URL": return checkUrl(messageInfo);
93 | case "File Extension": return checkFileExtension(messageInfo);
94 | case "Request": return checkRequest(messageInfo);
95 | case "Cookie Name": return checkCookieName(messageInfo);
96 | case "Cookie Value": return checkCookieValue(messageInfo);
97 | case "Any Header": return checkAnyHeader(messageInfo);
98 | case "Request Body": return checkRequestBody(messageInfo);
99 | case "Param Name": return checkParamName(messageInfo);
100 | case "Param Value": return checkParamValue(messageInfo);
101 | case "Sent From Tool": return checkSentFromTool(toolFlag);
102 | case "Listener Port": return checkListenerPort(messageInfo);
103 | case "Status Code": return checkStatusCode(messageInfo);
104 | case "String In Request": return checkStringInRequest(messageInfo);
105 | case "String In Response": return checkStringInResponse(messageInfo);
106 | default: throw new IllegalStateException("checkCondition() not defined for the input.");
107 | }
108 | }
109 |
110 |
111 | public static String[] getMatchRelationshipOptions(String inputString) {
112 | switch (inputString) {
113 | case "Domain Name":
114 | return new String[]{"Matches", "Does Not Match"};
115 | case "IP Address":
116 | return new String[]{"Is In Range", "Is Not In Range"};
117 | case "Protocol":
118 | return new String[]{"Is HTTP", "Is Not HTTP"};
119 | case "HTTP Method":
120 | return new String[]{"Matches", "Does Not Match"};
121 | case "URL":
122 | return new String[]{"Matches", "Does Not Match", "Is In Scope"};
123 | case "File Extension":
124 | return new String[]{"Matches", "Does Not Match"};
125 | case "Request":
126 | return new String[]{"Contains Parameters", "Does Not Contain Parameters"};
127 | case "Status Code":
128 | return new String[]{"Is Greater Than", "Is Less Than", "Equals", "Does Not Equal"};
129 | case "Cookie Name":
130 | return new String[]{"Matches", "Does Not Match"};
131 | case "Cookie Value":
132 | return new String[]{"Matches", "Does Not Match"};
133 | case "Any Header":
134 | return new String[]{"Matches", "Does Not Match"};
135 | case "Request Body":
136 | return new String[]{"Matches", "Does Not Match"};
137 | case "Param Name":
138 | return new String[]{"Matches", "Does Not Match"};
139 | case "Param Value":
140 | return new String[]{"Matches", "Does Not Match"};
141 | case "Sent From Tool":
142 | return new String[]{
143 | "Burp",
144 | "Proxy",
145 | "Repeater",
146 | "Spider",
147 | "Intruder",
148 | "Scanner"
149 | };
150 | case "Listener Port":
151 | return new String[]{"Matches", "Does Not Match"};
152 | case "String In Request":
153 | return new String[]{"Matches", "Does Not Match", "Matches Regex", "Does Not Match Regex"};
154 | case "String In Response":
155 | return new String[]{"Matches", "Does Not Match", "Matches Regex", "Does Not Match Regex"};
156 | case "Request Length":
157 | return new String[]{"Is Greater Than", "Is Less Than", "Equals"};
158 | case "Response Length":
159 | return new String[]{"Is Greater Than", "Is Less Than", "Equals"};
160 | case "Mime Type":
161 | return new String[]{"Is Text", "Is Not Text", "Is Media", "Is Not Media"};
162 | default:
163 | throw new IllegalStateException("getMatchRelationshipOptions() not defined for "+inputString);
164 | }
165 | }
166 |
167 | public static boolean matchConditionIsEditable(String inputString) {
168 | switch (inputString) {
169 | case "Domain Name":
170 | return true;
171 | case "IP Address":
172 | return true;
173 | case "Protocol":
174 | return false;
175 | case "HTTP Method":
176 | return true;
177 | case "URL":
178 | return true;
179 | case "File Extension":
180 | return true;
181 | case "Request":
182 | return false;
183 | case "Cookie Name":
184 | return true;
185 | case "Cookie Value":
186 | return true;
187 | case "Any Header":
188 | return true;
189 | case "Request Body":
190 | return true;
191 | case "Param Name":
192 | return true;
193 | case "Param Value":
194 | return true;
195 | case "Sent From Tool":
196 | return false;
197 | case "Listener Port":
198 | return true;
199 | case "Status Code":
200 | return true;
201 | case "String In Request":
202 | return true;
203 | case "String In Response":
204 | return true;
205 | case "Request Length":
206 | return true;
207 | case "Response Length":
208 | return true;
209 | default:
210 | throw new IllegalStateException("matchConditionIsEditable() not defined for input "+inputString);
211 | }
212 | }
213 |
214 | private boolean checkRequestLength(IHttpRequestResponse messageInfo) {
215 | try {
216 | int matchInt = Integer.parseInt(this.matchCondition);
217 | switch (this.matchRelationship) {
218 | case "Is Greater Than":
219 | return messageInfo.getRequest().length > matchInt;
220 | case "Is Less Than":
221 | return messageInfo.getRequest().length < matchInt;
222 | default:
223 | return messageInfo.getRequest().length == matchInt;
224 | }
225 | } catch(Exception NumberFormatException) {
226 | return false;
227 | }
228 | }
229 |
230 | private boolean checkResponseLength(IHttpRequestResponse messageInfo) {
231 | try {
232 | int matchInt = Integer.parseInt(this.matchCondition);
233 | switch (this.matchRelationship) {
234 | case "Is Greater Than":
235 | return messageInfo.getResponse().length > matchInt;
236 | case "Is Less Than":
237 | return messageInfo.getResponse().length < matchInt;
238 | default:
239 | return messageInfo.getResponse().length == matchInt;
240 | }
241 | } catch(Exception NumberFormatException) {
242 | return false;
243 | }
244 | }
245 |
246 | private boolean checkStringInRequest(IHttpRequestResponse messageInfo) {
247 | switch (this.matchRelationship) {
248 | case "Matches":
249 | return new String(messageInfo.getRequest()).contains(this.matchCondition);
250 | case "Does Not Match":
251 | return !(new String(messageInfo.getRequest()).contains(this.matchCondition));
252 | case "Matches Regex":
253 | return Pattern.compile(this.matchCondition).matcher(new String(messageInfo.getRequest())).find();
254 | default:
255 | return !(Pattern.compile(this.matchCondition).matcher(new String(messageInfo.getRequest())).find());
256 | }
257 | }
258 |
259 | private boolean checkStringInResponse(IHttpRequestResponse messageInfo) {
260 | switch (this.matchRelationship) {
261 | case "Matches":
262 | return new String(messageInfo.getResponse()).contains(this.matchCondition);
263 | case "Does Not Match":
264 | return !(new String(messageInfo.getResponse()).contains(this.matchCondition));
265 | case "Matches Regex":
266 | return Pattern.compile(this.matchCondition).matcher(new String(messageInfo.getResponse())).find();
267 | default:
268 | return !(Pattern.compile(this.matchCondition).matcher(new String(messageInfo.getResponse())).find());
269 | }
270 | }
271 |
272 | private boolean checkStatusCode(IHttpRequestResponse messageInfo) {
273 | IResponseInfo analyzedResponse = BurpExtender.getHelpers().analyzeResponse(messageInfo.getResponse());
274 | try {
275 | short responseCodeAsShort = Short.parseShort(this.matchCondition);
276 | switch (this.matchRelationship) {
277 | case "Is Greater Than":
278 | return analyzedResponse.getStatusCode() > responseCodeAsShort;
279 | case "Is Less Than":
280 | return analyzedResponse.getStatusCode() < responseCodeAsShort;
281 | case "Equals":
282 | return (analyzedResponse.getStatusCode() == responseCodeAsShort);
283 | default:
284 | return !(analyzedResponse.getStatusCode() == responseCodeAsShort);
285 | }
286 | } catch (NumberFormatException e) {
287 | return false;
288 | }
289 | }
290 |
291 | private boolean checkDomainName(IHttpRequestResponse messageInfo) {
292 | switch (this.matchRelationship) {
293 | case "Matches":
294 | return messageInfo.getHttpService().getHost().equals(this.matchCondition);
295 | default:
296 | return !messageInfo.getHttpService().getHost().equals(this.matchCondition);
297 | }
298 | }
299 |
300 | private boolean checkProtocol(IHttpRequestResponse messageInfo) {
301 | String protocol = messageInfo.getHttpService().getProtocol();
302 | switch (this.matchRelationship) {
303 | case "Is HTTP":
304 | return protocol.equals("http");
305 | default:
306 | return !protocol.equals("http");
307 | }
308 | }
309 |
310 | private boolean checkHttpMethod(IHttpRequestResponse messageInfo) {
311 | IRequestInfo analyzedRequest = BurpExtender.getHelpers().analyzeRequest(messageInfo);
312 | switch (this.matchRelationship) {
313 | case "Matches":
314 | return analyzedRequest.getMethod().matches(this.matchCondition);
315 | default:
316 | return !analyzedRequest.getMethod().matches(this.matchCondition);
317 | }
318 | }
319 |
320 | private boolean checkUrl(IHttpRequestResponse messageInfo) {
321 | IRequestInfo analyzedRequest = BurpExtender.getHelpers().analyzeRequest(messageInfo);
322 | switch (this.matchRelationship) {
323 | case "Is In Scope":
324 | return BurpExtender.getCallbacks().isInScope(analyzedRequest.getUrl());
325 | case "Matches":
326 | return analyzedRequest.getUrl().toString().matches(this.matchCondition);
327 | default:
328 | return !analyzedRequest.getUrl().toString().matches(this.matchCondition);
329 | }
330 | }
331 |
332 | private boolean checkFileExtension(IHttpRequestResponse messageInfo) {
333 | IRequestInfo analyzedRequest = BurpExtender.getHelpers().analyzeRequest(messageInfo);
334 | String fileExtension = Files.getFileExtension(analyzedRequest.getUrl().getPath().toString());
335 | switch (this.matchRelationship) {
336 | case "Matches":
337 | return fileExtension.matches(this.matchCondition);
338 | default:
339 | return !fileExtension.matches(this.matchCondition);
340 | }
341 | }
342 |
343 | private boolean checkRequest(IHttpRequestResponse messageInfo) {
344 | IRequestInfo analyzedRequest = BurpExtender.getHelpers().analyzeRequest(messageInfo);
345 | long parameterCount = analyzedRequest.getParameters()
346 | .stream()
347 | .filter(
348 | p -> p.getType() == IParameter.PARAM_URL || p.getType() == IParameter.PARAM_BODY)
349 | .count();
350 | switch (this.matchRelationship) {
351 | case "Contains Parameters":
352 | return parameterCount > 0;
353 | default:
354 | return !(parameterCount > 0);
355 | }
356 | }
357 |
358 | private boolean checkCookieName(IHttpRequestResponse messageInfo) {
359 | IRequestInfo analyzedRequest = BurpExtender.getHelpers().analyzeRequest(messageInfo);
360 | List cookiesByName = analyzedRequest.getParameters()
361 | .stream()
362 | .filter(p -> p.getType() == IParameter.PARAM_COOKIE)
363 | .filter(p -> p.getName().matches(this.matchCondition))
364 | .collect(Collectors.toList());
365 | switch (this.matchRelationship) {
366 | case "Matches":
367 | return cookiesByName.size() > 0;
368 | default:
369 | return !(cookiesByName.size() > 0);
370 | }
371 | }
372 |
373 | private boolean checkCookieValue(IHttpRequestResponse messageInfo) {
374 | IRequestInfo analyzedRequest = BurpExtender.getHelpers().analyzeRequest(messageInfo);
375 | List cookiesByName = analyzedRequest.getParameters()
376 | .stream()
377 | .filter(p -> p.getType() == IParameter.PARAM_COOKIE)
378 | .filter(p -> p.getName().matches(this.matchCondition))
379 | .collect(Collectors.toList());
380 | switch (this.matchRelationship) {
381 | case "Matches":
382 | return cookiesByName.size() > 0;
383 | default:
384 | return !(cookiesByName.size() > 0);
385 | }
386 | }
387 |
388 | private boolean checkAnyHeader(IHttpRequestResponse messageInfo) {
389 | IRequestInfo analyzedRequest = BurpExtender.getHelpers().analyzeRequest(messageInfo);
390 | List matchingHeaders = analyzedRequest.getHeaders()
391 | .stream()
392 | .filter(h -> h.matches(this.matchCondition))
393 | .collect(Collectors.toList());
394 | switch (this.matchRelationship) {
395 | case "Matches":
396 | return matchingHeaders.size() > 0;
397 | default:
398 | return !(matchingHeaders.size() > 0);
399 | }
400 | }
401 |
402 | private boolean checkRequestBody(IHttpRequestResponse messageInfo) {
403 | IRequestInfo analyzedRequest = BurpExtender.getHelpers().analyzeRequest(messageInfo);
404 | byte[] request = messageInfo.getRequest();
405 | String bodyString = new String(
406 | Arrays.copyOfRange(request, analyzedRequest.getBodyOffset(), request.length));
407 | switch (this.matchRelationship) {
408 | case ("Matches"):
409 | return bodyString.matches(this.matchCondition);
410 | default:
411 | return !bodyString.matches(this.matchCondition);
412 | }
413 | }
414 |
415 | private boolean checkParamName(IHttpRequestResponse messageInfo) {
416 | IRequestInfo analyzedRequest = BurpExtender.getHelpers().analyzeRequest(messageInfo);
417 | List parametersByName = analyzedRequest.getParameters()
418 | .stream()
419 | .filter(p -> p.getName().matches(this.matchCondition))
420 | .collect(Collectors.toList());
421 | switch (this.matchRelationship) {
422 | case "Matches":
423 | return parametersByName.size() > 0;
424 | default:
425 | return !(parametersByName.size() > 0);
426 | }
427 | }
428 |
429 | private boolean checkParamValue(IHttpRequestResponse messageInfo) {
430 | IRequestInfo analyzedRequest = BurpExtender.getHelpers().analyzeRequest(messageInfo);
431 | List parametersByValue = analyzedRequest.getParameters()
432 | .stream()
433 | .filter(p -> p.getValue().matches(this.matchCondition))
434 | .collect(Collectors.toList());
435 | switch (this.matchRelationship) {
436 | case "Matches":
437 | return parametersByValue.size() > 0;
438 | default:
439 | return !(parametersByValue.size() > 0);
440 | }
441 | }
442 |
443 | private boolean checkSentFromTool(int toolFlag) {
444 | switch (this.matchRelationship) {
445 | case "Burp":
446 | return toolFlag != BurpExtender.getCallbacks().TOOL_EXTENDER &&
447 | toolFlag != BurpExtender.getCallbacks().TOOL_SCANNER;
448 | case "Proxy":
449 | return toolFlag == BurpExtender.getCallbacks().TOOL_PROXY;
450 | case "Repeater":
451 | return toolFlag == BurpExtender.getCallbacks().TOOL_REPEATER;
452 | case "Spider":
453 | return toolFlag == BurpExtender.getCallbacks().TOOL_SPIDER;
454 | case "Intruder":
455 | return toolFlag == BurpExtender.getCallbacks().TOOL_INTRUDER;
456 | default:
457 | return toolFlag == BurpExtender.getCallbacks().TOOL_SCANNER;
458 | }
459 | }
460 |
461 | private boolean checkListenerPort(IHttpRequestResponse messageInfo) {
462 | if (this.matchType.equals("Matches")) {
463 | return messageInfo.getHttpService().getPort() == Integer.parseInt(this.matchCondition);
464 | } else {
465 | return !(messageInfo.getHttpService().getPort() == Integer.parseInt(this.matchCondition));
466 | }
467 | }
468 |
469 | public String getBooleanOperator() {
470 | return booleanOperator;
471 | }
472 |
473 | public void setBooleanOperator(String booleanOperator) {
474 | if(Arrays.stream(BOOLEAN_OPERATOR_OPTIONS).anyMatch(s -> s.equals(booleanOperator))) {
475 | this.booleanOperator = booleanOperator;
476 | } else if (booleanOperator.equals("")) {
477 | this.booleanOperator = booleanOperator;
478 | } else {
479 | this.booleanOperator = BOOLEAN_OPERATOR_OPTIONS[0];
480 | }
481 | }
482 |
483 | public String getMatchType() {
484 | return matchType;
485 | }
486 |
487 | public void setMatchType(String matchType) {
488 | if(Arrays.stream(MATCH_TYPE_OPTIONS).anyMatch(s -> s.equals(matchType))) {
489 | this.matchType = matchType;
490 | } else {
491 | this.matchType = MATCH_TYPE_OPTIONS[0];
492 | }
493 | }
494 |
495 | public String getMatchRelationship() {
496 | return matchRelationship;
497 | }
498 |
499 | public void setMatchRelationship(String matchRelationship) {
500 | this.matchRelationship = matchRelationship;
501 | }
502 |
503 | public String getMatchCondition() {
504 | return matchCondition;
505 | }
506 |
507 | public void setMatchCondition(String matchCondition) {
508 | this.matchCondition = matchCondition;
509 | }
510 |
511 | public boolean isEnabled() {
512 | return isEnabled;
513 | }
514 |
515 | public void setEnabled(boolean enabled) {
516 | isEnabled = enabled;
517 | }
518 |
519 | }
520 |
--------------------------------------------------------------------------------
/src/burp/Conditions/ConditionTableModel.java:
--------------------------------------------------------------------------------
1 | package burp.Conditions;
2 |
3 | import burp.IHttpRequestResponse;
4 | import javax.swing.table.AbstractTableModel;
5 | import java.util.ArrayList;
6 |
7 | public class ConditionTableModel extends AbstractTableModel {
8 |
9 | private final static String[] columnNames = {
10 | "Enabled",
11 | "Boolean Operator",
12 | "Match Type",
13 | "Match Relationship",
14 | "Match Condition"
15 | };
16 |
17 | private ArrayList conditions;
18 |
19 | // Setting default conditions
20 | public ConditionTableModel() {
21 | conditions = new ArrayList<>();
22 | }
23 |
24 | public void add(Condition condition) { conditions.add(condition); }
25 |
26 | public void update(int index, Condition condition) {
27 | if (index == 0) {
28 | condition.setBooleanOperator("");
29 | }
30 | conditions.set(index, condition);
31 | }
32 |
33 | public boolean check(int toolFlag, IHttpRequestResponse messageInfo) {
34 | boolean meetsConditions = false;
35 | if (getConditions().size() == 0) {
36 | meetsConditions = false;
37 | } else {
38 | if (getConditions()
39 | .stream()
40 | .filter(Condition::isEnabled)
41 | .filter(c -> c.getBooleanOperator().equals("Or"))
42 | .anyMatch(c -> c.checkCondition(toolFlag, messageInfo))) {
43 | meetsConditions = true;
44 | }
45 | if (getConditions()
46 | .stream()
47 | .filter(Condition::isEnabled)
48 | .filter(
49 | c -> c.getBooleanOperator().equals("And") || c.getBooleanOperator().equals(""))
50 | .allMatch(c -> c.checkCondition(toolFlag, messageInfo))) {
51 | meetsConditions = true;
52 | }
53 | }
54 | return meetsConditions;
55 | }
56 |
57 | public ArrayList getConditions() {
58 | return conditions;
59 | }
60 |
61 | public Condition get(int conditionIndex) {
62 | return conditions.get(conditionIndex);
63 | }
64 |
65 | public void remove(int index) {
66 | if (index != 0) { conditions.remove(index); }
67 | }
68 |
69 | public void clear() {
70 | conditions.clear();
71 | }
72 |
73 | @Override
74 | public int getColumnCount() {
75 | return columnNames.length;
76 | }
77 |
78 | @Override
79 | public int getRowCount() {
80 | return conditions.size();
81 | }
82 |
83 | @Override
84 | public String getColumnName(int col) {
85 | return columnNames[col];
86 | }
87 |
88 | @Override
89 | public Object getValueAt(int row, int col) {
90 | Condition tempCondition = conditions.get(row);
91 | switch (col) {
92 | case 0:
93 | return tempCondition.isEnabled();
94 | case 1:
95 | return tempCondition.getBooleanOperator();
96 | case 2:
97 | return tempCondition.getMatchType();
98 | case 3:
99 | return tempCondition.getMatchRelationship();
100 | case 4:
101 | return tempCondition.getMatchCondition();
102 | default:
103 | throw new IllegalStateException("getValueAt not defined for "+Integer.toString(col));
104 | }
105 | }
106 |
107 | @Override
108 | public Class getColumnClass(int column) {
109 | return (getValueAt(0, column).getClass());
110 | }
111 |
112 | @Override
113 | public boolean isCellEditable(int row, int column) {
114 | return (getColumnName(column).equals("Enabled") && row != 0);
115 | }
116 |
117 | @Override
118 | public void setValueAt(Object value, int row, int col) {
119 | Condition tempCondition = conditions.get(row);
120 | switch (col) {
121 | case 0:
122 | tempCondition.setEnabled((Boolean) value);
123 | break;
124 | case 1:
125 | tempCondition.setBooleanOperator((String) value);
126 | break;
127 | case 2:
128 | tempCondition.setMatchType((String) value);
129 | break;
130 | case 3:
131 | tempCondition.setMatchRelationship((String) value);
132 | break;
133 | default:
134 | tempCondition.setMatchCondition((String) value);
135 | break;
136 | }
137 | conditions.set(row, tempCondition);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/burp/Conditions/Conditions.java:
--------------------------------------------------------------------------------
1 | package burp.Conditions;
2 |
3 | import burp.AutoRepeater;
4 | import burp.BurpExtender;
5 | import java.awt.Color;
6 | import java.awt.GridBagConstraints;
7 | import java.awt.GridBagLayout;
8 | import javax.swing.DefaultComboBoxModel;
9 | import javax.swing.JButton;
10 | import javax.swing.JComboBox;
11 | import javax.swing.JLabel;
12 | import javax.swing.JOptionPane;
13 | import javax.swing.JPanel;
14 | import javax.swing.JScrollPane;
15 | import javax.swing.JTable;
16 | import javax.swing.JTextField;
17 |
18 | public class Conditions {
19 | // Conditions UI
20 | JPanel conditionsPanel;
21 | private JPanel conditionPanel;
22 | private JScrollPane conditionScrollPane;
23 | private JTable conditionTable;
24 | private JPanel conditionsButtonPanel;
25 | private JButton addConditionButton;
26 | private JButton editConditionButton;
27 | private JButton deleteConditionButton;
28 | private JButton duplicateConditionButton;
29 |
30 | // Conditions Popup UI
31 | private JComboBox booleanOperatorComboBox;
32 | private JComboBox matchTypeComboBox;
33 | private JComboBox matchRelationshipComboBox;
34 | private JTextField matchConditionTextField;
35 |
36 | private JLabel booleanOperatorLabel;
37 | private JLabel matchTypeLabel;
38 | private JLabel matchRelationshipLabel;
39 | private JLabel matchConditionLabel;
40 |
41 | private ConditionTableModel conditionTableModel;
42 |
43 | public Conditions() {
44 | conditionTableModel = new ConditionTableModel();
45 | createUI();
46 | }
47 |
48 | public ConditionTableModel getConditionTableModel() { return conditionTableModel; }
49 | public JPanel getUI() { return conditionsPanel; }
50 |
51 | private void resetConditionDialog() {
52 | booleanOperatorComboBox.setSelectedIndex(0);
53 | matchTypeComboBox.setSelectedIndex(0);
54 | matchRelationshipComboBox.setSelectedIndex(0);
55 | matchConditionTextField.setText("");
56 | }
57 |
58 | private void createUI() {
59 | GridBagConstraints c;
60 | //Condition Dialog
61 | c = new GridBagConstraints();
62 | conditionsPanel = new JPanel();
63 | conditionPanel = new JPanel();
64 | conditionPanel.setLayout(new GridBagLayout());
65 | conditionPanel.setPreferredSize(AutoRepeater.dialogDimension);
66 |
67 | booleanOperatorComboBox = new JComboBox<>(Condition.BOOLEAN_OPERATOR_OPTIONS);
68 | matchTypeComboBox = new JComboBox<>(Condition.MATCH_TYPE_OPTIONS);
69 | matchRelationshipComboBox = new JComboBox<>(Condition.getMatchRelationshipOptions(
70 | Condition.MATCH_TYPE_OPTIONS[0]));
71 | matchConditionTextField = new JTextField();
72 |
73 | booleanOperatorComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
74 | matchTypeComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
75 | matchRelationshipComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
76 | matchConditionTextField.setPreferredSize(AutoRepeater.textFieldDimension);
77 |
78 | matchTypeComboBox.addActionListener(e -> {
79 | matchRelationshipComboBox
80 | .setModel(new DefaultComboBoxModel<>(Condition.getMatchRelationshipOptions(
81 | (String) matchTypeComboBox.getSelectedItem())));
82 | matchConditionTextField.setEnabled(Condition.matchConditionIsEditable(
83 | (String) matchTypeComboBox.getSelectedItem()));
84 | });
85 |
86 | booleanOperatorLabel = new JLabel("Boolean Operator: ");
87 | matchTypeLabel = new JLabel("Match Type: ");
88 | matchRelationshipLabel = new JLabel("Match Relationship: ");
89 | matchConditionLabel = new JLabel("Match Condition: ");
90 |
91 | c.gridx = 0;
92 | c.gridy = 0;
93 | c.anchor = GridBagConstraints.WEST;
94 | conditionPanel.add(booleanOperatorLabel, c);
95 | c.gridy = 1;
96 | conditionPanel.add(matchTypeLabel, c);
97 | c.gridy = 2;
98 | conditionPanel.add(matchRelationshipLabel, c);
99 | c.gridy = 3;
100 | conditionPanel.add(matchConditionLabel, c);
101 |
102 | c.anchor = GridBagConstraints.EAST;
103 | c.fill = GridBagConstraints.HORIZONTAL;
104 | c.gridx = 1;
105 | c.gridy = 0;
106 | conditionPanel.add(booleanOperatorComboBox, c);
107 | c.gridy = 1;
108 | conditionPanel.add(matchTypeComboBox, c);
109 | c.gridy = 2;
110 | conditionPanel.add(matchRelationshipComboBox, c);
111 | c.gridy = 3;
112 | conditionPanel.add(matchConditionTextField, c);
113 |
114 | // Condition Buttons
115 | addConditionButton = new JButton("Add");
116 | addConditionButton.setPreferredSize(AutoRepeater.buttonDimension);
117 | addConditionButton.setMinimumSize(AutoRepeater.buttonDimension);
118 | addConditionButton.setMaximumSize(AutoRepeater.buttonDimension);
119 |
120 | addConditionButton.addActionListener(e -> {
121 | int result = JOptionPane.showConfirmDialog(
122 | BurpExtender.getParentTabbedPane(),
123 | conditionPanel,
124 | "Add Condition",
125 | JOptionPane.OK_CANCEL_OPTION,
126 | JOptionPane.PLAIN_MESSAGE);
127 | if (result == JOptionPane.OK_OPTION) {
128 | Condition newCondition = new Condition(
129 | (String) booleanOperatorComboBox.getSelectedItem(),
130 | (String) matchTypeComboBox.getSelectedItem(),
131 | (String) matchRelationshipComboBox.getSelectedItem(),
132 | matchConditionTextField.getText()
133 | );
134 | conditionTableModel.add(newCondition);
135 | conditionTableModel.fireTableDataChanged();
136 | }
137 | resetConditionDialog();
138 | });
139 |
140 | editConditionButton = new JButton("Edit");
141 | editConditionButton.setPreferredSize(AutoRepeater.buttonDimension);
142 | editConditionButton.setMinimumSize(AutoRepeater.buttonDimension);
143 | editConditionButton.setMaximumSize(AutoRepeater.buttonDimension);
144 |
145 | editConditionButton.addActionListener(e -> {
146 | if (conditionTable.getSelectedRow() != -1) {
147 | int selectedRow = conditionTable.getSelectedRow();
148 | Condition tempCondition = conditionTableModel.get(selectedRow);
149 |
150 | booleanOperatorComboBox.setSelectedItem(tempCondition.getBooleanOperator());
151 | matchTypeComboBox.setSelectedItem(tempCondition.getMatchType());
152 | matchRelationshipComboBox.setSelectedItem(tempCondition.getMatchRelationship());
153 | matchConditionTextField.setText(tempCondition.getMatchCondition());
154 |
155 | int result = JOptionPane.showConfirmDialog(
156 | BurpExtender.getParentTabbedPane(),
157 | conditionPanel,
158 | "Edit Condition",
159 | JOptionPane.OK_CANCEL_OPTION,
160 | JOptionPane.PLAIN_MESSAGE);
161 | if (result == JOptionPane.OK_OPTION) {
162 | Condition newCondition = new Condition(
163 | (String) booleanOperatorComboBox.getSelectedItem(),
164 | (String) matchTypeComboBox.getSelectedItem(),
165 | (String) matchRelationshipComboBox.getSelectedItem(),
166 | matchConditionTextField.getText()
167 | );
168 | newCondition.setEnabled(tempCondition.isEnabled());
169 | conditionTableModel.update(selectedRow, newCondition);
170 | conditionTableModel.fireTableDataChanged();
171 | }
172 | resetConditionDialog();
173 | }
174 | });
175 |
176 | deleteConditionButton = new JButton("Remove");
177 | deleteConditionButton.setPreferredSize(AutoRepeater.buttonDimension);
178 | deleteConditionButton.setMinimumSize(AutoRepeater.buttonDimension);
179 | deleteConditionButton.setMaximumSize(AutoRepeater.buttonDimension);
180 |
181 | deleteConditionButton.addActionListener(e -> {
182 | int selectedRow = conditionTable.getSelectedRow();
183 | if (selectedRow != -1) {
184 | conditionTableModel.remove(selectedRow);
185 | conditionTableModel.fireTableDataChanged();
186 | }
187 | });
188 |
189 |
190 | // Duplicate Condition
191 | duplicateConditionButton = new JButton("Duplicate");
192 | duplicateConditionButton.setPreferredSize(AutoRepeater.buttonDimension);
193 | duplicateConditionButton.setMinimumSize(AutoRepeater.buttonDimension);
194 | duplicateConditionButton.setMaximumSize(AutoRepeater.buttonDimension);
195 |
196 | duplicateConditionButton.addActionListener(e -> {
197 | int selectedRow = conditionTable.getSelectedRow();
198 | if (conditionTable.getSelectedRow() != -1
199 | && selectedRow < getConditionTableModel().getConditions().size()) {
200 | conditionTableModel.add(new Condition(getConditionTableModel().get(selectedRow)));
201 | conditionTableModel.fireTableDataChanged();
202 | }
203 | });
204 |
205 | conditionsButtonPanel = new JPanel();
206 | conditionsButtonPanel.setLayout(new GridBagLayout());
207 | conditionsButtonPanel.setPreferredSize(AutoRepeater.buttonPanelDimension);
208 | conditionsButtonPanel.setMaximumSize(AutoRepeater.buttonPanelDimension);
209 | conditionsButtonPanel.setPreferredSize(AutoRepeater.buttonPanelDimension);
210 |
211 | c = new GridBagConstraints();
212 | c.anchor = GridBagConstraints.FIRST_LINE_END;
213 | c.gridx = 0;
214 | c.weightx = 1;
215 |
216 | conditionsButtonPanel.add(addConditionButton, c);
217 | conditionsButtonPanel.add(editConditionButton, c);
218 | conditionsButtonPanel.add(deleteConditionButton, c);
219 | conditionsButtonPanel.add(duplicateConditionButton, c);
220 |
221 | conditionTableModel = new ConditionTableModel();
222 | conditionTable = new JTable(conditionTableModel);
223 | conditionTable.getColumnModel().getColumn(0).setMaxWidth(55);
224 | conditionTable.getColumnModel().getColumn(0).setMinWidth(55);
225 | conditionScrollPane = new JScrollPane(conditionTable);
226 |
227 | // Panel containing condition options
228 | conditionsPanel.setLayout(new GridBagLayout());
229 | c = new GridBagConstraints();
230 | c.ipady = 0;
231 | c.anchor = GridBagConstraints.PAGE_START;
232 | c.gridx = 0;
233 | conditionsPanel.add(conditionsButtonPanel, c);
234 | c.fill = GridBagConstraints.BOTH;
235 | c.weightx = 1;
236 | c.weighty = 1;
237 | c.gridx = 1;
238 | conditionsPanel.add(conditionScrollPane, c);
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/burp/Filter/Filter.java:
--------------------------------------------------------------------------------
1 | package burp.Filter;
2 |
3 | import burp.Conditions.Condition;
4 | import burp.Logs.LogEntry;
5 |
6 | public class Filter extends Condition {
7 | private String originalOrModified;
8 | public static final String[] ORIGINAL_OR_MODIFIED= {"Original", "Modified"};
9 |
10 | public Filter(
11 | String booleanOperator,
12 | String originalOrModified,
13 | String matchType,
14 | String matchRelationship,
15 | String matchCondition,
16 | boolean isEnabled) {
17 | super(booleanOperator, matchType, matchRelationship, matchCondition, isEnabled);
18 | setOriginalOrModified(originalOrModified);
19 | }
20 |
21 | public Filter(
22 | String booleanOperator,
23 | String originalOrModified,
24 | String matchType,
25 | String matchRelationship,
26 | String matchCondition) {
27 | this(booleanOperator, originalOrModified, matchType, matchRelationship, matchCondition, true);
28 | }
29 |
30 | public Filter(Filter filter) {
31 | this(filter.getBooleanOperator(),
32 | filter.getOriginalOrModified(),
33 | filter.getMatchType(),
34 | filter.getMatchRelationship(),
35 | filter.getMatchCondition(),
36 | filter.isEnabled());
37 | if(getBooleanOperator().equals("")) {
38 | setBooleanOperator("And");
39 | }
40 | }
41 |
42 | public boolean checkCondition(LogEntry logEntry) {
43 | if (getOriginalOrModified().equals("Original")) {
44 | return checkCondition(logEntry.getToolFlag(), logEntry.getOriginalRequestResponse());
45 | } else {
46 | return checkCondition(logEntry.getToolFlag(), logEntry.getModifiedRequestResponse());
47 | }
48 | }
49 |
50 | public String getOriginalOrModified() {
51 | return originalOrModified;
52 | }
53 |
54 | public void setOriginalOrModified(String originalOrModified) {
55 | if (originalOrModified.equals("Original")) {
56 | this.originalOrModified = "Original";
57 | } else {
58 | this.originalOrModified = "Modified";
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/burp/Filter/FilterTableModel.java:
--------------------------------------------------------------------------------
1 | package burp.Filter;
2 |
3 | import burp.Conditions.ConditionTableModel;
4 | import burp.Logs.LogEntry;
5 | import java.util.ArrayList;
6 | import java.util.stream.Collectors;
7 |
8 | public class FilterTableModel extends ConditionTableModel {
9 |
10 | private final static String[] columnNames = {
11 | "Enabled",
12 | "Boolean Operator",
13 | "Original Or Modified",
14 | "Match Type",
15 | "Match Relationship",
16 | "Match Condition"
17 | };
18 |
19 | public boolean check(LogEntry logEntry) {
20 | boolean meetsFilters = false;
21 | if (getFilters().isEmpty()) {
22 | meetsFilters = true;
23 | } else {
24 | if (getFilters()
25 | .stream()
26 | .filter(Filter::isEnabled)
27 | .filter(f -> f.getBooleanOperator().equals("Or"))
28 | .anyMatch(f -> f.checkCondition(logEntry))) {
29 | meetsFilters = true;
30 | }
31 | if (getFilters()
32 | .stream()
33 | .filter(Filter::isEnabled)
34 | .filter(f -> f.getBooleanOperator().equals("And") || f.getBooleanOperator().equals(""))
35 | .allMatch(f -> f.checkCondition(logEntry))) {
36 | meetsFilters = true;
37 | }
38 | }
39 | return meetsFilters;
40 | }
41 |
42 | public ArrayList getFilters() {
43 | return getConditions().stream()
44 | .map(x -> (Filter)x)
45 | .collect(Collectors.toCollection(ArrayList::new));
46 | }
47 |
48 | public Filter get(int index) { return (Filter)super.get(index); }
49 |
50 | @Override
51 | public String getColumnName(int col) {
52 | return columnNames[col];
53 | }
54 |
55 | @Override
56 | public int getColumnCount() {
57 | return columnNames.length;
58 | }
59 |
60 | @Override
61 | public Object getValueAt(int row, int col) {
62 | Filter filter = get(row);
63 | switch (col) {
64 | case 0:
65 | return filter.isEnabled();
66 | case 1:
67 | return filter.getBooleanOperator();
68 | case 2:
69 | return filter.getOriginalOrModified();
70 | case 3:
71 | return filter.getMatchType();
72 | case 4:
73 | return filter.getMatchRelationship();
74 | case 5:
75 | return filter.getMatchCondition();
76 | default:
77 | throw new IllegalStateException("getValueAt not defined for "+Integer.toString(col));
78 | }
79 | }
80 |
81 | @Override
82 | public void setValueAt(Object value, int row, int col) {
83 | Filter filter = get(row);
84 | switch (col) {
85 | case 0:
86 | filter.setEnabled((Boolean) value);
87 | break;
88 | case 1:
89 | filter.setBooleanOperator((String) value);
90 | break;
91 | case 2:
92 | filter.setOriginalOrModified((String) value);
93 | break;
94 | case 3:
95 | filter.setMatchType((String) value);
96 | break;
97 | case 4:
98 | filter.setMatchRelationship((String) value);
99 | break;
100 | case 5:
101 | filter.setMatchCondition((String) value);
102 | break;
103 | default:
104 | throw new IllegalStateException("setValueAt not defined for "+Integer.toString(col));
105 | }
106 | update(row, filter);
107 | fireTableCellUpdated(row, col);
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/burp/Filter/Filters.java:
--------------------------------------------------------------------------------
1 | package burp.Filter;
2 |
3 | import burp.AutoRepeater;
4 | import burp.BurpExtender;
5 | import burp.Logs.LogEntry;
6 | import burp.Logs.LogManager;
7 | import java.awt.GridBagConstraints;
8 | import java.awt.GridBagLayout;
9 | import javax.swing.ButtonGroup;
10 | import javax.swing.DefaultComboBoxModel;
11 | import javax.swing.JButton;
12 | import javax.swing.JComboBox;
13 | import javax.swing.JLabel;
14 | import javax.swing.JOptionPane;
15 | import javax.swing.JPanel;
16 | import javax.swing.JRadioButton;
17 | import javax.swing.JScrollPane;
18 | import javax.swing.JTable;
19 | import javax.swing.JTextField;
20 |
21 | public class Filters {
22 |
23 | // Filters UI
24 | private JPanel filtersPanel;
25 | private JPanel filterPanel;
26 | private JScrollPane filterScrollPane;
27 | private JRadioButton whitelistFilterRadioButton;
28 | private JRadioButton blacklistFilterRadioButton;
29 | private ButtonGroup buttonGroup;
30 | private JTable filterTable;
31 | private JButton addFilterButton;
32 | private JPanel filtersButtonPanel;
33 | private JButton editFilterButton;
34 | private JButton deleteFilterButton;
35 | private JButton duplicateFilterButton;
36 |
37 | // Filters Popup UI
38 | private JComboBox booleanOperatorComboBox;
39 | private JComboBox originalOrModifiedComboBox;
40 | private JComboBox matchTypeComboBox;
41 | private JComboBox matchRelationshipComboBox;
42 | private JTextField matchFilterTextField;
43 |
44 | private JLabel booleanOperatorLabel;
45 | private JLabel originalOrModifiedLabel;
46 | private JLabel matchTypeLabel;
47 | private JLabel matchRelationshipLabel;
48 | private JLabel matchFilterLabel;
49 |
50 | private FilterTableModel filterTableModel;
51 | private LogManager logManager;
52 | private boolean isWhitelist = true;
53 |
54 | public Filters(LogManager logManager) {
55 | filterTableModel = new FilterTableModel();
56 | this.logManager = logManager;
57 | createUI();
58 | }
59 |
60 | public FilterTableModel getFilterTableModel() { return filterTableModel; }
61 | public JPanel getUI() { return filtersPanel; }
62 |
63 | private void resetFilterDialog() {
64 | booleanOperatorComboBox.setSelectedIndex(0);
65 | originalOrModifiedComboBox.setSelectedIndex(0);
66 | matchTypeComboBox.setSelectedIndex(0);
67 | matchRelationshipComboBox.setSelectedIndex(0);
68 | matchFilterTextField.setText("");
69 | }
70 |
71 | public boolean filter(LogEntry logEntry) {
72 | if (isWhitelist) {
73 | return filterTableModel.check(logEntry);
74 | } else {
75 | return !filterTableModel.check(logEntry);
76 | }
77 | }
78 |
79 | public boolean isWhitelist() { return isWhitelist; }
80 |
81 | private void createUI() {
82 | GridBagConstraints c;
83 | //Filter Dialog
84 | c = new GridBagConstraints();
85 | filtersPanel = new JPanel();
86 | filterPanel = new JPanel();
87 | filterPanel.setLayout(new GridBagLayout());
88 | filterPanel.setPreferredSize(AutoRepeater.dialogDimension);
89 |
90 | whitelistFilterRadioButton = new JRadioButton("Whitelist");
91 | whitelistFilterRadioButton.setSelected(true);
92 | blacklistFilterRadioButton = new JRadioButton("Blacklist");
93 | buttonGroup = new ButtonGroup();
94 | buttonGroup.add(whitelistFilterRadioButton);
95 | buttonGroup.add(blacklistFilterRadioButton);
96 |
97 | booleanOperatorComboBox = new JComboBox<>(Filter.BOOLEAN_OPERATOR_OPTIONS);
98 | originalOrModifiedComboBox = new JComboBox<>(Filter.ORIGINAL_OR_MODIFIED);
99 | matchTypeComboBox = new JComboBox<>(Filter.MATCH_TYPE_OPTIONS);
100 | matchRelationshipComboBox = new JComboBox<>(Filter.getMatchRelationshipOptions(
101 | Filter.MATCH_TYPE_OPTIONS[0]));
102 | matchFilterTextField = new JTextField();
103 |
104 | booleanOperatorComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
105 | originalOrModifiedComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
106 | matchTypeComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
107 | matchRelationshipComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
108 | matchFilterTextField.setPreferredSize(AutoRepeater.textFieldDimension);
109 |
110 | matchTypeComboBox.addActionListener(e -> {
111 | matchRelationshipComboBox
112 | .setModel(new DefaultComboBoxModel<>(Filter.getMatchRelationshipOptions(
113 | (String) matchTypeComboBox.getSelectedItem())));
114 | matchFilterTextField.setEnabled(Filter.matchConditionIsEditable(
115 | (String) matchTypeComboBox.getSelectedItem()));
116 | });
117 |
118 | booleanOperatorLabel = new JLabel("Boolean Operator: ");
119 | originalOrModifiedLabel = new JLabel("Match Original Or Modified: ");
120 | matchTypeLabel = new JLabel("Match Type: ");
121 | matchRelationshipLabel = new JLabel("Match Relationship: ");
122 | matchFilterLabel = new JLabel("Match Condition: ");
123 |
124 | c.gridx = 0;
125 | c.gridy = 0;
126 | c.anchor = GridBagConstraints.WEST;
127 | filterPanel.add(booleanOperatorLabel, c);
128 | c.gridy = 1;
129 | filterPanel.add(originalOrModifiedLabel, c);
130 | c.gridy = 2;
131 | filterPanel.add(matchTypeLabel, c);
132 | c.gridy = 3;
133 | filterPanel.add(matchRelationshipLabel, c);
134 | c.gridy = 4;
135 | filterPanel.add(matchFilterLabel, c);
136 |
137 | c.anchor = GridBagConstraints.EAST;
138 | c.fill = GridBagConstraints.HORIZONTAL;
139 | c.gridx = 1;
140 | c.gridy = 0;
141 | filterPanel.add(booleanOperatorComboBox, c);
142 | c.gridy = 1;
143 | filterPanel.add(originalOrModifiedComboBox, c);
144 | c.gridy = 2;
145 | filterPanel.add(matchTypeComboBox, c);
146 | c.gridy = 3;
147 | filterPanel.add(matchRelationshipComboBox, c);
148 | c.gridy = 4;
149 | filterPanel.add(matchFilterTextField, c);
150 |
151 | // Filter Buttons
152 | addFilterButton = new JButton("Add");
153 | addFilterButton.setPreferredSize(AutoRepeater.buttonDimension);
154 | addFilterButton.setMinimumSize(AutoRepeater.buttonDimension);
155 | addFilterButton.setMaximumSize(AutoRepeater.buttonDimension);
156 |
157 | addFilterButton.addActionListener(e -> {
158 | int result = JOptionPane.showConfirmDialog(
159 | BurpExtender.getParentTabbedPane(),
160 | filterPanel,
161 | "Add Filter",
162 | JOptionPane.OK_CANCEL_OPTION,
163 | JOptionPane.PLAIN_MESSAGE);
164 | if (result == JOptionPane.OK_OPTION) {
165 | Filter newFilter = new Filter(
166 | (String) booleanOperatorComboBox.getSelectedItem(),
167 | (String) originalOrModifiedComboBox.getSelectedItem(),
168 | (String) matchTypeComboBox.getSelectedItem(),
169 | (String) matchRelationshipComboBox.getSelectedItem(),
170 | matchFilterTextField.getText()
171 | );
172 | filterTableModel.add(newFilter);
173 | filterTableModel.fireTableDataChanged();
174 | }
175 | resetFilterDialog();
176 | });
177 |
178 | editFilterButton = new JButton("Edit");
179 | editFilterButton.setPreferredSize(AutoRepeater.buttonDimension);
180 | editFilterButton.setMinimumSize(AutoRepeater.buttonDimension);
181 | editFilterButton.setMaximumSize(AutoRepeater.buttonDimension);
182 |
183 | editFilterButton.addActionListener(e -> {
184 | int selectedRow = filterTable.getSelectedRow();
185 | if (selectedRow != -1) {
186 | Filter tempFilter = filterTableModel.get(selectedRow);
187 |
188 | booleanOperatorComboBox.setSelectedItem(tempFilter.getBooleanOperator());
189 | originalOrModifiedComboBox.setSelectedItem(tempFilter.getOriginalOrModified());
190 | matchTypeComboBox.setSelectedItem(tempFilter.getMatchType());
191 | matchRelationshipComboBox.setSelectedItem(tempFilter.getMatchRelationship());
192 | matchFilterTextField.setText(tempFilter.getMatchCondition());
193 |
194 | int result = JOptionPane.showConfirmDialog(
195 | BurpExtender.getParentTabbedPane(),
196 | filterPanel,
197 | "Edit Filter",
198 | JOptionPane.OK_CANCEL_OPTION,
199 | JOptionPane.PLAIN_MESSAGE);
200 | if (result == JOptionPane.OK_OPTION) {
201 | Filter newFilter = new Filter(
202 | (String) booleanOperatorComboBox.getSelectedItem(),
203 | (String) originalOrModifiedComboBox.getSelectedItem(),
204 | (String) matchTypeComboBox.getSelectedItem(),
205 | (String) matchRelationshipComboBox.getSelectedItem(),
206 | matchFilterTextField.getText()
207 | );
208 | newFilter.setEnabled(tempFilter.isEnabled());
209 |
210 | filterTableModel.update(selectedRow, newFilter);
211 | filterTableModel.fireTableDataChanged();
212 | }
213 | resetFilterDialog();
214 | }
215 | });
216 |
217 | deleteFilterButton = new JButton("Remove");
218 | deleteFilterButton.setPreferredSize(AutoRepeater.buttonDimension);
219 | deleteFilterButton.setMinimumSize(AutoRepeater.buttonDimension);
220 | deleteFilterButton.setMaximumSize(AutoRepeater.buttonDimension);
221 |
222 | deleteFilterButton.addActionListener(e -> {
223 | int selectedRow = filterTable.getSelectedRow();
224 | if (selectedRow != -1) {
225 | filterTableModel.remove(selectedRow);
226 | filterTableModel.fireTableDataChanged();
227 | }
228 | });
229 |
230 | filtersButtonPanel = new JPanel();
231 | filtersButtonPanel.setLayout(new GridBagLayout());
232 |
233 | duplicateFilterButton = new JButton("Duplicate");
234 | duplicateFilterButton.setPreferredSize(AutoRepeater.buttonDimension);
235 | duplicateFilterButton.setMinimumSize(AutoRepeater.buttonDimension);
236 | duplicateFilterButton.setMaximumSize(AutoRepeater.buttonDimension);
237 |
238 | duplicateFilterButton.addActionListener(e -> {
239 | int selectedRow = filterTable.getSelectedRow();
240 | if (selectedRow != -1 && selectedRow < filterTableModel.getConditions().size()) {
241 | filterTableModel.add(new Filter(filterTableModel.getFilters().get(selectedRow)));
242 | filterTableModel.fireTableDataChanged();
243 | }
244 | });
245 |
246 | c = new GridBagConstraints();
247 | c.anchor = GridBagConstraints.FIRST_LINE_START;
248 | c.gridx = 0;
249 | c.weightx = 1;
250 |
251 | filtersButtonPanel.add(addFilterButton, c);
252 | filtersButtonPanel.add(editFilterButton, c);
253 | filtersButtonPanel.add(deleteFilterButton, c);
254 | filtersButtonPanel.add(duplicateFilterButton, c);
255 | filtersButtonPanel.add(whitelistFilterRadioButton, c);
256 | filtersButtonPanel.add(blacklistFilterRadioButton, c);
257 |
258 | filterTableModel = new FilterTableModel();
259 | filterTable = new JTable(filterTableModel);
260 | filterTable.getColumnModel().getColumn(0).setMinWidth(55);
261 | filterTable.getColumnModel().getColumn(0).setMaxWidth(55);
262 |
263 | filterTable.setPreferredSize(AutoRepeater.tableDimension);
264 | filterTable.setMaximumSize(AutoRepeater.tableDimension);
265 | filterTable.setMinimumSize(AutoRepeater.tableDimension);
266 |
267 | filterScrollPane = new JScrollPane(filterTable);
268 |
269 | // Panel containing filter options
270 | filtersPanel.setLayout(new GridBagLayout());
271 | c = new GridBagConstraints();
272 | c.ipady = 0;
273 | c.anchor = GridBagConstraints.PAGE_START;
274 | c.gridx = 0;
275 | filtersPanel.add(filtersButtonPanel, c);
276 | c.fill = GridBagConstraints.BOTH;
277 | c.weightx = 1;
278 | c.weighty = 1;
279 | c.gridx = 1;
280 | filtersPanel.add(filterScrollPane, c);
281 | // Refilter the logs whenever anything is touched. For whatever reason click the enabled
282 |
283 | whitelistFilterRadioButton.addActionListener(e -> {
284 | setWhitelist(whitelistFilterRadioButton.isSelected());
285 | logManager.setFilter(this);
286 | });
287 | blacklistFilterRadioButton.addActionListener(e -> {
288 | setWhitelist(!blacklistFilterRadioButton.isSelected());
289 | logManager.setFilter(this);
290 | });
291 | filterTableModel.addTableModelListener(e -> logManager.setFilter(this));
292 | }
293 |
294 | public void setWhitelist(boolean whitelist) {
295 | isWhitelist = whitelist;
296 | if(isWhitelist) {
297 | whitelistFilterRadioButton.setSelected(true);
298 | } else {
299 | blacklistFilterRadioButton.setSelected(true);
300 | }
301 | }
302 | }
303 |
--------------------------------------------------------------------------------
/src/burp/Highlighter/Highlighter.java:
--------------------------------------------------------------------------------
1 | package burp.Highlighter;
2 |
3 | import burp.Filter.Filter;
4 | import java.awt.Color;
5 |
6 | public class Highlighter extends Filter {
7 | public final static Color[] COLORS = {
8 | new Color(0xFFFFFF),
9 | new Color(0xFB6063),
10 | new Color(0xFFC562),
11 | new Color(0xFDFF5F),
12 | new Color(0x60FE62),
13 | new Color(0x64FFFF),
14 | new Color(0x6262FF),
15 | new Color(0xFFC6CC),
16 | new Color(0xFE63FD) ,
17 | new Color(0xB3B5B2),
18 | };
19 |
20 | public final static Color[] SELECTED_COLORS = {
21 | new Color(0xFFC498),
22 | new Color(0xDF4444),
23 | new Color(0xDFa844),
24 | new Color(0xDFDF44),
25 | new Color(0x44DF44),
26 | new Color(0x44DFDF),
27 | new Color(0x4444DF),
28 | new Color(0xDFA8A8),
29 | new Color(0xDF44DF),
30 | new Color(0x949494),
31 | };
32 |
33 | public final static String[] COLOR_NAMES = {
34 | "WHITE",
35 | "RED",
36 | "ORANGE",
37 | "YELLOW",
38 | "GREEN",
39 | "CYAN",
40 | "PURPLE",
41 | "PINK",
42 | "MAGENTA",
43 | "GRAY"
44 | };
45 |
46 |
47 | public static Color getColorFromColorName(String colorName) {
48 | for(int i = 0; i < Highlighter.COLOR_NAMES.length; i++) {
49 | if (Highlighter.COLOR_NAMES[i].equals(colorName)) {
50 | return Highlighter.COLORS[i];
51 | }
52 | }
53 | return Highlighter.COLORS[0];
54 | }
55 |
56 | public static Color getSelectedColorFromColorName(String colorName) {
57 | for(int i = 0; i < Highlighter.COLOR_NAMES.length; i++) {
58 | if (Highlighter.COLOR_NAMES[i].equals(colorName)) {
59 | return Highlighter.SELECTED_COLORS[i];
60 | }
61 | }
62 | return Highlighter.SELECTED_COLORS[0];
63 | }
64 |
65 | public Highlighter(
66 | String booleanOperator,
67 | String originalOrModified,
68 | String matchType,
69 | String matchRelationship,
70 | String matchCondition,
71 | boolean isEnabled) {
72 | super(booleanOperator, originalOrModified, matchType, matchRelationship, matchCondition, isEnabled);
73 | }
74 |
75 | public Highlighter(
76 | String booleanOperator,
77 | String originalOrModified,
78 | String matchType,
79 | String matchRelationship,
80 | String matchCondition) {
81 | this(booleanOperator, originalOrModified, matchType, matchRelationship, matchCondition, true);
82 | }
83 |
84 | public Highlighter(Highlighter highlighter) {
85 | this(highlighter.getBooleanOperator(),
86 | highlighter.getOriginalOrModified(),
87 | highlighter.getMatchType(),
88 | highlighter.getMatchRelationship(),
89 | highlighter.getMatchCondition(),
90 | highlighter.isEnabled());
91 | if (this.getBooleanOperator().equals("")) {
92 | setBooleanOperator("And");
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/burp/Highlighter/HighlighterTableModel.java:
--------------------------------------------------------------------------------
1 | package burp.Highlighter;
2 |
3 | import burp.Filter.FilterTableModel;
4 | import java.awt.Color;
5 | import java.util.ArrayList;
6 | import java.util.stream.Collectors;
7 |
8 | public class HighlighterTableModel extends FilterTableModel{
9 | private String colorName;
10 | private boolean isEnabled;
11 | private Color backgroundColor;
12 | private Color selectedBackgroundColor;
13 | private String comment;
14 |
15 | public HighlighterTableModel() {
16 | super();
17 | backgroundColor = Highlighter.COLORS[0];
18 | selectedBackgroundColor = Highlighter.SELECTED_COLORS[0];
19 | setColorName(Highlighter.COLOR_NAMES[0]);
20 | }
21 |
22 | public void setComment(String comment) {
23 | this.comment = comment;
24 | fireTableDataChanged();
25 | }
26 |
27 | public String getComment() {return this.comment;}
28 |
29 | public Color getColor() {
30 | return backgroundColor;
31 | }
32 |
33 | public Color getSelectedColor() {
34 | return selectedBackgroundColor;
35 | }
36 |
37 | public void add(Highlighter highlighter) {
38 | super.add(highlighter);
39 | // Clear out the boolean if it's the first entry
40 | if (getConditions().get(0).equals(highlighter)) {
41 | getConditions().get(0).setBooleanOperator("");
42 | }
43 | fireTableDataChanged();
44 | }
45 |
46 | @Override
47 | public void remove(int index) {
48 | getConditions().remove(index);
49 | // Clear out the boolean if it's the first entry
50 | getConditions().get(0).setBooleanOperator("");
51 | fireTableDataChanged();
52 | }
53 |
54 | public ArrayList getHighlighters() {
55 | return getConditions().stream()
56 | .map(x -> (Highlighter)x)
57 | .collect(Collectors.toCollection(ArrayList::new));
58 | }
59 |
60 | public HighlighterTableModel (HighlighterTableModel highlighterTableModel) {
61 | super();
62 | for (Highlighter highlighter : highlighterTableModel.getHighlighters()) {
63 | add(highlighter);
64 | }
65 | this.colorName = highlighterTableModel.getColorName();
66 | this.isEnabled = highlighterTableModel.isEnabled();
67 | this.backgroundColor = highlighterTableModel.getColor();
68 | this.selectedBackgroundColor = highlighterTableModel.getSelectedColor();
69 | this.comment = highlighterTableModel.getComment();
70 | }
71 |
72 | public String getColorName() { return colorName; }
73 |
74 | public void setColorName(String colorName) {
75 | for(int i = 0; i < Highlighter.COLOR_NAMES.length; i++) {
76 | if (Highlighter.COLOR_NAMES[i].equals(colorName)) {
77 | this.colorName = colorName;
78 | this.backgroundColor = Highlighter.COLORS[i];
79 | this.selectedBackgroundColor = Highlighter.SELECTED_COLORS[i];
80 | fireTableDataChanged();
81 | return;
82 | }
83 | }
84 | // This should never actually happen
85 | this.colorName = Highlighter.COLOR_NAMES[0];
86 | this.backgroundColor = Highlighter.COLORS[0];
87 | this.selectedBackgroundColor = Highlighter.SELECTED_COLORS[0];
88 | fireTableDataChanged();
89 | }
90 |
91 | public Highlighter get(int index) { return (Highlighter)super.get(index); }
92 |
93 | public boolean isEnabled() {
94 | return isEnabled;
95 | }
96 |
97 | public void setEnabled(boolean enabled) {
98 | isEnabled = enabled;
99 | fireTableDataChanged();
100 | }
101 |
102 | @Override
103 | public boolean isCellEditable(int row, int column) {
104 | return (getColumnName(column).equals("Enabled"));
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/burp/Highlighter/HighlighterUITableModel.java:
--------------------------------------------------------------------------------
1 | package burp.Highlighter;
2 |
3 | import java.util.ArrayList;
4 | import java.util.stream.Collectors;
5 | import javax.swing.table.AbstractTableModel;
6 |
7 | public class HighlighterUITableModel extends AbstractTableModel {
8 | private ArrayList tableModels;
9 |
10 | private static final String[] columnNames = {"Enabled", "Color", "Comment"};
11 |
12 | public HighlighterUITableModel() {
13 | tableModels = new ArrayList<>();
14 | }
15 |
16 | public HighlighterUITableModel(HighlighterUITableModel highlighterUITableModel) {
17 | this();
18 | tableModels.addAll(highlighterUITableModel.getTableModels());
19 | }
20 |
21 | public ArrayList getTableModels() { return tableModels; }
22 |
23 | public void add(HighlighterTableModel tableModel) {
24 | tableModels.add(tableModel);
25 | fireTableDataChanged();
26 | }
27 |
28 | public void update(int index, HighlighterTableModel tableModel) {
29 | tableModels.set(index, tableModel);
30 | fireTableDataChanged();
31 | }
32 |
33 | public HighlighterTableModel get(int index) {
34 | return tableModels.get(index);
35 | }
36 |
37 | public void remove(int index) {
38 | tableModels.remove(index);
39 | }
40 |
41 | @Override
42 | public int getRowCount() {
43 | return tableModels.size();
44 | }
45 |
46 | @Override
47 | public int getColumnCount() {
48 | return columnNames.length;
49 | }
50 |
51 | @Override
52 | public void setValueAt(Object value, int row, int col) {
53 | HighlighterTableModel tableModel = tableModels.get(row);
54 | switch (col) {
55 | case 0:
56 | tableModel.setEnabled((Boolean) value);
57 | break;
58 | default:
59 | break;
60 | }
61 | tableModels.set(row, tableModel);
62 | fireTableCellUpdated(row, col);
63 | }
64 |
65 | @Override
66 | public Class getColumnClass(int column) {
67 | switch (column) {
68 | case 0:
69 | return Boolean.class;
70 | case 1:
71 | return String.class;
72 | case 2:
73 | return String.class;
74 | default:
75 | throw new IllegalStateException("getColumnClass not defined for "+Integer.toString(column));
76 | }
77 | }
78 |
79 | @Override
80 | public Object getValueAt(int row, int col) {
81 | HighlighterTableModel tableModel = tableModels.get(row);
82 | switch (col) {
83 | case 0:
84 | return tableModel.isEnabled();
85 | case 1:
86 | return tableModel.getColorName();
87 | case 2:
88 | return tableModel.getComment();
89 | default:
90 | throw new IllegalStateException("getValueAt not defined for "+Integer.toString(col));
91 | }
92 | }
93 |
94 | @Override
95 | public boolean isCellEditable(int row, int column) {
96 | return (getColumnName(column).equals("Enabled"));
97 | }
98 |
99 | @Override
100 | public String getColumnName(int col) {
101 | return columnNames[col];
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/burp/Highlighter/Highlighters.java:
--------------------------------------------------------------------------------
1 | package burp.Highlighter;
2 |
3 | import burp.AutoRepeater;
4 | import burp.AutoRepeater.LogTable;
5 | import burp.BurpExtender;
6 | import burp.Logs.LogEntry;
7 | import burp.Logs.LogManager;
8 | import java.awt.Component;
9 | import java.awt.GridBagConstraints;
10 | import java.awt.GridBagLayout;
11 | import javax.swing.BoxLayout;
12 | import javax.swing.DefaultComboBoxModel;
13 | import javax.swing.DefaultListCellRenderer;
14 | import javax.swing.JButton;
15 | import javax.swing.JComboBox;
16 | import javax.swing.JLabel;
17 | import javax.swing.JOptionPane;
18 | import javax.swing.JPanel;
19 | import javax.swing.JScrollPane;
20 | import javax.swing.JTable;
21 | import javax.swing.JTextField;
22 | import javax.swing.event.DocumentEvent;
23 | import javax.swing.event.DocumentListener;
24 | import javax.swing.table.DefaultTableCellRenderer;
25 |
26 | public class Highlighters {
27 | // Highlighters UI
28 | private JPanel highlightsPanel;
29 |
30 | // Highlighters Popup UI
31 | private JComboBox booleanOperatorComboBox;
32 | private JComboBox originalOrModifiedComboBox;
33 | private JComboBox matchTypeComboBox;
34 | private JComboBox matchRelationshipComboBox;
35 | private JTextField matchHighlighterTextField;
36 |
37 | // Highlighters Menu UI
38 | private JLabel booleanOperatorLabel;
39 | private JLabel originalOrModifiedLabel;
40 | private JLabel matchTypeLabel;
41 | private JLabel matchRelationshipLabel;
42 | private JLabel matchHighlighterLabel;
43 | private JTable highlighterTable;
44 |
45 | private HighlighterUITableModel highlighterUITableModel;
46 | private LogManager logManager;
47 | private LogTable logTable;
48 |
49 | public Highlighters(LogManager logManager, LogTable logTable) {
50 | highlighterUITableModel = new HighlighterUITableModel();
51 | highlighterUITableModel.addTableModelListener(l -> highlight());
52 | this.logManager = logManager;
53 | this.logTable = logTable;
54 | highlightsPanel = createMenuUI();
55 | }
56 |
57 | public HighlighterUITableModel getHighlighterUITableModel() { return highlighterUITableModel; }
58 | public JPanel getUI() { return highlightsPanel; }
59 |
60 | public void highlight() {
61 | for (LogEntry logEntry : logManager.getLogTableModel().getLog()) {
62 | highlight(logEntry);
63 | }
64 | logTable.repaint();
65 | }
66 |
67 | public void highlight(LogEntry logEntry) {
68 | logEntry.setBackgroundColor(Highlighter.COLORS[0], Highlighter.SELECTED_COLORS[0]);
69 | for (HighlighterTableModel highlighterTableModel : highlighterUITableModel.getTableModels()) {
70 | //if (highlighterTableModel.isEnabled()) {
71 | //for (Highlighter highlighter : highlighterTableModel.getHighlighters()) {
72 | if (highlighterTableModel.check(logEntry)) {
73 | logEntry.setBackgroundColor(
74 | highlighterTableModel.getColor(), highlighterTableModel.getSelectedColor());
75 | }
76 | //}
77 | //}
78 | }
79 | logTable.repaint();
80 | }
81 |
82 | private JPanel createMenuUI() {
83 | GridBagConstraints c;
84 | JPanel menuPanel = new JPanel();
85 | JButton addHighlighterButton = new JButton("Add");
86 | JButton editHighlighterButton = new JButton("Edit");
87 | JButton deleteHighlighterButton = new JButton("Remove");
88 | JButton duplicateHighlighterButton = new JButton("Duplicate");
89 | JPanel buttonsPanel = new JPanel();
90 | JTable menuTable = new JTable(highlighterUITableModel);
91 |
92 | menuTable.getColumnModel().getColumn(0).setMaxWidth(55);
93 | menuTable.getColumnModel().getColumn(0).setMinWidth(55);
94 | menuTable.getColumnModel().getColumn(1).setMaxWidth(70);
95 | menuTable.getColumnModel().getColumn(1).setMinWidth(70);
96 | //menuTable.getColumnModel().getColumn(2).setPreferredWidth(20);
97 |
98 | menuTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
99 | @Override
100 | public Component getTableCellRendererComponent(
101 | JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
102 | Component c =
103 | super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
104 | // Only color the color column
105 | if (column == 1) {
106 | c.setBackground(highlighterUITableModel.getTableModels().get(row).getColor());
107 | if (isSelected) {
108 | c.setBackground(highlighterUITableModel.getTableModels().get(row).getSelectedColor());
109 | }
110 | } else {
111 | c.setBackground(Highlighter.COLORS[0]);
112 | if (isSelected) {
113 | c.setBackground(Highlighter.SELECTED_COLORS[0]);
114 | }
115 | }
116 | return c;
117 | }
118 | });
119 |
120 | addHighlighterButton.setPreferredSize(AutoRepeater.buttonDimension);
121 | editHighlighterButton.setPreferredSize(AutoRepeater.buttonDimension);
122 | deleteHighlighterButton.setPreferredSize(AutoRepeater.buttonDimension);
123 | duplicateHighlighterButton.setPreferredSize(AutoRepeater.buttonDimension);
124 |
125 | addHighlighterButton.setMinimumSize(AutoRepeater.buttonDimension);
126 | editHighlighterButton.setMinimumSize(AutoRepeater.buttonDimension);
127 | deleteHighlighterButton.setMinimumSize(AutoRepeater.buttonDimension);
128 | duplicateHighlighterButton.setMinimumSize(AutoRepeater.buttonDimension);
129 |
130 | addHighlighterButton.setMaximumSize(AutoRepeater.buttonDimension);
131 | editHighlighterButton.setMaximumSize(AutoRepeater.buttonDimension);
132 | deleteHighlighterButton.setMaximumSize(AutoRepeater.buttonDimension);
133 | duplicateHighlighterButton.setMaximumSize(AutoRepeater.buttonDimension);
134 |
135 | buttonsPanel.setPreferredSize(AutoRepeater.buttonPanelDimension);
136 |
137 | buttonsPanel.setLayout(new GridBagLayout());
138 | c = new GridBagConstraints();
139 | c.anchor = GridBagConstraints.FIRST_LINE_START;
140 | c.gridx = 0;
141 | c.weightx = 0;
142 | buttonsPanel.add(addHighlighterButton, c);
143 | buttonsPanel.add(editHighlighterButton, c);
144 | buttonsPanel.add(deleteHighlighterButton, c);
145 | buttonsPanel.add(duplicateHighlighterButton, c);
146 |
147 | JScrollPane menuScrollPane = new JScrollPane(menuTable);
148 |
149 | // Panel containing filter options
150 | menuPanel.setLayout(new GridBagLayout());
151 | c = new GridBagConstraints();
152 | c.ipady = 0;
153 | c.anchor = GridBagConstraints.PAGE_START;
154 | c.gridx = 0;
155 | c.gridy = 1;
156 | menuPanel.add(buttonsPanel, c);
157 | c.fill = GridBagConstraints.BOTH;
158 | c.weightx = 1;
159 | c.weighty = 1;
160 | c.gridx = 1;
161 | menuPanel.add(menuScrollPane, c);
162 |
163 | addHighlighterButton.addActionListener(l -> {
164 | HighlighterTableModel tableModel = new HighlighterTableModel();
165 | tableModel.addTableModelListener(e -> highlight());
166 | int result = JOptionPane.showConfirmDialog(
167 | BurpExtender.getParentTabbedPane(),
168 | createHighlighterUI(tableModel),
169 | "Add Highlighter",
170 | JOptionPane.OK_CANCEL_OPTION,
171 | JOptionPane.PLAIN_MESSAGE);
172 | if (result == JOptionPane.OK_OPTION) {
173 | HighlighterTableModel tempTableModel = new HighlighterTableModel(tableModel);
174 | if(tempTableModel.getConditions().size() > 0) {
175 | tempTableModel.setEnabled(true);
176 | highlighterUITableModel.add(tempTableModel);
177 | highlight();
178 | highlighterUITableModel.fireTableDataChanged();
179 | }
180 | }
181 | });
182 | editHighlighterButton.addActionListener(l -> {
183 | if (menuTable.getSelectedRow() != -1) {
184 | HighlighterTableModel tableModel = highlighterUITableModel.get(menuTable.getSelectedRow());
185 | tableModel.addTableModelListener(e -> highlight());
186 | int result = JOptionPane.showConfirmDialog(
187 | BurpExtender.getParentTabbedPane(),
188 | createHighlighterUI(tableModel),
189 | "Add Highlighter",
190 | JOptionPane.OK_CANCEL_OPTION,
191 | JOptionPane.PLAIN_MESSAGE);
192 | if (result == JOptionPane.OK_OPTION) {
193 | HighlighterTableModel tempTableModel = new HighlighterTableModel(tableModel);
194 | if (tempTableModel.getConditions().size() > 0) {
195 | highlighterUITableModel.update(menuTable.getSelectedRow(), tempTableModel);
196 | highlight();
197 | highlighterUITableModel.fireTableDataChanged();
198 | }
199 | }
200 | }
201 | });
202 | deleteHighlighterButton.addActionListener(l -> {
203 | if (menuTable.getSelectedRow() != -1) {
204 | highlighterUITableModel.remove(menuTable.getSelectedRow());
205 | highlight();
206 | highlighterUITableModel.fireTableDataChanged();
207 | }
208 | });
209 | duplicateHighlighterButton.addActionListener(l -> {
210 | int selectedRow = menuTable.getSelectedRow();
211 | if (selectedRow != -1 && selectedRow < highlighterUITableModel.getRowCount()) {
212 | highlighterUITableModel.add(new HighlighterTableModel(highlighterUITableModel.get(selectedRow)));
213 | highlighterUITableModel.fireTableDataChanged();
214 | }
215 | });
216 | return menuPanel;
217 | }
218 |
219 | // This is the UI for the highlighter pop up with the table
220 | private JPanel createHighlighterUI(HighlighterTableModel highlighterTableModel) {
221 | GridBagConstraints c;
222 | JPanel menuPanel = new JPanel();
223 | JButton addHighlighterButton = new JButton("Add");
224 | JButton editHighlighterButton = new JButton("Edit");
225 | JButton deleteHighlighterButton = new JButton("Remove");
226 | JButton duplicateHighlighterButton = new JButton("Duplicate");
227 | JPanel buttonsPanel = new JPanel();
228 | JTextField commentTextField = new JTextField();
229 | commentTextField.setText(highlighterTableModel.getComment());
230 | JLabel commentTextFieldLabel = new JLabel("Comment: ");
231 |
232 | commentTextField.getDocument().addDocumentListener(new DocumentListener() {
233 | @Override
234 | public void insertUpdate(DocumentEvent e) {
235 | highlighterTableModel.setComment(commentTextField.getText());
236 | }
237 | @Override
238 | public void removeUpdate(DocumentEvent e) {
239 | highlighterTableModel.setComment(commentTextField.getText());
240 | }
241 | @Override
242 | public void changedUpdate(DocumentEvent e) {
243 | highlighterTableModel.setComment(commentTextField.getText());
244 | }
245 | }
246 | );
247 |
248 | commentTextFieldLabel.setMaximumSize(AutoRepeater.buttonDimension);
249 | commentTextFieldLabel.setMinimumSize(AutoRepeater.buttonDimension);
250 | commentTextFieldLabel.setPreferredSize(AutoRepeater.buttonDimension);
251 |
252 | highlighterTable = new JTable(highlighterTableModel);
253 | highlighterTable.getColumnModel().getColumn(0).setMaxWidth(55);
254 | highlighterTable.getColumnModel().getColumn(0).setMinWidth(55);
255 |
256 | JLabel colorComboBoxLabel = new JLabel("Highlight Color: ");
257 | JComboBox colorComboBox = new JComboBox<>(Highlighter.COLOR_NAMES);
258 | colorComboBox.setSelectedItem(highlighterTableModel.getColorName());
259 | colorComboBox.addActionListener(e ->
260 | highlighterTableModel.setColorName((String)colorComboBox.getSelectedItem()));
261 | colorComboBox.setRenderer((list, value, index, isSelected, cellHasFocus) -> {
262 | DefaultListCellRenderer defaultListCellRenderer = new DefaultListCellRenderer();
263 | JLabel label =
264 | (JLabel) defaultListCellRenderer
265 | .getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
266 | label.setOpaque(true);
267 | for (int i = 0; i < Highlighter.COLOR_NAMES.length; i++) {
268 | if (label.getText().equals(Highlighter.COLOR_NAMES[i])) {
269 | label.setBackground(Highlighter.COLORS[i]);
270 | }
271 | }
272 | return label;
273 | });
274 | colorComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
275 | colorComboBox.setMinimumSize(AutoRepeater.comboBoxDimension);
276 | colorComboBox.setMaximumSize(AutoRepeater.comboBoxDimension);
277 |
278 | JPanel colorComboBoxPanel = new JPanel();
279 | colorComboBoxPanel.setLayout(new GridBagLayout());
280 | c = new GridBagConstraints();
281 | c.anchor = GridBagConstraints.FIRST_LINE_START;
282 | c.gridx = 0;
283 | c.weightx = 0;
284 | colorComboBoxPanel.add(colorComboBoxLabel, c);
285 | c.gridx = 1;
286 | colorComboBoxPanel.add(colorComboBox, c);
287 |
288 | addHighlighterButton.setPreferredSize(AutoRepeater.buttonDimension);
289 | addHighlighterButton.setMinimumSize(AutoRepeater.buttonDimension);
290 | addHighlighterButton.setMaximumSize(AutoRepeater.buttonDimension);
291 |
292 | editHighlighterButton.setPreferredSize(AutoRepeater.buttonDimension);
293 | editHighlighterButton.setMinimumSize(AutoRepeater.buttonDimension);
294 | editHighlighterButton.setMaximumSize(AutoRepeater.buttonDimension);
295 |
296 | deleteHighlighterButton.setPreferredSize(AutoRepeater.buttonDimension);
297 | deleteHighlighterButton.setMinimumSize(AutoRepeater.buttonDimension);
298 | deleteHighlighterButton.setMaximumSize(AutoRepeater.buttonDimension);
299 |
300 | duplicateHighlighterButton.setPreferredSize(AutoRepeater.buttonDimension);
301 | duplicateHighlighterButton.setMinimumSize(AutoRepeater.buttonDimension);
302 | duplicateHighlighterButton.setMaximumSize(AutoRepeater.buttonDimension);
303 |
304 | buttonsPanel.setPreferredSize(AutoRepeater.buttonPanelDimension);
305 |
306 | buttonsPanel.setLayout(new GridBagLayout());
307 | c = new GridBagConstraints();
308 | c.anchor = GridBagConstraints.FIRST_LINE_START;
309 | c.gridx = 0;
310 | c.weightx = 0;
311 | buttonsPanel.add(addHighlighterButton, c);
312 | buttonsPanel.add(editHighlighterButton, c);
313 | buttonsPanel.add(deleteHighlighterButton, c);
314 | buttonsPanel.add(duplicateHighlighterButton, c);
315 |
316 | JScrollPane menuScrollPane = new JScrollPane(highlighterTable);
317 |
318 | // Panel containing filter options
319 | menuPanel.setLayout(new GridBagLayout());
320 | c = new GridBagConstraints();
321 | c.ipady = 0;
322 | c.anchor = GridBagConstraints.PAGE_START;
323 | c.gridx = 0;
324 | c.gridy = 1;
325 | menuPanel.add(buttonsPanel, c);
326 | c.fill = GridBagConstraints.BOTH;
327 | c.weightx = 1;
328 | c.weighty = 1;
329 | c.gridx = 1;
330 | menuPanel.add(menuScrollPane, c);
331 |
332 | JPanel outputPanel = new JPanel();
333 | outputPanel.setLayout(new BoxLayout(outputPanel, BoxLayout.PAGE_AXIS));
334 | outputPanel.add(colorComboBoxPanel);
335 | outputPanel.add(menuPanel);
336 | outputPanel.setPreferredSize(AutoRepeater.dialogDimension);
337 | outputPanel.setMaximumSize(AutoRepeater.dialogDimension);
338 | outputPanel.setMinimumSize(AutoRepeater.dialogDimension);
339 | JPanel commentPanel = new JPanel();
340 | commentPanel.setLayout(new BoxLayout(commentPanel, BoxLayout.LINE_AXIS));
341 | commentPanel.add(commentTextFieldLabel);
342 | commentPanel.add(commentTextField);
343 | outputPanel.add(commentPanel);
344 |
345 | // Button Actions
346 | addHighlighterButton.addActionListener(l -> {
347 | int result = JOptionPane.showConfirmDialog(
348 | BurpExtender.getParentTabbedPane(),
349 | createHighlightEditorUI(highlighterTableModel),
350 | "Edit Highlighter",
351 | JOptionPane.OK_CANCEL_OPTION,
352 | JOptionPane.PLAIN_MESSAGE);
353 | if (result == JOptionPane.OK_OPTION) {
354 | highlighterTableModel.add(
355 | new Highlighter(
356 | (String) booleanOperatorComboBox.getSelectedItem(),
357 | (String) originalOrModifiedComboBox.getSelectedItem(),
358 | (String) matchTypeComboBox.getSelectedItem(),
359 | (String) matchRelationshipComboBox.getSelectedItem(),
360 | matchHighlighterTextField.getText(),
361 | true
362 | )
363 | );
364 | highlighterTableModel.fireTableDataChanged();
365 | }
366 | resetHighlighterDialog();
367 | });
368 | editHighlighterButton.addActionListener(l -> {
369 | if (highlighterTable.getSelectedRow() != -1) {
370 | int result = JOptionPane.showConfirmDialog(
371 | BurpExtender.getParentTabbedPane(),
372 | createHighlightEditorUI(highlighterTableModel),
373 | "Edit Highlighter",
374 | JOptionPane.OK_CANCEL_OPTION,
375 | JOptionPane.PLAIN_MESSAGE);
376 | if (result == JOptionPane.OK_OPTION) {
377 | Highlighter newHighlighter = new Highlighter(
378 | (String) booleanOperatorComboBox.getSelectedItem(),
379 | (String) originalOrModifiedComboBox.getSelectedItem(),
380 | (String) matchTypeComboBox.getSelectedItem(),
381 | (String) matchRelationshipComboBox.getSelectedItem(),
382 | matchHighlighterTextField.getText()
383 | );
384 | newHighlighter.setEnabled(newHighlighter.isEnabled());
385 | highlighterTableModel.update(highlighterTable.getSelectedRow(), newHighlighter);
386 | highlighterTableModel.fireTableDataChanged();
387 | }
388 | }
389 | resetHighlighterDialog();
390 | });
391 | deleteHighlighterButton.addActionListener(l -> {
392 | if (highlighterTable.getSelectedRow() != -1 ) {
393 | highlighterTableModel.remove(highlighterTable.getSelectedRow());
394 | highlighterTableModel.fireTableDataChanged();
395 | }
396 | });
397 | duplicateHighlighterButton.addActionListener(l -> {
398 | int selectedRow = highlighterTable.getSelectedRow();
399 | if (selectedRow != -1 && selectedRow < highlighterTableModel.getHighlighters().size()) {
400 | highlighterTableModel.add(
401 | new Highlighter(
402 | highlighterTableModel.getHighlighters().get(highlighterTable.getSelectedRow())));
403 | highlighterTableModel.fireTableDataChanged();
404 | }
405 | });
406 | return outputPanel;
407 | }
408 |
409 | private void resetHighlighterDialog() {
410 | booleanOperatorComboBox.setSelectedIndex(0);
411 | originalOrModifiedComboBox.setSelectedIndex(0);
412 | matchTypeComboBox.setSelectedIndex(0);
413 | matchRelationshipComboBox.setSelectedItem(0);
414 | matchHighlighterTextField.setText("");
415 | }
416 |
417 | public JPanel createHighlightEditorUI(HighlighterTableModel highlighterTableModel) {
418 | booleanOperatorComboBox = new JComboBox<>(Highlighter.BOOLEAN_OPERATOR_OPTIONS);
419 | originalOrModifiedComboBox = new JComboBox<>(Highlighter.ORIGINAL_OR_MODIFIED);
420 | matchTypeComboBox = new JComboBox<>(Highlighter.MATCH_TYPE_OPTIONS);
421 | matchRelationshipComboBox = new JComboBox<>(
422 | Highlighter.getMatchRelationshipOptions(
423 | Highlighter.MATCH_TYPE_OPTIONS[0]));
424 | matchHighlighterTextField = new JTextField();
425 |
426 | booleanOperatorComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
427 | originalOrModifiedComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
428 | matchTypeComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
429 | matchRelationshipComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
430 | matchHighlighterTextField.setPreferredSize(AutoRepeater.textFieldDimension);
431 |
432 | matchTypeComboBox.addActionListener(e -> {
433 | matchRelationshipComboBox
434 | .setModel(new DefaultComboBoxModel<>(Highlighter.getMatchRelationshipOptions(
435 | (String) matchTypeComboBox.getSelectedItem())));
436 | matchHighlighterTextField.setEnabled(Highlighter.matchConditionIsEditable(
437 | (String) matchTypeComboBox.getSelectedItem()));
438 | });
439 |
440 | if (highlighterTable.getSelectedRow() != -1) {
441 | Highlighter tempHighlighter = highlighterTableModel.getHighlighters()
442 | .get(highlighterTable.getSelectedRow());
443 | booleanOperatorComboBox.setSelectedItem(tempHighlighter.getBooleanOperator());
444 | originalOrModifiedComboBox.setSelectedItem(tempHighlighter.getOriginalOrModified());
445 | matchTypeComboBox.setSelectedItem(tempHighlighter.getMatchType());
446 | matchRelationshipComboBox.setSelectedItem(tempHighlighter.getMatchRelationship());
447 | matchHighlighterTextField.setText(tempHighlighter.getMatchCondition());
448 | }
449 |
450 | booleanOperatorLabel = new JLabel("Boolean Operator: ");
451 | originalOrModifiedLabel = new JLabel("Match Original Or Modified: ");
452 | matchTypeLabel = new JLabel("Match Type: ");
453 | matchRelationshipLabel = new JLabel("Match Relationship: ");
454 | matchHighlighterLabel = new JLabel("Match Condition: ");
455 |
456 | JPanel outputPanel = new JPanel();
457 | outputPanel.setLayout(new GridBagLayout());
458 | GridBagConstraints c;
459 | c = new GridBagConstraints();
460 |
461 | c.gridx = 0;
462 | c.gridy = 0;
463 | c.anchor = GridBagConstraints.WEST;
464 | outputPanel.add(booleanOperatorLabel, c);
465 | c.gridy = 1;
466 | outputPanel.add(originalOrModifiedLabel, c);
467 | c.gridy = 2;
468 | outputPanel.add(matchTypeLabel, c);
469 | c.gridy = 3;
470 | outputPanel.add(matchRelationshipLabel, c);
471 | c.gridy = 4;
472 | outputPanel.add(matchHighlighterLabel, c);
473 |
474 | c.anchor = GridBagConstraints.EAST;
475 | c.fill = GridBagConstraints.HORIZONTAL;
476 | c.gridx = 1;
477 | c.gridy = 0;
478 | outputPanel.add(booleanOperatorComboBox, c);
479 | c.gridy = 1;
480 | outputPanel.add(originalOrModifiedComboBox, c);
481 | c.gridy = 2;
482 | outputPanel.add(matchTypeComboBox, c);
483 | c.gridy = 3;
484 | outputPanel.add(matchRelationshipComboBox, c);
485 | c.gridy = 4;
486 | outputPanel.add(matchHighlighterTextField, c);
487 | return outputPanel;
488 | }
489 | }
490 |
--------------------------------------------------------------------------------
/src/burp/Logs/LogEntry.java:
--------------------------------------------------------------------------------
1 | package burp.Logs;
2 |
3 | import burp.BurpExtender;
4 | import burp.Highlighter.Highlighter;
5 | import burp.IHttpRequestResponsePersisted;
6 | import burp.IRequestInfo;
7 | import burp.IResponseInfo;
8 | import java.awt.Color;
9 | import java.net.URL;
10 | import java.util.Arrays;
11 |
12 | public class LogEntry {
13 |
14 | private long requestResponseId;
15 | private IHttpRequestResponsePersisted originalRequestResponse;
16 | private IHttpRequestResponsePersisted modifiedRequestResponse;
17 |
18 | private URL originalURL;
19 | private URL modifiedURL;
20 |
21 | private String originalMethod;
22 | private String modifiedMethod;
23 |
24 | private int originalLength;
25 | private int modifiedLength;
26 | private int lengthDifference;
27 | private double responseDistance;
28 |
29 | private int originalResponseStatus;
30 | private int modifiedResponseStatus;
31 |
32 | private int originalRequestHashCode;
33 | private int modifiedRequestHashCode;
34 |
35 | private int toolFlag;
36 |
37 | private long requestSentTime;
38 |
39 | private Color backgroundColor;
40 | private Color selectedBackgroundColor;
41 |
42 | public long getRequestResponseId() {
43 | return requestResponseId;
44 | }
45 |
46 | public Color getSelectedBackgroundColor() {
47 | return this.selectedBackgroundColor;
48 | }
49 |
50 | public void setBackgroundColor(Color backgroundColor, Color selectedBackgroundColor) {
51 | this.backgroundColor = backgroundColor;
52 | this.selectedBackgroundColor = selectedBackgroundColor;
53 | }
54 |
55 | public Color getBackgroundColor() { return this.backgroundColor; }
56 |
57 | public Color getFontColor() { return this.backgroundColor; }
58 |
59 | public void setRequestResponseId(long requestResponseId) {
60 | this.requestResponseId = requestResponseId;
61 | }
62 |
63 | public IHttpRequestResponsePersisted getOriginalRequestResponse() {
64 | return originalRequestResponse;
65 | }
66 |
67 | public void setOriginalRequestResponse(IHttpRequestResponsePersisted originalRequestResponse) {
68 | this.originalRequestResponse = originalRequestResponse;
69 | }
70 |
71 | public IHttpRequestResponsePersisted getModifiedRequestResponse() {
72 | return modifiedRequestResponse;
73 | }
74 |
75 | public void setModifiedRequestResponse(IHttpRequestResponsePersisted modifiedRequestResponse) {
76 | this.modifiedRequestResponse = modifiedRequestResponse;
77 | }
78 |
79 | public URL getOriginalURL() {
80 | return originalURL;
81 | }
82 |
83 | public void setOriginalURL(URL originalURL) {
84 | this.originalURL = originalURL;
85 | }
86 |
87 | public URL getModifiedURL() {
88 | return modifiedURL;
89 | }
90 |
91 | public void setModifiedURL(URL modifiedURL) {
92 | this.modifiedURL = modifiedURL;
93 | }
94 |
95 | public int getOriginalRequestHashCode() {
96 | return originalRequestHashCode;
97 | }
98 |
99 | public int getModifiedRequestHashCode() {
100 | return modifiedRequestHashCode;
101 | }
102 |
103 | public long getRequestSentTime() {
104 | return requestSentTime;
105 | }
106 |
107 | public String getOriginalMethod() {
108 | return originalMethod;
109 | }
110 |
111 | public void setOriginalMethod(String originalMethod) {
112 | this.originalMethod = originalMethod;
113 | }
114 |
115 | public String getModifiedMethod() {
116 | return modifiedMethod;
117 | }
118 |
119 | public void setModifiedMethod(String modifiedMethod) {
120 | this.modifiedMethod = modifiedMethod;
121 | }
122 |
123 | public int getOriginalLength() {
124 | return originalLength;
125 | }
126 |
127 | public void setOriginalLength(int originalLength) {
128 | this.originalLength = originalLength;
129 | }
130 |
131 | public int getModifiedLength() {
132 | return modifiedLength;
133 | }
134 |
135 | public void setModifiedLength(int modifiedLength) {
136 | this.modifiedLength = modifiedLength;
137 | }
138 |
139 | public int getLengthDifference() {
140 | return lengthDifference;
141 | }
142 |
143 | public void setLengthDifference(int lengthDifference) {
144 | this.lengthDifference = lengthDifference;
145 | }
146 |
147 | public double getResponseDistance() {
148 | return responseDistance;
149 | }
150 |
151 | public void setResponseDistance(double responseDistance) {
152 | this.responseDistance = responseDistance;
153 | }
154 |
155 | public int getOriginalResponseStatus() {
156 | return originalResponseStatus;
157 | }
158 |
159 | public void setOriginalResponseStatus(int originalResponseStatus) {
160 | this.originalResponseStatus = originalResponseStatus;
161 | }
162 |
163 | public int getModifiedResponseStatus() {
164 | return modifiedResponseStatus;
165 | }
166 |
167 | public void setModifiedResponseStatus(int modifiedResponseStatus) {
168 | this.modifiedResponseStatus = modifiedResponseStatus;
169 | }
170 |
171 | public void setOriginalRequestHashCode(int originalRequestHashCode) {
172 | this.originalRequestHashCode = originalRequestHashCode;
173 | }
174 |
175 | public void setModifiedRequestHashCode(int modifiedRequestHashCode) {
176 | this.modifiedRequestHashCode = modifiedRequestHashCode;
177 | }
178 |
179 | public void setRequestSentTime(long requestSentTime) {
180 | this.requestSentTime = requestSentTime;
181 | }
182 |
183 | //#, Host, Method, URL, Status, Length
184 | // #
185 | // Host
186 | // Orig. Method
187 | // Mod. Method
188 | // Orig. URL
189 | // Mod. URL
190 | // Orig. Status
191 | // Mod. Status
192 | // Orig. Length
193 | // Mod. Length
194 |
195 | public LogEntry(long requestResponseId,
196 | int toolFlag,
197 | IHttpRequestResponsePersisted originalRequestResponse,
198 | IHttpRequestResponsePersisted modifiedRequestResponse) {
199 |
200 | IRequestInfo originalAnalyzedRequest = BurpExtender.getHelpers()
201 | .analyzeRequest(originalRequestResponse);
202 |
203 | IRequestInfo modifiedAnalyzedRequest = BurpExtender.getHelpers()
204 | .analyzeRequest(modifiedRequestResponse);
205 |
206 | IResponseInfo originalAnalyzedResponse = BurpExtender.getHelpers()
207 | .analyzeResponse(originalRequestResponse.getResponse());
208 | this.originalResponseStatus = originalAnalyzedResponse.getStatusCode();
209 |
210 | IResponseInfo modifiedAnalyzedResponse = BurpExtender.getHelpers()
211 | .analyzeResponse(modifiedRequestResponse.getResponse());
212 | this.modifiedResponseStatus = modifiedAnalyzedResponse.getStatusCode();
213 |
214 | // Request ID
215 | this.requestResponseId = requestResponseId;
216 |
217 | // Original Request Info
218 | this.originalRequestResponse = originalRequestResponse;
219 | this.originalURL = originalAnalyzedRequest.getUrl();
220 | this.originalMethod = originalAnalyzedRequest.getMethod();
221 | this.originalLength = originalRequestResponse.getResponse().length;
222 |
223 | // Modified Request Info
224 | this.modifiedRequestResponse = modifiedRequestResponse;
225 | this.modifiedURL = modifiedAnalyzedRequest.getUrl();
226 | this.modifiedMethod = modifiedAnalyzedRequest.getMethod();
227 | this.modifiedLength = modifiedRequestResponse.getResponse().length;
228 |
229 | // Comparisons
230 | this.lengthDifference = Math.abs(this.originalLength - this.modifiedLength);
231 | this.responseDistance = 0;
232 |
233 | this.originalRequestHashCode = Arrays.hashCode(originalRequestResponse.getRequest());
234 | this.modifiedRequestHashCode = Arrays.hashCode(modifiedRequestResponse.getRequest());
235 |
236 | this.toolFlag = toolFlag;
237 |
238 | this.requestSentTime = System.currentTimeMillis();
239 |
240 | backgroundColor = Highlighter.COLORS[0];
241 | selectedBackgroundColor = Highlighter.SELECTED_COLORS[0];
242 | }
243 |
244 | public int getToolFlag() {
245 | return toolFlag;
246 | }
247 |
248 | public void setToolFlag(int toolFlag) {
249 | this.toolFlag = toolFlag;
250 | }
251 | }
252 |
253 |
--------------------------------------------------------------------------------
/src/burp/Logs/LogEntryMenu.java:
--------------------------------------------------------------------------------
1 | package burp.Logs;
2 |
3 | import burp.AutoRepeater.LogTable;
4 | import javax.swing.*;
5 | import java.awt.event.ActionEvent;
6 |
7 | /**
8 | * Largely taken from Logger++
9 | */
10 | public class LogEntryMenu extends JPopupMenu {
11 |
12 | public LogEntryMenu(final LogManager logManager, final LogTable logTable, final int row, final int col) {
13 | final LogEntry entry = logManager.getLogTableModel().getLogEntry(row);
14 | final String columnName = logManager.getLogTableModel().getColumnName(col);
15 | final String columnValue = logManager.getLogTableModel().getValueAt(row, col).toString();
16 | //this.add(new JPopupMenu.Separator());
17 |
18 | JMenuItem useAsFilter = new JMenuItem(
19 | new AbstractAction("Clear Logs") {
20 | @Override
21 | public void actionPerformed(ActionEvent actionEvent) {
22 | logManager.getLogTableModel().clearLogs();
23 | logTable.revalidate();
24 | }
25 | });
26 | this.add(useAsFilter);
27 | }
28 | }
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/burp/Logs/LogManager.java:
--------------------------------------------------------------------------------
1 | package burp.Logs;
2 |
3 | import burp.BurpExtender;
4 | import burp.Filter.Filters;
5 | import burp.IHttpRequestResponse;
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.ListIterator;
9 | import javax.swing.SwingUtilities;
10 | import javax.swing.table.TableRowSorter;
11 |
12 | public class LogManager {
13 |
14 | private LogTableModel logTableModel;
15 | private ArrayList entriesWithoutResponses;
16 | private TableRowSorter tableRowSorter;
17 | private int matchCounter = 0;
18 |
19 | public LogManager() {
20 | //tableRowSorter.setRowFilter(filter.getRowFilter());
21 | entriesWithoutResponses = new ArrayList<>();
22 | logTableModel = new LogTableModel();
23 | tableRowSorter = new TableRowSorter<>(logTableModel);
24 | }
25 |
26 | public synchronized int getRowCount() {
27 | return logTableModel.getRowCount();
28 | }
29 |
30 | public synchronized LogTableModel getLogTableModel() {
31 | return logTableModel;
32 | }
33 |
34 | public synchronized LogEntry getLogEntry(int row) {
35 | return logTableModel.getLogEntry(row);
36 | }
37 |
38 | public synchronized void addEntry(LogEntry logEntry, Filters filters) {
39 | SwingUtilities.invokeLater(() -> {
40 | logTableModel.addLogEntry(logEntry, filters);
41 | logTableModel.fireTableDataChanged();
42 | });
43 | //entriesWithoutResponses.add(logEntry);
44 | }
45 |
46 | public synchronized void setFilter(Filters filters) {
47 | logTableModel.filterLogs(filters);
48 | logTableModel.fireTableDataChanged();
49 | }
50 |
51 | //Keeping this around incase i go to switch the trigger back to on request
52 | public synchronized void addEntryResponse(IHttpRequestResponse messageInfo) {
53 | int requestHashCode = Arrays.hashCode(messageInfo.getRequest());
54 | ListIterator iter = entriesWithoutResponses.listIterator();
55 |
56 | System.out.println("LogEntriesWithoutResponses Length");
57 | System.out.println(entriesWithoutResponses.size());
58 |
59 | for (LogEntry lg : entriesWithoutResponses) {
60 | System.out.print("Entries Hash is: ");
61 | System.out.println(lg.getOriginalRequestHashCode());
62 | }
63 | System.out.print("Current hashcode: ");
64 | System.out.println(requestHashCode);
65 |
66 | System.out.print("matchCounter is: ");
67 | System.out.println(matchCounter);
68 |
69 | while (iter.hasNext()) {
70 | // If the current LogEntry's originalRequest matches the received request set them equal
71 | LogEntry currentLogEntry = iter.next();
72 | if (currentLogEntry.getOriginalRequestHashCode() == requestHashCode) {
73 | currentLogEntry.setOriginalRequestResponse(
74 | BurpExtender.getCallbacks().saveBuffersToTempFiles(messageInfo));
75 | iter.remove();
76 | matchCounter++;
77 | break;
78 | }
79 | if (currentLogEntry.getRequestSentTime() + 60000 < System.currentTimeMillis()) {
80 | System.out.println("TIME OUT");
81 | System.out.println(currentLogEntry.getRequestSentTime());
82 | System.out.println(System.currentTimeMillis());
83 | iter.remove();
84 | }
85 | }
86 | System.out.println();
87 | }
88 |
89 | public synchronized void fireTableRowsUpdated(int firstRow, int lastRow) {
90 | logTableModel.fireTableRowsInserted(firstRow, lastRow);
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/src/burp/Logs/LogTableModel.java:
--------------------------------------------------------------------------------
1 | package burp.Logs;
2 |
3 | import burp.Filter.Filters;
4 | import javax.swing.table.AbstractTableModel;
5 | import java.util.ArrayList;
6 |
7 | public class LogTableModel extends AbstractTableModel {
8 |
9 | private final ArrayList log;
10 | private ArrayList filteredLogs;
11 |
12 | public LogTableModel() {
13 | log = new ArrayList<>();
14 | filteredLogs = new ArrayList<>();
15 | }
16 |
17 | public void addLogEntry(LogEntry logEntry, Filters filters) {
18 | log.add(logEntry);
19 | if(filters.filter(logEntry)) {
20 | filteredLogs.add(logEntry);
21 | }
22 | fireTableDataChanged();
23 | }
24 |
25 | public void filterLogs(Filters filters) {
26 | filteredLogs = new ArrayList<>();
27 | for (LogEntry logEntry : log) {
28 | if(filters.filter(logEntry)) {
29 | filteredLogs.add(logEntry);
30 | //System.out.println("Adding row "+logEntry.getRequestResponseId());
31 | fireTableDataChanged();
32 | }
33 | }
34 | fireTableDataChanged();
35 | //new Thread(() -> {
36 | //}).start();
37 | }
38 |
39 | public void clearLogs() {
40 | log.clear();
41 | filteredLogs.clear();
42 | fireTableDataChanged();
43 | }
44 |
45 | public LogEntry getLogEntry(int row) {
46 | //return log.get(row);
47 | return filteredLogs.get(row);
48 | }
49 |
50 | public ArrayList getLog() {
51 | //return log;
52 | return log;
53 | }
54 |
55 | public ArrayList getFilteredLogs() {
56 | //return log;
57 | return filteredLogs;
58 | }
59 |
60 | public int getLogCount() {
61 | return log.size();
62 | }
63 |
64 | @Override
65 | public int getRowCount() {
66 | //return log.size();
67 | return filteredLogs.size();
68 | }
69 |
70 | @Override
71 | public int getColumnCount() {
72 | return 8;
73 | }
74 |
75 | @Override
76 | public String getColumnName(int columnIndex) {
77 | switch (columnIndex) {
78 | case 0:
79 | return "#";
80 | case 1:
81 | return "Method";
82 | case 2:
83 | return "URL";
84 | case 3:
85 | return "Orig. Status";
86 | case 4:
87 | return "Status";
88 | case 5:
89 | return "Orig. Resp. Len.";
90 | case 6:
91 | return "Resp. Len.";
92 | case 7:
93 | return "Resp. Len. Diff.";
94 | default:
95 | return "";
96 | }
97 | }
98 |
99 | @Override
100 | public Class> getColumnClass(int columnIndex) {
101 | switch (columnIndex) {
102 | case 0:
103 | return Integer.class;
104 | case 1:
105 | return String.class;
106 | case 2:
107 | return String.class;
108 | case 3:
109 | return Integer.class;
110 | case 4:
111 | return Integer.class;
112 | case 5:
113 | return Integer.class;
114 | case 6:
115 | return Integer.class;
116 | case 7:
117 | return Integer.class;
118 | default:
119 | return null;
120 | }
121 | }
122 |
123 | @Override
124 | public Object getValueAt(int rowIndex, int columnIndex) {
125 | //LogEntry logEntry = log.get(rowIndex);
126 | LogEntry logEntry = filteredLogs.get(rowIndex);
127 | // ID, Mod Method, Mod URL, Orig Status, Mod Status, Orig Size, Mod Size, Size Difference, Response Distance
128 | switch (columnIndex) {
129 | case 0:
130 | return logEntry.getRequestResponseId();
131 | case 1:
132 | return logEntry.getModifiedMethod();
133 | case 2:
134 | return logEntry.getModifiedURL().toString();
135 | case 3:
136 | return logEntry.getOriginalResponseStatus();
137 | case 4:
138 | return logEntry.getModifiedResponseStatus();
139 | case 5:
140 | return logEntry.getOriginalLength();
141 | case 6:
142 | return logEntry.getModifiedLength();
143 | case 7:
144 | return logEntry.getLengthDifference();
145 | default:
146 | return "";
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/burp/Replacements/Replacement.java:
--------------------------------------------------------------------------------
1 | package burp.Replacements;
2 |
3 | import burp.BurpExtender;
4 | import burp.IExtensionHelpers;
5 | import burp.IHttpRequestResponse;
6 | import burp.IParameter;
7 | import burp.IRequestInfo;
8 | import burp.Utils.Utils;
9 | import java.io.UnsupportedEncodingException;
10 | import java.util.ArrayList;
11 | import java.util.Arrays;
12 | import java.util.List;
13 | import java.util.ListIterator;
14 | import java.util.concurrent.atomic.AtomicInteger;
15 | import java.util.stream.Collectors;
16 |
17 | public class Replacement {
18 | public static final String[] REPLACEMENT_TYPE_OPTIONS = {
19 | "Request String",
20 |
21 | "Request Header",
22 | "Request Body",
23 | "Request Param Name",
24 | "Request Param Value",
25 | "Request Cookie Name",
26 | "Request Cookie Value",
27 | "Request First Line",
28 |
29 | "Add Header",
30 |
31 | "Remove Parameter By Name",
32 | "Remove Parameter By Value",
33 | "Remove Cookie By Name",
34 | "Remove Cookie By Value",
35 | "Remove Header By Name",
36 | "Remove Header By Value",
37 |
38 | "Match Param Name, Replace Value",
39 | "Match Cookie Name, Replace Value",
40 | "Match Header Name, Replace Value"
41 | //"Remove Header By Name",
42 | //"Remove Header By Value"
43 | };
44 |
45 | public static final String[] REPLACEMENT_COUNT_OPTINONS = {
46 | "Replace First",
47 | "Replace All"
48 | };
49 |
50 | private enum MatchAndReplaceType {
51 | MATCH_NAME_REPLACE_NAME,
52 | MATCH_NAME_REPLACE_VALUE,
53 | MATCH_VALUE_REPLACE_VALUE,
54 | MATCH_VALUE_REPLACE_NAME,
55 | MATCH_NAME_REMOVE,
56 | MATCH_VALUE_REMOVE
57 | }
58 |
59 | private String type;
60 | private String match;
61 | private String replace;
62 | private String comment;
63 | private String which;
64 |
65 | private Boolean isRegexMatch;
66 | private Boolean isEnabled;
67 |
68 | public Replacement(
69 | String type,
70 | String match,
71 | String replace,
72 | String which,
73 | String comment,
74 | boolean isRegexMatch) {
75 | this.type = type;
76 | this.match = match;
77 | this.replace = replace;
78 | this.which = which;
79 | this.comment = comment;
80 | this.isRegexMatch = isRegexMatch;
81 | this.isEnabled = true;
82 | }
83 |
84 | public Replacement(
85 | String type,
86 | String match,
87 | String replace,
88 | String which,
89 | String comment,
90 | boolean isRegexMatch,
91 | boolean isEnabled) {
92 | this(type, match, replace, which, comment, isRegexMatch);
93 | this.setEnabled(isEnabled);
94 | }
95 |
96 | public Replacement(Replacement replacement) {
97 | this(replacement.getType(),
98 | replacement.getMatch(),
99 | replacement.getReplace(),
100 | replacement.getWhich(),
101 | replacement.getComment(),
102 | replacement.isRegexMatch(),
103 | replacement.isEnabled());
104 | }
105 |
106 | private byte[] updateBurpParam(
107 | byte[] request,
108 | int parameterType,
109 | MatchAndReplaceType matchAndReplaceType) {
110 | IExtensionHelpers helpers = BurpExtender.getHelpers();
111 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
112 | // Need to only use params that can be added or removed.
113 | List parameters = analyzedRequest.getParameters().stream()
114 | .filter(p -> p.getType() == parameterType)
115 | .collect(Collectors.toList());
116 | List originalParameters = analyzedRequest.getParameters().stream()
117 | .filter(p -> p.getType() == parameterType)
118 | .collect(Collectors.toList());
119 |
120 | //for (IParameter param : originalParameters) {
121 | // BurpExtender.getCallbacks().printOutput(param.getName());
122 | //}
123 | //BurpExtender.getCallbacks().printOutput("-----");
124 | //for (IParameter param : parameters) {
125 | // BurpExtender.getCallbacks().printOutput(param.getName());
126 | //}
127 |
128 | boolean wasChanged = false;
129 | for (ListIterator iterator = parameters.listIterator(); iterator.hasNext(); ) {
130 | int i = iterator.nextIndex();
131 | IParameter currentParameter = iterator.next();
132 | //BurpExtender.getCallbacks().printOutput(currentParameter.getName());
133 | //BurpExtender.getCallbacks().printOutput(currentParameter.getValue());
134 | if (currentParameter.getType() == parameterType) {
135 | switch (matchAndReplaceType) {
136 | case MATCH_NAME_REPLACE_NAME:
137 | // Each if statement checks if isRegexMatch && check regex
138 | // || regular string compare
139 | if ((this.isRegexMatch && currentParameter.getName().matches(this.match))
140 | || currentParameter.getName().equals(this.match)) {
141 | parameters.set(i, helpers.buildParameter(
142 | this.replace,
143 | currentParameter.getValue(),
144 | currentParameter.getType()));
145 | wasChanged = true;
146 | }
147 | break;
148 | case MATCH_NAME_REPLACE_VALUE:
149 | if ((this.isRegexMatch && currentParameter.getName().matches(this.match))
150 | || currentParameter.getName().equals(this.match)) {
151 | parameters.set(i, helpers.buildParameter(
152 | currentParameter.getName(),
153 | this.replace,
154 | currentParameter.getType()));
155 | wasChanged = true;
156 | }
157 | break;
158 | case MATCH_VALUE_REPLACE_VALUE:
159 | if ((this.isRegexMatch && currentParameter.getValue().matches(this.match))
160 | || currentParameter.getValue().equals(this.match)) {
161 | parameters.set(i, helpers.buildParameter(
162 | currentParameter.getName(),
163 | this.replace,
164 | currentParameter.getType()));
165 | wasChanged = true;
166 | }
167 | break;
168 | case MATCH_VALUE_REPLACE_NAME:
169 | if ((this.isRegexMatch && currentParameter.getValue().matches(this.match))
170 | || currentParameter.getValue().equals(this.match)) {
171 | parameters.set(i, helpers.buildParameter(
172 | currentParameter.getName(),
173 | this.replace,
174 | currentParameter.getType()));
175 | wasChanged = true;
176 | }
177 | break;
178 | case MATCH_NAME_REMOVE:
179 | if ((this.isRegexMatch && currentParameter.getName().matches(this.match))
180 | || currentParameter.getName().equals(this.match)) {
181 | iterator.remove();
182 | wasChanged = true;
183 | }
184 | break;
185 | case MATCH_VALUE_REMOVE:
186 | if ((this.isRegexMatch && currentParameter.getValue().matches(this.match))
187 | || currentParameter.getValue().equals(this.match)) {
188 | iterator.remove();
189 | wasChanged = true;
190 | }
191 | break;
192 | default:
193 | break;
194 | }
195 | }
196 | // Bail if anything was changed
197 | if (this.which.equals("Replace First")) {
198 | if (wasChanged) {
199 | break;
200 | }
201 | }
202 | }
203 | if (wasChanged) {
204 | byte[] tempRequest = Arrays.copyOf(request, request.length);
205 | // Remove every parameter
206 | for (IParameter param : originalParameters) {
207 | tempRequest = helpers.removeParameter(tempRequest, param);
208 | }
209 | // Add them back
210 | for (IParameter param : parameters) {
211 | tempRequest = helpers.addParameter(tempRequest, param);
212 | }
213 | // Update the body and headers
214 | IRequestInfo tempAnalyzedRequest = helpers.analyzeRequest(tempRequest);
215 | byte[] body = Arrays
216 | .copyOfRange(tempRequest, tempAnalyzedRequest.getBodyOffset(), tempRequest.length);
217 | List headers = tempAnalyzedRequest.getHeaders();
218 | return helpers.buildHttpMessage(headers, body);
219 | }
220 | // Return the modified request
221 | return request;
222 | }
223 |
224 | // This is a hack around binary content causing requests to send
225 | private byte[] updateContent(byte[] request) {
226 | if (replaceFirst()) {
227 | return Utils.byteArrayRegexReplaceFirst(request, this.match, this.replace);
228 | } else {
229 | return Utils.byteArrayRegexReplaceAll(request, this.match, this.replace);
230 | }
231 | }
232 |
233 | private boolean replaceFirst() {
234 | return this.which.equals("Replace First");
235 | }
236 |
237 | private byte[] updateHeader(byte[] request) {
238 | IExtensionHelpers helpers = BurpExtender.getHelpers();
239 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
240 | List headers = analyzedRequest.getHeaders();
241 | byte[] body = Arrays.copyOfRange(request, analyzedRequest.getBodyOffset(), request.length);
242 | ArrayList newHeaders = new ArrayList<>();
243 | boolean wasChanged = false;
244 | for (String header : headers) {
245 | if (!replaceFirst() || (replaceFirst() && !wasChanged)) {
246 | if (this.isRegexMatch) {
247 | if (header.matches(this.match)) {
248 | header = this.replace;
249 | wasChanged = true;
250 | }
251 | } else {
252 | if (header.equals(this.match)) {
253 | header = this.replace;
254 | wasChanged = true;
255 | }
256 | }
257 | }
258 | // Don't add empty headers, they mess things up
259 | if (!header.equals("")) {
260 | newHeaders.add(header);
261 | }
262 | }
263 | return helpers.buildHttpMessage(newHeaders, body);
264 | }
265 |
266 | private byte[] addHeader(byte[] request) {
267 | IExtensionHelpers helpers = BurpExtender.getHelpers();
268 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
269 | List headers = analyzedRequest.getHeaders();
270 | // Strip content-length to make sure it's the last param
271 | if (headers.get(headers.size()-1).startsWith("Content-Length:")) {
272 | headers.remove(headers.size()-1);
273 | }
274 | byte[] body = Arrays.copyOfRange(request, analyzedRequest.getBodyOffset(), request.length);
275 | headers.add(this.replace);
276 | return helpers.buildHttpMessage(headers, body);
277 | }
278 |
279 | private byte[] matchHeaderNameUpdateValue(byte[] request) {
280 | IExtensionHelpers helpers = BurpExtender.getHelpers();
281 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
282 | List headers = analyzedRequest.getHeaders();
283 | byte[] body = Arrays.copyOfRange(request, analyzedRequest.getBodyOffset(), request.length);
284 | ArrayList newHeaders = new ArrayList<>();
285 | boolean wasChanged = false;
286 | for (String header : headers) {
287 | String[] splitHeader = header.split(":", 2);
288 | if (splitHeader.length == 2) {
289 | String headerName = splitHeader[0];
290 | if (!replaceFirst() || (replaceFirst() && !wasChanged)) {
291 | if (this.isRegexMatch) {
292 | if (headerName.matches(this.match)) {
293 | header = headerName+": "+this.replace;
294 | wasChanged = true;
295 | }
296 | } else {
297 | if (headerName.equals(this.match)) {
298 | header = headerName+": "+this.replace;
299 | wasChanged = true;
300 | }
301 | }
302 | }
303 | }
304 | // Don't add empty headers, they mess things up
305 | if (!header.equals("")) {
306 | newHeaders.add(header);
307 | }
308 | }
309 | return helpers.buildHttpMessage(newHeaders, body);
310 | }
311 |
312 | private byte[] updateRequestBody(byte[] request) {
313 | IExtensionHelpers helpers = BurpExtender.getHelpers();
314 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
315 | List headers = analyzedRequest.getHeaders();
316 | byte[] body = Arrays.copyOfRange(request, analyzedRequest.getBodyOffset(), request.length);
317 | boolean wasChanged = false;
318 | String bodyString;
319 |
320 | try {
321 | bodyString = new String(body, "UTF-8");
322 | } catch (UnsupportedEncodingException e) {
323 | return request;
324 | }
325 |
326 | if (this.isRegexMatch) {
327 | if (bodyString.matches(this.match)) {
328 | body = this.replace.getBytes();
329 | wasChanged = true;
330 | }
331 | } else {
332 | if (bodyString.equals(this.match)) {
333 | body = bodyString.replace(this.match, this.replace).getBytes();
334 | wasChanged = true;
335 | }
336 | }
337 | // This helps deal with binary data getting messed up from the string conversion and causing a new request.
338 | if (wasChanged) {
339 | return helpers.buildHttpMessage(headers, body);
340 | } else {
341 | return request;
342 | }
343 | }
344 |
345 | private byte[] updateRequestParamName(byte[] request) {
346 | if (!Utils.isRequestMultipartForm(request)) {
347 | request = updateBurpParam(request, IParameter.PARAM_BODY,
348 | MatchAndReplaceType.MATCH_NAME_REPLACE_NAME);
349 | return updateBurpParam(request, IParameter.PARAM_URL,
350 | MatchAndReplaceType.MATCH_NAME_REPLACE_NAME);
351 | } else {
352 | return request;
353 | }
354 | }
355 |
356 | private byte[] updateRequestParamValue(byte[] request) {
357 | if (!Utils.isRequestMultipartForm(request)) {
358 | request = updateBurpParam(request, IParameter.PARAM_BODY,
359 | MatchAndReplaceType.MATCH_VALUE_REPLACE_VALUE);
360 | return updateBurpParam(request, IParameter.PARAM_URL,
361 | MatchAndReplaceType.MATCH_VALUE_REPLACE_VALUE);
362 | } else {
363 | return request;
364 | }
365 | }
366 |
367 | private byte[] updateRequestParamValueByName(byte[] request) {
368 | if (!Utils.isRequestMultipartForm(request)) {
369 | request = updateBurpParam(request, IParameter.PARAM_BODY,
370 | MatchAndReplaceType.MATCH_NAME_REPLACE_VALUE);
371 | return updateBurpParam(request, IParameter.PARAM_URL,
372 | MatchAndReplaceType.MATCH_NAME_REPLACE_VALUE);
373 | } else {
374 | return request;
375 | }
376 | }
377 |
378 | private byte[] updateCookieName(byte[] request) {
379 | return updateBurpParam(request, IParameter.PARAM_COOKIE,
380 | MatchAndReplaceType.MATCH_NAME_REPLACE_NAME);
381 | }
382 |
383 | private byte[] updateCookieValue(byte[] request) {
384 | return updateBurpParam(request, IParameter.PARAM_COOKIE,
385 | MatchAndReplaceType.MATCH_VALUE_REPLACE_VALUE);
386 | }
387 |
388 | private byte[] removeParameterByName(byte[] request) {
389 | if (!Utils.isRequestMultipartForm(request)) {
390 | request = updateBurpParam(request, IParameter.PARAM_BODY,
391 | MatchAndReplaceType.MATCH_NAME_REMOVE);
392 | return updateBurpParam(request, IParameter.PARAM_URL,
393 | MatchAndReplaceType.MATCH_NAME_REMOVE);
394 | } else {
395 | return request;
396 | }
397 | }
398 |
399 | private byte[] removeParameterByValue(byte[] request) {
400 | if (Utils.isRequestMultipartForm(request)) {
401 | request = updateBurpParam(request, IParameter.PARAM_BODY,
402 | MatchAndReplaceType.MATCH_VALUE_REMOVE);
403 | return updateBurpParam(request, IParameter.PARAM_URL,
404 | MatchAndReplaceType.MATCH_VALUE_REMOVE);
405 | } else {
406 | return request;
407 | }
408 | }
409 |
410 | private byte[] removeCookieByName(byte[] request) {
411 | return updateBurpParam(request, IParameter.PARAM_COOKIE,
412 | MatchAndReplaceType.MATCH_NAME_REMOVE);
413 | }
414 |
415 | private byte[] removeCookieByValue(byte[] request) {
416 | return updateBurpParam(request, IParameter.PARAM_COOKIE,
417 | MatchAndReplaceType.MATCH_VALUE_REMOVE);
418 | }
419 |
420 | private byte[] removeHeaderByName(byte[] request) {
421 | IExtensionHelpers helpers = BurpExtender.getHelpers();
422 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
423 | byte[] body = Arrays.copyOfRange(request, analyzedRequest.getBodyOffset(), request.length);
424 | List headers;
425 | if(replaceFirst()) {
426 | AtomicInteger index = new AtomicInteger(0);
427 | if(isRegexMatch()) {
428 | headers = analyzedRequest.getHeaders().stream()
429 | .filter((x -> !(x.split(":")[0].matches(getMatch()) && index.getAndIncrement() < 1)))
430 | .collect(Collectors.toCollection(ArrayList::new));
431 | } else {
432 | headers = analyzedRequest.getHeaders().stream()
433 | .filter(x -> !(x.split(":")[0].equals(getMatch()) && index.getAndIncrement() < 1))
434 | .collect(Collectors.toCollection(ArrayList::new));
435 | }
436 | } else {
437 | if(isRegexMatch()) {
438 | headers = analyzedRequest.getHeaders().stream()
439 | .filter(x -> !(x.split(":")[0].matches(getMatch())))
440 | .collect(Collectors.toCollection(ArrayList::new));
441 | } else {
442 | headers = analyzedRequest.getHeaders().stream()
443 | .filter(x -> !(x.split(":")[0].equals(getMatch())))
444 | .collect(Collectors.toCollection(ArrayList::new));
445 | }
446 | }
447 | return helpers.buildHttpMessage(headers, body);
448 | }
449 |
450 | private byte[] removeHeaderByValue(byte[] request) {
451 | IExtensionHelpers helpers = BurpExtender.getHelpers();
452 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
453 | byte[] body = Arrays.copyOfRange(request, analyzedRequest.getBodyOffset(), request.length);
454 | List headers;
455 | if(replaceFirst()) {
456 | AtomicInteger index = new AtomicInteger(0);
457 | if(isRegexMatch()) {
458 | headers = analyzedRequest.getHeaders().stream()
459 | .filter(x -> x.split(":")[1].matches(getMatch()) && index.getAndIncrement() < 1)
460 | .collect(Collectors.toCollection(ArrayList::new));
461 | } else {
462 | headers = analyzedRequest.getHeaders().stream()
463 | .filter(x -> x.split(":")[1].equals(getMatch()) && index.getAndIncrement() < 1)
464 | .collect(Collectors.toCollection(ArrayList::new));
465 | }
466 | } else {
467 | if(isRegexMatch()) {
468 | headers = analyzedRequest.getHeaders().stream()
469 | .filter(x -> x.split(":")[1].matches(getMatch()))
470 | .collect(Collectors.toCollection(ArrayList::new));
471 | } else {
472 | headers = analyzedRequest.getHeaders().stream()
473 | .filter(x -> x.split(":")[1].equals(getMatch()))
474 | .collect(Collectors.toCollection(ArrayList::new));
475 | }
476 | }
477 | return helpers.buildHttpMessage(headers, body);
478 | }
479 |
480 | private byte[] updateCookieValueByName(byte[] request) {
481 | return updateBurpParam(request, IParameter.PARAM_COOKIE,
482 | MatchAndReplaceType.MATCH_NAME_REPLACE_VALUE);
483 | }
484 |
485 | private byte[] updateRequestFirstLine(byte[] request) {
486 | IExtensionHelpers helpers = BurpExtender.getHelpers();
487 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
488 | List headers = analyzedRequest.getHeaders();
489 | byte[] body = Arrays.copyOfRange(request, analyzedRequest.getBodyOffset(), request.length);
490 | String firstRequestString = headers.get(0);
491 | if (replaceFirst()) {
492 | headers.set(0, firstRequestString.replaceFirst(this.match, this.replace));
493 | } else {
494 | headers.set(0, firstRequestString.replaceAll(this.match, this.replace));
495 | }
496 | return helpers.buildHttpMessage(headers, body);
497 | }
498 |
499 | // TODO: Modify this to return List to support "Replace Each"
500 | public byte[] performReplacement(IHttpRequestResponse messageInfo) {
501 | byte[] request = messageInfo.getRequest();
502 | if (this.isEnabled) {
503 | switch (this.type) {
504 | case ("Request Header"):
505 | return updateHeader(request);
506 | case ("Request Body"):
507 | return updateRequestBody(request);
508 | case ("Request Param Name"):
509 | return updateRequestParamName(request);
510 | case ("Request Param Value"):
511 | return updateRequestParamValue(request);
512 | case ("Request Cookie Name"):
513 | return updateCookieName(request);
514 | case ("Request Cookie Value"):
515 | return updateCookieValue(request);
516 | case ("Request First Line"):
517 | return updateRequestFirstLine(request);
518 | case ("Request String"):
519 | return updateContent(request);
520 | case ("Add Header"):
521 | return addHeader(request);
522 | case ("Remove Parameter By Name"):
523 | return removeParameterByName(request);
524 | case ("Remove Parameter By Value"):
525 | return removeParameterByValue(request);
526 | case ("Remove Cookie By Name"):
527 | return removeCookieByName(request);
528 | case ("Remove Cookie By Value"):
529 | return removeCookieByValue(request);
530 | case ("Remove Header By Name"):
531 | return removeHeaderByName(request);
532 | case ("Remove Header By Value"):
533 | return removeHeaderByValue(request);
534 | case ("Match Param Name, Replace Value"):
535 | return updateRequestParamValueByName(request);
536 | case ("Match Cookie Name, Replace Value"):
537 | return updateCookieValueByName(request);
538 | case ("Match Header Name, Replace Value"):
539 | return matchHeaderNameUpdateValue(request);
540 | default:
541 | return request;
542 | }
543 | }
544 | return request;
545 | }
546 |
547 | public String getType() {
548 | return type;
549 | }
550 |
551 | public String getMatch() {
552 | return match;
553 | }
554 |
555 | public String getReplace() {
556 | return replace;
557 | }
558 |
559 | public String getComment() {
560 | return comment;
561 | }
562 |
563 | public boolean isRegexMatch() {
564 | return isRegexMatch;
565 | }
566 |
567 | public boolean isEnabled() {
568 | return isEnabled;
569 | }
570 |
571 | public void setType(String type) {
572 | this.type = type;
573 | }
574 |
575 | public void setMatch(String match) {
576 | this.match = match;
577 | }
578 |
579 | public void setReplace(String replace) {
580 | this.replace = replace;
581 | }
582 |
583 | public void setComment(String comment) {
584 | this.comment = comment;
585 | }
586 |
587 | public void setRegexMatch(Boolean regexMatch) {
588 | isRegexMatch = regexMatch;
589 | }
590 |
591 | public void setEnabled(Boolean enabled) {
592 | isEnabled = enabled;
593 | }
594 |
595 | public String getWhich() {
596 | return which;
597 | }
598 |
599 | public void setWhich(String which) {
600 | this.which = which;
601 | }
602 |
603 | public Boolean getRegexMatch() {
604 | return isRegexMatch;
605 | }
606 |
607 | public Boolean getEnabled() {
608 | return isEnabled;
609 | }
610 |
611 |
612 | }
613 |
--------------------------------------------------------------------------------
/src/burp/Replacements/ReplacementTableModel.java:
--------------------------------------------------------------------------------
1 | package burp.Replacements;
2 |
3 | import burp.Replacements.Replacement;
4 | import java.util.stream.Collectors;
5 | import javax.swing.table.AbstractTableModel;
6 | import java.util.ArrayList;
7 |
8 | public class ReplacementTableModel extends AbstractTableModel {
9 |
10 | private String[] columnNames = {
11 | "Enabled",
12 | "Type",
13 | "Match",
14 | "Replace",
15 | "Which",
16 | "Comment",
17 | "Regex Match",
18 | };
19 |
20 | private ArrayList replacements;
21 |
22 | public ReplacementTableModel() {
23 | replacements = new ArrayList<>();
24 | }
25 |
26 | public void addReplacement(Replacement newReplacement) {
27 | replacements.add(newReplacement);
28 | }
29 |
30 | public void updateReplacement(int replacementIndex, Replacement newReplacement) {
31 | replacements.set(replacementIndex, newReplacement);
32 | }
33 |
34 | public Replacement getReplacement(int replacementIndex) {
35 | return replacements.get(replacementIndex);
36 | }
37 |
38 | public ArrayList getReplacements() {
39 | return replacements.stream()
40 | .filter(Replacement::isEnabled)
41 | .collect(Collectors.toCollection(ArrayList::new));
42 | }
43 |
44 | public void deleteReplacement(int replacementIndex) {
45 | replacements.remove(replacementIndex);
46 | }
47 |
48 | @Override
49 | public int getColumnCount() {
50 | return columnNames.length;
51 | }
52 |
53 | @Override
54 | public int getRowCount() {
55 | return replacements.size();
56 | }
57 |
58 | @Override
59 | public String getColumnName(int col) {
60 | return columnNames[col];
61 | }
62 |
63 | @Override
64 | public Object getValueAt(int row, int col) {
65 | Replacement tempReplacement = replacements.get(row);
66 | switch (col) {
67 | case 0:
68 | return tempReplacement.isEnabled();
69 | case 1:
70 | return tempReplacement.getType();
71 | case 2:
72 | return tempReplacement.getMatch();
73 | case 3:
74 | return tempReplacement.getReplace();
75 | case 4:
76 | return tempReplacement.getWhich();
77 | case 5:
78 | return tempReplacement.getComment();
79 | default:
80 | return tempReplacement.isRegexMatch();
81 | }
82 | }
83 |
84 | @Override
85 | public Class getColumnClass(int column) {
86 | return (getValueAt(0, column).getClass());
87 | }
88 |
89 | @Override
90 | public boolean isCellEditable(int row, int column) {
91 | return (getColumnName(column).equals("Enabled"));
92 | }
93 |
94 | @Override
95 | public void setValueAt(Object value, int row, int col) {
96 | Replacement tempReplacement = replacements.get(row);
97 | switch (col) {
98 | case 0:
99 | tempReplacement.setEnabled((Boolean) value);
100 | break;
101 | case 1:
102 | tempReplacement.setType((String) value);
103 | break;
104 | case 2:
105 | tempReplacement.setMatch((String) value);
106 | break;
107 | case 3:
108 | tempReplacement.setReplace((String) value);
109 | break;
110 | case 4:
111 | tempReplacement.setWhich((String) value);
112 | break;
113 | case 5:
114 | tempReplacement.setComment((String) value);
115 | break;
116 | default:
117 | tempReplacement.setRegexMatch((Boolean) value);
118 | break;
119 | }
120 | replacements.set(row, tempReplacement);
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/burp/Replacements/Replacements.java:
--------------------------------------------------------------------------------
1 | package burp.Replacements;
2 |
3 | import burp.AutoRepeater;
4 | import burp.BurpExtender;
5 | import java.awt.GridBagConstraints;
6 | import java.awt.GridBagLayout;
7 | import javax.swing.JButton;
8 | import javax.swing.JCheckBox;
9 | import javax.swing.JComboBox;
10 | import javax.swing.JLabel;
11 | import javax.swing.JOptionPane;
12 | import javax.swing.JPanel;
13 | import javax.swing.JScrollPane;
14 | import javax.swing.JTable;
15 | import javax.swing.JTextField;
16 |
17 | public class Replacements {
18 |
19 | // Replacements UI
20 | private JPanel replacementsPanel;
21 | private JScrollPane replacementScrollPane;
22 | private JTable replacementTable;
23 | private JButton addReplacementButton;
24 | private JPanel replacementsButtonPanel;
25 | private JButton editReplacementButton;
26 | private JButton deleteReplacementButton;
27 | private JButton duplicateReplacementButton;
28 |
29 | // Replacements popup UI
30 | private JPanel replacementPanel;
31 | private JComboBox replacementTypeComboBox;
32 | private JTextField replacementMatchTextField;
33 | private JTextField replacementReplaceTextField;
34 | private JTextField replacementCommentTextField;
35 | private JCheckBox replacementIsRegexCheckBox;
36 | private JComboBox replacementCountComboBox;
37 | private JLabel replacementMatchLabel;
38 | private JLabel replacementReplaceLabel;
39 | private JLabel replacementCommentLabel;
40 | private JLabel replacementTypeLabel;
41 | private JLabel replacementIsRegexLabel;
42 | private JLabel replacementCountLabel;
43 |
44 | // Replacements Data Store
45 | private ReplacementTableModel replacementTableModel;
46 |
47 | public Replacements() {
48 | replacementTableModel = new ReplacementTableModel();
49 | createUI();
50 | }
51 |
52 | public ReplacementTableModel getReplacementTableModel() { return replacementTableModel; }
53 | public JPanel getUI() { return replacementsPanel; }
54 |
55 | private void createUI() {
56 | GridBagConstraints c;
57 | replacementPanel = new JPanel();
58 | replacementPanel.setLayout(new GridBagLayout());
59 | replacementPanel.setPreferredSize(AutoRepeater.dialogDimension);
60 |
61 | c = new GridBagConstraints();
62 |
63 | replacementTypeComboBox = new JComboBox<>(Replacement.REPLACEMENT_TYPE_OPTIONS);
64 | replacementCountComboBox = new JComboBox<>(Replacement.REPLACEMENT_COUNT_OPTINONS);
65 | replacementMatchTextField = new JTextField();
66 | replacementReplaceTextField = new JTextField();
67 | replacementCommentTextField = new JTextField();
68 | replacementIsRegexCheckBox = new JCheckBox();
69 |
70 | replacementTypeComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
71 | replacementCountComboBox.setPreferredSize(AutoRepeater.comboBoxDimension);
72 | replacementMatchTextField.setPreferredSize(AutoRepeater.textFieldDimension);
73 | replacementReplaceTextField.setPreferredSize(AutoRepeater.textFieldDimension);
74 | replacementCommentTextField.setPreferredSize(AutoRepeater.textFieldDimension);
75 |
76 | replacementTypeLabel = new JLabel("Type: ");
77 | replacementMatchLabel = new JLabel("Match: ");
78 | replacementCountLabel = new JLabel("Which: ");
79 | replacementReplaceLabel = new JLabel("Replace: ");
80 | replacementCommentLabel = new JLabel("Comment: ");
81 | replacementIsRegexLabel = new JLabel("Regex Match: ");
82 |
83 | c.anchor = GridBagConstraints.WEST;
84 | c.gridx = 0;
85 | c.gridy = 0;
86 | replacementPanel.add(replacementTypeLabel, c);
87 | c.gridy = 1;
88 | replacementPanel.add(replacementMatchLabel, c);
89 | c.gridy = 2;
90 | replacementPanel.add(replacementReplaceLabel, c);
91 | c.gridy = 3;
92 | replacementPanel.add(replacementCountLabel, c);
93 | c.gridy = 4;
94 | replacementPanel.add(replacementCommentLabel, c);
95 | c.gridy = 5;
96 | replacementPanel.add(replacementIsRegexLabel, c);
97 |
98 | c.anchor = GridBagConstraints.EAST;
99 | c.fill = GridBagConstraints.HORIZONTAL;
100 | c.gridx = 1;
101 | c.gridy = 0;
102 | replacementPanel.add(replacementTypeComboBox, c);
103 | c.gridy = 1;
104 | replacementPanel.add(replacementMatchTextField, c);
105 | c.gridy = 2;
106 | replacementPanel.add(replacementReplaceTextField, c);
107 | c.gridy = 3;
108 | replacementPanel.add(replacementCountComboBox, c);
109 | c.gridy = 4;
110 | replacementPanel.add(replacementCommentTextField, c);
111 | c.gridy = 5;
112 | replacementPanel.add(replacementIsRegexCheckBox, c);
113 |
114 | // Replacement Buttons
115 | addReplacementButton = new JButton("Add");
116 | addReplacementButton.setPreferredSize(AutoRepeater.buttonDimension);
117 | addReplacementButton.setMinimumSize(AutoRepeater.buttonDimension);
118 | addReplacementButton.setMaximumSize(AutoRepeater.buttonDimension);
119 |
120 | // Add New Replacement
121 | addReplacementButton.addActionListener(e -> {
122 | int result = JOptionPane.showConfirmDialog(
123 | BurpExtender.getParentTabbedPane(),
124 | replacementPanel,
125 | "Add Replacement",
126 | JOptionPane.OK_CANCEL_OPTION,
127 | JOptionPane.PLAIN_MESSAGE);
128 | if (result == JOptionPane.OK_OPTION) {
129 | Replacement newReplacement = new Replacement(
130 | (String) replacementTypeComboBox.getSelectedItem(),
131 | replacementMatchTextField.getText(),
132 | replacementReplaceTextField.getText(),
133 | (String) replacementCountComboBox.getSelectedItem(),
134 | replacementCommentTextField.getText(),
135 | replacementIsRegexCheckBox.isSelected()
136 | );
137 | replacementTableModel.addReplacement(newReplacement);
138 | replacementTableModel.fireTableDataChanged();
139 | }
140 | resetReplacementDialog();
141 | });
142 |
143 | editReplacementButton = new JButton("Edit");
144 | editReplacementButton.setPreferredSize(AutoRepeater.buttonDimension);
145 | editReplacementButton.setMinimumSize(AutoRepeater.buttonDimension);
146 | editReplacementButton.setMaximumSize(AutoRepeater.buttonDimension);
147 |
148 | // Edit selected Replacement
149 | editReplacementButton.addActionListener(e -> {
150 | int selectedRow = replacementTable.getSelectedRow();
151 | if (selectedRow != -1) {
152 | Replacement tempReplacement = replacementTableModel.getReplacement(selectedRow);
153 |
154 | replacementTypeComboBox.setSelectedItem(tempReplacement.getType());
155 | replacementMatchTextField.setText(tempReplacement.getMatch());
156 | replacementReplaceTextField.setText(tempReplacement.getReplace());
157 | replacementCountComboBox.setSelectedItem(tempReplacement.getWhich());
158 | replacementCommentTextField.setText(tempReplacement.getComment());
159 | replacementIsRegexCheckBox.setSelected(tempReplacement.isRegexMatch());
160 |
161 | int result = JOptionPane.showConfirmDialog(
162 | BurpExtender.getParentTabbedPane(),
163 | replacementPanel,
164 | "Edit Replacement",
165 | JOptionPane.OK_CANCEL_OPTION,
166 | JOptionPane.PLAIN_MESSAGE);
167 | if (result == JOptionPane.OK_OPTION) {
168 | Replacement newReplacement = new Replacement(
169 | (String) replacementTypeComboBox.getSelectedItem(),
170 | replacementMatchTextField.getText(),
171 | replacementReplaceTextField.getText(),
172 | (String) replacementCountComboBox.getSelectedItem(),
173 | replacementCommentTextField.getText(),
174 | replacementIsRegexCheckBox.isSelected()
175 | );
176 | replacementTableModel.updateReplacement(selectedRow, newReplacement);
177 | replacementTableModel.fireTableDataChanged();
178 | }
179 | resetReplacementDialog();
180 | }
181 | });
182 |
183 | deleteReplacementButton = new JButton("Remove");
184 | deleteReplacementButton.setPreferredSize(AutoRepeater.buttonDimension);
185 | deleteReplacementButton.setMinimumSize(AutoRepeater.buttonDimension);
186 | deleteReplacementButton.setMaximumSize(AutoRepeater.buttonDimension);
187 |
188 | //Delete Replacement
189 | deleteReplacementButton.addActionListener(e -> {
190 | int selectedRow = replacementTable.getSelectedRow();
191 | if (selectedRow != -1) {
192 | replacementTableModel.deleteReplacement(selectedRow);
193 | replacementTableModel.fireTableDataChanged();
194 | }
195 | });
196 |
197 | duplicateReplacementButton = new JButton("Duplicate");
198 | duplicateReplacementButton.setPreferredSize(AutoRepeater.buttonDimension);
199 | duplicateReplacementButton.setMinimumSize(AutoRepeater.buttonDimension);
200 | duplicateReplacementButton.setMaximumSize(AutoRepeater.buttonDimension);
201 |
202 | // Duplicate a replacement
203 | duplicateReplacementButton.addActionListener(e -> {
204 | int selectedRow = replacementTable.getSelectedRow();
205 | if (selectedRow != -1 && selectedRow < replacementTableModel.getReplacements().size()) {
206 | replacementTableModel.addReplacement(
207 | new Replacement(replacementTableModel.getReplacement(selectedRow)));
208 | replacementTableModel.fireTableDataChanged();
209 | }
210 | });
211 |
212 | replacementsButtonPanel = new JPanel();
213 | replacementsButtonPanel.setLayout(new GridBagLayout());
214 | replacementsButtonPanel.setPreferredSize(AutoRepeater.buttonPanelDimension);
215 |
216 | c = new GridBagConstraints();
217 | c.anchor = GridBagConstraints.FIRST_LINE_END;
218 | c.gridx = 0;
219 | c.weightx = 1;
220 |
221 | replacementsButtonPanel.add(addReplacementButton, c);
222 | replacementsButtonPanel.add(editReplacementButton, c);
223 | replacementsButtonPanel.add(deleteReplacementButton, c);
224 | replacementsButtonPanel.add(duplicateReplacementButton, c);
225 |
226 | replacementTableModel = new ReplacementTableModel();
227 | replacementTable = new JTable(replacementTableModel);
228 | replacementTable.getColumnModel().getColumn(0).setMaxWidth(55);
229 | replacementTable.getColumnModel().getColumn(0).setMinWidth(55);
230 | replacementScrollPane = new JScrollPane(replacementTable);
231 | replacementScrollPane.setMinimumSize(AutoRepeater.tableDimension);
232 |
233 | // Panel containing replacement options
234 | replacementsPanel = new JPanel();
235 | replacementsPanel.setLayout(new GridBagLayout());
236 |
237 | c = new GridBagConstraints();
238 | c.anchor = GridBagConstraints.PAGE_START;
239 | c.gridx = 0;
240 | replacementsPanel.add(replacementsButtonPanel, c);
241 |
242 | c.fill = GridBagConstraints.BOTH;
243 | c.weightx = 1;
244 | c.weighty = 1;
245 | c.gridx = 1;
246 | replacementsPanel.add(replacementScrollPane, c);
247 | }
248 |
249 | private void resetReplacementDialog() {
250 | replacementTypeComboBox.setSelectedIndex(0);
251 | replacementCountComboBox.setSelectedIndex(0);
252 | replacementMatchTextField.setText("");
253 | replacementReplaceTextField.setText("");
254 | replacementCommentTextField.setText("");
255 | replacementIsRegexCheckBox.setSelected(false);
256 | }
257 |
258 | }
259 |
--------------------------------------------------------------------------------
/src/burp/Utils/AutoRepeaterMenu.java:
--------------------------------------------------------------------------------
1 | package burp.Utils;
2 |
3 | import burp.AutoRepeater;
4 | import burp.BurpExtender;
5 | import burp.IExtensionStateListener;
6 | import burp.Logs.LogEntry;
7 | import burp.Logs.LogManager;
8 |
9 | import com.google.gson.JsonArray;
10 | import com.google.gson.JsonElement;
11 | import javax.swing.*;
12 | import java.awt.event.ActionEvent;
13 | import java.awt.event.ActionListener;
14 | import java.io.File;
15 | import java.io.FileNotFoundException;
16 | import java.io.PrintWriter;
17 | import java.util.ArrayList;
18 |
19 | public class AutoRepeaterMenu implements Runnable, IExtensionStateListener {
20 | private final JRootPane rootPane;
21 | private static ArrayList autoRepeaters;
22 |
23 | private static JMenu autoRepeaterJMenu;
24 | private static JMenuItem toggleSettingsVisibility;
25 |
26 | private static boolean showSettingsPanel;
27 |
28 | public static boolean sendRequestsToPassiveScanner;
29 | public static boolean addRequestsToSiteMap;
30 |
31 | public AutoRepeaterMenu(JRootPane rootPane) {
32 | this.rootPane = rootPane;
33 | autoRepeaters = BurpExtender.getAutoRepeaters();
34 | showSettingsPanel = true;
35 | BurpExtender.getCallbacks().registerExtensionStateListener(this);
36 | }
37 |
38 | /**
39 | * Action listener for setting visibility
40 | */
41 | class SettingVisibilityListener implements ActionListener {
42 | @Override
43 | public void actionPerformed(ActionEvent e) {
44 | // Toggling settings panel
45 | if (toggleSettingsVisibility.getText().equals("Hide Settings Panel")) {
46 | showSettingsPanel = false;
47 | toggleSettingsVisibility.setText("Show Settings Panel");
48 | } else {
49 | showSettingsPanel = true;
50 | toggleSettingsVisibility.setText("Hide Settings Panel");
51 | }
52 | // toggling every AutoRepeater tab
53 | for (AutoRepeater ar : autoRepeaters) {
54 | ar.toggleConfigurationPane(showSettingsPanel);
55 | }
56 | }
57 | }
58 |
59 | /**
60 | * Action listener for import settings menu
61 | */
62 | class ImportSettingListener implements ActionListener {
63 |
64 | @Override
65 | public void actionPerformed(ActionEvent e) {
66 | final JFileChooser importPathChooser = new JFileChooser();
67 | int replaceTabs = JOptionPane.showConfirmDialog(rootPane, "Would you like to replace the current tabs?", "Replace Tabs", JOptionPane.YES_NO_CANCEL_OPTION);
68 | if (replaceTabs == 2) {
69 | // cancel selected
70 | return;
71 | }
72 | int returnVal = importPathChooser.showOpenDialog(rootPane);
73 | if (returnVal != JFileChooser.APPROVE_OPTION) {
74 | BurpExtender.getCallbacks().printOutput("Cannot open a file dialog for importing settings.");
75 | return;
76 | }
77 | File file = importPathChooser.getSelectedFile();
78 | String fileData = Utils.readFile(file);
79 | if (fileData.equals("")) {
80 | // file empty
81 | return;
82 | }
83 | if (replaceTabs == 1) {
84 | // do not replace current tabs
85 | BurpExtender.initializeFromSave(fileData, false);
86 | } else if (replaceTabs == 0) {
87 | // replace current tabs
88 | BurpExtender.getCallbacks().printOutput("Removing Tabs");
89 | BurpExtender.initializeFromSave(fileData, true);
90 | }
91 | }
92 | }
93 |
94 | /**
95 | * Action listener for export settings menu.
96 | */
97 | class ExportSettingListener implements ActionListener {
98 | @Override
99 | public void actionPerformed(ActionEvent e) {
100 | Object[] options = {"Current Tab", "Every Tab", "Cancel"};
101 | int option = JOptionPane.showOptionDialog(rootPane, "Which tab would you like to export?", "Export Tabs",
102 | JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
103 | if (option == 2) { return; }
104 | final JFileChooser exportPathChooser = new JFileChooser();
105 | int returnVal = exportPathChooser.showSaveDialog(rootPane);
106 | if (returnVal != JFileChooser.APPROVE_OPTION) {
107 | BurpExtender.getCallbacks().printOutput("Cannot open a file dialog for exporting settings.");
108 | return;
109 | }
110 | File file = exportPathChooser.getSelectedFile();
111 | try (PrintWriter out = new PrintWriter(file.getAbsolutePath())) {
112 | if (option == 0) {
113 | // export current tab
114 | out.println(BurpExtender.exportSave(BurpExtender.getSelectedAutoRepeater()));
115 | } else if (option == 1) {
116 | // export every tab
117 | out.println(BurpExtender.exportSave());
118 | }
119 | } catch (FileNotFoundException error) {
120 | error.printStackTrace();
121 | }
122 | }
123 | }
124 |
125 | /**
126 | * Action listener for export logs menu.
127 | */
128 | class ExportLogsListener implements ActionListener {
129 |
130 | @Override
131 | public void actionPerformed(ActionEvent e) {
132 | Object[] options = {"Export", "Cancel"};
133 | final String[] EXPORT_OPTIONS = {"CSV", "JSON"};
134 | final String[] EXPORT_WHICH_OPTIONS = {"All Tab Logs", "Selected Tab Logs"};
135 | final String[] EXPORT_VALUE_OPTIONS = {"Log Entry", "Log Entry + Full HTTP Request"};
136 |
137 | final JComboBox exportTypeComboBox = new JComboBox<>(EXPORT_OPTIONS);
138 | final JComboBox exportWhichComboBox = new JComboBox<>(EXPORT_WHICH_OPTIONS);
139 | final JComboBox exportValueComboBox = new JComboBox<>(EXPORT_VALUE_OPTIONS);
140 |
141 | final JFileChooser exportLogsPathChooser = new JFileChooser();
142 |
143 | JPanel exportLogsPanel = new JPanel();
144 | exportLogsPanel.setLayout(new BoxLayout(exportLogsPanel, BoxLayout.PAGE_AXIS));
145 | exportLogsPanel.add(exportWhichComboBox);
146 | exportLogsPanel.add(exportValueComboBox);
147 | exportLogsPanel.add(exportTypeComboBox);
148 | JPanel buttonPanel = new JPanel();
149 | exportLogsPanel.add(buttonPanel);
150 |
151 | int option = JOptionPane.showOptionDialog(rootPane, exportLogsPanel,
152 | "Export Logs", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
153 | if (option == 1) {
154 | return;
155 | }
156 | int returnVal = exportLogsPathChooser.showSaveDialog(rootPane);
157 | if (returnVal != JFileChooser.APPROVE_OPTION) {
158 | BurpExtender.getCallbacks().printOutput("Cannot open a file dialog for exporting logs.");
159 | return;
160 | }
161 | AutoRepeater autoRepeater = BurpExtender.getSelectedAutoRepeater();
162 | LogManager logManager = autoRepeater.getLogManager();
163 | File file = exportLogsPathChooser.getSelectedFile();
164 | ArrayList logEntries = new ArrayList<>();
165 | // collect relevant entries
166 | if ((exportWhichComboBox.getSelectedItem()).equals("All Tab Logs")) {
167 | logEntries = autoRepeater.getLogTableModel().getLog();
168 | } else if ((exportWhichComboBox.getSelectedItem()).equals("Selected Tab Logs")) {
169 | int[] selectedRows = autoRepeater.getLogTable().getSelectedRows();
170 | for (int row : selectedRows) {
171 | logEntries.add(logManager.getLogEntry(autoRepeater.getLogTable().convertRowIndexToModel(row)));
172 | }
173 | }
174 | // determine if whole request should be exported or just the log contents
175 | boolean exportFullHttp = !((exportValueComboBox.getSelectedItem()).equals("Log Entry"));
176 |
177 | try (PrintWriter out = new PrintWriter(file.getAbsolutePath())) {
178 | if ((exportTypeComboBox.getSelectedItem()).equals("CSV")) {
179 | out.println(Utils.exportLogEntriesToCsv(logEntries, exportFullHttp));
180 | } else if ((exportTypeComboBox.getSelectedItem()).equals("JSON")) {
181 | out.println(Utils.exportLogEntriesToJson(logEntries, exportFullHttp));
182 | }
183 | } catch (FileNotFoundException error) {
184 | error.printStackTrace();
185 | }
186 | }
187 | }
188 |
189 | class DuplicateCurrentTabListener implements ActionListener {
190 |
191 | @Override
192 | public void actionPerformed(ActionEvent e) {
193 | JsonArray serializedTab = BurpExtender.exportSaveAsJson(BurpExtender.getSelectedAutoRepeater());
194 | for (JsonElement tabConfiguration : serializedTab) {
195 | BurpExtender.addNewTab(tabConfiguration.getAsJsonObject());
196 | }
197 | }
198 | }
199 |
200 | @Override
201 | public void extensionUnloaded() {
202 | // unregister menu
203 | JMenuBar burpMenuBar = rootPane.getJMenuBar();
204 | BurpExtender.getCallbacks().printOutput("Unregistering menu");
205 | burpMenuBar.remove(autoRepeaterJMenu);
206 | burpMenuBar.repaint();
207 | }
208 |
209 | @Override
210 | public void run() {
211 | JMenuBar burpJMenuBar = rootPane.getJMenuBar();
212 | autoRepeaterJMenu = new JMenu("AutoRepeater");
213 | // initialize menu items and add action listeners
214 | JMenuItem duplicateCurrentTab = new JMenuItem("Duplicate Selected Tab");
215 | duplicateCurrentTab.addActionListener(new DuplicateCurrentTabListener());
216 |
217 | toggleSettingsVisibility = new JMenuItem("Hide Settings Panel");
218 | toggleSettingsVisibility.addActionListener(new SettingVisibilityListener());
219 | JCheckBoxMenuItem toggleSendRequestsToPassiveScanner = new JCheckBoxMenuItem("Send Requests To Passive Scanner");
220 | toggleSendRequestsToPassiveScanner.addActionListener(l ->
221 | sendRequestsToPassiveScanner = toggleSendRequestsToPassiveScanner.getState());
222 |
223 | JCheckBoxMenuItem toggleAddRequestsToSiteMap = new JCheckBoxMenuItem("Add Requests To Site Map");
224 | toggleAddRequestsToSiteMap.addActionListener(l ->
225 | addRequestsToSiteMap = toggleAddRequestsToSiteMap.getState());
226 |
227 | JMenuItem showImportMenu = new JMenuItem("Import Settings");
228 | showImportMenu.addActionListener(new ImportSettingListener());
229 |
230 | JMenuItem showExportMenu = new JMenuItem("Export Settings");
231 | showExportMenu.addActionListener(new ExportSettingListener());
232 |
233 | JMenuItem showExportLogsMenu = new JMenuItem("Export Logs");
234 | showExportLogsMenu.addActionListener(new ExportLogsListener());
235 | // add menu items to the menu
236 | autoRepeaterJMenu.add(duplicateCurrentTab);
237 | autoRepeaterJMenu.add(toggleSettingsVisibility);
238 | autoRepeaterJMenu.addSeparator();
239 | autoRepeaterJMenu.add(toggleAddRequestsToSiteMap);
240 | autoRepeaterJMenu.add(toggleSendRequestsToPassiveScanner);
241 | autoRepeaterJMenu.addSeparator();
242 | autoRepeaterJMenu.add(showImportMenu);
243 | autoRepeaterJMenu.add(showExportMenu);
244 | autoRepeaterJMenu.add(showExportLogsMenu);
245 | // add menu to menu bar
246 | burpJMenuBar.add(autoRepeaterJMenu, burpJMenuBar.getMenuCount() - 2);
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/src/burp/Utils/DiffViewerPane.java:
--------------------------------------------------------------------------------
1 | package burp.Utils;
2 |
3 | import javax.swing.*;
4 |
5 | public class DiffViewerPane extends JEditorPane {
6 |
7 | public DiffViewerPane(byte[] original, byte[] modified) {
8 | this.setEditable(false);
9 | this.setContentType("text/html");
10 | }
11 |
12 | public DiffViewerPane() {
13 | this.setEditable(false);
14 | this.setContentType("text/html");
15 | }
16 |
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/burp/Utils/HttpComparer.java:
--------------------------------------------------------------------------------
1 | package burp.Utils;
2 |
3 | import burp.Utils.diff_match_patch;
4 | import java.util.LinkedList;
5 |
6 | public class HttpComparer {
7 |
8 | public static String diffText(String original, String modified) {
9 | if (original.length() > 10000 || modified.length() > 10000) {
10 | return "Input too large, cannot generate diff.";
11 | } else {
12 | diff_match_patch differ = new diff_match_patch();
13 | LinkedList diff;
14 | diff = differ.diff_main(original, modified, true);
15 | differ.diff_cleanupSemantic(diff);
16 | return diffToHtml(diff);
17 | }
18 | }
19 |
20 | public static String diffLines(String original, String modified) {
21 | if (original.length() > 20000 || modified.length() > 20000) {
22 | return "Input too large, cannot generate diff.";
23 | } else {
24 | diff_match_patch differ = new diff_match_patch();
25 | diff_match_patch.LinesToCharsResult linesTochars = differ
26 | .diff_linesToChars(original, modified);
27 | LinkedList diff = differ
28 | .diff_main(linesTochars.chars1, linesTochars.chars2, false);
29 | differ.diff_charsToLines(diff, linesTochars.lineArray);
30 | //differ.diff_cleanupSemantic(diff);
31 | return diffToHtml(diff);
32 | }
33 | }
34 |
35 | private static String diffToHtml(LinkedList diff) {
36 | StringBuilder html = new StringBuilder();
37 | for (diff_match_patch.Diff aDiff : diff) {
38 | String text = aDiff.text
39 | .replace("&", "&")
40 | .replace("<", "<")
41 | .replace(">", ">")
42 | .replace("\n", "
");
43 | switch (aDiff.operation) {
44 | case INSERT:
45 | html.append("").append(text).append("");
46 | break;
47 | case DELETE:
48 | html.append("").append(text).append("");
49 | break;
50 | case EQUAL:
51 | html.append("").append(text).append("");
52 | break;
53 | }
54 | }
55 | return html.toString();
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/burp/Utils/MessageEditorController.java:
--------------------------------------------------------------------------------
1 | package burp.Utils;
2 |
3 | import burp.IHttpRequestResponse;
4 | import burp.IHttpService;
5 | import burp.IMessageEditorController;
6 |
7 | public class MessageEditorController implements IMessageEditorController {
8 |
9 | private IHttpRequestResponse displayedItem;
10 |
11 | public IHttpRequestResponse getDisplayedItem() {
12 | return displayedItem;
13 | }
14 |
15 | public void setDisplayedItem(IHttpRequestResponse displayedItem) {
16 | this.displayedItem = displayedItem;
17 | }
18 |
19 | @Override
20 | public byte[] getRequest() {
21 | return displayedItem.getRequest();
22 | }
23 |
24 | @Override
25 | public byte[] getResponse() {
26 | return displayedItem.getResponse();
27 | }
28 |
29 | @Override
30 | public IHttpService getHttpService() {
31 | return displayedItem.getHttpService();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/burp/Utils/ResponseStore.java:
--------------------------------------------------------------------------------
1 | package burp.Utils;
2 |
3 | import burp.IHttpListener;
4 | import burp.IHttpRequestResponse;
5 | import java.util.HashMap;
6 | import java.util.Set;
7 | import java.util.stream.Collectors;
8 |
9 | // This will allow AutoRepeater to replace values in Requests based on previous responses from domains
10 | // This will be its own HTTPListener to keep track of all requests, not just the one's AutoRepeater sends
11 | // The Data store should be something along the lines of a Map of some storage object which holds the timestamp + RequestResponse
12 | // and the key being the domain. It might make sense to use the full path instead of just the URL.
13 | // TODO: Move this idea into a new plugin.
14 | public class ResponseStore implements IHttpListener {
15 |
16 | // Container class for the response body + time it was received
17 | public class Response {
18 | byte[] responseBody;
19 | long time;
20 |
21 | public Response(byte[] responseBody) {
22 | time = System.currentTimeMillis();
23 | this.responseBody = responseBody;
24 | }
25 | }
26 |
27 | // Hashmap to store responses and their URL
28 | private HashMap responseHashMap;
29 |
30 | public ResponseStore() {
31 | responseHashMap = new HashMap<>();
32 | }
33 |
34 | public byte[] getMostRecentResponseBodyByRegex (String urlRegex) {
35 | // Get all the keys that match the regex
36 | Set matchingUrls = responseHashMap.keySet()
37 | .stream()
38 | .filter(key -> key.matches(urlRegex))
39 | .collect(Collectors.toSet());
40 | // Get an element from the hashmap to start from, it doesn't matter which element
41 | Response mostRecentResponse = responseHashMap.get(matchingUrls.iterator().next());
42 | // Iterate over the matching url responses to find the most recent one
43 | for (String matchingUrl : matchingUrls) {
44 | Response tempResponse = responseHashMap.get(matchingUrl);
45 | if (mostRecentResponse.time < tempResponse.time) {
46 | mostRecentResponse = tempResponse;
47 | }
48 | }
49 | return mostRecentResponse.responseBody;
50 | }
51 |
52 | public byte[] getMostRecentResponseBody (String url) {
53 | return responseHashMap.get(url).responseBody;
54 | }
55 |
56 | @Override
57 | public void processHttpMessage(int toolFlag, boolean messageIsRequest,
58 | IHttpRequestResponse messageInfo) {
59 | if (!messageIsRequest) {
60 | // Add the response body to the hashmap
61 | responseHashMap.put(messageInfo.getHttpService().getHost(), new Response(messageInfo.getResponse()));
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/burp/Utils/Utils.java:
--------------------------------------------------------------------------------
1 | package burp.Utils;
2 |
3 | import burp.BurpExtender;
4 | import burp.IExtensionHelpers;
5 | import burp.IHttpRequestResponse;
6 | import burp.IHttpService;
7 | import burp.IParameter;
8 | import burp.IRequestInfo;
9 | import burp.Logs.LogEntry;
10 | import com.google.gson.JsonArray;
11 | import com.google.gson.JsonObject;
12 | import java.io.BufferedReader;
13 | import java.io.File;
14 | import java.io.FileReader;
15 | import java.io.IOException;
16 | import java.io.UnsupportedEncodingException;
17 | import java.util.ArrayList;
18 | import java.util.Arrays;
19 | import java.util.regex.Matcher;
20 | import java.util.regex.Pattern;
21 | import javax.swing.*;
22 | import java.awt.*;
23 | import java.util.List;
24 |
25 | public class Utils {
26 |
27 | public static String exportLogEntriesToJson (ArrayList logEntries, boolean exportHttp) {
28 | JsonArray json = new JsonArray();
29 | if(exportHttp) {
30 | for (LogEntry log : logEntries) {
31 | JsonObject logJson = new JsonObject();
32 | logJson.addProperty("#",log.getRequestResponseId());
33 | logJson.addProperty("Method",log.getModifiedMethod());
34 | logJson.addProperty("URL",log.getModifiedURL().toString());
35 | logJson.addProperty("Orig. Status",log.getOriginalResponseStatus());
36 | logJson.addProperty("Status",log.getModifiedResponseStatus());
37 | logJson.addProperty("Orig. Resp. Len.",log.getOriginalLength());
38 | logJson.addProperty("Resp. Len.",log.getModifiedLength());
39 | logJson.addProperty("Resp. Len. Diff.",log.getLengthDifference());
40 | logJson.addProperty("Orig. Request",new String(log.getOriginalRequestResponse().getRequest()));
41 | logJson.addProperty("Orig. Response",new String(log.getOriginalRequestResponse().getResponse()));
42 | logJson.addProperty("Request",new String(log.getModifiedRequestResponse().getRequest()));
43 | logJson.addProperty("Response",new String(log.getModifiedRequestResponse().getResponse()));
44 | json.add(logJson);
45 |
46 | }
47 | } else {
48 | for (LogEntry log : logEntries) {
49 | JsonObject logJson = new JsonObject();
50 | logJson.addProperty("#",log.getRequestResponseId());
51 | logJson.addProperty("Method",log.getModifiedMethod());
52 | logJson.addProperty("URL",log.getModifiedURL().toString());
53 | logJson.addProperty("Orig. Status",log.getOriginalResponseStatus());
54 | logJson.addProperty("Status",log.getModifiedResponseStatus());
55 | logJson.addProperty("Orig. Resp. Len.",log.getOriginalLength());
56 | logJson.addProperty("Resp. Len.",log.getModifiedLength());
57 | logJson.addProperty("Resp. Len. Diff.",log.getLengthDifference());
58 | json.add(logJson);
59 | }
60 | }
61 | return json.toString();
62 | }
63 |
64 | public static String readFile(File file) {
65 | BufferedReader br;
66 | StringBuilder output = new StringBuilder();
67 | try {
68 | br = new BufferedReader(new FileReader(file));
69 | String st;
70 | while ((st = br.readLine()) != null) {
71 | output.append(st);
72 | }
73 | } catch (IOException e) {
74 | return "";
75 | }
76 | return output.toString();
77 | }
78 |
79 | public static String exportLogEntriesToCsv (ArrayList logEntries, boolean exportHttp) {
80 | StringBuilder csv = new StringBuilder();
81 | if(exportHttp) {
82 | String csvHeader =
83 | "#"+","+
84 | "Method"+","+
85 | "URL"+","+
86 | "Orig. Status"+","+
87 | "Status"+","+
88 | "Orig. Resp. Len."+","+
89 | "Resp. Len."+","+
90 | "Resp. Len. Diff."+","+
91 | "Orig. Request"+","+
92 | "Orig. Response"+","+
93 | "Request"+","+
94 | "Response"+"\n";
95 | csv.append(csvHeader);
96 | for (LogEntry log : logEntries) {
97 | csv.append(log.getRequestResponseId());
98 | csv.append(",");
99 | csv.append(Utils.sanitizeForCsv(log.getModifiedMethod()));
100 | csv.append(",");
101 | csv.append(Utils.sanitizeForCsv(log.getModifiedURL().toString()));
102 | csv.append(",");
103 | csv.append(log.getOriginalResponseStatus());
104 | csv.append(",");
105 | csv.append(log.getModifiedResponseStatus());
106 | csv.append(",");
107 | csv.append(log.getOriginalLength());
108 | csv.append(",");
109 | csv.append(log.getModifiedLength());
110 | csv.append(",");
111 | csv.append(log.getLengthDifference());
112 | csv.append(",");
113 | csv.append(Utils.sanitizeForCsv(new String(log.getOriginalRequestResponse().getRequest())));
114 | csv.append(",");
115 | csv.append(Utils.sanitizeForCsv(new String(log.getOriginalRequestResponse().getResponse())));
116 | csv.append(",");
117 | csv.append(Utils.sanitizeForCsv(new String(log.getModifiedRequestResponse().getRequest())));
118 | csv.append(",");
119 | csv.append(Utils.sanitizeForCsv(new String(log.getModifiedRequestResponse().getResponse())));
120 | csv.append("\n");
121 | }
122 | } else {
123 | String csvHeader = "#"+","+
124 | "Method"+","+
125 | "URL"+","+
126 | "Orig. Status"+","+
127 | "Status"+","+
128 | "Orig. Resp. Len."+","+
129 | "Resp. Len."+","+
130 | "Resp. Len. Diff."+"\n";
131 | csv.append(csvHeader);
132 | for (LogEntry log : logEntries) {
133 | csv.append(log.getRequestResponseId());
134 | csv.append(",");
135 | csv.append(Utils.sanitizeForCsv(log.getModifiedMethod()));
136 | csv.append(",");
137 | csv.append(Utils.sanitizeForCsv(log.getModifiedURL().toString()));
138 | csv.append(",");
139 | csv.append(log.getOriginalResponseStatus());
140 | csv.append(",");
141 | csv.append(log.getModifiedResponseStatus());
142 | csv.append(",");
143 | csv.append(log.getOriginalLength());
144 | csv.append(",");
145 | csv.append(log.getModifiedLength());
146 | csv.append(",");
147 | csv.append(log.getLengthDifference());
148 | csv.append("\n");
149 | }
150 | }
151 | return csv.toString();
152 | }
153 |
154 | private static String sanitizeForCsv(String input) {
155 | return input.replaceAll("\n", "\\n").replaceAll(",", "\\,");
156 | }
157 |
158 | public static IHttpRequestResponse cloneIHttpRequestResponse(
159 | IHttpRequestResponse originalRequestResponse) {
160 | return new IHttpRequestResponse() {
161 | byte[] request = originalRequestResponse.getRequest();
162 | byte[] response = originalRequestResponse.getResponse();
163 | String comment = originalRequestResponse.getComment();
164 | String highlight = originalRequestResponse.getHighlight();
165 | IHttpService httpService = originalRequestResponse.getHttpService();
166 |
167 | @Override
168 | public byte[] getRequest() {
169 | return request;
170 | }
171 |
172 | @Override
173 | public void setRequest(byte[] message) {
174 | this.request = message;
175 | }
176 |
177 | @Override
178 | public byte[] getResponse() {
179 | return response;
180 | }
181 |
182 | @Override
183 | public void setResponse(byte[] message) {
184 | this.response = message;
185 | }
186 |
187 | @Override
188 | public String getComment() {
189 | return comment;
190 | }
191 |
192 | @Override
193 | public void setComment(String comment) {
194 | this.comment = comment;
195 | }
196 |
197 | @Override
198 | public String getHighlight() {
199 | return highlight;
200 | }
201 |
202 | @Override
203 | public void setHighlight(String color) {
204 | this.highlight = color;
205 | }
206 |
207 | @Override
208 | public IHttpService getHttpService() {
209 | return httpService;
210 | }
211 |
212 | @Override
213 | public void setHttpService(IHttpService httpService) {
214 | this.httpService = httpService;
215 | }
216 | };
217 | }
218 |
219 | public static String getStringAfterSubstring (String input, String substring) {
220 | return(input.substring(input.lastIndexOf(substring) + substring.length()));
221 | }
222 |
223 | public static byte[] byteArrayRegexReplaceFirst(byte[] input, String regex, String replacement) {
224 | try {
225 | // I need to specify ASCII here because it's the easiest way for me to ensure the byte[] and
226 | // resulting string are the same length.
227 | String inputString = new String(input, "US-ASCII");
228 | Pattern pattern = Pattern.compile(regex);
229 | Matcher matcher = pattern.matcher(inputString);
230 | // I'll be appending a lot of it's just easier to use a list here
231 | ArrayList output = new ArrayList<>();
232 | // the index of the start of the last match
233 | int currentIndex = 0;
234 | // Check all occurrences
235 | if (matcher.find()) {
236 | int start = matcher.start();
237 | // Add every item between start of the last match and the current match
238 | for (int i = currentIndex; i < start; i++) {
239 | output.add(input[i]);
240 | }
241 | // Add every character in the replacement
242 | for (int i = 0; i < replacement.length(); i++) {
243 | output.add((byte)replacement.charAt(i));
244 | }
245 | // Skip over the body of the match
246 | currentIndex = matcher.end();
247 | } else {
248 | //
249 | return input;
250 | }
251 | // Add everything after the last match
252 | for (int i = currentIndex; i < input.length; i++) {
253 | output.add(input[i]);
254 | }
255 | return byteArrayListToByteArray(output);
256 | } catch (UnsupportedEncodingException e) {
257 | return input;
258 | }
259 |
260 | }
261 |
262 | public static byte[] byteArrayRegexReplaceAll(byte[] input, String regex, String replacement) {
263 | try {
264 | // I need to specify ASCII here because it's the easiest way for me to ensure the byte[] and
265 | // resulting string are the same length.
266 | String inputString = new String(input, "US-ASCII");
267 | //String inputString = new String(input, "UTF-16");
268 | Pattern pattern = Pattern.compile(regex);
269 | Matcher matcher = pattern.matcher(inputString);
270 | // I'll be appending a lot of it's just easier to use a list here
271 | ArrayList output = new ArrayList<>();
272 | //BurpExtender.getCallbacks().printOutput("Input Length is: ");
273 | //BurpExtender.getCallbacks().printOutput(Integer.toString(input.length));
274 | //BurpExtender.getCallbacks().printOutput("Input String Length is: ");
275 | //BurpExtender.getCallbacks().printOutput(Integer.toString(inputString.length()));
276 | // the index of the start of the last match
277 | int currentIndex = 0;
278 | // Check all occurrences
279 | while (matcher.find()) {
280 | int start = matcher.start();
281 | // Add every item between start of the last match and the current match
282 | for (int i = currentIndex; i < start; i++) {
283 | output.add(input[i]);
284 | }
285 | // Add every character in the replacement
286 | for (int i = 0; i < replacement.length(); i++) {
287 | output.add((byte)replacement.charAt(i));
288 | }
289 | // Skip over the body of the match
290 | currentIndex = matcher.end();
291 | }
292 | // Add everything after the last match
293 | for (int i = currentIndex; i < input.length; i++) {
294 | output.add(input[i]);
295 | }
296 | return byteArrayListToByteArray(output);
297 | } catch (UnsupportedEncodingException e) {
298 | return input;
299 | }
300 | }
301 |
302 | public static byte[] byteArrayListToByteArray(ArrayList input) {
303 | byte[] output = new byte[input.size()];
304 | for (int i = 0; i < input.size(); i++) {
305 | output[i] = input.get(i);
306 | }
307 | return output;
308 | }
309 |
310 | //This method isn't efficient, i should refactor
311 | public static String getMultipartBoundary(byte[] request) {
312 | IExtensionHelpers helpers = BurpExtender.getHelpers();
313 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
314 | List headers = analyzedRequest.getHeaders();
315 | return headers.stream()
316 | .filter((h) -> h.startsWith("Content-Type: multipart/form-data;"))
317 | .findFirst()
318 | .map((h) -> getStringAfterSubstring(h, "Content-Type: multipart/form-data;"))
319 | .map((h) -> getStringAfterSubstring(h, "boundary="))
320 | .map((h) -> "--"+h)
321 | .orElse(null);
322 | }
323 |
324 | public static Color getBurpOrange() {
325 | return new Color(0xff6633);
326 | }
327 |
328 | // This method is a mess.
329 | public static ArrayList getMultipartParameters(byte[] request) {
330 | IExtensionHelpers helpers = BurpExtender.getHelpers();
331 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
332 | ArrayList parameters = new ArrayList<>();
333 | String boundary = getMultipartBoundary(request);
334 | String requestBodyString = new String(Arrays.copyOfRange(request, analyzedRequest.getBodyOffset(), request.length));
335 | int index = requestBodyString.indexOf(boundary);
336 | while (index >= 0) {
337 | //BurpExtender.getCallbacks().printOutput(Integer.toString(index));
338 | int nextNewLineIndex = requestBodyString.indexOf('\n', index);
339 | index = requestBodyString.indexOf(boundary, index+1);
340 | }
341 | return parameters;
342 | }
343 |
344 | // It seems that IParameter types are incorrectly stated by the Burp Suite API
345 | // so I need to check
346 | public static boolean isRequestMultipartForm(byte[] request) {
347 | IExtensionHelpers helpers = BurpExtender.getHelpers();
348 | IRequestInfo analyzedRequest = helpers.analyzeRequest(request);
349 | List headers = analyzedRequest.getHeaders();
350 | return headers.stream().anyMatch((h) -> h.startsWith("Content-Type: multipart/form-data;"));
351 | }
352 |
353 | public static void highlightParentTab(JTabbedPane parentTabbedPane, Component childComponent) {
354 | if (parentTabbedPane != null) {
355 | for (int i = 0; i < parentTabbedPane.getTabCount(); i++) {
356 | if (parentTabbedPane.getComponentAt(i).equals(childComponent)) {
357 | parentTabbedPane.setBackgroundAt(i, getBurpOrange());
358 | Timer timer = new Timer(3000, e -> {
359 | for (int j = 0; j < parentTabbedPane.getTabCount(); j++) {
360 | if (parentTabbedPane.getComponentAt(j).equals(childComponent)) {
361 | parentTabbedPane.setBackgroundAt(j, Color.BLACK);
362 | break;
363 | }
364 | }
365 | });
366 | timer.setRepeats(false);
367 | timer.start();
368 | break;
369 | }
370 | }
371 | }
372 | }
373 | }
374 |
--------------------------------------------------------------------------------