diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 823e672..6189c14 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -23,10 +23,10 @@ jobs: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 17 - name: Cache local Maven repository uses: actions/cache@v2 diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 9bd26e1..ddd6a80 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -23,10 +23,10 @@ jobs: - uses: actions/checkout@v2 - - name: Set up JDK 1.8 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 1.8 + java-version: 17 - name: Cache local Maven repository uses: actions/cache@v2 diff --git a/.gitignore b/.gitignore index aeb630c..5103b5c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ buildNumber.properties .classpath .project .settings/ +*/launch.json diff --git a/INTRODUCTION.md b/INTRODUCTION.md new file mode 100755 index 0000000..5720bc5 --- /dev/null +++ b/INTRODUCTION.md @@ -0,0 +1,92 @@ +# CSTC Introduction +This document serves as a written introduction to the Cyber Security Transformation Chef or in short: CSTC. It starts by giving an overview of the general UI of the tool and after that walking through a demonstrative use-case explaining the core functionalities along the way. + +## UI Overview + +### Main Panel + + + +The UI can be divided into three functional areas.
+On the far left **(1)** is the area for selecting operations, grouped categorically. These are added to the recipe in the appropriate lane via click-and-drag. There is a search bar above the operations tree for quick access.
+The recipe panel is located in the middle **(2)**. This is made up of up to 10 lanes, whereby the operations are applied in the order from top to bottom and left to right. Each lane works anew on the input sent to the CSTC. This makes it possible to carry out several transformations on the same input.
+Finally, the selection area on the right **(3)** provides an overview of the initial development of a recipe and is used for debugging a recipe. The input to be worked with is shown in the upper area, and the lower area shows the result after the recipe has been applied. + + +### Filter + + + +The CSTC enables HTTP requests and responses to be changed automatically according to the given recipe. The tabs for which the recipes are to be applied are selected using the ```Filter``` button at the top middle of the recipe panel. This opens the pop-up window for selection. + + +### Different Recipes + + + +The CSTC enables parallel work with HTTP requests and responses. Exactly one recipe can be created for each of them. Which recipe you are currently working on is controlled via the tab selection in the top left corner. In the tab ```Outgoing Requests``` you work on the HTTP requests, in ```Incoming Responses``` you work analogously on the HTTP responses. The third tab ```Formatting``` offers space to work with data independently of requests and responses and has no effect on regarding automatic transformation of requests/responses. It can be used to test recipes or perform static transformations comparable to the GCHQ CyberChef. + + +## Example 1 - Response + +We will now look at two examples using a demo application. A detailed video demonstration of the CSTC can be found [here](https://www.youtube.com/watch?v=6fjW4iXj5cg). + + + +In this first example we see a HTTP request and its response in the Repeater tab. The body of the response is encoded and to create a matching recipe with the CSTC, we send the response to the ```Incoming``` tab.
+Note here that the menu for sending the HTTP request to the CSTC can also be called up in the Proxy tab and, above all, in the HTTP history. + + + +Now the appropriate recipe must be created. In this case we use two lanes **(1)**: In the first, we extract the body of the HTTP response, decode it and store it in a variable named ```body```. In the second lane, we replace the body of the original HTTP response with the body we have manipulated and stored in the variable. We also add a suitable Content-Type Header so that Burp knows how to display the data in pretty print. We see the result on the right **(2)**. We finally instruct the CSTC to apply this recipe to all incoming responses in the Repeater tab **(3)**. + + + +If we now resend our request in the Repeater tab, we see that the recipe is working. + + +## Example 2 - Request + + + +For a second example, let's take a look at this HTTP POST request. We have three POST parameters and want to test the first parameter for SQL Injection. However, every time the value is changed, the API responds with an error message that the checksum is incorrect. In this case we found out that the values of the first two parameters are concatenated and then the SHA1 value is calculated of the resulting string. The result is cross-checked with the value of the integrity parameter. With the help of the CSTC, this scheme can be automatically applied to all outgoing requests and the testing process is greatly simplified. + + + +As before, we send the data to the CSTC to be able to work with it. This time we work with the HTTP request, so we send it to the ```Outgoing``` tab. + + + +At this point, another feature of the CSTC can be demonstrated. Created recipes can be saved in the local file system and reloaded if necessary. Here, selecting ```Load``` **(1)** opens a pop-up and the saved recipe can be selected **(2)**. + + + +As you can see in the overview on the right, the value of the integrity parameter is now recalculated dynamically depending on the values of the request. + + + +We now click on ```Filter``` again to select that the recipe should be applied to outgoing requests in the Repeater tab. + + + +When resending the request in the Repeater tab, we receive an Internal Server Error, which means that the checksum test was successful and we can start testing the POST parameters. + + + +With an appropriately adapted payload, we can now verify and exploit a SQL injection vulnerability in this API endpoint. + +### Automation with the help of the CSTC + +Suppose we wanted to test the POST parameter using the Burp Scanner. Without adapting the integrity POST parameter, it is almost impossible to carry out a meaningful test. It is useful here that CSTC recipes can also be used for the Scanner. + + + +First, we activate the use of the CSTC recipe for the Scanner. + + + +In the Intruder tab we now mark the parameter **(1)** to be tested and select the displayed menu item **(2)**. After selecting a suitable scan configuration, the scan can be started. + + + +Using the CSTC recipe for outgoing requests, the Burp Scanner was able to confirm the SQLi as the CSTC transforms all requests containing payloads dynamically by applying the defined recipe shown above. This shows that the good integration of the CSTC can also be chained with other Extensions or builtin functions of Burp Suite. \ No newline at end of file diff --git a/README.md b/README.md index 979042a..6ac6ed6 100644 --- a/README.md +++ b/README.md @@ -83,12 +83,6 @@ Take a look at our basic tutorial on [YouTube](https://www.youtube.com/watch?v=B **UPDATE:** Due to some incompatibility issues when installing *CSTC* via *BApp Store*, we had to switch to a new variable prefix. Variables from other *lanes* have now to be prefixed by ``$`` e.g. like ``$Outgoing_step1``. - -## Known Issues - -Unfortunately, the GUI of some *CSTC Operations* does not really work well together with the **dark theme** of *Burp Suite*. Therefore, -we recommend to use a **light theme** for the best user experience. - ## Feedback We gladly appreciate all feedback, bug reports and feature requests. diff --git a/media/introduction/fig01-overview.png b/media/introduction/fig01-overview.png new file mode 100755 index 0000000..4c0450a Binary files /dev/null and b/media/introduction/fig01-overview.png differ diff --git a/media/introduction/fig02-filter.png b/media/introduction/fig02-filter.png new file mode 100755 index 0000000..93c0680 Binary files /dev/null and b/media/introduction/fig02-filter.png differ diff --git a/media/introduction/fig03-different_recipes.png b/media/introduction/fig03-different_recipes.png new file mode 100755 index 0000000..373aee6 Binary files /dev/null and b/media/introduction/fig03-different_recipes.png differ diff --git a/media/introduction/fig04-send_to_incoming.png b/media/introduction/fig04-send_to_incoming.png new file mode 100755 index 0000000..4d1031e Binary files /dev/null and b/media/introduction/fig04-send_to_incoming.png differ diff --git a/media/introduction/fig05-example_1_response.png b/media/introduction/fig05-example_1_response.png new file mode 100755 index 0000000..28423dd Binary files /dev/null and b/media/introduction/fig05-example_1_response.png differ diff --git a/media/introduction/fig06-example_1_poc.png b/media/introduction/fig06-example_1_poc.png new file mode 100755 index 0000000..8416d29 Binary files /dev/null and b/media/introduction/fig06-example_1_poc.png differ diff --git a/media/introduction/fig07-example_2.png b/media/introduction/fig07-example_2.png new file mode 100755 index 0000000..93e8f9c Binary files /dev/null and b/media/introduction/fig07-example_2.png differ diff --git a/media/introduction/fig08-send_to_outgoing.png b/media/introduction/fig08-send_to_outgoing.png new file mode 100755 index 0000000..3902703 Binary files /dev/null and b/media/introduction/fig08-send_to_outgoing.png differ diff --git a/media/introduction/fig09-load_recipe.png b/media/introduction/fig09-load_recipe.png new file mode 100755 index 0000000..6ff940f Binary files /dev/null and b/media/introduction/fig09-load_recipe.png differ diff --git a/media/introduction/fig10-example_1_recipe.png b/media/introduction/fig10-example_1_recipe.png new file mode 100755 index 0000000..26f8c1a Binary files /dev/null and b/media/introduction/fig10-example_1_recipe.png differ diff --git a/media/introduction/fig11-example_2_filter.png b/media/introduction/fig11-example_2_filter.png new file mode 100755 index 0000000..0b2d20d Binary files /dev/null and b/media/introduction/fig11-example_2_filter.png differ diff --git a/media/introduction/fig12-example_2_poc.png b/media/introduction/fig12-example_2_poc.png new file mode 100755 index 0000000..b3b3d26 Binary files /dev/null and b/media/introduction/fig12-example_2_poc.png differ diff --git a/media/introduction/fig13-example_2_sqli.png b/media/introduction/fig13-example_2_sqli.png new file mode 100755 index 0000000..0654228 Binary files /dev/null and b/media/introduction/fig13-example_2_sqli.png differ diff --git a/media/introduction/fig14-scanner_filter.png b/media/introduction/fig14-scanner_filter.png new file mode 100755 index 0000000..1091471 Binary files /dev/null and b/media/introduction/fig14-scanner_filter.png differ diff --git a/media/introduction/fig15-intruder.png b/media/introduction/fig15-intruder.png new file mode 100755 index 0000000..5530e35 Binary files /dev/null and b/media/introduction/fig15-intruder.png differ diff --git a/media/introduction/fig16-scan_result.png b/media/introduction/fig16-scan_result.png new file mode 100755 index 0000000..dab9dfe Binary files /dev/null and b/media/introduction/fig16-scan_result.png differ diff --git a/pom.xml b/pom.xml index 89a61fd..5d3e429 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.usd.CSTC CSTC - 1.3.0 + 1.3.1 CSTC CSTC @@ -17,13 +17,13 @@ com.jayway.jsonpath json-path - 2.7.0 + 2.9.0 - net.portswigger.burp.extender - burp-extender-api - 2.3 + net.portswigger.burp.extensions + montoya-api + 2023.12.1 @@ -35,19 +35,25 @@ com.fasterxml.jackson.core jackson-core - 2.14.2 + 2.17.1 com.fasterxml.jackson.core jackson-databind - 2.14.2 + 2.17.1 org.apache.commons commons-text - 1.10.0 + 1.12.0 + + + + com.auth0 + java-jwt + 4.4.0 @@ -56,6 +62,12 @@ 4.13.2 test + + + org.javatuples + javatuples + 1.2 + @@ -80,17 +92,17 @@ maven-compiler-plugin - 3.11.0 + 3.13.0 - 1.8 - 1.8 + 17 + 17 org.apache.maven.plugins maven-surefire-plugin - 3.0.0 + 3.2.5 false true diff --git a/src/main/java/burp/BurpExtender.java b/src/main/java/burp/BurpExtender.java index 8262661..86c306f 100644 --- a/src/main/java/burp/BurpExtender.java +++ b/src/main/java/burp/BurpExtender.java @@ -1,114 +1,59 @@ package burp; -import java.awt.Component; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.List; +import com.fasterxml.jackson.databind.ObjectMapper; -import javax.swing.JMenuItem; - -import de.usd.cstchef.view.FormatTab; -import de.usd.cstchef.view.RecipePanel; +import burp.api.montoya.BurpExtension; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.BurpSuiteEdition; +import burp.api.montoya.persistence.PersistedObject; +import de.usd.cstchef.view.RequestFilterDialog; import de.usd.cstchef.view.View; +import de.usd.cstchef.view.filter.FilterState; +import de.usd.cstchef.view.filter.FilterState.BurpOperation; -public class BurpExtender implements IBurpExtender, ITab, IMessageEditorTabFactory, IHttpListener, IContextMenuFactory { +public class BurpExtender implements BurpExtension { private final String extensionName = "CSTC"; - private IBurpExtenderCallbacks callbacks; private View view; @Override - public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) { - this.callbacks = callbacks; - Logger.getInstance().init(callbacks.getStdout(), callbacks.getStderr()); - BurpUtils.getInstance().init(callbacks); - - callbacks.setExtensionName(this.extensionName); - callbacks.addSuiteTab(this); - callbacks.registerHttpListener(this); - callbacks.registerContextMenuFactory(this); - callbacks.registerMessageEditorTabFactory(this); - } - - - @Override - public String getTabCaption() { - return this.extensionName; - } - - @Override - public Component getUiComponent() { + public void initialize(MontoyaApi api) { + BurpUtils.getInstance().init(api); this.view = new View(); - return this.view; - } - - @Override - public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) { - if (messageIsRequest && view.getOutgoingRecipePanel().shouldProcess(toolFlag)) { - byte[] request = messageInfo.getRequest(); - byte[] modifiedRequest = view.getOutgoingRecipePanel().bake(request); - Logger.getInstance().log("modified request: \n" + new String(modifiedRequest)); - messageInfo.setRequest(modifiedRequest); - } else if (view.getIncomingRecipePanel().shouldProcess(toolFlag)) { - byte[] response = messageInfo.getResponse(); - byte[] modifiedResponse = view.getIncomingRecipePanel().bake(response); - messageInfo.setResponse(modifiedResponse); - Logger.getInstance().log("modified response: \n" + new String(modifiedResponse)); + BurpUtils.getInstance().setView(view); + api.extension().setName(extensionName); + api.userInterface().registerContextMenuItemsProvider(new CstcContextMenuItemsProvider(api, view)); + api.http().registerHttpHandler(new CstcHttpHandler(view)); + api.userInterface().registerSuiteTab(extensionName, view); + api.userInterface().registerHttpRequestEditorProvider(new MyHttpRequestEditorProvider(view)); + api.userInterface().registerHttpResponseEditorProvider(new MyHttpResponseEditorProvider(view)); + + if (!api.burpSuite().version().edition().equals(BurpSuiteEdition.COMMUNITY_EDITION)) { + PersistedObject persistence = api.persistence().extensionData(); + restoreFilterState(persistence); + restoreRecipe(persistence); } + view.updateInactiveWarnings(); } - @Override - public List createMenuItems(IContextMenuInvocation invoc) { - - List menuItems = new ArrayList<>(); - JMenuItem incomingMenu = new JMenuItem("Send to CSTC (Incoming)"); - JMenuItem outgoingMenu = new JMenuItem("Send to CSTC (Outgoing)"); - JMenuItem incomingFormatMenu = new JMenuItem("Send to CSTC (Formating)"); - - menuItems.add(incomingMenu); - menuItems.add(outgoingMenu); - menuItems.add(incomingFormatMenu); - - incomingMenu.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - IHttpRequestResponse[] msgs = invoc.getSelectedMessages(); - if (msgs != null && msgs.length > 0) { - view.getIncomingRecipePanel().setInput(msgs[0]); - } - } - }); - - outgoingMenu.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - IHttpRequestResponse[] msgs = invoc.getSelectedMessages(); - if (msgs != null && msgs.length > 0) { - view.getOutgoingRecipePanel().setInput(msgs[0]); - } - - } - }); - - incomingFormatMenu.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - IHttpRequestResponse[] msgs = invoc.getSelectedMessages(); - if (msgs != null && msgs.length > 0) { - view.getFormatRecipePanel().setInput(msgs[0]); - } - } - }); - - return menuItems; + private void restoreRecipe(PersistedObject persistence) { + try { + this.view.getFormatRecipePanel().restoreState(persistence.getString(BurpOperation.FORMAT + "Recipe")); + this.view.getIncomingRecipePanel().restoreState(persistence.getString(BurpOperation.INCOMING + "Recipe")); + this.view.getOutgoingRecipePanel().restoreState(persistence.getString(BurpOperation.OUTGOING + "Recipe")); + } catch (Exception e) { + Logger.getInstance().log( + "Could not restore the recipe for one or multiple panels. If this is the first time using CSTC in a project, you can ignore this message."); + } } - @Override - public IMessageEditorTab createNewInstance(IMessageEditorController controller, boolean editable) { - RecipePanel requestFormatPanel = this.view.getOutgoingRecipePanel(); - // TODO do we need the format panel or do we want to use the incoming recipe? - RecipePanel responseFormatPanel = this.view.getFormatRecipePanel(); - return new FormatTab(requestFormatPanel, responseFormatPanel, editable); + private void restoreFilterState(PersistedObject persistence) { + try { + BurpUtils.getInstance().setFilterState(new ObjectMapper().readValue(persistence.getString("FilterState"), FilterState.class)); + RequestFilterDialog.getInstance().updateFilterSettings(); + } catch (Exception e) { + Logger.getInstance().log( + "Could not restore the filter state. If this is the first time using CSTC in a project, you can ignore this message. " + e.getMessage()); + } } } diff --git a/src/main/java/burp/BurpObjectFactory.java b/src/main/java/burp/BurpObjectFactory.java new file mode 100644 index 0000000..0e58417 --- /dev/null +++ b/src/main/java/burp/BurpObjectFactory.java @@ -0,0 +1,44 @@ +package burp; + +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; + +public class BurpObjectFactory implements CstcObjectFactory{ + + @Override + public ByteArray createByteArray(String s) { + return ByteArray.byteArray(s); + } + + @Override + public ByteArray createByteArray(byte[] bytes) { + return ByteArray.byteArray(bytes); + } + + @Override + public ByteArray createByteArray(int i) { + return ByteArray.byteArray(i); + } + + @Override + public ByteArray getHttpRequestBody(ByteArray request) { + return HttpRequest.httpRequest(request).body(); + } + + @Override + public ByteArray getHttpResponseBody(ByteArray response) { + return HttpResponse.httpResponse(response).body(); + } + + @Override + public HttpRequest createHttpRequest(ByteArray request) { + return HttpRequest.httpRequest(request); + } + + @Override + public HttpResponse createHttpResponse(ByteArray response) { + return HttpResponse.httpResponse(response); + } + +} diff --git a/src/main/java/burp/BurpUtils.java b/src/main/java/burp/BurpUtils.java index 251d052..ce1cef6 100644 --- a/src/main/java/burp/BurpUtils.java +++ b/src/main/java/burp/BurpUtils.java @@ -1,9 +1,16 @@ package burp; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.view.View; +import de.usd.cstchef.view.filter.FilterState; + public class BurpUtils { private static BurpUtils instance; - private IBurpExtenderCallbacks callbacks; + private MontoyaApi api; + private View view; + private FilterState filterState; public static BurpUtils getInstance() { if (BurpUtils.instance == null) { @@ -15,24 +22,45 @@ public static BurpUtils getInstance() { private BurpUtils() { } - public void init(IBurpExtenderCallbacks callbacks) { - this.callbacks = callbacks; + public void init(MontoyaApi api) { + this.api = api; + this.filterState = new FilterState(); } - public IBurpExtenderCallbacks getCallbacks() throws IllegalAccessError { - if (this.callbacks == null) { + public MontoyaApi getApi() throws IllegalAccessError { + if (this.api == null) { throw new IllegalAccessError("Only works within burpsuite"); } - return callbacks; + return api; + } + + public void setView(View view){ + this.view = view; + } + + public View getView(){ + return view; + } + + public FilterState getFilterState(){ + return filterState; + } + + public void setFilterState(FilterState state){ + this.filterState = state; } public static boolean inBurp() { try { - BurpUtils.getInstance().getCallbacks(); + BurpUtils.getInstance().getApi(); return true; } catch (IllegalAccessError e) { return false; } } + public static ByteArray subArray(ByteArray array, int start, int end){ + return start == end ? ByteArray.byteArray(0) : array.subArray( start, end); + } + } diff --git a/src/main/java/burp/CstcContextMenuItemsProvider.java b/src/main/java/burp/CstcContextMenuItemsProvider.java new file mode 100644 index 0000000..5965093 --- /dev/null +++ b/src/main/java/burp/CstcContextMenuItemsProvider.java @@ -0,0 +1,73 @@ +package burp; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JMenuItem; + +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.HttpRequestResponse; +import burp.api.montoya.ui.contextmenu.ContextMenuEvent; +import burp.api.montoya.ui.contextmenu.ContextMenuItemsProvider; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.view.View; + +public class CstcContextMenuItemsProvider implements ContextMenuItemsProvider { + private MontoyaApi api; + private View view; + + public CstcContextMenuItemsProvider(MontoyaApi api, View view) + { + this.api = api; + this.view = view; + } + + @Override + public List provideMenuItems(ContextMenuEvent event) { + + List menuItems = new ArrayList<>(); + JMenuItem incomingMenu = new JMenuItem("Send to CSTC (Incoming)"); + JMenuItem outgoingMenu = new JMenuItem("Send to CSTC (Outgoing)"); + JMenuItem incomingReqFormatMenu = new JMenuItem("Send request to CSTC (Formatting)"); + JMenuItem incomingResFormatMenu = new JMenuItem("Send response to CSTC (Formatting)"); + + menuItems.add(outgoingMenu); + menuItems.add(incomingMenu); + menuItems.add(incomingReqFormatMenu); + menuItems.add(incomingResFormatMenu); + + incomingMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + view.getIncomingRecipePanel().setInput(event.messageEditorRequestResponse().isPresent() ? event.messageEditorRequestResponse().get().requestResponse() : event.selectedRequestResponses().get(0)); + } + }); + + outgoingMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + view.getOutgoingRecipePanel().setInput(event.messageEditorRequestResponse().isPresent() ? event.messageEditorRequestResponse().get().requestResponse() : event.selectedRequestResponses().get(0)); + } + }); + + incomingResFormatMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + view.getFormatRecipePanel().setFormatMessage(event.messageEditorRequestResponse().isPresent() ? event.messageEditorRequestResponse().get().requestResponse() : event.selectedRequestResponses().get(0), MessageType.RESPONSE); + } + }); + + incomingReqFormatMenu.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + view.getFormatRecipePanel().setFormatMessage(event.messageEditorRequestResponse().isPresent() ? event.messageEditorRequestResponse().get().requestResponse() : event.selectedRequestResponses().get(0), MessageType.REQUEST); + } + }); + + return menuItems; + } +} diff --git a/src/main/java/burp/CstcHttpHandler.java b/src/main/java/burp/CstcHttpHandler.java new file mode 100644 index 0000000..3b45924 --- /dev/null +++ b/src/main/java/burp/CstcHttpHandler.java @@ -0,0 +1,52 @@ +package burp; + +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.handler.HttpHandler; +import burp.api.montoya.http.handler.HttpRequestToBeSent; +import burp.api.montoya.http.handler.HttpResponseReceived; +import burp.api.montoya.http.handler.RequestToBeSentAction; +import burp.api.montoya.http.handler.ResponseReceivedAction; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.view.View; +import de.usd.cstchef.view.filter.FilterState; + +import static burp.api.montoya.http.handler.RequestToBeSentAction.continueWith; +import static burp.api.montoya.http.handler.ResponseReceivedAction.continueWith; + +public class CstcHttpHandler implements HttpHandler { + + private View view; + + CstcHttpHandler(View view) { + this.view = view; + } + + @Override + public RequestToBeSentAction handleHttpRequestToBeSent(HttpRequestToBeSent requestToBeSent) { + if (BurpUtils.getInstance().getFilterState().shouldProcess(FilterState.BurpOperation.OUTGOING, requestToBeSent.toolSource())) { + ByteArray request = requestToBeSent.toByteArray(); + ByteArray modifiedRequest = view.getOutgoingRecipePanel().bake(request, MessageType.REQUEST); + Logger.getInstance().log("modified request: \n" + new String(modifiedRequest.getBytes())); + return continueWith(HttpRequest.httpRequest(modifiedRequest).withService(requestToBeSent.httpService())); + } + else{ + return continueWith(requestToBeSent); + } + } + + @Override + public ResponseReceivedAction handleHttpResponseReceived(HttpResponseReceived responseReceived) { + if (BurpUtils.getInstance().getFilterState().shouldProcess(FilterState.BurpOperation.INCOMING, responseReceived.toolSource())) { + ByteArray response = responseReceived.toByteArray(); + ByteArray modifiedResponse = view.getIncomingRecipePanel().bake(response, MessageType.RESPONSE); + Logger.getInstance().log("modified response: \n" + new String(modifiedResponse.getBytes())); + return continueWith(HttpResponse.httpResponse(modifiedResponse)); + } + else{ + return continueWith(responseReceived); + } + } + +} diff --git a/src/main/java/burp/CstcMessageEditorController.java b/src/main/java/burp/CstcMessageEditorController.java index 79aecd0..2f89d00 100644 --- a/src/main/java/burp/CstcMessageEditorController.java +++ b/src/main/java/burp/CstcMessageEditorController.java @@ -1,37 +1,71 @@ package burp; -public class CstcMessageEditorController implements IMessageEditorController { +import java.util.Optional; - private IHttpService httpService = null; - private byte[] request = null; - private byte[] response = null; +import burp.api.montoya.core.Range; +import burp.api.montoya.http.HttpService; +import burp.api.montoya.http.message.HttpRequestResponse; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.api.montoya.ui.contextmenu.MessageEditorHttpRequestResponse; - public void setHttpRequestResponse(IHttpRequestResponse requestResponse) { - this.httpService = requestResponse.getHttpService(); - this.request = requestResponse.getRequest(); - this.response = requestResponse.getResponse(); +public class CstcMessageEditorController implements MessageEditorHttpRequestResponse{ + + private HttpService httpService = null; + private HttpRequest request = null; + private HttpResponse response = null; + private HttpRequestResponse requestResponse = null; + + public void setHttpRequestResponse(HttpRequestResponse requestResponse) { + this.httpService = requestResponse.httpService(); + this.request = requestResponse.request(); + this.response = requestResponse.response(); } - public void setRequest(byte[] request) { + public void setRequest(HttpRequest request) { this.request = request; } - public void setResponse(byte[] response) { - this.request = response; + public void setResponse(HttpResponse response) { + this.response = response; } + // @Override + // public HttpService getHttpService() { + // return httpService; + // } + + // @Override + // public HttpRequest getRequest() { + // return request; + // } + + // @Override + // public HttpResponse getResponse() { + // return response; + // } + @Override - public IHttpService getHttpService() { - return httpService; + public SelectionContext selectionContext() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'selectionContext'"); } @Override - public byte[] getRequest() { - return request; + public Optional selectionOffsets() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'selectionOffsets'"); } @Override - public byte[] getResponse() { - return response; + public int caretPosition() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'caretPosition'"); } + + @Override + public HttpRequestResponse requestResponse() { + return HttpRequestResponse.httpRequestResponse(request, response); + } + } diff --git a/src/main/java/burp/CstcObjectFactory.java b/src/main/java/burp/CstcObjectFactory.java new file mode 100644 index 0000000..a03d998 --- /dev/null +++ b/src/main/java/burp/CstcObjectFactory.java @@ -0,0 +1,15 @@ +package burp; + +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; + +public interface CstcObjectFactory { + public ByteArray createByteArray(String s); + public ByteArray createByteArray(int i); + public ByteArray createByteArray(byte[] bytes); + public ByteArray getHttpRequestBody(ByteArray request); + public ByteArray getHttpResponseBody(ByteArray response); + public HttpRequest createHttpRequest(ByteArray request); + public HttpResponse createHttpResponse(ByteArray response); +} diff --git a/src/main/java/burp/Logger.java b/src/main/java/burp/Logger.java index c3a73dd..8e1448f 100644 --- a/src/main/java/burp/Logger.java +++ b/src/main/java/burp/Logger.java @@ -1,14 +1,11 @@ package burp; -import java.io.OutputStream; -import java.io.PrintWriter; +import burp.api.montoya.logging.Logging; public class Logger { private static Logger instance; - - private PrintWriter stdout; - private PrintWriter stderr; + private Logging logging; public static Logger getInstance() { if (Logger.instance == null) { @@ -18,27 +15,26 @@ public static Logger getInstance() { } private Logger() { - + init(); } - public void init(OutputStream stdOut, OutputStream stdErr) { - this.stdout = new PrintWriter(stdOut, true); - this.stderr = new PrintWriter(stdErr, true); + public void init() { + logging = BurpUtils.getInstance().getApi().logging(); } public void log(String msg) { - if (this.stdout == null) { + if (this.logging == null) { System.out.println(msg); } else { - this.stdout.println(msg); + logging.logToOutput(msg); } } public void err(String msg) { - if (this.stderr == null) { + if (this.logging == null) { System.err.println(msg); } else { - this.stderr.println(msg); + logging.logToError(msg); } } } diff --git a/src/main/java/burp/MyExtensionProvidedHttpRequestEditor.java b/src/main/java/burp/MyExtensionProvidedHttpRequestEditor.java new file mode 100644 index 0000000..94191e2 --- /dev/null +++ b/src/main/java/burp/MyExtensionProvidedHttpRequestEditor.java @@ -0,0 +1,73 @@ +package burp; + +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.HttpRequestResponse; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.ui.Selection; +import burp.api.montoya.ui.editor.EditorOptions; +import burp.api.montoya.ui.editor.RawEditor; +import burp.api.montoya.ui.editor.extension.EditorCreationContext; +import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.view.View; + +import java.awt.*; + +public class MyExtensionProvidedHttpRequestEditor implements ExtensionProvidedHttpRequestEditor +{ + private final RawEditor requestEditor; + private HttpRequestResponse requestResponse; + private final MontoyaApi api; + private final View view; + + MyExtensionProvidedHttpRequestEditor(EditorCreationContext creationContext, View view) + { + this.api = BurpUtils.getInstance().getApi(); + this.view = view; + requestEditor = api.userInterface().createRawEditor(EditorOptions.READ_ONLY); + } + + @Override + public HttpRequest getRequest() + { + return requestResponse.request(); + } + + @Override + public void setRequestResponse(HttpRequestResponse requestResponse) + { + ByteArray result = view.getOutgoingRecipePanel().bake(requestResponse.request().toByteArray(), MessageType.REQUEST); + this.requestEditor.setContents(result); + } + + @Override + public boolean isEnabledFor(HttpRequestResponse requestResponse) + { + return requestResponse.request() != null; + } + + @Override + public String caption() + { + return "CSTC"; + } + + @Override + public Component uiComponent() + { + return requestEditor.uiComponent(); + } + + @Override + public Selection selectedData() + { + return requestEditor.selection().isPresent() ? requestEditor.selection().get() : null; + } + + @Override + public boolean isModified() + { + return requestEditor.isModified(); + } +} \ No newline at end of file diff --git a/src/main/java/burp/MyExtensionProvidedHttpResponseEditor.java b/src/main/java/burp/MyExtensionProvidedHttpResponseEditor.java new file mode 100644 index 0000000..2ccb508 --- /dev/null +++ b/src/main/java/burp/MyExtensionProvidedHttpResponseEditor.java @@ -0,0 +1,73 @@ +package burp; + +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.HttpRequestResponse; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.api.montoya.ui.Selection; +import burp.api.montoya.ui.editor.EditorOptions; +import burp.api.montoya.ui.editor.RawEditor; +import burp.api.montoya.ui.editor.extension.EditorCreationContext; +import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpResponseEditor; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.view.View; + +import java.awt.*; + +public class MyExtensionProvidedHttpResponseEditor implements ExtensionProvidedHttpResponseEditor +{ + private final RawEditor requestEditor; + private HttpRequestResponse requestResponse; + private final MontoyaApi api; + private final View view; + + MyExtensionProvidedHttpResponseEditor(EditorCreationContext creationContext, View view) + { + this.api = BurpUtils.getInstance().getApi(); + this.view = view; + requestEditor = api.userInterface().createRawEditor(EditorOptions.READ_ONLY); + } + + @Override + public HttpResponse getResponse() + { + return requestResponse.response(); + } + + @Override + public void setRequestResponse(HttpRequestResponse requestResponse) + { + ByteArray result = view.getIncomingRecipePanel().bake(requestResponse.response().toByteArray(), MessageType.RESPONSE); + this.requestEditor.setContents(result); + } + + @Override + public boolean isEnabledFor(HttpRequestResponse requestResponse) + { + return requestResponse.response() != null; + } + + @Override + public String caption() + { + return "CSTC"; + } + + @Override + public Component uiComponent() + { + return requestEditor.uiComponent(); + } + + @Override + public Selection selectedData() + { + return requestEditor.selection().isPresent() ? requestEditor.selection().get() : null; + } + + @Override + public boolean isModified() + { + return requestEditor.isModified(); + } +} \ No newline at end of file diff --git a/src/main/java/burp/MyHttpRequestEditorProvider.java b/src/main/java/burp/MyHttpRequestEditorProvider.java new file mode 100644 index 0000000..94b3253 --- /dev/null +++ b/src/main/java/burp/MyHttpRequestEditorProvider.java @@ -0,0 +1,22 @@ +package burp; + +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.ui.editor.extension.EditorCreationContext; +import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor; +import burp.api.montoya.ui.editor.extension.HttpRequestEditorProvider; +import de.usd.cstchef.view.View; + +class MyHttpRequestEditorProvider implements HttpRequestEditorProvider +{ + private final View view; + + MyHttpRequestEditorProvider(View view){ + this.view = view; + } + + @Override + public ExtensionProvidedHttpRequestEditor provideHttpRequestEditor(EditorCreationContext creationContext) + { + return new MyExtensionProvidedHttpRequestEditor(creationContext, view); + } +} \ No newline at end of file diff --git a/src/main/java/burp/MyHttpResponseEditorProvider.java b/src/main/java/burp/MyHttpResponseEditorProvider.java new file mode 100644 index 0000000..cc8a9c7 --- /dev/null +++ b/src/main/java/burp/MyHttpResponseEditorProvider.java @@ -0,0 +1,23 @@ +package burp; + +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.ui.editor.extension.EditorCreationContext; +import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor; +import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpResponseEditor; +import burp.api.montoya.ui.editor.extension.HttpResponseEditorProvider; +import de.usd.cstchef.view.View; + +class MyHttpResponseEditorProvider implements HttpResponseEditorProvider +{ + private final View view; + + MyHttpResponseEditorProvider(View view){ + this.view = view; + } + + + @Override + public ExtensionProvidedHttpResponseEditor provideHttpResponseEditor(EditorCreationContext creationContext) { + return new MyExtensionProvidedHttpResponseEditor(creationContext, view); + } +} \ No newline at end of file diff --git a/src/main/java/burp/objects/CstcByteArray.java b/src/main/java/burp/objects/CstcByteArray.java new file mode 100644 index 0000000..6795a31 --- /dev/null +++ b/src/main/java/burp/objects/CstcByteArray.java @@ -0,0 +1,235 @@ +package burp.objects; + +import java.util.Iterator; +import java.util.regex.Pattern; + +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.core.Range; + +public class CstcByteArray implements ByteArray{ + + byte[] bytes; + + CstcByteArray(byte[] bytes){ + this.bytes = bytes; + } + + public static ByteArray byteArray(String s){ + return new CstcByteArray(s.getBytes()); + } + + public static ByteArray byteArray(byte[] bytes){ + return new CstcByteArray(bytes); + } + + public static ByteArray byteArray(){ + return new CstcByteArray(new byte[0]); + } + + public static ByteArray byteArray(int i){ + return new CstcByteArray(new byte[i]); + } + + public String toString(){ + return new String(bytes); + } + + @Override + public byte[] getBytes() { + return bytes; + } + + @Override + public Iterator iterator() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'iterator'"); + } + + @Override + public byte getByte(int index) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getByte'"); + } + + @Override + public void setByte(int index, byte value) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setByte'"); + } + + @Override + public void setByte(int index, int value) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setByte'"); + } + + @Override + public void setBytes(int index, byte... data) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBytes'"); + } + + @Override + public void setBytes(int index, int... data) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBytes'"); + } + + @Override + public void setBytes(int index, ByteArray byteArray) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBytes'"); + } + + @Override + public int length() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'length'"); + } + + @Override + public ByteArray subArray(int startIndexInclusive, int endIndexExclusive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'subArray'"); + } + + @Override + public ByteArray subArray(Range range) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'subArray'"); + } + + @Override + public ByteArray copy() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'copy'"); + } + + @Override + public ByteArray copyToTempFile() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'copyToTempFile'"); + } + + @Override + public int indexOf(ByteArray searchTerm) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'indexOf'"); + } + + @Override + public int indexOf(String searchTerm) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'indexOf'"); + } + + @Override + public int indexOf(ByteArray searchTerm, boolean caseSensitive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'indexOf'"); + } + + @Override + public int indexOf(String searchTerm, boolean caseSensitive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'indexOf'"); + } + + @Override + public int indexOf(ByteArray searchTerm, boolean caseSensitive, int startIndexInclusive, int endIndexExclusive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'indexOf'"); + } + + @Override + public int indexOf(String searchTerm, boolean caseSensitive, int startIndexInclusive, int endIndexExclusive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'indexOf'"); + } + + @Override + public int countMatches(ByteArray searchTerm) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'countMatches'"); + } + + @Override + public int countMatches(String searchTerm) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'countMatches'"); + } + + @Override + public int countMatches(ByteArray searchTerm, boolean caseSensitive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'countMatches'"); + } + + @Override + public int countMatches(String searchTerm, boolean caseSensitive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'countMatches'"); + } + + @Override + public int countMatches(ByteArray searchTerm, boolean caseSensitive, int startIndexInclusive, + int endIndexExclusive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'countMatches'"); + } + + @Override + public int countMatches(String searchTerm, boolean caseSensitive, int startIndexInclusive, int endIndexExclusive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'countMatches'"); + } + + @Override + public ByteArray withAppended(byte... data) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withAppended'"); + } + + @Override + public ByteArray withAppended(int... data) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withAppended'"); + } + + @Override + public ByteArray withAppended(String text) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withAppended'"); + } + + @Override + public ByteArray withAppended(ByteArray byteArray) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withAppended'"); + } + + @Override + public int indexOf(Pattern pattern) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'indexOf'"); + } + + @Override + public int indexOf(Pattern pattern, int startIndexInclusive, int endIndexExclusive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'indexOf'"); + } + + @Override + public int countMatches(Pattern pattern) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'countMatches'"); + } + + @Override + public int countMatches(Pattern pattern, int startIndexInclusive, int endIndexExclusive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'countMatches'"); + } + +} diff --git a/src/main/java/burp/objects/CstcCookie.java b/src/main/java/burp/objects/CstcCookie.java new file mode 100644 index 0000000..e994937 --- /dev/null +++ b/src/main/java/burp/objects/CstcCookie.java @@ -0,0 +1,46 @@ +package burp.objects; + +import java.time.ZonedDateTime; +import java.util.Optional; + +import burp.api.montoya.http.message.Cookie; + +public class CstcCookie implements Cookie{ + + private String name; + private String value; + + public CstcCookie(String name, String value) { + this.name = name; + this.value = value; + } + + @Override + public String name() { + return this.name; + } + + @Override + public String value() { + return this.value; + } + + @Override + public String domain() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'domain'"); + } + + @Override + public String path() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'path'"); + } + + @Override + public Optional expiration() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'expiration'"); + } + +} diff --git a/src/main/java/burp/objects/CstcHttpRequest.java b/src/main/java/burp/objects/CstcHttpRequest.java new file mode 100644 index 0000000..f5b2011 --- /dev/null +++ b/src/main/java/burp/objects/CstcHttpRequest.java @@ -0,0 +1,449 @@ +package burp.objects; + +import java.util.List; +import java.util.regex.Pattern; + +import org.bouncycastle.util.Arrays; + +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.core.Marker; +import burp.api.montoya.http.HttpService; +import burp.api.montoya.http.message.ContentType; +import burp.api.montoya.http.message.HttpHeader; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.params.ParsedHttpParameter; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.requests.HttpTransformation; +import de.usd.cstchef.operations.extractors.JsonExtractor; + +public class CstcHttpRequest implements HttpRequest { + + ByteArray httpRequest; + + public CstcHttpRequest(ByteArray httpRequest) { + this.httpRequest = httpRequest; + } + + @Override + public ByteArray body() { + int index = 0; + byte[] request = this.httpRequest.getBytes(); + for(int i = 0; i < request.length - 1; i++) { + if(request[i] == (byte)'\n' && request[i+1] == (byte)'\n') { + index = i + 2; + } + } + + byte[] body = new byte[request.length - index]; + for(int i = index; i < request.length; i++) { + body[i - index] = request[i]; + } + + return CstcByteArray.byteArray(body); + } + + @Override + public String headerValue(String name) { + byte[] requestBytes = this.httpRequest.getBytes(); + String request = new String(requestBytes); + + String[] requestLines = request.split("\n"); + for(String line : requestLines) { + String[] header = line.split(": "); + if(header[0].equals(name)) { + return header[1]; + } + } + + throw new IllegalArgumentException("Parameter name not found."); + } + + @Override + public String parameterValue(String name, HttpParameterType type) { + byte[] requestBytes = this.httpRequest.getBytes(); + String request = new String(requestBytes); + switch(type) { + case URL: + String query = request.split("\n")[0].split("\\?")[1].split("\\s")[0]; + + for(String param : query.split("&")) { + String[] keyValue = param.split("="); + if(keyValue[0].equals(name)) { + return keyValue[1]; + } + } + + throw new IllegalArgumentException("Parameter name not found."); + case BODY: + // get Content-Type + String contentType = headerValue("Content-Type").split(";|\n")[0]; + + // multipart + if(contentType.equals("multipart/form-data")) { + String boundary = headerValue("Content-Type").split("boundary=")[1]; + String[] multipart = request.split(boundary + "\n\n")[1].split(boundary); + for(int i = 1; i < multipart.length; i++) { + String parameterName = multipart[i].split("name=\"")[1].split("\"")[0]; // throws ArrayIndexOutOfBoundsException in case param is not found + if(parameterName.equals(name)) { + String output = multipart[i].split("\n\n")[1]; + if(output.endsWith("\n")) { + return output.trim(); + } + else{ + return output + "\n"; + } + } + } + + throw new IllegalArgumentException("Input is not a valid request"); + } + + // application/x-www-form-urlencoded + else if(contentType.equals("application/x-www-form-urlencoded")) { + String postBody = request.split("\n\n")[1].trim(); + String[] params = postBody.split("&"); + for(String s : params) { + String[] keyValue = s.split("="); + if(keyValue[0].equals(name)) { + return keyValue[1]; + } + } + + throw new IllegalArgumentException("Input is not a vlaid request"); + } + case XML: + String postBody = request.split("\n\n")[1].trim(); + String fieldValue = postBody.split("<" + name + ">")[1].split("<")[0]; + return fieldValue; + + default: + return null; + } + } + + @Override + public String method() { + byte[] requestBytes = this.httpRequest.getBytes(); + String request = new String(requestBytes); + + String method = request.split("\\s")[0]; + return method; + } + + @Override + public String url() { + byte[] requestBytes = this.httpRequest.getBytes(); + String request = new String(requestBytes); + + String url = request.split("\\s")[1]; + return url; + } + + @Override + public ParsedHttpParameter parameter(String name, HttpParameterType type) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'parameter'"); + } + + @Override + public boolean isInScope() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'isInScope'"); + } + + @Override + public HttpService httpService() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'httpService'"); + } + + @Override + public String path() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'path'"); + } + + @Override + public String query() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'query'"); + } + + @Override + public String pathWithoutQuery() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'pathWithoutQuery'"); + } + + @Override + public String fileExtension() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'fileExtension'"); + } + + @Override + public ContentType contentType() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'contentType'"); + } + + @Override + public List parameters() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'parameters'"); + } + + @Override + public List parameters(HttpParameterType type) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'parameters'"); + } + + @Override + public boolean hasParameters() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasParameters'"); + } + + @Override + public boolean hasParameters(HttpParameterType type) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasParameters'"); + } + + @Override + public boolean hasParameter(String name, HttpParameterType type) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasParameter'"); + } + + @Override + public boolean hasParameter(HttpParameter parameter) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasParameter'"); + } + + @Override + public boolean hasHeader(HttpHeader header) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasHeader'"); + } + + @Override + public boolean hasHeader(String name) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasHeader'"); + } + + @Override + public boolean hasHeader(String name, String value) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasHeader'"); + } + + @Override + public HttpHeader header(String name) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'header'"); + } + + @Override + public List headers() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'headers'"); + } + + @Override + public String httpVersion() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'httpVersion'"); + } + + @Override + public int bodyOffset() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'bodyOffset'"); + } + + @Override + public String bodyToString() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'bodyToString'"); + } + + @Override + public List markers() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'markers'"); + } + + @Override + public boolean contains(String searchTerm, boolean caseSensitive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'contains'"); + } + + @Override + public boolean contains(Pattern pattern) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'contains'"); + } + + @Override + public ByteArray toByteArray() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'toByteArray'"); + } + + @Override + public HttpRequest copyToTempFile() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'copyToTempFile'"); + } + + @Override + public HttpRequest withService(HttpService service) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withService'"); + } + + @Override + public HttpRequest withPath(String path) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withPath'"); + } + + @Override + public HttpRequest withMethod(String method) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withMethod'"); + } + + @Override + public HttpRequest withHeader(HttpHeader header) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withHeader'"); + } + + @Override + public HttpRequest withHeader(String name, String value) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withHeader'"); + } + + @Override + public HttpRequest withParameter(HttpParameter parameters) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withParameter'"); + } + + @Override + public HttpRequest withAddedParameters(List parameters) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withAddedParameters'"); + } + + @Override + public HttpRequest withAddedParameters(HttpParameter... parameters) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withAddedParameters'"); + } + + @Override + public HttpRequest withRemovedParameters(List parameters) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withRemovedParameters'"); + } + + @Override + public HttpRequest withRemovedParameters(HttpParameter... parameters) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withRemovedParameters'"); + } + + @Override + public HttpRequest withUpdatedParameters(List parameters) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withUpdatedParameters'"); + } + + @Override + public HttpRequest withUpdatedParameters(HttpParameter... parameters) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withUpdatedParameters'"); + } + + @Override + public HttpRequest withTransformationApplied(HttpTransformation transformation) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withTransformationApplied'"); + } + + @Override + public HttpRequest withBody(String body) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withBody'"); + } + + @Override + public HttpRequest withBody(ByteArray body) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withBody'"); + } + + @Override + public HttpRequest withAddedHeader(String name, String value) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withAddedHeader'"); + } + + @Override + public HttpRequest withAddedHeader(HttpHeader header) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withAddedHeader'"); + } + + @Override + public HttpRequest withUpdatedHeader(String name, String value) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withUpdatedHeader'"); + } + + @Override + public HttpRequest withUpdatedHeader(HttpHeader header) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withUpdatedHeader'"); + } + + @Override + public HttpRequest withRemovedHeader(String name) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withRemovedHeader'"); + } + + @Override + public HttpRequest withRemovedHeader(HttpHeader header) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withRemovedHeader'"); + } + + @Override + public HttpRequest withMarkers(List markers) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withMarkers'"); + } + + @Override + public HttpRequest withMarkers(Marker... markers) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withMarkers'"); + } + + @Override + public HttpRequest withDefaultHeaders() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withDefaultHeaders'"); + } + +} diff --git a/src/main/java/burp/objects/CstcHttpResponse.java b/src/main/java/burp/objects/CstcHttpResponse.java new file mode 100644 index 0000000..6333fe3 --- /dev/null +++ b/src/main/java/burp/objects/CstcHttpResponse.java @@ -0,0 +1,315 @@ +package burp.objects; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.core.Marker; +import burp.api.montoya.http.message.Cookie; +import burp.api.montoya.http.message.HttpHeader; +import burp.api.montoya.http.message.MimeType; +import burp.api.montoya.http.message.StatusCodeClass; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.api.montoya.http.message.responses.analysis.Attribute; +import burp.api.montoya.http.message.responses.analysis.AttributeType; +import burp.api.montoya.http.message.responses.analysis.KeywordCount; + +public class CstcHttpResponse implements HttpResponse { + + ByteArray httpResponse; + + public CstcHttpResponse(ByteArray httpResponse) { + this.httpResponse = httpResponse; + } + + @Override + public ByteArray body() { + int index = 0; + byte[] response = this.httpResponse.getBytes(); + for(int i = 0; i < response.length - 1; i++) { + if(response[i] == (byte)'\n' && response[i+1] == (byte)'\n') { + index = i + 2; + } + } + + byte[] body = new byte[response.length - index]; + for(int i = index; i < response.length; i++) { + body[i - index] = response[i]; + } + + return CstcByteArray.byteArray(body); + } + + @Override + public List cookies() { + List cookieList = new ArrayList<>(); + String cookies = new String(); + + byte[] responseBytes = this.httpResponse.getBytes(); + String response = new String(responseBytes); + + String[] responseLines = response.split("\n"); + for(String line : responseLines) { + String[] header = line.split(": "); + if(header[0].equals("Set-Cookie")) { + cookies = header[1]; + } + } + + for(String cookie : cookies.split("; ")) { + String[] c = cookie.split("="); + Cookie cc = new CstcCookie(c[0], c[1]); + cookieList.add(cc); + } + + return cookieList; + } + + @Override + public String headerValue(String name) { + byte[] responseBytes = this.httpResponse.getBytes(); + String response = new String(responseBytes); + + String[] responseLines = response.split("\n"); + for(String line : responseLines) { + String[] header = line.split(": "); + if(header[0].equals(name)) { + return header[1]; + } + } + + throw new IllegalArgumentException("Parameter name not found."); + } + + @Override + public String bodyToString() { + byte[] responseBytes = this.httpResponse.getBytes(); + String response = new String(responseBytes); + + return response.split("\n\n")[1]; + } + + @Override + public short statusCode() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'statusCode'"); + } + + @Override + public String reasonPhrase() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'reasonPhrase'"); + } + + @Override + public boolean isStatusCodeClass(StatusCodeClass statusCodeClass) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'isStatusCodeClass'"); + } + + @Override + public Cookie cookie(String name) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'cookie'"); + } + + @Override + public String cookieValue(String name) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'cookieValue'"); + } + + @Override + public boolean hasCookie(String name) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasCookie'"); + } + + @Override + public boolean hasCookie(Cookie cookie) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasCookie'"); + } + + @Override + public MimeType mimeType() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'mimeType'"); + } + + @Override + public MimeType statedMimeType() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'statedMimeType'"); + } + + @Override + public MimeType inferredMimeType() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'inferredMimeType'"); + } + + @Override + public List keywordCounts(String... keywords) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'keywordCounts'"); + } + + @Override + public List attributes(AttributeType... types) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'attributes'"); + } + + @Override + public boolean hasHeader(HttpHeader header) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasHeader'"); + } + + @Override + public boolean hasHeader(String name) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasHeader'"); + } + + @Override + public boolean hasHeader(String name, String value) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'hasHeader'"); + } + + @Override + public HttpHeader header(String name) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'header'"); + } + + @Override + public List headers() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'headers'"); + } + + @Override + public String httpVersion() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'httpVersion'"); + } + + @Override + public int bodyOffset() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'bodyOffset'"); + } + + @Override + public List markers() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'markers'"); + } + + @Override + public boolean contains(String searchTerm, boolean caseSensitive) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'contains'"); + } + + @Override + public boolean contains(Pattern pattern) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'contains'"); + } + + @Override + public ByteArray toByteArray() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'toByteArray'"); + } + + @Override + public HttpResponse copyToTempFile() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'copyToTempFile'"); + } + + @Override + public HttpResponse withStatusCode(short statusCode) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withStatusCode'"); + } + + @Override + public HttpResponse withReasonPhrase(String reasonPhrase) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withReasonPhrase'"); + } + + @Override + public HttpResponse withHttpVersion(String httpVersion) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withHttpVersion'"); + } + + @Override + public HttpResponse withBody(String body) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withBody'"); + } + + @Override + public HttpResponse withBody(ByteArray body) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withBody'"); + } + + @Override + public HttpResponse withAddedHeader(HttpHeader header) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withAddedHeader'"); + } + + @Override + public HttpResponse withAddedHeader(String name, String value) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withAddedHeader'"); + } + + @Override + public HttpResponse withUpdatedHeader(HttpHeader header) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withUpdatedHeader'"); + } + + @Override + public HttpResponse withUpdatedHeader(String name, String value) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withUpdatedHeader'"); + } + + @Override + public HttpResponse withRemovedHeader(HttpHeader header) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withRemovedHeader'"); + } + + @Override + public HttpResponse withRemovedHeader(String name) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withRemovedHeader'"); + } + + @Override + public HttpResponse withMarkers(List markers) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withMarkers'"); + } + + @Override + public HttpResponse withMarkers(Marker... markers) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'withMarkers'"); + } + +} diff --git a/src/main/java/de/usd/cstchef/Utils.java b/src/main/java/de/usd/cstchef/Utils.java index 0e7b4f8..1e99477 100644 --- a/src/main/java/de/usd/cstchef/Utils.java +++ b/src/main/java/de/usd/cstchef/Utils.java @@ -1,22 +1,41 @@ package de.usd.cstchef; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.net.URISyntaxException; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; +import java.util.Base64; import java.util.HashMap; import java.util.List; import java.util.Map.Entry; +import java.util.Optional; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; + import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.BurpObjectFactory; +import burp.CstcObjectFactory; import burp.Logger; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.handler.ResponseAction; +import burp.api.montoya.http.message.Cookie; +import burp.api.montoya.http.message.HttpHeader; +import burp.api.montoya.http.message.requests.HttpRequest; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.arithmetic.Addition; import de.usd.cstchef.operations.arithmetic.Divide; @@ -48,6 +67,8 @@ import de.usd.cstchef.operations.dataformat.UrlDecode; import de.usd.cstchef.operations.dataformat.UrlEncode; import de.usd.cstchef.operations.datetime.DateTime; +import de.usd.cstchef.operations.datetime.TimestampOffset; +import de.usd.cstchef.operations.datetime.TimestampToDateTime; import de.usd.cstchef.operations.datetime.UnixTimestamp; import de.usd.cstchef.operations.encryption.AesDecryption; import de.usd.cstchef.operations.encryption.AesEncryption; @@ -55,12 +76,15 @@ import de.usd.cstchef.operations.encryption.DesEncryption; import de.usd.cstchef.operations.encryption.RsaDecryption; import de.usd.cstchef.operations.encryption.RsaEncryption; +import de.usd.cstchef.operations.encryption.SM4Decryption; +import de.usd.cstchef.operations.encryption.SM4Encryption; import de.usd.cstchef.operations.extractors.HttpBodyExtractor; import de.usd.cstchef.operations.extractors.HttpCookieExtractor; import de.usd.cstchef.operations.extractors.HttpGetExtractor; import de.usd.cstchef.operations.extractors.HttpHeaderExtractor; import de.usd.cstchef.operations.extractors.HttpJsonExtractor; import de.usd.cstchef.operations.extractors.HttpMethodExtractor; +import de.usd.cstchef.operations.extractors.HttpMultipartExtractor; import de.usd.cstchef.operations.extractors.HttpPostExtractor; import de.usd.cstchef.operations.extractors.HttpUriExtractor; import de.usd.cstchef.operations.extractors.HttpXmlExtractor; @@ -78,15 +102,19 @@ import de.usd.cstchef.operations.hashing.SHA1; import de.usd.cstchef.operations.hashing.SHA2; import de.usd.cstchef.operations.hashing.SHA3; +import de.usd.cstchef.operations.hashing.SM3; import de.usd.cstchef.operations.hashing.Skein; import de.usd.cstchef.operations.hashing.Tiger; +import de.usd.cstchef.operations.hashing.Luhn; import de.usd.cstchef.operations.hashing.Whirlpool; +import de.usd.cstchef.operations.misc.GetRequestBuilder; import de.usd.cstchef.operations.misc.ReadFile; import de.usd.cstchef.operations.misc.WriteFile; -import de.usd.cstchef.operations.networking.HTTPRequest; +import de.usd.cstchef.operations.networking.PlainRequest; import de.usd.cstchef.operations.setter.HttpGetSetter; import de.usd.cstchef.operations.setter.HttpHeaderSetter; import de.usd.cstchef.operations.setter.HttpJsonSetter; +import de.usd.cstchef.operations.setter.HttpMultipartSetter; import de.usd.cstchef.operations.setter.HttpPostSetter; import de.usd.cstchef.operations.setter.HttpSetBody; import de.usd.cstchef.operations.setter.HttpSetCookie; @@ -94,7 +122,10 @@ import de.usd.cstchef.operations.setter.HttpXmlSetter; import de.usd.cstchef.operations.setter.JsonSetter; import de.usd.cstchef.operations.setter.LineSetter; +import de.usd.cstchef.operations.signature.JWTDecode; +import de.usd.cstchef.operations.signature.JWTSign; import de.usd.cstchef.operations.signature.RsaSignature; +import de.usd.cstchef.operations.signature.SM2Signature; import de.usd.cstchef.operations.signature.SoapMultiSignature; import de.usd.cstchef.operations.signature.XmlFullSignature; import de.usd.cstchef.operations.signature.XmlMultiSignature; @@ -108,6 +139,8 @@ import de.usd.cstchef.operations.string.Suffix; import de.usd.cstchef.operations.string.Uppercase; import de.usd.cstchef.operations.string.Lowercase; +import de.usd.cstchef.operations.string.Concatenate; +import de.usd.cstchef.operations.utils.Counter; import de.usd.cstchef.operations.utils.GetVariable; import de.usd.cstchef.operations.utils.NoOperation; import de.usd.cstchef.operations.utils.RandomNumber; @@ -118,54 +151,105 @@ public class Utils { + public static CstcObjectFactory factory = new BurpObjectFactory(); + public static double parseNumber(String in) { // TODO hex values?? return Double.valueOf(in); } public static String replaceVariables(String text) { - HashMap variables = VariableStore.getInstance().getVariables(); - for (Entry entry : variables.entrySet()) { + HashMap variables = VariableStore.getInstance().getVariables(); + for (Entry entry : variables.entrySet()) { // TODO this is easy, but very bad, how to do this right? - text = text.replace("$" + entry.getKey(), new String(entry.getValue())); + text = text.replace("$" + entry.getKey(), entry.getValue().toString()); } return text; } - public static byte[] replaceVariablesByte(byte[] bytes) { - HashMap variables = VariableStore.getInstance().getVariables(); - - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); + public static ByteArray replaceVariablesByte(ByteArray bytes) { + HashMap variables = VariableStore.getInstance().getVariables(); + MontoyaApi api = BurpUtils.getInstance().getApi(); - byte[] currentKey; - for (Entry entry : variables.entrySet()) { + ByteArray currentKey; + for (Entry entry : variables.entrySet()) { int offset = 0; - currentKey = ("$" + entry.getKey()).getBytes(); + currentKey = ByteArray.byteArray("$" + entry.getKey()); - while( offset >= 0 ) { - offset = helpers.indexOf(bytes, currentKey, true, offset, bytes.length); - if( offset >= 0 ) - bytes = insertAtOffset(bytes, offset, offset + currentKey.length, entry.getValue()); + while (offset >= 0) { + offset = api.utilities().byteUtils().indexOf(bytes.getBytes(), currentKey.getBytes(), true, offset, + bytes.length()); + if (offset >= 0) + bytes = insertAtOffset(bytes, offset, offset + currentKey.length(), entry.getValue()); } } return bytes; } - public static byte[] insertAtOffset(byte[] input, int start, int end, byte[] newValue) { - byte[] prefix = Arrays.copyOfRange(input, 0, start); - byte[] rest = Arrays.copyOfRange(input, end, input.length); + public static ByteArray httpRequestCookieExtractor(HttpRequest request, String cookieName){ + String cookies = request.headerValue("Cookie"); + //return ByteArray.byteArray(cookieExtractor(cookies, cookieName)); + return factory.createByteArray(cookieExtractor(cookies, cookieName)); + } - byte[] output = new byte[prefix.length + newValue.length + rest.length]; - System.arraycopy(prefix, 0, output, 0, prefix.length); - System.arraycopy(newValue, 0, output, prefix.length, newValue.length); - System.arraycopy(rest, 0, output, prefix.length + newValue.length, rest.length); + private static String cookieExtractor(String cookies, String cookieName) { + String[] splitCookies = cookies.split("\\s*;\\s*"); + for(String sC : splitCookies){ + String[] separateCookie = sC.split("="); + if(separateCookie[0].equals(cookieName)){ + return separateCookie[1]; + } + } + throw new IllegalArgumentException("Parameter name not found."); + } + public static HttpRequest addCookieToHttpRequest(HttpRequest request, Cookie cookie){ + String cookies = request.headerValue("Cookie"); + if(cookies.contains(cookie.name())){ + cookies = cookies.replace(cookie.name() + "=" + cookieExtractor(cookies, cookie.name()), cookie.toString()); + } + else{ + cookies += "; " + cookie.toString(); + } + return request.withUpdatedHeader(HttpHeader.httpHeader("Cookie", cookies)); + } + + public static ByteArray insertAtOffset(ByteArray input, int start, int end, ByteArray newValue) { + ByteArray prefix = BurpUtils.subArray(input, 0, start); + ByteArray rest = BurpUtils.subArray(input, end, input.length()); + + ByteArray output = prefix.withAppended(newValue).withAppended(rest); return output; } + public static ByteArray jsonSetter(ByteArray input, String key, String value, boolean addIfNotPresent, String path){ + DocumentContext document = JsonPath.parse(input.toString()); + + try { + document.read(key); + } catch (Exception e) { + + if (!addIfNotPresent) + throw new IllegalArgumentException("Key not found."); + + String insertPath = path; + if (insertPath.equals("Insert-Path") || insertPath.equals("")) + insertPath = "$"; + + try { + document = document.put(insertPath, key, value); + return ByteArray.byteArray(document.jsonString()); + } catch (Exception ex) { + throw new IllegalArgumentException("Could not parse JSON from input"); + } + } + + document.set(key, value); + return ByteArray.byteArray(document.jsonString()); + } + public static Class[] getOperationsBurp() { ZipInputStream zip = null; List> operations = new ArrayList>(); @@ -208,27 +292,33 @@ public static Class[] getOperationsBurp() { @SuppressWarnings("unchecked") public static Class[] getOperationsDev() { return new Class[] { - Addition.class, AddKey.class, AesDecryption.class, AesEncryption.class, And.class, - Blake.class, DateTime.class, Deflate.class, DesDecryption.class, DesEncryption.class, - Divide.class, DivideList.class, DSTU7564.class, FromBase64.class, FromHex.class, - GetVariable.class, Gost.class, GUnzip.class, Gzip.class, Hmac.class, - HttpBodyExtractor.class, HttpCookieExtractor.class, HttpGetExtractor.class, - HttpGetSetter.class, HttpHeaderExtractor.class, HttpHeaderSetter.class, - HttpJsonExtractor.class, HttpJsonSetter.class, HttpMethodExtractor.class, - HttpPostExtractor.class, HttpPostSetter.class, HTTPRequest.class, HttpSetBody.class, - HttpSetCookie.class, HttpSetUri.class, HttpUriExtractor.class, HttpXmlExtractor.class, - HttpXmlSetter.class, HtmlEncode.class, HtmlDecode.class, Inflate.class, - JsonExtractor.class, JsonSetter.class, Length.class, LineExtractor.class, - LineSetter.class, MD2.class, MD4.class, MD5.class, Mean.class, Median.class, - Multiply.class, MultiplyList.class, NoOperation.class, NumberCompare.class, Prefix.class, - RandomNumber.class, RandomUUID.class ,ReadFile.class, RegexExtractor.class, Reverse.class, Replace.class, - RIPEMD.class, RsaDecryption.class, RsaEncryption.class, RsaSignature.class, RegexMatch.class, - SetIfEmpty.class, SHA1.class, SHA2.class, SHA3.class, Skein.class, SplitAndSelect.class, - StaticString.class, StoreVariable.class, Sub.class, Substring.class, Uppercase.class, Lowercase.class, Subtraction.class, - Suffix.class, Sum.class, StringContains.class, StringMatch.class, Tiger.class, - ToBase64.class, ToHex.class, UnixTimestamp.class, UrlDecode.class, UrlEncode.class, - Whirlpool.class, WriteFile.class, XmlFullSignature.class, XmlMultiSignature.class, - Xor.class, SoapMultiSignature.class + Addition.class, AddKey.class, AesDecryption.class, AesEncryption.class, And.class, + Blake.class, Counter.class, DateTime.class, Deflate.class, DesDecryption.class, DesEncryption.class, + Divide.class, DivideList.class, DSTU7564.class, FromBase64.class, FromHex.class, + GetRequestBuilder.class, + GetVariable.class, Gost.class, GUnzip.class, Gzip.class, Hmac.class, + HttpBodyExtractor.class, HttpCookieExtractor.class, HttpGetExtractor.class, + HttpGetSetter.class, HttpHeaderExtractor.class, HttpHeaderSetter.class, + HttpJsonExtractor.class, HttpJsonSetter.class, HttpMethodExtractor.class, HttpMultipartExtractor.class, + HttpMultipartSetter.class, + HttpPostExtractor.class, HttpPostSetter.class, PlainRequest.class, HttpSetBody.class, + HttpSetCookie.class, HttpSetUri.class, HttpUriExtractor.class, HttpXmlExtractor.class, + HttpXmlSetter.class, HtmlEncode.class, HtmlDecode.class, Inflate.class, + JsonExtractor.class, JsonSetter.class, JWTDecode.class, JWTSign.class, Length.class, + LineExtractor.class, + LineSetter.class, MD2.class, MD4.class, MD5.class, Mean.class, Median.class, + Multiply.class, MultiplyList.class, NoOperation.class, NumberCompare.class, Prefix.class, + RandomNumber.class, RandomUUID.class, ReadFile.class, RegexExtractor.class, Reverse.class, + Replace.class, + RIPEMD.class, RsaDecryption.class, RsaEncryption.class, RsaSignature.class, SM2Signature.class, SM3.class, SM4Encryption.class, SM4Decryption.class, RegexMatch.class, + SetIfEmpty.class, SHA1.class, SHA2.class, SHA3.class, Skein.class, SplitAndSelect.class, + StaticString.class, StoreVariable.class, Sub.class, Substring.class, Uppercase.class, Lowercase.class, + Subtraction.class, + Suffix.class, Sum.class, StringContains.class, StringMatch.class, Tiger.class, + TimestampOffset.class, TimestampToDateTime.class, ToBase64.class, ToHex.class, UnixTimestamp.class, + UrlDecode.class, UrlEncode.class, + Whirlpool.class, WriteFile.class, XmlFullSignature.class, XmlMultiSignature.class, + Xor.class, SoapMultiSignature.class, Luhn.class, Concatenate.class }; } @@ -236,4 +326,52 @@ public static Class[] getOperations() { return BurpUtils.inBurp() ? Utils.getOperationsDev() : Utils.getOperationsDev(); } + public enum MessageType { + REQUEST, + RESPONSE, + RAW + } + + public static class CSTCCookie implements Cookie{ + private String name; + private String value; + + public CSTCCookie(String cookieName, String cookieValue){ + this.name = cookieName; + this.value = cookieValue; + } + + @Override + public String name() { + return name; + } + + @Override + public String value() { + return value; + } + + @Override + public String domain() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'domain'"); + } + + @Override + public String path() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'path'"); + } + + @Override + public Optional expiration() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'expiration'"); + } + + public String toString(){ + return name() + "=" + value(); + } + } + } diff --git a/src/main/java/de/usd/cstchef/VariableStore.java b/src/main/java/de/usd/cstchef/VariableStore.java index a182599..64550fe 100644 --- a/src/main/java/de/usd/cstchef/VariableStore.java +++ b/src/main/java/de/usd/cstchef/VariableStore.java @@ -4,11 +4,14 @@ import java.util.Map; import java.util.concurrent.locks.ReentrantLock; +import burp.BurpUtils; +import burp.api.montoya.core.ByteArray; + public class VariableStore { private static VariableStore instance; - private HashMap variables = new HashMap<>(); + private HashMap variables = new HashMap<>(); private ReentrantLock lock = new ReentrantLock(); public static VariableStore getInstance() { @@ -29,11 +32,11 @@ public void unlock() { this.lock.unlock(); } - public synchronized byte[] getVariable(String name) { + public synchronized ByteArray getVariable(String name) { return this.variables.get(name); } - public synchronized void setVariable(String key, byte[] value) { + public synchronized void setVariable(String key, ByteArray value) { this.variables.put(key, value); } @@ -41,13 +44,12 @@ public synchronized void removeVariable(String key) { this.variables.remove(key); } - public synchronized HashMap getVariables() { - HashMap variablesCopy = new HashMap<>(); + public synchronized HashMap getVariables() { + HashMap variablesCopy = new HashMap<>(); - for (Map.Entry entry : this.variables.entrySet()) { - byte[] orig = entry.getValue(); - byte[] newContent = new byte[orig.length]; - System.arraycopy(orig, 0, newContent, 0, orig.length); + for (Map.Entry entry : this.variables.entrySet()) { + ByteArray orig = entry.getValue(); + ByteArray newContent = BurpUtils.subArray(orig, 0, orig.length()); variablesCopy.put(entry.getKey(), newContent); } return variablesCopy; diff --git a/src/main/java/de/usd/cstchef/operations/Operation.java b/src/main/java/de/usd/cstchef/operations/Operation.java index 3e38acd..fceb094 100644 --- a/src/main/java/de/usd/cstchef/operations/Operation.java +++ b/src/main/java/de/usd/cstchef/operations/Operation.java @@ -16,6 +16,8 @@ import java.lang.annotation.Target; import java.util.HashMap; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.Box; @@ -39,7 +41,13 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; +import burp.BurpObjectFactory; +import burp.CstcObjectFactory; import burp.Logger; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.view.ui.FormatTextField; import de.usd.cstchef.view.ui.VariableTextArea; import de.usd.cstchef.view.ui.VariableTextField; @@ -75,9 +83,15 @@ public abstract class Operation extends JPanel { private int operationSkip = 0; private int laneSkip = 0; + private final String httpRequestRegex = "(GET|POST|HEAD|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH)\\s/\\S*\\sHTTP/\\d(\\.\\d)?"; + private final String httpResponseRegex = "HTTP/\\d(\\.\\d)?\\s\\d{3}\\s(\\w*\\s?)*"; + + public CstcObjectFactory factory; + public Operation() { super(); this.uiElements = new HashMap<>(); + this.factory = new BurpObjectFactory(); this.setLayout(new BorderLayout()); this.setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); @@ -178,7 +192,7 @@ public void actionPerformed(ActionEvent e) { public Map getState() { Map properties = new HashMap<>(); for (String key : this.uiElements.keySet()) { - if( key.startsWith("noupdate") ) + if (key.startsWith("noupdate")) properties.put(key, null); else properties.put(key, getUiValues(this.uiElements.get(key))); @@ -187,6 +201,12 @@ public Map getState() { return properties; } + public T checkNull(T input) throws Exception{ + if(input == null) + throw new IllegalArgumentException("Parameter name not found."); + return input; + } + private Object getUiValues(Component comp) { Object result = null; if (comp instanceof JPasswordField) { @@ -201,7 +221,7 @@ private Object getUiValues(Component comp) { result = ((JSpinner) comp).getValue(); } else if (comp instanceof JComboBox) { result = ((JComboBox) comp).getSelectedItem(); - if( result != null ) + if (result != null) result = result.toString(); } else if (comp instanceof JCheckBox) { result = ((JCheckBox) comp).isSelected(); @@ -239,7 +259,7 @@ private void setUiValue(Component comp, Object value) { } else if (comp instanceof FormatTextField) { ((FormatTextField) comp).setValues((Map) value); } else if (comp instanceof JFileChooser) { - ((JFileChooser) comp).setName((String)value); + ((JFileChooser) comp).setName((String) value); } } @@ -297,7 +317,7 @@ protected void addUIElement(String caption, Component comp, String identifier) { this.addUIElement(caption, comp, true, identifier); } - protected void addUIElement(String caption, Component comp, boolean notifyChange, String identifier) { + protected void addUIElement(String caption, Component comp, boolean notifyChange, String identifier) { comp.setCursor(Cursor.getDefaultCursor()); Box box = Box.createHorizontalBox(); @@ -311,7 +331,7 @@ protected void addUIElement(String caption, Component comp, boolean notifyChange box.add(comp); this.contentBox.add(box); this.contentBox.add(Box.createVerticalStrut(10)); - if( identifier == null ) + if (identifier == null) identifier = caption; this.uiElements.put(identifier, comp); @@ -336,6 +356,7 @@ protected void addUIElement(String caption, Component comp, boolean notifyChange Logger.getInstance().err("could not add a default change listener for " + comp.getClass()); } } + refreshColors(); } @Override @@ -346,17 +367,17 @@ public Dimension getPreferredSize() { return dim; } - public byte[] performOperation(byte[] input) { + public ByteArray performOperation(ByteArray input, MessageType messageType) { try { - byte[] result = this.perform(input); + ByteArray result = this.perform(input, messageType); this.setErrorMessage(null); return result; } catch (EOFException e) { this.setErrorMessage(new EOFException("End of file")); - return new byte[0]; + return factory.createByteArray(0); } catch (Throwable e) { this.setErrorMessage(e); - return new byte[0]; + return factory.createByteArray(0); } } @@ -407,10 +428,10 @@ public boolean isDisabled() { public void setDisabled(boolean disabled) { this.disabled = disabled; - refreshColors(); - validate(); - repaint(); - notifyChange(); + refreshColors(); + validate(); + repaint(); + notifyChange(); } public boolean isError() { @@ -422,7 +443,7 @@ public void setError(boolean error) { } public void setOperationSkip(int count) { - if( count < 0 ) + if (count < 0) count = 0; this.operationSkip = count; } @@ -432,7 +453,7 @@ public int getOperationSkip() { } public void setLaneSkip(int count) { - if( count < 0 ) + if (count < 0) count = 0; this.laneSkip = count; } @@ -456,7 +477,7 @@ public String description() public OperationCategory category() default OperationCategory.MISC; } - protected abstract byte[] perform(byte[] input) throws Exception; + protected abstract ByteArray perform(ByteArray input, MessageType messageType) throws Exception; public void createUI() { @@ -465,6 +486,26 @@ public void createUI() { public void onRemove() { } + + public MessageType parseMessageType(ByteArray input) throws Exception{ + final Pattern requestPattern = Pattern.compile(httpRequestRegex); + final Matcher requestMatcher = requestPattern.matcher(input.toString().split("\n")[0].trim()); + if (requestMatcher.matches()) { + return MessageType.REQUEST; + } + + final Pattern responsePattern = Pattern.compile(httpResponseRegex); + final Matcher responseMatcher = responsePattern.matcher(input.toString().split("\n")[0].trim()); + if (responseMatcher.matches()) { + return MessageType.RESPONSE; + } + + throw new IllegalArgumentException("Input is not a valid HTTP message"); + } + + public ByteArray parseRawMessage(ByteArray input) throws Exception{ + return perform(input, parseMessageType(input)); + } private class NotifyChangeListener implements DocumentListener, ActionListener, ChangeListener { diff --git a/src/main/java/de/usd/cstchef/operations/arithmetic/ArithmeticDelimiterOperation.java b/src/main/java/de/usd/cstchef/operations/arithmetic/ArithmeticDelimiterOperation.java index 9050058..3a330ce 100644 --- a/src/main/java/de/usd/cstchef/operations/arithmetic/ArithmeticDelimiterOperation.java +++ b/src/main/java/de/usd/cstchef/operations/arithmetic/ArithmeticDelimiterOperation.java @@ -3,8 +3,10 @@ import javax.swing.JCheckBox; import javax.swing.JComboBox; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.Delimiter; import de.usd.cstchef.Utils; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; public abstract class ArithmeticDelimiterOperation extends Operation @@ -29,10 +31,10 @@ protected boolean isFloat() } @Override - protected byte[] perform(byte[] input) throws Exception + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String delimiter = getDelimiter().getValue(); - String[] lines = new String(input).split(delimiter); + String[] lines = new String(input.getBytes()).split(delimiter); if (lines.length < 2) return input; @@ -49,9 +51,9 @@ protected byte[] perform(byte[] input) throws Exception result = onFinish(result, numbers); if( !isFloat() ) - return String.valueOf(Math.round(result)).getBytes(); + return factory.createByteArray(String.valueOf(Math.round(result))); - return String.valueOf(result).getBytes(); + return factory.createByteArray(String.valueOf(result)); } protected double onFinish(double result, double[] lines) diff --git a/src/main/java/de/usd/cstchef/operations/arithmetic/ArithmeticOperation.java b/src/main/java/de/usd/cstchef/operations/arithmetic/ArithmeticOperation.java index c17bf06..5ad90ab 100644 --- a/src/main/java/de/usd/cstchef/operations/arithmetic/ArithmeticOperation.java +++ b/src/main/java/de/usd/cstchef/operations/arithmetic/ArithmeticOperation.java @@ -3,6 +3,8 @@ import javax.swing.JCheckBox; import javax.swing.JTextField; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; public abstract class ArithmeticOperation extends Operation @@ -21,11 +23,11 @@ protected boolean isFloat() } @Override - protected byte[] perform(byte[] input) throws Exception + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { try { - String i = new String(input); + String i = new String(input.getBytes()); if (i.isEmpty()) i = "0"; @@ -35,9 +37,9 @@ protected byte[] perform(byte[] input) throws Exception Double result_number = calculate(input_number, static_number); if ( isFloat() ) - return String.valueOf(result_number).getBytes(); + return factory.createByteArray(String.valueOf(result_number)); - return String.valueOf(Math.round(result_number)).getBytes(); + return factory.createByteArray(String.valueOf(Math.round(result_number))); } diff --git a/src/main/java/de/usd/cstchef/operations/byteoperation/ByteKeyOperation.java b/src/main/java/de/usd/cstchef/operations/byteoperation/ByteKeyOperation.java index 54026fc..da081cc 100644 --- a/src/main/java/de/usd/cstchef/operations/byteoperation/ByteKeyOperation.java +++ b/src/main/java/de/usd/cstchef/operations/byteoperation/ByteKeyOperation.java @@ -1,5 +1,7 @@ package de.usd.cstchef.operations.byteoperation; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.view.ui.FormatTextField; @@ -13,20 +15,20 @@ public void createUI() { } @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { - byte[] result = new byte[input.length]; - byte[] key = this.inputTxt.getText(); + ByteArray result = factory.createByteArray(input.length()); + ByteArray key = this.inputTxt.getText(); - if (key.length == 0) { + if (key.length() == 0) { return input; } int keyCounter = 0; - for (int i = 0; i < input.length; i++) { - byte var = key[keyCounter]; - keyCounter = (keyCounter + 1) % key.length; - result[i] = calculate(input[i], var); + for (int i = 0; i < input.length(); i++) { + byte var = key.getByte(keyCounter); + keyCounter = (keyCounter + 1) % key.length(); + result.setByte(i, calculate(input.getByte(i), var)); } return result; diff --git a/src/main/java/de/usd/cstchef/operations/compression/Deflate.java b/src/main/java/de/usd/cstchef/operations/compression/Deflate.java index a98b7a9..6e7cd17 100644 --- a/src/main/java/de/usd/cstchef/operations/compression/Deflate.java +++ b/src/main/java/de/usd/cstchef/operations/compression/Deflate.java @@ -1,8 +1,12 @@ package de.usd.cstchef.operations.compression; import java.util.zip.Deflater; + +import burp.api.montoya.core.ByteArray; + import java.io.ByteArrayOutputStream; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -12,11 +16,11 @@ public class Deflate extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { Deflater deflater = new Deflater(); - deflater.setInput(input); + deflater.setInput(input.getBytes()); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(input.length); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(input.length()); deflater.finish(); byte[] buffer = new byte[1024]; @@ -26,6 +30,6 @@ protected byte[] perform(byte[] input) throws Exception { } outputStream.close(); - return outputStream.toByteArray(); + return factory.createByteArray(outputStream.toByteArray()); } } diff --git a/src/main/java/de/usd/cstchef/operations/compression/GUnzip.java b/src/main/java/de/usd/cstchef/operations/compression/GUnzip.java index 8216951..4010144 100644 --- a/src/main/java/de/usd/cstchef/operations/compression/GUnzip.java +++ b/src/main/java/de/usd/cstchef/operations/compression/GUnzip.java @@ -1,9 +1,13 @@ package de.usd.cstchef.operations.compression; import java.util.zip.GZIPInputStream; + +import burp.api.montoya.core.ByteArray; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -12,8 +16,8 @@ public class GUnzip extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { - ByteArrayInputStream in = new ByteArrayInputStream(input); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + ByteArrayInputStream in = new ByteArrayInputStream(input.getBytes()); ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPInputStream gis = new GZIPInputStream(in); @@ -26,7 +30,7 @@ protected byte[] perform(byte[] input) throws Exception { gis.close(); out.close(); in.close(); - return out.toByteArray(); + return factory.createByteArray(out.toByteArray()); } } diff --git a/src/main/java/de/usd/cstchef/operations/compression/Gzip.java b/src/main/java/de/usd/cstchef/operations/compression/Gzip.java index 656ef6d..649b4a6 100644 --- a/src/main/java/de/usd/cstchef/operations/compression/Gzip.java +++ b/src/main/java/de/usd/cstchef/operations/compression/Gzip.java @@ -1,9 +1,13 @@ package de.usd.cstchef.operations.compression; import java.util.zip.GZIPOutputStream; + +import burp.api.montoya.core.ByteArray; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -12,10 +16,10 @@ public class Gzip extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); GZIPOutputStream gzos = new GZIPOutputStream(out); - ByteArrayInputStream in = new ByteArrayInputStream(input); + ByteArrayInputStream in = new ByteArrayInputStream(input.getBytes()); byte[] buffer = new byte[1024]; int len; @@ -26,7 +30,7 @@ protected byte[] perform(byte[] input) throws Exception { in.close(); gzos.close(); out.close(); - return out.toByteArray(); + return factory.createByteArray(out.toByteArray()); } } diff --git a/src/main/java/de/usd/cstchef/operations/compression/Inflate.java b/src/main/java/de/usd/cstchef/operations/compression/Inflate.java index 93668cb..0487b96 100644 --- a/src/main/java/de/usd/cstchef/operations/compression/Inflate.java +++ b/src/main/java/de/usd/cstchef/operations/compression/Inflate.java @@ -1,8 +1,12 @@ package de.usd.cstchef.operations.compression; import java.util.zip.Inflater; + +import burp.api.montoya.core.ByteArray; + import java.io.ByteArrayOutputStream; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -12,11 +16,11 @@ public class Inflate extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { Inflater inflater = new Inflater(); - inflater.setInput(input); + inflater.setInput(input.getBytes()); - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(input.length); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(input.length()); byte[] buffer = new byte[1024]; while( !inflater.finished() ) { @@ -25,6 +29,6 @@ protected byte[] perform(byte[] input) throws Exception { } outputStream.close(); - return outputStream.toByteArray(); + return factory.createByteArray(outputStream.toByteArray()); } } diff --git a/src/main/java/de/usd/cstchef/operations/conditional/ConditionalOperation.java b/src/main/java/de/usd/cstchef/operations/conditional/ConditionalOperation.java index 3a9acd7..4a688c0 100644 --- a/src/main/java/de/usd/cstchef/operations/conditional/ConditionalOperation.java +++ b/src/main/java/de/usd/cstchef/operations/conditional/ConditionalOperation.java @@ -42,10 +42,10 @@ public void createUI() { this.addUIElement("Expr", this.expr); this.operationSkipField = new JTextField("0"); - this.addUIElement("Skip Operations", this.operationSkipField); + this.addUIElement("Skip # Operations in Current Lane", this.operationSkipField); this.laneSkipField = new JTextField("0"); - this.addUIElement("Skip Lanes", this.laneSkipField); + this.addUIElement("Skip # Following Lanes", this.laneSkipField); } diff --git a/src/main/java/de/usd/cstchef/operations/conditional/NumberCompare.java b/src/main/java/de/usd/cstchef/operations/conditional/NumberCompare.java index 6358b9e..dea2060 100644 --- a/src/main/java/de/usd/cstchef/operations/conditional/NumberCompare.java +++ b/src/main/java/de/usd/cstchef/operations/conditional/NumberCompare.java @@ -2,6 +2,8 @@ import javax.swing.JComboBox; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -11,13 +13,13 @@ public class NumberCompare extends ConditionalOperation { private JComboBox operationBox; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { Double inputNumber; Double userNumber; try { - String tmp = new String(input); + String tmp = input.toString(); inputNumber = Double.valueOf(tmp); userNumber = Double.valueOf(this.expr.getText()); } catch( Exception e ) { diff --git a/src/main/java/de/usd/cstchef/operations/conditional/RegexMatch.java b/src/main/java/de/usd/cstchef/operations/conditional/RegexMatch.java index fb9b9a8..e26a00d 100644 --- a/src/main/java/de/usd/cstchef/operations/conditional/RegexMatch.java +++ b/src/main/java/de/usd/cstchef/operations/conditional/RegexMatch.java @@ -5,6 +5,8 @@ import javax.swing.JCheckBox; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -15,10 +17,10 @@ public class RegexMatch extends ConditionalOperation { private JCheckBox find; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { Pattern p = Pattern.compile(this.expr.getText()); - Matcher m = p.matcher(new String(input)); + Matcher m = p.matcher(input.toString()); boolean condition = false; if( find.isSelected() ) { diff --git a/src/main/java/de/usd/cstchef/operations/conditional/StringContains.java b/src/main/java/de/usd/cstchef/operations/conditional/StringContains.java index ec118d4..1746b76 100644 --- a/src/main/java/de/usd/cstchef/operations/conditional/StringContains.java +++ b/src/main/java/de/usd/cstchef/operations/conditional/StringContains.java @@ -2,9 +2,11 @@ import javax.swing.JCheckBox; +import burp.BurpExtender; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -15,12 +17,11 @@ public class StringContains extends ConditionalOperation { private JCheckBox caseSensitive; @Override - protected byte[] perform(byte[] input) throws Exception { - - IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = cbs.getHelpers(); - int start = helpers.indexOf(input, this.expr.getBytes(), caseSensitive.isSelected(), 0, input.length); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + MontoyaApi api = BurpUtils.getInstance().getApi(); + int start = api.utilities().byteUtils().indexOf(input.getBytes(), this.expr.getBytes().getBytes(), caseSensitive.isSelected(), 0, input.length()); + api.utilities().byteUtils().indexOf(input.getBytes(), this.expr.getBytes().getBytes(), caseSensitive.isSelected(), 0, input.length()); if( (start >= 0) ^ invert.isSelected() ) { this.setOperationSkip(); this.setLaneSkip(); diff --git a/src/main/java/de/usd/cstchef/operations/conditional/StringMatch.java b/src/main/java/de/usd/cstchef/operations/conditional/StringMatch.java index cd749a5..f911252 100644 --- a/src/main/java/de/usd/cstchef/operations/conditional/StringMatch.java +++ b/src/main/java/de/usd/cstchef/operations/conditional/StringMatch.java @@ -2,9 +2,11 @@ import javax.swing.JCheckBox; +import burp.BurpExtender; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -15,10 +17,10 @@ public class StringMatch extends ConditionalOperation { private JCheckBox caseSensitive; @Override - protected byte[] perform(byte[] input) throws Exception { - - byte[] search = this.expr.getBytes(); - if( search.length != input.length ) { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + + ByteArray search = this.expr.getBytes(); + if( search.length() != input.length() ) { if( invert.isSelected() ) { this.setOperationSkip(); this.setLaneSkip(); @@ -28,9 +30,8 @@ protected byte[] perform(byte[] input) throws Exception { return input; } - IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = cbs.getHelpers(); - int start = helpers.indexOf(input, search, caseSensitive.isSelected(), 0, input.length); + MontoyaApi api = BurpUtils.getInstance().getApi(); + int start = api.utilities().byteUtils().indexOf(input.getBytes(), search.getBytes(), caseSensitive.isSelected(), 0, input.length()); if( (start >= 0) ^ invert.isSelected() ) { this.setOperationSkip(); diff --git a/src/main/java/de/usd/cstchef/operations/dataformat/FromBase64.java b/src/main/java/de/usd/cstchef/operations/dataformat/FromBase64.java index 7caa4b0..03d0cb6 100644 --- a/src/main/java/de/usd/cstchef/operations/dataformat/FromBase64.java +++ b/src/main/java/de/usd/cstchef/operations/dataformat/FromBase64.java @@ -1,16 +1,48 @@ package de.usd.cstchef.operations.dataformat; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.Base64; +import javax.swing.JCheckBox; + +import burp.BurpUtils; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.utilities.Base64DecodingOptions; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @OperationInfos(name = "From Base64", category = OperationCategory.DATAFORMAT, description = "Decode a base64 string.") -public class FromBase64 extends Operation { +public class FromBase64 extends Operation implements ActionListener { + private boolean urlSafe=false; + private JCheckBox urlSafeCheckBox; + @Override - protected byte[] perform(byte[] input) { - return Base64.getDecoder().decode(input); + protected ByteArray perform(ByteArray input, MessageType messageType) { + MontoyaApi api = BurpUtils.getInstance().getApi(); + if(!this.urlSafe) { + return api.utilities().base64Utils().decode(input); + } + else { + return api.utilities().base64Utils().decode(input, Base64DecodingOptions.URL); + } + } + + public void createUI() { + this.urlSafeCheckBox = new JCheckBox(); + urlSafeCheckBox.setText("URL Safe"); + urlSafeCheckBox.setToolTipText("The Base64 input string is using the URL safe character set where / and + are replaced with _ and -"); + urlSafeCheckBox.setSelected(this.urlSafe); + urlSafeCheckBox.addActionListener(this); + this.addUIElement("", urlSafeCheckBox); } + + @Override + public void actionPerformed(ActionEvent e) { + this.urlSafe = this.urlSafeCheckBox.isSelected(); + } } diff --git a/src/main/java/de/usd/cstchef/operations/dataformat/FromHex.java b/src/main/java/de/usd/cstchef/operations/dataformat/FromHex.java index 7122eb9..24f43df 100644 --- a/src/main/java/de/usd/cstchef/operations/dataformat/FromHex.java +++ b/src/main/java/de/usd/cstchef/operations/dataformat/FromHex.java @@ -5,6 +5,10 @@ import javax.swing.JComboBox; import org.bouncycastle.util.encoders.Hex; +import burp.BurpUtils; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.dataformat.ToHex.Delimiter; @@ -16,19 +20,19 @@ public class FromHex extends Operation { private JComboBox delimiterBox; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String selectedKey = (String) this.delimiterBox.getSelectedItem(); Delimiter delimiter = ToHex.delimiters.get(selectedKey); if (delimiter.value.length == 0) { // No delimiter - return Hex.decode(input); + return factory.createByteArray(Hex.decode(input.getBytes())); } String delimiterStr = new String(delimiter.value); - String inputStr = new String(input); + String inputStr = input.toString(); inputStr = inputStr.replace(delimiterStr, ""); - return Hex.decode(inputStr); + return factory.createByteArray(Hex.decode(inputStr.getBytes())); } @Override diff --git a/src/main/java/de/usd/cstchef/operations/dataformat/HtmlDecode.java b/src/main/java/de/usd/cstchef/operations/dataformat/HtmlDecode.java index 6d6c445..9422acf 100644 --- a/src/main/java/de/usd/cstchef/operations/dataformat/HtmlDecode.java +++ b/src/main/java/de/usd/cstchef/operations/dataformat/HtmlDecode.java @@ -4,6 +4,8 @@ import org.apache.commons.text.StringEscapeUtils; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -12,11 +14,8 @@ public class HtmlDecode extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { - - String tmp = new String(input, StandardCharsets.ISO_8859_1); - tmp = StringEscapeUtils.unescapeHtml4(tmp); - return tmp.getBytes(StandardCharsets.ISO_8859_1); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + return factory.createByteArray(StringEscapeUtils.unescapeHtml4(input.toString())); } } diff --git a/src/main/java/de/usd/cstchef/operations/dataformat/HtmlEncode.java b/src/main/java/de/usd/cstchef/operations/dataformat/HtmlEncode.java index b73e076..d46f83d 100644 --- a/src/main/java/de/usd/cstchef/operations/dataformat/HtmlEncode.java +++ b/src/main/java/de/usd/cstchef/operations/dataformat/HtmlEncode.java @@ -6,6 +6,8 @@ import org.apache.commons.text.StringEscapeUtils; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -16,30 +18,30 @@ public class HtmlEncode extends Operation { private JCheckBox checkbox; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { - byte[] result = null; + ByteArray result = null; if( checkbox.isSelected() ) { - byte[] delimiter = "&#".getBytes(); - byte[] closer = ";".getBytes(); + ByteArray delimiter = factory.createByteArray("&#"); + ByteArray closer = factory.createByteArray(";"); ByteArrayOutputStream out = new ByteArrayOutputStream(); - out.write(delimiter); - for (int i = 0; i < input.length - 1; i++) { - out.write(String.valueOf(Byte.toUnsignedInt(input[i])).getBytes()); - out.write(closer); - out.write(delimiter); + out.write(delimiter.getBytes()); + for (int i = 0; i < input.length() - 1; i++) { + out.write(String.valueOf(Byte.toUnsignedInt(input.getByte(i))).getBytes()); + out.write(closer.getBytes()); + out.write(delimiter.getBytes()); } - out.write(String.valueOf(Byte.toUnsignedInt(input[input.length - 1])).getBytes()); - out.write(closer); - result = out.toByteArray(); + out.write(String.valueOf(Byte.toUnsignedInt(input.getByte(input.length() - 1))).getBytes()); + out.write(closer.getBytes()); + result = factory.createByteArray(out.toByteArray()); } else { - String tmp = new String(input); + String tmp = input.toString(); tmp = StringEscapeUtils.escapeHtml4(tmp); - result = tmp.getBytes(); + result = factory.createByteArray(tmp); } return result; diff --git a/src/main/java/de/usd/cstchef/operations/dataformat/ToBase64.java b/src/main/java/de/usd/cstchef/operations/dataformat/ToBase64.java index 2606483..5fd9cf1 100644 --- a/src/main/java/de/usd/cstchef/operations/dataformat/ToBase64.java +++ b/src/main/java/de/usd/cstchef/operations/dataformat/ToBase64.java @@ -1,17 +1,53 @@ package de.usd.cstchef.operations.dataformat; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.Base64; +import javax.swing.BoxLayout; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import burp.BurpUtils; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.utilities.Base64DecodingOptions; +import burp.api.montoya.utilities.Base64EncodingOptions; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @OperationInfos(name = "To Base64", category = OperationCategory.DATAFORMAT, description = "Encodes a string to base64.") -public class ToBase64 extends Operation { +public class ToBase64 extends Operation implements ActionListener { + private boolean urlSafe=false; + private JCheckBox urlSafeCheckBox; + @Override - protected byte[] perform(byte[] input) { - return Base64.getEncoder().encode(input); + protected ByteArray perform(ByteArray input, MessageType messageType) { + MontoyaApi api = BurpUtils.getInstance().getApi(); + if(!this.urlSafe) { + return api.utilities().base64Utils().encode(input); + } + else { + return api.utilities().base64Utils().encode(input, Base64EncodingOptions.URL); + } + } + + public void createUI() { + this.urlSafeCheckBox = new JCheckBox(); + urlSafeCheckBox.setText("URL Safe"); + urlSafeCheckBox.setToolTipText("When activated Base64 encoding is done URL safe. / and + are replaced with _ and -"); + urlSafeCheckBox.setSelected(this.urlSafe); + urlSafeCheckBox.addActionListener(this); + this.addUIElement("", urlSafeCheckBox); } + + @Override + public void actionPerformed(ActionEvent e) { + this.urlSafe = this.urlSafeCheckBox.isSelected(); + } } diff --git a/src/main/java/de/usd/cstchef/operations/dataformat/ToHex.java b/src/main/java/de/usd/cstchef/operations/dataformat/ToHex.java index 3885cb4..2460d7b 100644 --- a/src/main/java/de/usd/cstchef/operations/dataformat/ToHex.java +++ b/src/main/java/de/usd/cstchef/operations/dataformat/ToHex.java @@ -7,6 +7,8 @@ import javax.swing.JComboBox; import org.bouncycastle.util.encoders.Hex; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -32,33 +34,33 @@ public class ToHex extends Operation { private JComboBox delimiterBox; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String selectedKey = (String) this.delimiterBox.getSelectedItem(); Delimiter delimiter = ToHex.delimiters.get(selectedKey); if (delimiter.value.length == 0) { // No delimiter - return Hex.encode(input); + return factory.createByteArray(Hex.encode(input.getBytes())); } ByteArrayOutputStream out = new ByteArrayOutputStream(); - if (input.length > 0 && delimiter.writeAtStart) { + if (input.length() > 0 && delimiter.writeAtStart) { out.write(delimiter.value); } - for (int i = 0; i < input.length - 1; i++) { - out.write(Hex.encode(new byte[] { input[i] })); + for (int i = 0; i < input.length() - 1; i++) { + out.write(Hex.encode(new byte[] { input.getByte(i) })); out.write(delimiter.value); } - if (input.length > 0) { - out.write(Hex.encode(new byte[] { input[input.length - 1] })); // wow + if (input.length() > 0) { + out.write(Hex.encode(new byte[] { input.getByte(input.length() - 1) })); // wow if (delimiter.writeAtEnd) { out.write(delimiter.value); } } - return out.toByteArray(); + return factory.createByteArray(out.toByteArray()); } @Override diff --git a/src/main/java/de/usd/cstchef/operations/dataformat/UrlDecode.java b/src/main/java/de/usd/cstchef/operations/dataformat/UrlDecode.java index dd9dd46..cc4b747 100644 --- a/src/main/java/de/usd/cstchef/operations/dataformat/UrlDecode.java +++ b/src/main/java/de/usd/cstchef/operations/dataformat/UrlDecode.java @@ -1,8 +1,10 @@ package de.usd.cstchef.operations.dataformat; +import burp.BurpExtender; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -11,11 +13,10 @@ public class UrlDecode extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { - IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = cbs.getHelpers(); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + MontoyaApi api = BurpUtils.getInstance().getApi(); - byte[] result = helpers.urlDecode(input); + ByteArray result = api.utilities().urlUtils().decode(input); return result; } diff --git a/src/main/java/de/usd/cstchef/operations/dataformat/UrlEncode.java b/src/main/java/de/usd/cstchef/operations/dataformat/UrlEncode.java index e481530..b7c072c 100644 --- a/src/main/java/de/usd/cstchef/operations/dataformat/UrlEncode.java +++ b/src/main/java/de/usd/cstchef/operations/dataformat/UrlEncode.java @@ -6,9 +6,11 @@ import org.bouncycastle.util.encoders.Hex; +import burp.BurpExtender; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -19,27 +21,27 @@ public class UrlEncode extends Operation { private JCheckBox checkbox; @Override - protected byte[] perform(byte[] input) throws Exception { - IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = cbs.getHelpers(); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { - byte[] result = null; + ByteArray result = null; + MontoyaApi api = BurpUtils.getInstance().getApi(); if( checkbox.isSelected() ) { - byte[] delimiter = "%".getBytes(); + ByteArray delimiter = factory.createByteArray("%"); ByteArrayOutputStream out = new ByteArrayOutputStream(); - out.write(delimiter); + out.write(delimiter.getBytes()); - for (int i = 0; i < input.length - 1; i++) { - out.write(Hex.encode(new byte[] { input[i] })); - out.write(delimiter); + for (int i = 0; i < input.length() - 1; i++) { + out.write(Hex.encode( new byte[] { input.getByte(i) })); + out.write(delimiter.getBytes()); } - out.write(Hex.encode(new byte[] { input[input.length - 1] })); - result = out.toByteArray(); + out.write(Hex.encode(new byte[] { input.getByte(input.length() - 1) })); + result = factory.createByteArray(out.toByteArray()); } else { - result = helpers.urlEncode(input); + //TODO: double conversion! + result = api.utilities().urlUtils().encode(input); } return result; diff --git a/src/main/java/de/usd/cstchef/operations/datetime/DateTime.java b/src/main/java/de/usd/cstchef/operations/datetime/DateTime.java index 24a6b8e..fe0b77d 100644 --- a/src/main/java/de/usd/cstchef/operations/datetime/DateTime.java +++ b/src/main/java/de/usd/cstchef/operations/datetime/DateTime.java @@ -3,6 +3,8 @@ import java.text.SimpleDateFormat; import java.util.Date; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -14,10 +16,10 @@ public class DateTime extends Operation { private VariableTextField patternTxt; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String pattern = this.patternTxt.getText().trim(); SimpleDateFormat format = new SimpleDateFormat(pattern); - return format.format(new Date()).getBytes(); + return factory.createByteArray(format.format(new Date())); } public void createUI() { diff --git a/src/main/java/de/usd/cstchef/operations/datetime/TimestampOffset.java b/src/main/java/de/usd/cstchef/operations/datetime/TimestampOffset.java new file mode 100644 index 0000000..2184cd5 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/datetime/TimestampOffset.java @@ -0,0 +1,147 @@ +package de.usd.cstchef.operations.datetime; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import javax.swing.JCheckBox; +import javax.swing.JComboBox; + +import org.bouncycastle.jcajce.provider.asymmetric.dsa.DSASigner.stdDSA; + +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.view.ui.VariableTextField; + +@OperationInfos(name = "Unix Timestamp Offset", category = OperationCategory.DATES, description = "Returns a Epoch timestamp shifted into future or past.") +public class TimestampOffset extends Operation { + + private VariableTextField offsetTxt; + private JComboBox interval; + private JCheckBox toPast; + private JCheckBox milliseconds; + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + + String interval = (String)this.interval.getSelectedItem(); + TimeOffsets intervalEnum = TimeOffsets.getEnumObj(interval); + int offset = Integer.parseInt(offsetTxt.getText()); + boolean milliseconds = this.milliseconds.isSelected(); + + + long calculatedOffset = this.getOffset(intervalEnum, offset, milliseconds); + long timestamp = 0; + if(milliseconds) { + timestamp = System.currentTimeMillis(); + } + else { + timestamp = System.currentTimeMillis() / 1000L; + } + + long shiftedTimestamp = 0; + if(this.toPast.isSelected()) { + shiftedTimestamp = timestamp - calculatedOffset; + } + else { + shiftedTimestamp = timestamp + calculatedOffset; + } + + return factory.createByteArray(String.valueOf(shiftedTimestamp)); + } + + private long getOffset(TimeOffsets interval, int offset, boolean milliseconds) { + int unitOffset = 0; + switch(interval) { + case SECONDS: + unitOffset = 1; + break; + case MINUTES: + unitOffset = 60; + break; + case HOURS: + unitOffset = 60 * 60; + break; + case DAYS: + unitOffset = 60 * 60 * 24; + break; + case WEEKS: + unitOffset = 60 * 60 * 24 * 7; + break; + default: + throw new IllegalArgumentException("Unkown interval"); + } + + return milliseconds ? (unitOffset * offset * 1000) : (unitOffset * offset); + } + + public void createUI() { + this.offsetTxt = new VariableTextField(); + this.addUIElement("Offset", this.offsetTxt); + + this.interval = new JComboBox(); + this.interval.addItem(TimeOffsets.getName(TimeOffsets.SECONDS)); + this.interval.addItem(TimeOffsets.getName(TimeOffsets.MINUTES)); + this.interval.addItem(TimeOffsets.getName(TimeOffsets.HOURS)); + this.interval.addItem(TimeOffsets.getName(TimeOffsets.DAYS)); + this.interval.addItem(TimeOffsets.getName(TimeOffsets.WEEKS)); + this.interval.setSelectedIndex(1); + this.addUIElement("Interval", this.interval); + + this.toPast = new JCheckBox(); + this.toPast.setSelected(true); + this.toPast.setToolTipText("If checked the timestamp is shifted to the past, otherwise to the future."); + this.addUIElement("Shift to past", this.toPast); + + this.milliseconds = new JCheckBox(); + this.milliseconds.setSelected(false); + this.milliseconds.setToolTipText("If set the timestamp is returned in the milliseconds format. Otherwise in seconds format."); + this.addUIElement("Milliseconds", this.milliseconds); + } + + private enum TimeOffsets{ + SECONDS, MINUTES, HOURS, DAYS, WEEKS; + + public static String getName(TimeOffsets offsets) { + switch(offsets) { + case SECONDS: + return "Seconds"; + case MINUTES: + return "Minutes"; + case HOURS: + return "Hours"; + case DAYS: + return "Days"; + case WEEKS: + return "Weeks"; + default: + return ""; + } + } + + public static TimeOffsets getEnumObj(String timeOffsetStr) { + if(timeOffsetStr.equalsIgnoreCase(TimeOffsets.getName(TimeOffsets.SECONDS))) { + return TimeOffsets.SECONDS; + } + else if (timeOffsetStr.equalsIgnoreCase(TimeOffsets.getName(TimeOffsets.MINUTES))) { + return TimeOffsets.MINUTES; + } + else if (timeOffsetStr.equalsIgnoreCase(TimeOffsets.getName(TimeOffsets.HOURS))) { + return TimeOffsets.HOURS; + } + else if (timeOffsetStr.equalsIgnoreCase(TimeOffsets.getName(TimeOffsets.DAYS))) { + return TimeOffsets.DAYS; + } + else if (timeOffsetStr.equalsIgnoreCase(TimeOffsets.getName(TimeOffsets.WEEKS))) { + return TimeOffsets.WEEKS; + } + else { + throw new IllegalArgumentException("Unkown Unit"); + } + } + + } + +} diff --git a/src/main/java/de/usd/cstchef/operations/datetime/TimestampToDateTime.java b/src/main/java/de/usd/cstchef/operations/datetime/TimestampToDateTime.java new file mode 100644 index 0000000..712a555 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/datetime/TimestampToDateTime.java @@ -0,0 +1,45 @@ +package de.usd.cstchef.operations.datetime; + +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.util.Date; + +import javax.swing.JCheckBox; + +import burp.Logger; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.view.ui.VariableTextField; + +@OperationInfos(name = "Epoch to DateTime", category = OperationCategory.DATES, description = "Returns a given Unix (Epoch) timestamp formatted with the provided date time pattern.") +public class TimestampToDateTime extends Operation { + + private VariableTextField patternTxt; + private JCheckBox milliseconds; + + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + String pattern = this.patternTxt.getText().trim(); + SimpleDateFormat format = new SimpleDateFormat(pattern); + + long timestamp = Long.parseLong(input.toString()); + Instant instant = this.milliseconds.isSelected() ? Instant.ofEpochMilli(timestamp) : Instant.ofEpochSecond(timestamp); + Date date = Date.from(instant); + + return factory.createByteArray(format.format(date)); + } + + public void createUI() { + this.patternTxt = new VariableTextField(); + this.addUIElement("Pattern", this.patternTxt); + + this.milliseconds = new JCheckBox(); + milliseconds.setToolTipText("Check if a timestamp given is in milliseconds format. Otherwise seconds format is used."); + this.milliseconds.setSelected(false); + this.addUIElement("Input in milliseconds", this.milliseconds); + } +} diff --git a/src/main/java/de/usd/cstchef/operations/datetime/UnixTimestamp.java b/src/main/java/de/usd/cstchef/operations/datetime/UnixTimestamp.java index f8004fd..7c4a65a 100644 --- a/src/main/java/de/usd/cstchef/operations/datetime/UnixTimestamp.java +++ b/src/main/java/de/usd/cstchef/operations/datetime/UnixTimestamp.java @@ -2,6 +2,8 @@ import javax.swing.JCheckBox; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -12,7 +14,7 @@ public class UnixTimestamp extends Operation { private JCheckBox milliBox; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { long timestamp = 0; if (milliBox.isSelected()) { timestamp = System.currentTimeMillis(); @@ -20,7 +22,7 @@ protected byte[] perform(byte[] input) throws Exception { else { timestamp = System.currentTimeMillis() / 1000L; } - return String.valueOf(timestamp).getBytes(); + return factory.createByteArray(String.valueOf(timestamp)); } public void createUI() { diff --git a/src/main/java/de/usd/cstchef/operations/encryption/CipherUtils.java b/src/main/java/de/usd/cstchef/operations/encryption/CipherUtils.java index 5a38482..7758654 100644 --- a/src/main/java/de/usd/cstchef/operations/encryption/CipherUtils.java +++ b/src/main/java/de/usd/cstchef/operations/encryption/CipherUtils.java @@ -41,6 +41,11 @@ private void getCipherInfos() { } } } + // Add info for SM4 + CipherInfo info = new CipherInfo(); + info.setModes(new String[]{"ECB", "CBC", "CTR", "OFB", "CFB"}); + info.setPaddings(new String[]{"NOPADDING", "PKCS5PADDING"}); + algos.put("SM4", info); } public static CipherUtils getInstance() { diff --git a/src/main/java/de/usd/cstchef/operations/encryption/CryptOperation.java b/src/main/java/de/usd/cstchef/operations/encryption/CryptOperation.java index 552989f..01a602c 100644 --- a/src/main/java/de/usd/cstchef/operations/encryption/CryptOperation.java +++ b/src/main/java/de/usd/cstchef/operations/encryption/CryptOperation.java @@ -1,11 +1,17 @@ package de.usd.cstchef.operations.encryption; +import java.security.Security; + import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.swing.JComboBox; import org.bouncycastle.util.encoders.Hex; + +import burp.api.montoya.core.ByteArray; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.encoders.Base64; import de.usd.cstchef.operations.Operation; @@ -14,6 +20,10 @@ public abstract class CryptOperation extends Operation { + static { + Security.addProvider(new BouncyCastleProvider()); + } + private static String[] inOutModes = new String[] { "Raw", "Hex", "Base64" }; protected String algorithm; @@ -25,20 +35,26 @@ public abstract class CryptOperation extends Operation { protected JComboBox outputMode; protected JComboBox paddings; - public CryptOperation(String alogrithm) { + public CryptOperation(String algorithm) { super(); - this.algorithm = alogrithm; + this.algorithm = algorithm; this.createMyUI(); } protected byte[] crypt(byte[] input, int cipherMode, String algorithm, String mode, String padding) throws Exception { - byte[] key = keyTxt.getText(); - byte[] iv = ivTxt.getText(); - - SecretKeySpec secretKeySpec = new SecretKeySpec(key, algorithm); - IvParameterSpec ivSpec = new IvParameterSpec(iv); - Cipher cipher = Cipher.getInstance(String.format("%s/%s/%s", algorithm, mode, padding)); + ByteArray key = keyTxt.getText(); + ByteArray iv = ivTxt.getText(); + + SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm); + IvParameterSpec ivSpec = new IvParameterSpec(iv.getBytes()); + Cipher cipher; + if(algorithm.equals("SM4")){ + cipher = Cipher.getInstance(String.format("%s/%s/%s", algorithm, mode, padding), BouncyCastleProvider.PROVIDER_NAME); + } + else{ + cipher = Cipher.getInstance(String.format("%s/%s/%s", algorithm, mode, padding)); + } if( mode.equals("ECB") ) { cipher.init(cipherMode, secretKeySpec); @@ -66,6 +82,8 @@ protected byte[] crypt(byte[] input, int cipherMode, String algorithm, String mo public void createMyUI() { this.ivTxt = new FormatTextField(); + this.ivTxt.addOption("Empty"); + this.ivTxt.setDefault("Empty"); this.addUIElement("IV", this.ivTxt); this.keyTxt = new FormatTextField(); diff --git a/src/main/java/de/usd/cstchef/operations/encryption/DecryptionOperation.java b/src/main/java/de/usd/cstchef/operations/encryption/DecryptionOperation.java index a232ad7..5f2049d 100644 --- a/src/main/java/de/usd/cstchef/operations/encryption/DecryptionOperation.java +++ b/src/main/java/de/usd/cstchef/operations/encryption/DecryptionOperation.java @@ -2,16 +2,19 @@ import javax.crypto.Cipher; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; + public abstract class DecryptionOperation extends CryptOperation { - public DecryptionOperation(String alogrithm) { - super(alogrithm); + public DecryptionOperation(String algorithm) { + super(algorithm); } @Override - protected byte[] perform(byte[] input) throws Exception { - return this.crypt(input, Cipher.DECRYPT_MODE, this.algorithm, (String) this.cipherMode.getSelectedItem(), - (String) this.paddings.getSelectedItem()); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + return factory.createByteArray(this.crypt(input.getBytes(), Cipher.DECRYPT_MODE, this.algorithm, (String) this.cipherMode.getSelectedItem(), + (String) this.paddings.getSelectedItem())); } } diff --git a/src/main/java/de/usd/cstchef/operations/encryption/EncryptionOperation.java b/src/main/java/de/usd/cstchef/operations/encryption/EncryptionOperation.java index 0026cc8..86ae779 100644 --- a/src/main/java/de/usd/cstchef/operations/encryption/EncryptionOperation.java +++ b/src/main/java/de/usd/cstchef/operations/encryption/EncryptionOperation.java @@ -2,6 +2,9 @@ import javax.crypto.Cipher; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; + public abstract class EncryptionOperation extends CryptOperation { public EncryptionOperation(String alogrithm) { @@ -9,9 +12,9 @@ public EncryptionOperation(String alogrithm) { } @Override - protected byte[] perform(byte[] input) throws Exception { - return this.crypt(input, Cipher.ENCRYPT_MODE, this.algorithm, (String) this.cipherMode.getSelectedItem(), - (String) this.paddings.getSelectedItem()); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + return factory.createByteArray(this.crypt(input.getBytes(), Cipher.ENCRYPT_MODE, this.algorithm, (String) this.cipherMode.getSelectedItem(), + (String) this.paddings.getSelectedItem())); } } diff --git a/src/main/java/de/usd/cstchef/operations/encryption/RsaDecryption.java b/src/main/java/de/usd/cstchef/operations/encryption/RsaDecryption.java index 16b363e..0fc16ca 100644 --- a/src/main/java/de/usd/cstchef/operations/encryption/RsaDecryption.java +++ b/src/main/java/de/usd/cstchef/operations/encryption/RsaDecryption.java @@ -6,7 +6,9 @@ import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.encryption.CipherUtils.CipherInfo; import de.usd.cstchef.operations.signature.KeystoreOperation; @@ -28,7 +30,7 @@ public RsaDecryption() { this.createMyUI(); } - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { if( ! this.keyAvailable.isSelected() ) throw new IllegalArgumentException("No private key available."); @@ -39,20 +41,20 @@ protected byte[] perform(byte[] input) throws Exception { String selectedInputMode = (String)inputMode.getSelectedItem(); String selectedOutputMode = (String)outputMode.getSelectedItem(); - + byte[] in = new byte[0]; if( selectedInputMode.equals("Hex") ) - input = Hex.decode(input); + in = Hex.decode(input.getBytes()); if( selectedInputMode.equals("Base64") ) - input = Base64.decode(input); + in = Base64.decode(input.getBytes()); - byte[] encrypted = cipher.doFinal(input); + byte[] encrypted = cipher.doFinal(input.getBytes()); if( selectedOutputMode.equals("Hex") ) encrypted = Hex.encode(encrypted); if( selectedOutputMode.equals("Base64") ) encrypted = Base64.encode(encrypted); - return encrypted; + return factory.createByteArray(encrypted); } public void createMyUI() { diff --git a/src/main/java/de/usd/cstchef/operations/encryption/RsaEncryption.java b/src/main/java/de/usd/cstchef/operations/encryption/RsaEncryption.java index 49db61a..6bf75f9 100644 --- a/src/main/java/de/usd/cstchef/operations/encryption/RsaEncryption.java +++ b/src/main/java/de/usd/cstchef/operations/encryption/RsaEncryption.java @@ -6,7 +6,9 @@ import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.encryption.CipherUtils.CipherInfo; import de.usd.cstchef.operations.signature.KeystoreOperation; @@ -28,7 +30,7 @@ public RsaEncryption() { this.createMyUI(); } - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { if( ! this.certAvailable.isSelected() ) throw new IllegalArgumentException("No certificate available."); @@ -39,20 +41,20 @@ protected byte[] perform(byte[] input) throws Exception { String selectedInputMode = (String)inputMode.getSelectedItem(); String selectedOutputMode = (String)outputMode.getSelectedItem(); - + byte[] in = new byte[0]; if( selectedInputMode.equals("Hex") ) - input = Hex.decode(input); + in = Hex.decode(input.getBytes()); if( selectedInputMode.equals("Base64") ) - input = Base64.decode(input); + in = Base64.decode(input.getBytes()); - byte[] encrypted = cipher.doFinal(input); + byte[] encrypted = cipher.doFinal(input.getBytes()); if( selectedOutputMode.equals("Hex") ) encrypted = Hex.encode(encrypted); if( selectedOutputMode.equals("Base64") ) encrypted = Base64.encode(encrypted); - return encrypted; + return factory.createByteArray(encrypted); } public void createMyUI() { diff --git a/src/main/java/de/usd/cstchef/operations/encryption/SM4Decryption.java b/src/main/java/de/usd/cstchef/operations/encryption/SM4Decryption.java new file mode 100644 index 0000000..5cbd938 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/encryption/SM4Decryption.java @@ -0,0 +1,13 @@ +package de.usd.cstchef.operations.encryption; + +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; + +@OperationInfos(name = "SM4 Decryption", category = OperationCategory.ENCRYPTION, description = "Decrypts via the SM4 algorithm.") +public class SM4Decryption extends DecryptionOperation { + + public SM4Decryption() { + super("SM4"); + } + +} diff --git a/src/main/java/de/usd/cstchef/operations/encryption/SM4Encryption.java b/src/main/java/de/usd/cstchef/operations/encryption/SM4Encryption.java new file mode 100644 index 0000000..9af4d74 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/encryption/SM4Encryption.java @@ -0,0 +1,13 @@ +package de.usd.cstchef.operations.encryption; + +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; + +@OperationInfos(name = "SM4 Encryption", category = OperationCategory.ENCRYPTION, description = "Encrypts via the SM4 algorithm.") +public class SM4Encryption extends EncryptionOperation { + + public SM4Encryption() { + super("SM4"); + } + +} diff --git a/src/main/java/de/usd/cstchef/operations/extractors/HttpBodyExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/HttpBodyExtractor.java index ac682e6..1346583 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/HttpBodyExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/HttpBodyExtractor.java @@ -3,26 +3,27 @@ import java.util.Arrays; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IRequestInfo; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; -@OperationInfos(name = "HTTP Body", category = OperationCategory.EXTRACTORS, description = "Extracts the body of a HTTP request.") +@OperationInfos(name = "HTTP Body", category = OperationCategory.EXTRACTORS, description = "Extracts the body of a HTTP messages.") public class HttpBodyExtractor extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { - try { - IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks(); - IRequestInfo requestInfo = cbs.getHelpers().analyzeRequest(input); - int bodyOffset = requestInfo.getBodyOffset(); - - byte[] body = Arrays.copyOfRange(input, bodyOffset, input.length); - return body; - } catch (Exception e) { - throw new IllegalArgumentException("Provided input is not a valid http request."); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + if(messageType == MessageType.REQUEST){ + return factory.getHttpRequestBody(input); + } + else if(messageType == MessageType.RESPONSE){ + return factory.getHttpResponseBody(input); + } + else{ + return parseRawMessage(input); } } } \ No newline at end of file diff --git a/src/main/java/de/usd/cstchef/operations/extractors/HttpCookieExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/HttpCookieExtractor.java index ae1d10f..3a4e32e 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/HttpCookieExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/HttpCookieExtractor.java @@ -2,56 +2,47 @@ import org.bouncycastle.util.Arrays; +import burp.BurpExtender; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IResponseInfo; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.Cookie; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.objects.CstcByteArray; +import de.usd.cstchef.Utils; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.view.ui.VariableTextField; -@OperationInfos(name = "HTTP Cookie", category = OperationCategory.EXTRACTORS, description = "Extracts a cookie from a HTTP request.") +@OperationInfos(name = "HTTP Cookie", category = OperationCategory.EXTRACTORS, description = "Extracts a cookie from a HTTP message.") public class HttpCookieExtractor extends Operation { - private VariableTextField cookieNameField; + protected VariableTextField cookieNameField; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { - byte[] cookieName = cookieNameField.getBytes(); - if( cookieName.length == 0 ) - return input; + String cookieName = cookieNameField.getText(); + if( cookieName.length() == 0 ) + return factory.createByteArray(0); - byte[] cookieSearch = new byte[cookieName.length + 1]; - System.arraycopy(cookieName, 0, cookieSearch, 0, cookieName.length); - System.arraycopy("=".getBytes(), 0, cookieSearch, cookieName.length, 1); - - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - int length = input.length; - - IResponseInfo resp = helpers.analyzeResponse(input); - boolean isRequest = (resp.getStatusCode() == 0); - - String cookieHeader = "\r\nSet-Cookie: "; - if(isRequest) - cookieHeader = "\r\nCookie: "; - - try { - - int offset = helpers.indexOf(input, cookieHeader.getBytes(), false, 0, length); - int line_end = helpers.indexOf(input, "\r\n".getBytes(), false, offset + 2, length); - int start = helpers.indexOf(input, cookieSearch, true, offset, line_end); - int end = helpers.indexOf(input, ";".getBytes(), true, start, line_end); - - if( end < 0 ) - end = line_end; - - return Arrays.copyOfRange(input, start + cookieName.length + 1, end); - - } catch( IllegalArgumentException e ) { - throw new IllegalArgumentException("Cookie not found."); + if(messageType == MessageType.REQUEST){ + HttpRequest request = factory.createHttpRequest(input); + return checkNull(Utils.httpRequestCookieExtractor(request, cookieName)); + } + else if(messageType == MessageType.RESPONSE){ + HttpResponse response = factory.createHttpResponse(input); + for(Cookie c : response.cookies()){ + if(c.name().equals(cookieName)) + return factory.createByteArray(checkNull(c.value())); + } + return factory.createByteArray(0); + } + else{ + return parseRawMessage(input); } } diff --git a/src/main/java/de/usd/cstchef/operations/extractors/HttpGetExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/HttpGetExtractor.java index 21e06a7..2307c7e 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/HttpGetExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/HttpGetExtractor.java @@ -2,10 +2,19 @@ import java.util.Arrays; +import burp.BurpExtender; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IParameter; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.Cookie; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.params.ParsedHttpParameter; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.api.montoya.ui.editor.HttpRequestEditor; +import de.usd.cstchef.Utils; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -17,26 +26,19 @@ public class HttpGetExtractor extends Operation { protected VariableTextField parameter; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String parameterName = parameter.getText(); - if( parameterName.equals("") ) - return input; - - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - - IParameter param = helpers.getRequestParameter(input, parameterName); - if( param == null) - throw new IllegalArgumentException("Parameter name not found."); - if( param.getType() != IParameter.PARAM_URL ) - throw new IllegalArgumentException("Parameter type is not GET."); - - int start = param.getValueStart(); - int end = param.getValueEnd(); - - byte[] result = Arrays.copyOfRange(input, start, end); - return result; + if (parameterName.equals("")) + return factory.createByteArray(0); + + if (messageType == MessageType.REQUEST) { + return factory.createByteArray(factory.createHttpRequest(input).parameterValue(parameterName, HttpParameterType.URL)); + } else if (messageType == MessageType.RESPONSE) { + throw new IllegalArgumentException("Input is not a valid HTTP Request"); + } else { + return parseRawMessage(input); + } } diff --git a/src/main/java/de/usd/cstchef/operations/extractors/HttpHeaderExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/HttpHeaderExtractor.java index 91870cb..9103516 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/HttpHeaderExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/HttpHeaderExtractor.java @@ -1,50 +1,34 @@ package de.usd.cstchef.operations.extractors; -import org.bouncycastle.util.Arrays; - -import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.view.ui.VariableTextField; -@OperationInfos(name = "HTTP Header", category = OperationCategory.EXTRACTORS, description = "Extracts a header of a HTTP request.") +@OperationInfos(name = "HTTP Header", category = OperationCategory.EXTRACTORS, description = "Extracts a header of a HTTP message.") public class HttpHeaderExtractor extends Operation { - private VariableTextField headerNameField; + protected VariableTextField headerNameField; @Override - protected byte[] perform(byte[] input) throws Exception { - - byte[] headerName = headerNameField.getBytes(); - if( headerName.length == 0 ) - return input; - - byte[] headerSearch = new byte[headerName.length + 4]; - System.arraycopy("\r\n".getBytes(), 0, headerSearch, 0, 2); - System.arraycopy(headerName, 0, headerSearch, 2, headerName.length); - System.arraycopy(": ".getBytes(), 0, headerSearch, headerName.length + 2, 2); - - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - int length = input.length; - - int offset = helpers.indexOf(input, headerSearch, true, 0, length); - - if( offset < 0 ) - throw new IllegalArgumentException("Header not found."); - - int valueStart = helpers.indexOf(input, " ".getBytes(), false, offset, length); - if( valueStart < 0 ) - throw new IllegalArgumentException("Invalid Header format."); - int valueEnd = helpers.indexOf(input, "\r\n".getBytes(), false, valueStart, length); - if( valueEnd < 0 ) - throw new IllegalArgumentException("Invalid Header format."); - - byte[] result = Arrays.copyOfRange(input, valueStart + 1, valueEnd); - return result; + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + + String headerName = headerNameField.getText(); + if( headerName.length() == 0 ) + return factory.createByteArray(0); + if(messageType == MessageType.REQUEST){ + return factory.createByteArray(checkNull(factory.createHttpRequest(input).headerValue(headerName))); + } + else if(messageType == MessageType.RESPONSE){ + return factory.createByteArray(checkNull(factory.createHttpResponse(input).headerValue(headerName))); + } + else{ + return parseRawMessage(input); + } } @Override diff --git a/src/main/java/de/usd/cstchef/operations/extractors/HttpJsonExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/HttpJsonExtractor.java index 3f639e2..a6cb96b 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/HttpJsonExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/HttpJsonExtractor.java @@ -1,13 +1,12 @@ package de.usd.cstchef.operations.extractors; -import java.util.Arrays; - import javax.swing.JTextField; -import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IParameter; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -15,29 +14,26 @@ @OperationInfos(name = "HTTP JSON", category = OperationCategory.EXTRACTORS, description = "Get a JSON value from HTTP message.") public class HttpJsonExtractor extends Operation { - private JTextField fieldTxt; + protected JTextField fieldTxt; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String keyName = fieldTxt.getText(); if( keyName.equals("") ) - return input; - - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - - IParameter param = helpers.getRequestParameter(input, keyName); - if( param == null) - throw new IllegalArgumentException("Key not found."); - if( param.getType() != IParameter.PARAM_JSON ) - throw new IllegalArgumentException("Parameter type is not JSON"); - - int start = param.getValueStart(); - int end = param.getValueEnd(); - - byte[] result = Arrays.copyOfRange(input, start, end); - return result; + //return ByteArray.byteArray(0); + return factory.createByteArray(0); + + JsonExtractor extractor = new JsonExtractor(keyName); + if(messageType == MessageType.REQUEST){ + return checkNull(extractor.perform(factory.createHttpRequest(input).body(), messageType)); + } + else if(messageType == MessageType.RESPONSE){ + return checkNull(extractor.perform(factory.createHttpResponse(input).body(), messageType)); + } + else{ + return parseRawMessage(input); + } } @Override diff --git a/src/main/java/de/usd/cstchef/operations/extractors/HttpMethodExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/HttpMethodExtractor.java index 3789a05..76010bc 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/HttpMethodExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/HttpMethodExtractor.java @@ -1,10 +1,10 @@ package de.usd.cstchef.operations.extractors; -import org.bouncycastle.util.Arrays; -import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; + +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.requests.HttpRequest; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -13,19 +13,20 @@ public class HttpMethodExtractor extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { - try { - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - int length = input.length; - - int methodEnd = helpers.indexOf(input, " ".getBytes(), false, 0, length); - byte[] result = Arrays.copyOfRange(input, 0, methodEnd); - - return result; - - } catch (Exception e) { - throw new IllegalArgumentException("Provided input is not a valid http request."); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + if(messageType == MessageType.REQUEST){ + try{ + return factory.createByteArray(factory.createHttpRequest(input).method()); + } + catch(Exception e){ + throw new IllegalArgumentException("Input is not a valid request"); + } + } + else if(messageType == MessageType.RESPONSE){ + throw new IllegalArgumentException("Input is not a valid HTTP Request"); + } + else{ + return parseRawMessage(input); } } } diff --git a/src/main/java/de/usd/cstchef/operations/extractors/HttpMultipartExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/HttpMultipartExtractor.java new file mode 100644 index 0000000..ffd2052 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/extractors/HttpMultipartExtractor.java @@ -0,0 +1,46 @@ +package de.usd.cstchef.operations.extractors; + +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.requests.HttpRequest; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.view.ui.VariableTextField; + +@OperationInfos(name = "HTTP Multipart Param", category = OperationCategory.EXTRACTORS, description = "Extracts a part of a multipart/form-data request.") +public class HttpMultipartExtractor extends Operation { + + protected VariableTextField parameter; + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + + String parameterName = parameter.getText(); + if (parameterName.equals("")) + return factory.createByteArray(0); + + if (messageType == MessageType.REQUEST) { + try{ + return factory.createByteArray(checkNull(factory.createHttpRequest(input).parameterValue(parameterName, HttpParameterType.BODY))); + } + catch(Exception e){ + throw new IllegalArgumentException("Input is not a valid request"); + } + } else if (messageType == MessageType.RESPONSE) { + throw new IllegalArgumentException("Input is not a valid HTTP Request"); + } else { + return parseRawMessage(input); + } + + + } + + @Override + public void createUI() { + this.parameter = new VariableTextField(); + this.addUIElement("Parameter", this.parameter); + } + +} \ No newline at end of file diff --git a/src/main/java/de/usd/cstchef/operations/extractors/HttpPostExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/HttpPostExtractor.java index a253ea0..6cdfb60 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/HttpPostExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/HttpPostExtractor.java @@ -3,9 +3,12 @@ import java.util.Arrays; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IParameter; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.params.ParsedHttpParameter; +import burp.api.montoya.http.message.requests.HttpRequest; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -17,26 +20,23 @@ public class HttpPostExtractor extends Operation { protected VariableTextField parameter; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String parameterName = parameter.getText(); - if( parameterName.equals("") ) - return input; - - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - - IParameter param = helpers.getRequestParameter(input, parameterName); - if( param == null) - throw new IllegalArgumentException("Parameter name not found."); - if( param.getType() != IParameter.PARAM_BODY ) - throw new IllegalArgumentException("Parameter type is not POST"); - - int start = param.getValueStart(); - int end = param.getValueEnd(); - - byte[] result = Arrays.copyOfRange(input, start, end); - return result; + if (parameterName.equals("")) + return factory.createByteArray(0); + + if (messageType == MessageType.REQUEST) { + try { + return checkNull(factory.createByteArray(factory.createHttpRequest(input).parameterValue(parameterName, HttpParameterType.BODY))); + } catch (Exception e) { + throw new IllegalArgumentException("Input is not a valid request"); + } + } else if (messageType == MessageType.RESPONSE) { + throw new IllegalArgumentException("Input is not a valid HTTP Request"); + } else { + return parseRawMessage(input); + } } @Override diff --git a/src/main/java/de/usd/cstchef/operations/extractors/HttpUriExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/HttpUriExtractor.java index cc67c5e..bdcc24c 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/HttpUriExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/HttpUriExtractor.java @@ -1,12 +1,17 @@ package de.usd.cstchef.operations.extractors; import java.util.Arrays; +import java.util.List; import javax.swing.JCheckBox; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.params.ParsedHttpParameter; +import burp.api.montoya.http.message.requests.HttpRequest; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -14,7 +19,7 @@ @OperationInfos(name = "HTTP URI", category = OperationCategory.EXTRACTORS, description = "Extracts the URI of a HTTP request.") public class HttpUriExtractor extends Operation { - private JCheckBox checkbox; + protected JCheckBox checkbox; @Override public void createUI() { @@ -24,27 +29,23 @@ public void createUI() { } @Override - protected byte[] perform(byte[] input) throws Exception { - try { - - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - int length = input.length; - - int firstMark = helpers.indexOf(input, " ".getBytes(), false, 0, length); - int lineMark = helpers.indexOf(input, " ".getBytes(), false, firstMark + 1, length); - - int secondMark = helpers.indexOf(input, "?".getBytes(), false, firstMark + 1, length); - - if( this.checkbox.isSelected() || secondMark < 0 || secondMark >= lineMark) { - secondMark = lineMark; + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + + if (messageType == MessageType.REQUEST) { + try { + String url = factory.createHttpRequest(input).url(); + if (!checkbox.isSelected()) { + return factory.createByteArray(url.split("\\?")[0]); + } else { + return factory.createByteArray(url); + } + } catch (Exception e) { + throw new IllegalArgumentException("Input is not a valid request"); } - - byte[] result = Arrays.copyOfRange(input, firstMark + 1, secondMark); - return result; - - } catch (Exception e) { - throw new IllegalArgumentException("Provided input is not a valid http request."); + } else if (messageType == MessageType.RESPONSE) { + throw new IllegalArgumentException("Input is not a valid HTTP Request"); + } else { + return parseRawMessage(input); } } } diff --git a/src/main/java/de/usd/cstchef/operations/extractors/HttpXmlExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/HttpXmlExtractor.java index 37f845c..9d09fac 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/HttpXmlExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/HttpXmlExtractor.java @@ -1,43 +1,57 @@ package de.usd.cstchef.operations.extractors; -import java.util.Arrays; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.io.InputStream; import javax.swing.JTextField; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; -import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IParameter; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; + +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; -@OperationInfos(name = "HTTP XML", category = OperationCategory.EXTRACTORS, description = "Extract XML value from HTTP message.") +@OperationInfos(name = "HTTP XML", category = OperationCategory.EXTRACTORS, description = "Extract the first occurrence of a XML value from HTTP message.") public class HttpXmlExtractor extends Operation { - private JTextField fieldTxt; + protected JTextField fieldTxt; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String keyName = fieldTxt.getText(); - if( keyName.equals("") ) - return input; - - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - - IParameter param = helpers.getRequestParameter(input, keyName); - if( param == null) - throw new IllegalArgumentException("Key not found."); - if( param.getType() != IParameter.PARAM_XML ) - throw new IllegalArgumentException("Parameter type is not XML"); - - int start = param.getValueStart(); - int end = param.getValueEnd(); - - byte[] result = Arrays.copyOfRange(input, start, end); - return result; + if (keyName.equals("")) + return factory.createByteArray(0); + + if (messageType == MessageType.REQUEST) { + try { + return factory.createByteArray(checkNull(factory.createHttpRequest(input).parameterValue(keyName, HttpParameterType.XML))); + } catch (Exception e) { + throw new IllegalArgumentException("Input is not a valid request"); + } + } else if (messageType == MessageType.RESPONSE) { + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = builder.parse(new ByteArrayInputStream(factory.createHttpResponse(input).bodyToString().getBytes())); + doc.getDocumentElement().normalize(); + NodeList nodeList = doc.getElementsByTagName(keyName); + try { + return factory.createByteArray(checkNull(nodeList.item(0).getTextContent())); + } catch (NullPointerException e) { + throw new IllegalArgumentException("Input is not a valid request"); + } + } else { + return parseRawMessage(input); + } } @Override diff --git a/src/main/java/de/usd/cstchef/operations/extractors/JsonExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/JsonExtractor.java index 0975fa3..0a91062 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/JsonExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/JsonExtractor.java @@ -1,53 +1,49 @@ package de.usd.cstchef.operations.extractors; +import java.util.LinkedHashMap; + import javax.swing.JTextField; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.spi.json.JsonProvider; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; -@OperationInfos(name = "JSON", category = OperationCategory.EXTRACTORS, description = "Extracts values of json objects.") +@OperationInfos(name = "JSON", category = OperationCategory.EXTRACTORS, description = "Extracts values of JSON objects.") public class JsonExtractor extends Operation { private static JsonProvider provider; //TODO should this be a VariableTextField? - private JTextField fieldTxt; + protected JTextField fieldTxt; + + public JsonExtractor(){ + this(new String()); + } - public JsonExtractor() { + public JsonExtractor(String key) { super(); if (JsonExtractor.provider == null) { JsonExtractor.provider = Configuration.defaultConfiguration().jsonProvider(); } + this.setKey(key); } @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { if( fieldTxt.getText().equals("") ) - return input; + return factory.createByteArray(0); - Object document = provider.parse(new String(input)); + Object document = provider.parse(input.toString()); Object result = JsonPath.read(document, fieldTxt.getText()); - if( result == null ) - result = "null"; - - Class resultClass = result.getClass(); - - if( resultClass == String.class ) { - return ((String)result).getBytes(); - } else if( resultClass == Integer.class || resultClass == Float.class || resultClass == Double.class ) { - return String.valueOf(result).getBytes(); - } else if( resultClass == Boolean.class ) { - return String.valueOf(result).getBytes(); - } - - throw new IllegalArgumentException("JSON data of unknown type."); + return factory.createByteArray(result.toString()); } @Override @@ -55,5 +51,9 @@ public void createUI() { this.fieldTxt = new JTextField(); this.addUIElement("Field", this.fieldTxt); } + + public void setKey(String key){ + this.fieldTxt.setText(key); + } } diff --git a/src/main/java/de/usd/cstchef/operations/extractors/LineExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/LineExtractor.java index 59de978..a9cd99a 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/LineExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/LineExtractor.java @@ -5,8 +5,9 @@ import org.bouncycastle.util.Arrays; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -15,11 +16,11 @@ @OperationInfos(name = "Line Extractor", category = OperationCategory.EXTRACTORS, description = "Extracts the specified line number.") public class LineExtractor extends Operation { - private VariableTextField lineNumberField; - private JComboBox formatBox; + protected VariableTextField lineNumberField; + protected JComboBox formatBox; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { int lineNumber = 0; try { @@ -45,15 +46,14 @@ protected byte[] perform(byte[] input) throws Exception { break; } - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - int length = input.length; + MontoyaApi api = BurpUtils.getInstance().getApi(); + int length = input.length(); int start = 0; int offset = 0; int counter = 0; while( counter < lineNumber - 1 ) { - offset = helpers.indexOf(input, lineEndings, false, start, length); + offset = api.utilities().byteUtils().indexOf(input.getBytes(), lineEndings, false, start, length); if( offset >= 0 ) { start = offset + lineEndings.length; counter++; @@ -62,11 +62,11 @@ protected byte[] perform(byte[] input) throws Exception { } } - int end = helpers.indexOf(input, lineEndings, false, start, length); + int end = api.utilities().byteUtils().indexOf(input.getBytes(), lineEndings, false, start, length); if( end < 0 ) end = length; - byte[] result = Arrays.copyOfRange(input, start, end); + ByteArray result = BurpUtils.subArray(input, start, end); return result; } diff --git a/src/main/java/de/usd/cstchef/operations/extractors/RegexExtractor.java b/src/main/java/de/usd/cstchef/operations/extractors/RegexExtractor.java index d3a7c9d..bda4ec5 100644 --- a/src/main/java/de/usd/cstchef/operations/extractors/RegexExtractor.java +++ b/src/main/java/de/usd/cstchef/operations/extractors/RegexExtractor.java @@ -5,6 +5,8 @@ import javax.swing.JComboBox; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -16,13 +18,13 @@ public class RegexExtractor extends Operation { private static String LIST_MATCHES = "List matches"; private static String LIST_GROUPS = "List capture groups"; - private VariableTextField regexTxt; - private JComboBox outputBox; + protected VariableTextField regexTxt; + protected JComboBox outputBox; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { Pattern p = Pattern.compile(this.regexTxt.getText()); - Matcher m = p.matcher(new String(input)); + Matcher m = p.matcher(input.toString()); String outputType = (String) this.outputBox.getSelectedItem(); StringBuffer buf = new StringBuffer(); @@ -40,7 +42,7 @@ protected byte[] perform(byte[] input) throws Exception { if( buf.length() > 0 ) buf.setLength(buf.length() - 1); - return buf.toString().getBytes(); + return factory.createByteArray(buf.toString()); } @Override diff --git a/src/main/java/de/usd/cstchef/operations/hashing/HashOperation.java b/src/main/java/de/usd/cstchef/operations/hashing/HashOperation.java index 8b134ff..fb472ad 100644 --- a/src/main/java/de/usd/cstchef/operations/hashing/HashOperation.java +++ b/src/main/java/de/usd/cstchef/operations/hashing/HashOperation.java @@ -7,6 +7,8 @@ import org.bouncycastle.util.encoders.Hex; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; public abstract class HashOperation extends Operation { @@ -26,8 +28,8 @@ public HashOperation(String alogrithm, String... sizes) { } @Override - protected byte[] perform(byte[] input) throws Exception { - return this.hash(input); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + return factory.createByteArray(this.hash(input.getBytes())); } protected byte[] hash(byte[] input) throws NoSuchAlgorithmException { diff --git a/src/main/java/de/usd/cstchef/operations/hashing/Hmac.java b/src/main/java/de/usd/cstchef/operations/hashing/Hmac.java index 88ed871..45d3a25 100644 --- a/src/main/java/de/usd/cstchef/operations/hashing/Hmac.java +++ b/src/main/java/de/usd/cstchef/operations/hashing/Hmac.java @@ -5,6 +5,8 @@ import javax.swing.JComboBox; import org.bouncycastle.util.encoders.Hex; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -17,13 +19,13 @@ public class Hmac extends Operation { private JComboBox hashAlgoBox; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { byte[] key = this.keyTxt.getText().getBytes(); String algo = "Hmac" + (String) hashAlgoBox.getSelectedItem(); SecretKeySpec signingKey = new SecretKeySpec(key, algo); Mac mac = Mac.getInstance(algo); mac.init(signingKey); - return Hex.encode(mac.doFinal(input)); + return factory.createByteArray(Hex.encode(mac.doFinal(input.getBytes()))); } @Override diff --git a/src/main/java/de/usd/cstchef/operations/hashing/Luhn.java b/src/main/java/de/usd/cstchef/operations/hashing/Luhn.java new file mode 100644 index 0000000..b544a60 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/hashing/Luhn.java @@ -0,0 +1,49 @@ +package de.usd.cstchef.operations.hashing; + +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; + + +@OperationInfos(name = "Luhn", category = OperationCategory.HASHING, description = "Calculate Luhn of a number") +public class Luhn extends Operation { + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + + for (int i = 0; i < input.length(); i++){ + if ((input.getByte(i) < '0') || (input.getByte(i) > '9')) { + throw new IllegalArgumentException("Luhn can only be applied to numerical values."); + } + } + + int check_digit = calculateLuhnCheckDigit(input); + return factory.createByteArray((byte)check_digit + '0'); + } + + private int calculateLuhnCheckDigit(ByteArray input) { + int sum = 0; + boolean doubleDigit = true; + + for (int i = input.length() - 1; i >= 0; i--) { + + + + int digit = Integer.valueOf(Character.toString ((char)input.getByte(i))); + + if (doubleDigit) { + digit *= 2; + if (digit > 9) { + digit -= 9; + } + } + sum += digit; + doubleDigit = !doubleDigit; + } + + int mod = sum % 10; + return mod == 0 ? 0 : 10 - mod; + } +} diff --git a/src/main/java/de/usd/cstchef/operations/hashing/SM3.java b/src/main/java/de/usd/cstchef/operations/hashing/SM3.java new file mode 100644 index 0000000..1296445 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/hashing/SM3.java @@ -0,0 +1,13 @@ +package de.usd.cstchef.operations.hashing; + +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; + +@OperationInfos(name = "SM3", category = OperationCategory.HASHING, description = "The SM3 algorithm") +public class SM3 extends HashOperation { + + public SM3() { + super("SM3"); + } + +} diff --git a/src/main/java/de/usd/cstchef/operations/misc/GetRequestBuilder.java b/src/main/java/de/usd/cstchef/operations/misc/GetRequestBuilder.java new file mode 100644 index 0000000..92a100c --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/misc/GetRequestBuilder.java @@ -0,0 +1,37 @@ +package de.usd.cstchef.operations.misc; + +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.view.ui.VariableTextField; + +@OperationInfos(name = "GET Request Builder", category = OperationCategory.MISC, description = "Build a basic GET request.") +public class GetRequestBuilder extends Operation { + + private VariableTextField host; + private VariableTextField document; + private VariableTextField accept; + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + return factory.createByteArray(String.format("GET %s HTTP/1.1\n" + // + "Host: %s\n" + // + "Accept: %s", document.getText(), host.getText(), accept.getText())); + } + + @Override + public void createUI() { + this.document = new VariableTextField(); + this.addUIElement("Document", this.document); + + this.host = new VariableTextField(); + this.addUIElement("Host", this.host); + + this.accept = new VariableTextField(); + this.accept.setText("*/*"); + this.addUIElement("Accept", this.accept); + } + +} diff --git a/src/main/java/de/usd/cstchef/operations/misc/ReadFile.java b/src/main/java/de/usd/cstchef/operations/misc/ReadFile.java index f40acee..8dc0e7e 100644 --- a/src/main/java/de/usd/cstchef/operations/misc/ReadFile.java +++ b/src/main/java/de/usd/cstchef/operations/misc/ReadFile.java @@ -7,6 +7,8 @@ import javax.swing.JButton; import javax.swing.JFileChooser; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -19,7 +21,7 @@ public class ReadFile extends Operation implements ActionListener { private VariableTextField fileNameTxt; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String path = fileNameTxt.getText(); File file = new File(path); @@ -28,7 +30,7 @@ protected byte[] perform(byte[] input) throws Exception { fis.read(data); fis.close(); - return data; + return factory.createByteArray(data); } public void createUI() { diff --git a/src/main/java/de/usd/cstchef/operations/misc/WriteFile.java b/src/main/java/de/usd/cstchef/operations/misc/WriteFile.java index f2cf7bb..1ce99c9 100644 --- a/src/main/java/de/usd/cstchef/operations/misc/WriteFile.java +++ b/src/main/java/de/usd/cstchef/operations/misc/WriteFile.java @@ -7,8 +7,11 @@ import java.io.IOException; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JFileChooser; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -19,29 +22,20 @@ public class WriteFile extends Operation implements ActionListener { private final JFileChooser fileChooser = new JFileChooser(); private VariableTextField fileNameTxt; + private JCheckBox overwriteCheckbox; private String lastPath = ""; private FileOutputStream out; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String path = fileNameTxt.getText(); - if (!lastPath.equals(path)) { - if (out != null) { - out.close(); - out = null; - } - if (!path.isEmpty()) { - out = new FileOutputStream(path); - } - lastPath = path; - } - - if (out != null) { - out.write(input); + if (!path.isEmpty()) { + out = new FileOutputStream(path, this.overwriteCheckbox.isSelected()); + out.write(input.getBytes()); out.write('\n'); + out.close(); } - return input; } @@ -53,6 +47,10 @@ public void createUI() { JButton chooseFileButton = new JButton("Select file"); chooseFileButton.addActionListener(this); this.addUIElement(null, chooseFileButton, false, "button1"); + + this.overwriteCheckbox = new JCheckBox("Append Contents"); + this.overwriteCheckbox.setSelected(true); + this.addUIElement("Append Contents", overwriteCheckbox); } diff --git a/src/main/java/de/usd/cstchef/operations/networking/HTTPRequest.java b/src/main/java/de/usd/cstchef/operations/networking/HTTPRequest.java deleted file mode 100644 index d76335c..0000000 --- a/src/main/java/de/usd/cstchef/operations/networking/HTTPRequest.java +++ /dev/null @@ -1,43 +0,0 @@ -package de.usd.cstchef.operations.networking; - -import javax.swing.JCheckBox; -import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IHttpRequestResponse; -import burp.IHttpService; -import de.usd.cstchef.operations.Operation; -import de.usd.cstchef.operations.OperationCategory; -import de.usd.cstchef.operations.Operation.OperationInfos; -import de.usd.cstchef.view.ui.VariableTextField; - -@OperationInfos(name = "HTTP Request", category = OperationCategory.NETWORKING, description = "Makes an http reqeust and returns the response.") -public class HTTPRequest extends Operation { - - private VariableTextField hostTxt; - private VariableTextField portTxt; - private JCheckBox sslEnabledBox; - - @Override - protected byte[] perform(byte[] input) throws Exception { - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helper = callbacks.getHelpers(); - String protocol = sslEnabledBox.isSelected() ? "https" : "http"; - IHttpService service = helper.buildHttpService(hostTxt.getText(), Integer.valueOf(portTxt.getText()), protocol); - IHttpRequestResponse response = callbacks.makeHttpRequest(service, input); - return response.getResponse(); - } - - @Override - public void createUI() { - this.hostTxt = new VariableTextField(); - this.addUIElement("Host", this.hostTxt); - - this.portTxt = new VariableTextField(); - this.addUIElement("Port", this.portTxt); - - this.sslEnabledBox = new JCheckBox(); - this.addUIElement("SSL", this.sslEnabledBox); - } - -} diff --git a/src/main/java/de/usd/cstchef/operations/networking/PlainRequest.java b/src/main/java/de/usd/cstchef/operations/networking/PlainRequest.java new file mode 100644 index 0000000..5937108 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/networking/PlainRequest.java @@ -0,0 +1,71 @@ +package de.usd.cstchef.operations.networking; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import javax.swing.JCheckBox; +import burp.BurpUtils; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.HttpService; +import burp.api.montoya.http.message.HttpRequestResponse; +import burp.api.montoya.http.message.requests.HttpRequest; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.view.ui.VariableTextField; + +@OperationInfos(name = "Send Plain Request", category = OperationCategory.NETWORKING, description = "Makes an request and returns the response. You can use this operation in combination with e.g. \"Static String\" to perform more complex requests.") +public class PlainRequest extends Operation { + + private VariableTextField hostTxt; + private VariableTextField portTxt; + private JCheckBox sslEnabledBox; + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + MontoyaApi api = BurpUtils.getInstance().getApi(); + HttpService service = HttpService.httpService(hostTxt.getText(), Integer.valueOf(portTxt.getText()), sslEnabledBox.isSelected()); + + Callable runnable = new PlainRequestRunnable(input, service, api); + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future future = executor.submit(runnable); + HttpRequestResponse result = future.get(); + return result == null ? null : result.response().toByteArray(); + } + + @Override + public void createUI() { + this.hostTxt = new VariableTextField(); + this.addUIElement("Host", this.hostTxt); + + this.portTxt = new VariableTextField(); + this.addUIElement("Port", this.portTxt); + + this.sslEnabledBox = new JCheckBox(); + this.addUIElement("SSL", this.sslEnabledBox); + } + + public class PlainRequestRunnable implements Callable{ + + private ByteArray data; + private HttpService service; + private MontoyaApi api; + + public PlainRequestRunnable(ByteArray dataToSent, HttpService service, MontoyaApi api){ + this.data = dataToSent; + this.service = service; + this.api = api; + } + + @Override + public HttpRequestResponse call() throws Exception { + return api.http().sendRequest(HttpRequest.httpRequest(service, data)); + } + + } + +} \ No newline at end of file diff --git a/src/main/java/de/usd/cstchef/operations/setter/HttpGetSetter.java b/src/main/java/de/usd/cstchef/operations/setter/HttpGetSetter.java index c241dcf..14339c3 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/HttpGetSetter.java +++ b/src/main/java/de/usd/cstchef/operations/setter/HttpGetSetter.java @@ -2,10 +2,11 @@ import javax.swing.JCheckBox; -import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IParameter; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.requests.HttpRequest; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -17,34 +18,30 @@ public class HttpGetSetter extends SetterOperation { private JCheckBox urlEncodeAll; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String parameterName = getWhere(); - if( parameterName.equals("") ) + if (parameterName.equals("")) return input; - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - - byte[] newValue = getWhatBytes(); - - if( urlEncodeAll.isSelected() || urlEncode.isSelected() ) - newValue = urlEncode(newValue, urlEncodeAll.isSelected(), helpers); - - IParameter param = getParameter(input, parameterName, IParameter.PARAM_URL, helpers); - - if( param == null ) { - - if( !addIfNotPresent.isSelected() ) - return input; - - param = helpers.buildParameter(parameterName, "dummy", IParameter.PARAM_URL); - input = helpers.addParameter(input, param); - param = getParameter(input, parameterName, IParameter.PARAM_URL, helpers); + if (messageType == MessageType.REQUEST) { + try { + HttpRequest request = HttpRequest.httpRequest(input); + if (request.hasParameter(parameterName, HttpParameterType.URL) || addIfNotPresent.isSelected()) { + return request + .withParameter(HttpParameter.parameter(parameterName, getWhat(), HttpParameterType.URL)) + .toByteArray(); + } else { + return input; + } + } catch (Exception e) { + throw new IllegalArgumentException("Input is not a valid request"); + } + } else if (messageType == MessageType.RESPONSE) { + throw new IllegalArgumentException("Input is not a valid HTTP Request"); + } else { + return parseRawMessage(input); } - - byte[] newRequest = replaceParam(input, param, newValue); - return newRequest; } @Override diff --git a/src/main/java/de/usd/cstchef/operations/setter/HttpHeaderSetter.java b/src/main/java/de/usd/cstchef/operations/setter/HttpHeaderSetter.java index 9feb7e1..95f8599 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/HttpHeaderSetter.java +++ b/src/main/java/de/usd/cstchef/operations/setter/HttpHeaderSetter.java @@ -3,12 +3,18 @@ import javax.swing.JCheckBox; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IRequestInfo; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.HttpHeader; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.api.montoya.ui.editor.extension.HttpRequestEditorProvider; import de.usd.cstchef.Utils; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.extractors.JsonExtractor; @OperationInfos(name = "HTTP Header", category = OperationCategory.SETTER, description = "Set a HTTP header to the specified value.") public class HttpHeaderSetter extends SetterOperation { @@ -16,42 +22,33 @@ public class HttpHeaderSetter extends SetterOperation { private JCheckBox addIfNotPresent; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { - byte[] newValue = getWhatBytes(); - byte[] headerName = getWhereBytes(); - if( headerName.length == 0 ) + String newValue = getWhat(); + String headerName = getWhere(); + if( headerName.length() == 0 ) return input; - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - int length = input.length; - - byte[] headerSearch = new byte[headerName.length + 2]; - System.arraycopy(headerName, 0, headerSearch, 0, headerName.length); - System.arraycopy(": ".getBytes(), 0, headerSearch, headerName.length, 2); - - try { - - int offset = helpers.indexOf(input, headerSearch, false, 0, length); - int start = helpers.indexOf(input, ": ".getBytes(), false, offset, length) + 2; - int end = helpers.indexOf(input, "\r\n".getBytes(), false, start, length); - return Utils.insertAtOffset(input, start, end, newValue); - - } catch( IllegalArgumentException e ) { - - if( !addIfNotPresent.isSelected() ) + if(messageType == MessageType.REQUEST){ + HttpRequest request = HttpRequest.httpRequest(input); + if(request.hasHeader(headerName) || addIfNotPresent.isSelected()){ + return request.withHeader(HttpHeader.httpHeader(headerName, newValue)).toByteArray(); + } + else{ return input; - - IRequestInfo info = helpers.analyzeRequest(input); - int bodyOffset = info.getBodyOffset() - 2; - - byte[] value = new byte[headerSearch.length + newValue.length + 2]; - System.arraycopy(headerSearch, 0, value, 0, headerSearch.length); - System.arraycopy(newValue, 0, value, headerName.length + 2, newValue.length); - System.arraycopy("\r\n".getBytes(), 0, value, headerName.length + 2 + newValue.length, 2); - return Utils.insertAtOffset(input, bodyOffset, bodyOffset, value); - + } + } + else if(messageType == MessageType.RESPONSE){ + HttpResponse response = HttpResponse.httpResponse(input); + if(response.hasHeader(headerName) || addIfNotPresent.isSelected()){ + return response.withAddedHeader(HttpHeader.httpHeader(headerName, newValue)).toByteArray(); + } + else{ + return input; + } + } + else{ + return parseRawMessage(input); } } diff --git a/src/main/java/de/usd/cstchef/operations/setter/HttpJsonSetter.java b/src/main/java/de/usd/cstchef/operations/setter/HttpJsonSetter.java index 542e92c..4f6f643 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/HttpJsonSetter.java +++ b/src/main/java/de/usd/cstchef/operations/setter/HttpJsonSetter.java @@ -1,33 +1,85 @@ package de.usd.cstchef.operations.setter; +import java.awt.Color; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +import javax.swing.JCheckBox; + +import com.jayway.jsonpath.DocumentContext; +import com.jayway.jsonpath.JsonPath; + import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IParameter; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.HttpHeader; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.params.ParsedHttpParameter; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import de.usd.cstchef.Utils; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.extractors.JsonExtractor; +import de.usd.cstchef.view.ui.VariableTextField; @OperationInfos(name = "HTTP JSON", category = OperationCategory.SETTER, description = "Set a JSON parameter to the specified value.") public class HttpJsonSetter extends SetterOperation { + private JCheckBox addIfNotPresent; + private VariableTextField path; + @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String parameterName = getWhere(); - if( parameterName.equals("") ) + if (parameterName.equals("")) return input; - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); + if (messageType == MessageType.REQUEST) { + return HttpRequest.httpRequest(input) + .withParameter(HttpParameter.parameter(parameterName, getWhat(), HttpParameterType.JSON)) + .toByteArray(); + } else if (messageType == MessageType.RESPONSE) { + HttpResponse response = HttpResponse.httpResponse(input); + return response.withBody(Utils.jsonSetter(response.body(), parameterName, getWhat(), + addIfNotPresent.isSelected(), path.getText())).toByteArray(); + + } else { + return parseRawMessage(input); + } + } - byte[] newValue = getWhatBytes(); - IParameter param = getParameter(input, parameterName, IParameter.PARAM_JSON, helpers); + @Override + public void createUI() { + super.createUI(); - if( param == null ) - return input; + this.addIfNotPresent = new JCheckBox("Add if not present"); + this.addIfNotPresent.setSelected(true); + this.addUIElement(null, this.addIfNotPresent, "checkbox1"); - byte[] newRequest = replaceParam(input, param, newValue); - return newRequest; - } + this.path = new VariableTextField(); + this.path.setText("Insert-Path"); + this.path.setForeground(Color.GRAY); + this.path.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + if (path.getText().equals("Insertion Path")) { + path.setText(""); + path.setForeground(null); + } + } + @Override + public void focusLost(FocusEvent e) { + if (path.getText().isEmpty()) { + path.setForeground(Color.GRAY); + path.setText("Insertion Path"); + } + } + }); + this.addUIElement(null, this.path, "textbox1"); + } } diff --git a/src/main/java/de/usd/cstchef/operations/setter/HttpMultipartSetter.java b/src/main/java/de/usd/cstchef/operations/setter/HttpMultipartSetter.java new file mode 100644 index 0000000..9607ffb --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/setter/HttpMultipartSetter.java @@ -0,0 +1,36 @@ +package de.usd.cstchef.operations.setter; + +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.requests.HttpRequest; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.OperationCategory; + +@OperationInfos(name = "HTTP Multipart Param", category = OperationCategory.SETTER, description = "Sets a part of a multipart/form-data request to the specified value.") +public class HttpMultipartSetter extends SetterOperation { + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + + String parameterName = getWhere(); + if (parameterName.equals("")) + return input; + + if (messageType == MessageType.REQUEST) { + try { + return HttpRequest.httpRequest(input) + .withParameter(HttpParameter.parameter(parameterName, getWhat(), HttpParameterType.BODY)) + .toByteArray(); + } catch (Exception e) { + throw new IllegalArgumentException("Input is not a valid request"); + } + } else if (messageType == MessageType.RESPONSE) { + throw new IllegalArgumentException("Input is not a valid HTTP Request"); + } else { + return parseRawMessage(input); + } + + } +} \ No newline at end of file diff --git a/src/main/java/de/usd/cstchef/operations/setter/HttpPostSetter.java b/src/main/java/de/usd/cstchef/operations/setter/HttpPostSetter.java index 8dc1158..593c2f8 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/HttpPostSetter.java +++ b/src/main/java/de/usd/cstchef/operations/setter/HttpPostSetter.java @@ -2,10 +2,11 @@ import javax.swing.JCheckBox; -import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IParameter; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.requests.HttpRequest; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -17,38 +18,30 @@ public class HttpPostSetter extends SetterOperation { private JCheckBox urlEncodeAll; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String parameterName = getWhere(); - if( parameterName.equals("") ) + if (parameterName.equals("")) return input; - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - - byte[] newValue = getWhatBytes(); - - if( urlEncodeAll.isSelected() || urlEncode.isSelected() ) - newValue = urlEncode(newValue, urlEncodeAll.isSelected(), helpers); - - IParameter param = getParameter(input, parameterName, IParameter.PARAM_BODY, helpers); - - if( param == null ) { - - if( !addIfNotPresent.isSelected() ) - return input; - - param = helpers.buildParameter(parameterName, "dummy", IParameter.PARAM_BODY); - input = helpers.addParameter(input, param); - param = getParameter(input, parameterName, IParameter.PARAM_BODY, helpers); - if( param == null ) - // This case occurs when the HTTP request is a JSON or XML request. Burp does not - // support adding parameters to these and therefore the request should stay unmodified. - throw new IllegalArgumentException("Failure while adding the parameter. Operation cannot be used on XML or JSON."); + if (messageType == MessageType.REQUEST) { + try { + HttpRequest request = HttpRequest.httpRequest(input); + if (request.hasParameter(parameterName, HttpParameterType.BODY) || addIfNotPresent.isSelected()) { + return request + .withParameter(HttpParameter.parameter(parameterName, getWhat(), HttpParameterType.BODY)) + .toByteArray(); + } else { + return input; + } + } catch (Exception e) { + throw new IllegalArgumentException("Input is not a valid request"); + } + } else if (messageType == MessageType.RESPONSE) { + throw new IllegalArgumentException("Input is not a valid HTTP Request"); + } else { + return parseRawMessage(input); } - - byte[] newRequest = replaceParam(input, param, newValue); - return newRequest; } @Override diff --git a/src/main/java/de/usd/cstchef/operations/setter/HttpSetBody.java b/src/main/java/de/usd/cstchef/operations/setter/HttpSetBody.java index b07ff2e..c7e5acc 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/HttpSetBody.java +++ b/src/main/java/de/usd/cstchef/operations/setter/HttpSetBody.java @@ -3,8 +3,14 @@ import java.util.Arrays; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IRequestInfo; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.HttpHeader; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -16,18 +22,20 @@ public class HttpSetBody extends Operation { private FormatTextField replacementTxt; @Override - protected byte[] perform(byte[] input) throws Exception { - IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks(); - IRequestInfo requestInfo = cbs.getHelpers().analyzeRequest(input); - int bodyOffset = requestInfo.getBodyOffset(); - - byte[] noBody = Arrays.copyOfRange(input, 0, bodyOffset); - byte[] newBody = replacementTxt.getText(); - byte[] newRequest = new byte[noBody.length + newBody.length]; - System.arraycopy(noBody, 0, newRequest, 0, noBody.length); - System.arraycopy(newBody, 0, newRequest, noBody.length, newBody.length); - - return newRequest; + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + ByteArray replacementBody = replacementTxt.getText(); + if( replacementBody.toString().equals("") ) + return input; + + if(messageType == MessageType.REQUEST){ + return HttpRequest.httpRequest(input).withBody(replacementBody).toByteArray(); + } + else if(messageType == MessageType.RESPONSE){ + return HttpResponse.httpResponse(input).withBody(replacementBody).toByteArray(); + } + else{ + return parseRawMessage(input); + } } @Override diff --git a/src/main/java/de/usd/cstchef/operations/setter/HttpSetCookie.java b/src/main/java/de/usd/cstchef/operations/setter/HttpSetCookie.java index 2c8a3a5..21969b0 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/HttpSetCookie.java +++ b/src/main/java/de/usd/cstchef/operations/setter/HttpSetCookie.java @@ -1,12 +1,20 @@ package de.usd.cstchef.operations.setter; +import java.util.List; + import javax.swing.JCheckBox; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IResponseInfo; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.Cookie; +import burp.api.montoya.http.message.HttpHeader; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; import de.usd.cstchef.Utils; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -16,68 +24,42 @@ public class HttpSetCookie extends SetterOperation { private JCheckBox addIfNotPresent; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { - byte[] newValue = getWhatBytes(); - byte[] cookieName = getWhereBytes(); - if( cookieName.length == 0 ) + String cookieName = getWhere(); + String cookieValue = getWhat(); + if (getWhat().equals("")) return input; - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - int length = input.length; - - byte[] cookieSearch = new byte[cookieName.length + 1]; - System.arraycopy(cookieName, 0, cookieSearch, 0, cookieName.length); - System.arraycopy("=".getBytes(), 0, cookieSearch, cookieName.length, 1); - - IResponseInfo resp = helpers.analyzeResponse(input); - boolean isRequest = (resp.getStatusCode() == 0); - - String cookieHeader = "\r\nSet-Cookie: "; - if(isRequest) - cookieHeader = "\r\nCookie: "; - - int offset = -1; - int cookieHeaderLength = cookieHeader.length(); - - try { - - offset = helpers.indexOf(input, cookieHeader.getBytes(), false, 0, length); - int line_end = helpers.indexOf(input, "\r\n".getBytes(), false, offset + 2, length); - int start = helpers.indexOf(input, cookieSearch, true, offset, line_end); - int end = helpers.indexOf(input, ";".getBytes(), true, start, line_end); - - if( end < 0 ) - end = line_end; - - return Utils.insertAtOffset(input, start + cookieSearch.length, end, newValue); - - } catch( IllegalArgumentException e ) { - - if( !addIfNotPresent.isSelected() ) + if (messageType == MessageType.REQUEST) { + HttpRequest request = HttpRequest.httpRequest(input); + if (!Utils.httpRequestCookieExtractor(request, cookieName).equals(ByteArray.byteArray(0)) + || addIfNotPresent.isSelected()) { + return Utils.addCookieToHttpRequest(request, new Utils.CSTCCookie(cookieName, cookieValue)) + .toByteArray(); + } else { return input; - - if( (offset > 0) && isRequest ) { - - byte[] value = new byte[cookieName.length + newValue.length + 3]; - System.arraycopy(cookieName, 0, value, 0, cookieName.length); - System.arraycopy("=".getBytes(), 0, value, cookieName.length, 1); - System.arraycopy(newValue, 0, value, cookieName.length + 1, newValue.length); - System.arraycopy("; ".getBytes(), 0, value, cookieName.length + 1 + newValue.length, 2); - return Utils.insertAtOffset(input, offset + cookieHeaderLength, offset + cookieHeaderLength, value); - + } + } else if (messageType == MessageType.RESPONSE) { + HttpResponse response = HttpResponse.httpResponse(input); + List headers = response.headers(); + for (HttpHeader h : headers) { + if (h.name().equals("Set-Cookie")) { + if (h.value().contains(cookieName)) { + return response.withRemovedHeader(h) + .withAddedHeader(HttpHeader.httpHeader("Set-Cookie", cookieName + "=" + cookieValue)) + .toByteArray(); + } + } + } + if (addIfNotPresent.isSelected()) { + return response.withAddedHeader(HttpHeader.httpHeader("Set-Cookie", cookieName + "=" + cookieValue)) + .toByteArray(); } else { - - int bodyOffset = resp.getBodyOffset() - 4; - byte[] value = new byte[cookieName.length + newValue.length + cookieHeaderLength + 2]; - System.arraycopy(cookieHeader.getBytes(), 0, value, 0, cookieHeaderLength); - System.arraycopy(cookieName, 0, value, cookieHeaderLength, cookieName.length); - System.arraycopy("=".getBytes(), 0, value, cookieHeaderLength + cookieName.length, 1); - System.arraycopy(newValue, 0, value, cookieHeaderLength + cookieName.length + 1, newValue.length); - System.arraycopy(";".getBytes(), 0, value, cookieHeaderLength + cookieName.length + 1 + newValue.length, 1); - return Utils.insertAtOffset(input, bodyOffset, bodyOffset, value); + return input; } + } else { + return parseRawMessage(input); } } diff --git a/src/main/java/de/usd/cstchef/operations/setter/HttpSetUri.java b/src/main/java/de/usd/cstchef/operations/setter/HttpSetUri.java index 348d4c0..607882b 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/HttpSetUri.java +++ b/src/main/java/de/usd/cstchef/operations/setter/HttpSetUri.java @@ -5,8 +5,11 @@ import javax.swing.JCheckBox; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -28,37 +31,45 @@ public void createUI() { this.addUIElement(null, this.checkbox, "checkbox1"); } - @Override - protected byte[] perform(byte[] input) throws Exception { - try { - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - int length = input.length; - - int firstMark = helpers.indexOf(input, " ".getBytes(), false, 0, length); - int lineMark = helpers.indexOf(input, " ".getBytes(), false, firstMark + 1, length); - - int secondMark = helpers.indexOf(input, "?".getBytes(), false, firstMark + 1, length); - - if( !this.checkbox.isSelected() || secondMark < 0 || secondMark >= lineMark ) { - secondMark = lineMark; + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + if( this.uriTxt.getText().equals("") ) + return input; + + if(messageType == MessageType.REQUEST){ + try { + MontoyaApi api = BurpUtils.getInstance().getApi(); + + int length = input.length(); + + int firstMark = api.utilities().byteUtils().indexOf(input.getBytes(), " ".getBytes(), false, 0, length); + int lineMark = api.utilities().byteUtils().indexOf(input.getBytes(), " ".getBytes(), false, firstMark + 1, length); + + int secondMark = api.utilities().byteUtils().indexOf(input.getBytes(), "?".getBytes(), false, firstMark + 1, length); + + if (!this.checkbox.isSelected() || secondMark < 0 || secondMark >= lineMark) { + secondMark = lineMark; + } + + ByteArray method = BurpUtils.subArray(input, 0, firstMark + 1); + ByteArray newUri = this.uriTxt.getBytes(); + ByteArray rest = BurpUtils.subArray(input, secondMark, length); + + ByteArray newRequest = method.withAppended(newUri).withAppended(rest); + + return newRequest; + + } catch (Exception e) { + throw new IllegalArgumentException("Provided input is not a valid http request."); } - - byte[] method = Arrays.copyOfRange(input, 0, firstMark + 1); - byte[] newUri = this.uriTxt.getBytes(); - byte[] rest = Arrays.copyOfRange(input, secondMark, length); - - byte[] newRequest = new byte[method.length + newUri.length + rest.length]; - System.arraycopy(method, 0, newRequest, 0, method.length); - System.arraycopy(newUri, 0, newRequest, method.length, newUri.length); - System.arraycopy(rest, 0, newRequest, method.length + newUri.length, rest.length); - - return newRequest; - - } catch (Exception e) { + } + else if(messageType == MessageType.RESPONSE){ throw new IllegalArgumentException("Provided input is not a valid http request."); } + else{ + return parseRawMessage(input); + } + } } diff --git a/src/main/java/de/usd/cstchef/operations/setter/HttpXmlSetter.java b/src/main/java/de/usd/cstchef/operations/setter/HttpXmlSetter.java index a291fa4..363a6ef 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/HttpXmlSetter.java +++ b/src/main/java/de/usd/cstchef/operations/setter/HttpXmlSetter.java @@ -1,33 +1,81 @@ package de.usd.cstchef.operations.setter; +import java.io.ByteArrayInputStream; +import java.io.StringWriter; + +import javax.swing.JCheckBox; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IParameter; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.params.ParsedHttpParameter; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import de.usd.cstchef.Utils; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.view.ui.VariableTextField; @OperationInfos(name = "HTTP XML", category = OperationCategory.SETTER, description = "Set a XML parameter to the specified value.") public class HttpXmlSetter extends SetterOperation { @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String parameterName = getWhere(); - if( parameterName.equals("") ) + if (parameterName.equals("")) return input; - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - - byte[] newValue = getWhatBytes(); - IParameter param = getParameter(input, parameterName, IParameter.PARAM_XML, helpers); - - if( param == null ) - return input; + if (messageType == MessageType.REQUEST) { + try { + HttpRequest request = HttpRequest.httpRequest(input); + if (request.hasParameter(parameterName, HttpParameterType.XML)) { + return request + .withParameter(HttpParameter.parameter(parameterName, getWhat(), HttpParameterType.XML)) + .toByteArray(); + } else { + return input; + } + } catch (Exception e) { + throw new IllegalArgumentException("Input is not a valid request"); + } + } else if (messageType == MessageType.RESPONSE) { + HttpResponse response = HttpResponse.httpResponse(input); + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = builder.parse(new ByteArrayInputStream(response.bodyToString().getBytes())); + doc.getDocumentElement().normalize(); + NodeList nodeList = doc.getElementsByTagName(parameterName); + Element first = (Element) nodeList.item(0); + if (first != null) { + first.setTextContent(getWhat()); + } + else{ + throw new IllegalArgumentException("Parameter could not be found"); + } + DOMSource domSource = new DOMSource(doc); + StringWriter writer = new StringWriter(); + StreamResult result = new StreamResult(writer); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.transform(domSource, result); + return response.withBody(writer.toString()).toByteArray(); + } else { + return parseRawMessage(input); + } - byte[] newRequest = replaceParam(input, param, newValue); - return newRequest; } } diff --git a/src/main/java/de/usd/cstchef/operations/setter/JsonSetter.java b/src/main/java/de/usd/cstchef/operations/setter/JsonSetter.java index d22d0e8..07ec9f0 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/JsonSetter.java +++ b/src/main/java/de/usd/cstchef/operations/setter/JsonSetter.java @@ -8,44 +8,22 @@ import javax.swing.JCheckBox; -import com.jayway.jsonpath.DocumentContext; -import com.jayway.jsonpath.JsonPath; - +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.view.ui.VariableTextField; -@OperationInfos(name = "JSON", category = OperationCategory.SETTER, description = "Set value of json object.") +@OperationInfos(name = "JSON", category = OperationCategory.SETTER, description = "Set the value of a JSON object.") public class JsonSetter extends SetterOperation implements ActionListener { private JCheckBox addIfNotPresent; private VariableTextField path; @Override - protected byte[] perform(byte[] input) throws Exception { - - if( getWhere().equals("") ) - return input; - - DocumentContext document = JsonPath.parse(new String(input)); - - try { - document.read(getWhere()); - } catch( Exception e ) { - - if( !addIfNotPresent.isSelected() ) - throw new IllegalArgumentException("Key not found."); - - String insertPath = this.path.getText(); - if( insertPath.equals("Insert-Path") || insertPath.equals("") ) - insertPath = "$"; - - document = document.put(insertPath, getWhere(), getWhat()); - return document.jsonString().getBytes(); - } - - document.set(getWhere(), getWhat()); - return document.jsonString().getBytes(); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + return Utils.jsonSetter(input, getWhere(), getWhat(), addIfNotPresent.isSelected(), path.getText()); } @Override diff --git a/src/main/java/de/usd/cstchef/operations/setter/LineSetter.java b/src/main/java/de/usd/cstchef/operations/setter/LineSetter.java index 9ef1b1e..05fa2cc 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/LineSetter.java +++ b/src/main/java/de/usd/cstchef/operations/setter/LineSetter.java @@ -6,9 +6,10 @@ import javax.swing.JComboBox; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.Utils; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -19,7 +20,7 @@ public class LineSetter extends SetterOperation { private JComboBox formatBox; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { int lineNumber; try { @@ -32,45 +33,42 @@ protected byte[] perform(byte[] input) throws Exception { if( lineNumber <= 0 ) return input; - byte[] newValue = getWhatBytes(); - byte[] lineEndings = "\r\n".getBytes(); + ByteArray newValue = getWhatBytes(); + ByteArray lineEndings = factory.createByteArray("\r\n"); switch ((String) this.formatBox.getSelectedItem()) { case "\\r\\n": - lineEndings = "\r\n".getBytes(); + lineEndings = factory.createByteArray("\r\n"); break; case "\\r": - lineEndings = "\r".getBytes(); + lineEndings = factory.createByteArray("\r"); break; case "\\n": - lineEndings = "\n".getBytes(); + lineEndings = factory.createByteArray("\n"); break; } - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - int length = input.length; + MontoyaApi api = BurpUtils.getInstance().getApi(); + int length = input.length(); int start = 0; int offset = 0; int counter = 0; while( counter < lineNumber - 1 ) { - offset = helpers.indexOf(input, lineEndings, false, start, length); + offset = api.utilities().byteUtils().indexOf(input.getBytes(), lineEndings.getBytes(), false, start, length); if( offset >= 0 ) { - start = offset + lineEndings.length; + start = offset + lineEndings.length(); counter++; } else { break; } } - int end = helpers.indexOf(input, lineEndings, false, start, length); + int end = api.utilities().byteUtils().indexOf(input.getBytes(), lineEndings.getBytes(), false, start, length); if( end < 0 ) end = length; if( append.isSelected() ) { - byte[] value = new byte[newValue.length + lineEndings.length]; - System.arraycopy(lineEndings, 0, value, 0, lineEndings.length); - System.arraycopy(newValue, 0, value, lineEndings.length, newValue.length); + ByteArray value = lineEndings.withAppended(newValue); return Utils.insertAtOffset(input, end, end, value); } else { return Utils.insertAtOffset(input, start, end, newValue); diff --git a/src/main/java/de/usd/cstchef/operations/setter/SetterOperation.java b/src/main/java/de/usd/cstchef/operations/setter/SetterOperation.java index 2a6aafb..0679ee7 100644 --- a/src/main/java/de/usd/cstchef/operations/setter/SetterOperation.java +++ b/src/main/java/de/usd/cstchef/operations/setter/SetterOperation.java @@ -7,9 +7,13 @@ import org.bouncycastle.util.encoders.Hex; -import burp.IExtensionHelpers; -import burp.IParameter; -import burp.IRequestInfo; +import burp.BurpUtils; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.HttpParameterType; +import burp.api.montoya.http.message.params.ParsedHttpParameter; +import burp.api.montoya.http.message.requests.HttpRequest; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.view.ui.VariableTextField; @@ -30,7 +34,7 @@ protected String getWhere() { return whereToSet.getText(); } - protected byte[] getWhereBytes() { + protected ByteArray getWhereBytes() { return whereToSet.getBytes(); } @@ -38,22 +42,21 @@ protected String getWhat() { return whatToSet.getText(); } - protected byte[] getWhatBytes() { + protected ByteArray getWhatBytes() { return whatToSet.getBytes(); } // This is required because Burps getRequestParameter returns always the first occurrence of the parameter name. // If you have e.g. a cookie with the same name as the POST parameter, you have no chance of getting the POST // parameter using getRequestParameter (at least I do not know how). - protected IParameter getParameter(byte[] request, String paramName, byte type, IExtensionHelpers helpers) { + protected ParsedHttpParameter getParameter(ByteArray request, String paramName, HttpParameterType type, MontoyaApi api) { - IRequestInfo info = helpers.analyzeRequest(request); - List parameters = info.getParameters(); - IParameter param = null; + List parameters = HttpRequest.httpRequest(request).parameters(); + ParsedHttpParameter param = null; - for(IParameter p:parameters) { - if( p.getName().equals(paramName) ) - if( p.getType() == type ) { + for(ParsedHttpParameter p:parameters) { + if( p.name().equals(paramName) ) + if( p.type().equals(type) ) { param = p; break; } @@ -61,43 +64,40 @@ protected IParameter getParameter(byte[] request, String paramName, byte type, I return param; } - protected byte[] urlEncode(byte[] input, boolean all, IExtensionHelpers helpers) throws IOException { + protected ByteArray urlEncode(ByteArray input, boolean all, MontoyaApi api) throws IOException { - byte[] newValue = input; + ByteArray newValue = input; if( all ) { byte[] delimiter = "%".getBytes(); ByteArrayOutputStream out = new ByteArrayOutputStream(); out.write(delimiter); - for (int i = 0; i < newValue.length - 1; i++) { - out.write(Hex.encode(new byte[] { newValue[i] })); + for (int i = 0; i < newValue.length() - 1; i++) { + out.write(Hex.encode(new byte[] { newValue.getByte(i) })); out.write(delimiter); } - out.write(Hex.encode(new byte[] { newValue[newValue.length - 1] })); - newValue = out.toByteArray(); + out.write(Hex.encode(new byte[] { newValue.getByte(newValue.length() - 1) })); + newValue = factory.createByteArray(out.toByteArray()); } else { - newValue = helpers.urlEncode(input); + newValue = api.utilities().urlUtils().encode(input); } return newValue; } - protected byte[] replaceParam(byte[] request, IParameter param, byte[] newValue) { + protected ByteArray replaceParam(ByteArray request, ParsedHttpParameter param, ByteArray newValue) { - int length = request.length; - int start = param.getValueStart(); - int end = param.getValueEnd(); + int length = request.length(); + int start = param.valueOffsets().startIndexInclusive(); + int end = param.valueOffsets().endIndexExclusive(); - byte[] prefix = Arrays.copyOfRange(request, 0, start); - byte[] rest = Arrays.copyOfRange(request, end, length); - - byte[] newRequest = new byte[prefix.length + newValue.length + rest.length]; - System.arraycopy(prefix, 0, newRequest, 0, prefix.length); - System.arraycopy(newValue, 0, newRequest, prefix.length, newValue.length); - System.arraycopy(rest, 0, newRequest, prefix.length + newValue.length, rest.length); + ByteArray prefix = BurpUtils.subArray(request, 0, start); + ByteArray rest = BurpUtils.subArray(request, end, length); + + ByteArray newRequest = prefix.withAppended(newValue).withAppended(rest); return newRequest; } diff --git a/src/main/java/de/usd/cstchef/operations/signature/JWTDecode.java b/src/main/java/de/usd/cstchef/operations/signature/JWTDecode.java new file mode 100644 index 0000000..6022c69 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/signature/JWTDecode.java @@ -0,0 +1,45 @@ +package de.usd.cstchef.operations.signature; + +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.view.ui.VariableTextField; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.interfaces.RSAKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +import javax.swing.JComboBox; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import org.apache.commons.lang3.NotImplementedException; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.RSAKeyProvider; + +import burp.Logger; +import burp.api.montoya.core.ByteArray; + +@OperationInfos(name = "JWT Decode", category = OperationCategory.SIGNATURE, description = "Decode a given JWT payload and return its contents") +public class JWTDecode extends Operation { + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + DecodedJWT content = JWT.decode(input.toString()); + return factory.createByteArray(Base64.getDecoder().decode(content.getPayload())); + } +} diff --git a/src/main/java/de/usd/cstchef/operations/signature/JWTSign.java b/src/main/java/de/usd/cstchef/operations/signature/JWTSign.java new file mode 100644 index 0000000..f1a2f0b --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/signature/JWTSign.java @@ -0,0 +1,160 @@ +package de.usd.cstchef.operations.signature; + +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.view.ui.VariableTextField; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.interfaces.RSAKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +import javax.swing.JComboBox; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import org.apache.commons.lang3.NotImplementedException; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.RSAKeyProvider; + +import burp.Logger; +import burp.api.montoya.core.ByteArray; + +@OperationInfos(name = "JWT Sign", category = OperationCategory.SIGNATURE, description = "Sign a given JWT payload") +public class JWTSign extends Operation implements ActionListener, DocumentListener { + + private VariableTextField secretValue; + private JComboBox algorithms; + private Algorithm currentAlgorithm; + private boolean hasError= false; + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + if(this.hasError) { + throw new IllegalArgumentException("Key not valid"); + } + String token = JWT.create().withPayload(input.toString()).sign(this.currentAlgorithm); + return factory.createByteArray(token); + } + + public void createUI() { + this.secretValue = new VariableTextField(); + this.secretValue.getDocument().addDocumentListener(this); + this.secretValue.setText(""); + + this.addUIElement("Secret or Key", this.secretValue); + + this.algorithms = new JComboBox(); + this.algorithms.addItem(AlgorithmItem.HS256); + this.algorithms.addItem(AlgorithmItem.HS384); + this.algorithms.addItem(AlgorithmItem.HS512); + this.algorithms.addItem(AlgorithmItem.RSA256); + this.algorithms.addItem(AlgorithmItem.RSA384); + this.algorithms.addItem(AlgorithmItem.RSA512); + this.algorithms.addActionListener(this); + this.algorithms.setSelectedIndex(0); + this.addUIElement("Algorithm to sign the JWT with", algorithms); + } + + private void reconfigureAlgorithmAndKey() { + try { + AlgorithmItem item = (AlgorithmItem)this.algorithms.getSelectedItem(); + String secret = this.secretValue.getText(); + switch(item){ + case HS256: + this.currentAlgorithm = Algorithm.HMAC256(secret); + break; + case HS384: + this.currentAlgorithm = Algorithm.HMAC384(secret); + break; + case HS512: + this.currentAlgorithm = Algorithm.HMAC512(secret); + break; + case RSA256: + this.currentAlgorithm = Algorithm.RSA256(this.createKeyFromString(secret)); + break; + case RSA384: + this.currentAlgorithm = Algorithm.RSA384(this.createKeyFromString(secret)); + break; + case RSA512: + this.currentAlgorithm = Algorithm.RSA512(this.createKeyFromString(secret)); + break; + default: + throw new NotImplementedException("Chosen algorithm unknown"); + } + this.hasError=false; + } + catch(Exception ex) { + this.hasError=true; + } + + + } + + @Override + public void actionPerformed(ActionEvent e) { + this.reconfigureAlgorithmAndKey(); + } + + private RSAKey createKeyFromString(String input) throws Exception { + StringBuilder pkcs8Lines = new StringBuilder(); + BufferedReader rdr = new BufferedReader(new StringReader(input)); + String line; + while ((line = rdr.readLine()) != null) { + pkcs8Lines.append(line); + } + + String pkcs8Pem = pkcs8Lines.toString(); + pkcs8Pem = pkcs8Pem.replace("-----BEGIN PRIVATE KEY-----", ""); + pkcs8Pem = pkcs8Pem.replace("-----END PRIVATE KEY-----", ""); + pkcs8Pem = pkcs8Pem.replaceAll("\\s+",""); + + byte [] pkcs8EncodedBytes = Base64.getDecoder().decode(pkcs8Pem); + + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes); + KeyFactory kf = KeyFactory.getInstance("RSA"); + RSAPrivateKey privKey = (RSAPrivateKey)kf.generatePrivate(keySpec); + return privKey; + } + + private enum AlgorithmItem{ + HS256("HS256"), HS384("HS384"), HS512("HS512"), RSA256("RSA256"), RSA384("RSA384"), RSA512("RSA512"); + private String name; + private AlgorithmItem(String name) { + this.name = name; + } + + @Override + public String toString(){ + return name; + } + } + + @Override + public void insertUpdate(DocumentEvent e) { + this.reconfigureAlgorithmAndKey(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + this.reconfigureAlgorithmAndKey(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + this.reconfigureAlgorithmAndKey(); + } +} diff --git a/src/main/java/de/usd/cstchef/operations/signature/RsaSignature.java b/src/main/java/de/usd/cstchef/operations/signature/RsaSignature.java index af9b959..cdb8436 100644 --- a/src/main/java/de/usd/cstchef/operations/signature/RsaSignature.java +++ b/src/main/java/de/usd/cstchef/operations/signature/RsaSignature.java @@ -7,6 +7,8 @@ import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -24,7 +26,7 @@ public RsaSignature() { this.createMyUI(); } - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { if( !this.keyAvailable.isSelected() ) throw new IllegalArgumentException("No private key available."); @@ -36,18 +38,18 @@ protected byte[] perform(byte[] input) throws Exception { String selectedOutputMode = (String)outputMode.getSelectedItem(); if( selectedInputMode.equals("Hex") ) - input = Hex.decode(input); + input = factory.createByteArray(Hex.decode(input.getBytes())); if( selectedInputMode.equals("Base64") ) - input = Base64.decode(input); + input = factory.createByteArray(Base64.decode(input.getBytes())); signature.initSign(this.selectedEntry.getPrivateKey()); - signature.update(input); - byte[] result = signature.sign(); + signature.update(input.getBytes()); + ByteArray result = factory.createByteArray(signature.sign()); if( selectedOutputMode.equals("Hex") ) - result = Hex.encode(result); + result = factory.createByteArray(Hex.encode(result.getBytes())); if( selectedOutputMode.equals("Base64") ) - result = Base64.encode(result); + result = factory.createByteArray(Base64.encode(result.getBytes())); return result; } @@ -57,7 +59,7 @@ public void createMyUI() { super.createMyUI(); SignatureUtils utils = SignatureUtils.getInstance(); - this.algos = new JComboBox<>(utils.getRsaAlgos()); + this.algos = new JComboBox<>(utils.getAlgos("RSA")); this.addUIElement("Padding", this.algos); this.inputMode = new JComboBox<>(inOutModes); diff --git a/src/main/java/de/usd/cstchef/operations/signature/SM2Signature.java b/src/main/java/de/usd/cstchef/operations/signature/SM2Signature.java new file mode 100644 index 0000000..15d1fe5 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/signature/SM2Signature.java @@ -0,0 +1,70 @@ +package de.usd.cstchef.operations.signature; + +import java.security.Signature; + +import javax.swing.JComboBox; + +import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.encoders.Hex; + +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.OperationCategory; + +@OperationInfos(name = "SM2 Signature", category = OperationCategory.SIGNATURE, description = "Create a SM2 signature") +public class SM2Signature extends KeystoreOperation { + + private static String[] inOutModes = new String[] { "Raw", "Hex", "Base64" }; + + protected JComboBox algos; + protected JComboBox inputMode; + protected JComboBox outputMode; + + public SM2Signature() { + super(); + this.createMyUI(); + } + + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + if( !this.keyAvailable.isSelected() ) + throw new IllegalArgumentException("No private key available."); + + String algo = (String)algos.getSelectedItem(); + Signature signature = Signature.getInstance(algo); + + String selectedInputMode = (String)inputMode.getSelectedItem(); + String selectedOutputMode = (String)outputMode.getSelectedItem(); + + if( selectedInputMode.equals("Hex") ) + input = factory.createByteArray(Hex.decode(input.getBytes())); + if( selectedInputMode.equals("Base64") ) + input = factory.createByteArray(Base64.decode(input.getBytes())); + + signature.initSign(this.selectedEntry.getPrivateKey()); + signature.update(input.getBytes()); + ByteArray result = factory.createByteArray(signature.sign()); + + if( selectedOutputMode.equals("Hex") ) + result = factory.createByteArray(Hex.encode(result.getBytes())); + if( selectedOutputMode.equals("Base64") ) + result = factory.createByteArray(Base64.encode(result.getBytes())); + + return result; + } + + public void createMyUI() { + + super.createMyUI(); + SignatureUtils utils = SignatureUtils.getInstance(); + + this.algos = new JComboBox<>(utils.getAlgos("SM2")); + this.addUIElement("Padding", this.algos); + + this.inputMode = new JComboBox<>(inOutModes); + this.addUIElement("Input", this.inputMode); + + this.outputMode = new JComboBox<>(inOutModes); + this.addUIElement("Output", this.outputMode); + } +} diff --git a/src/main/java/de/usd/cstchef/operations/signature/SignatureUtils.java b/src/main/java/de/usd/cstchef/operations/signature/SignatureUtils.java index f474b2b..3c0a271 100644 --- a/src/main/java/de/usd/cstchef/operations/signature/SignatureUtils.java +++ b/src/main/java/de/usd/cstchef/operations/signature/SignatureUtils.java @@ -35,8 +35,9 @@ public static SignatureUtils getInstance() { public String[] getAlgos() { return algos.toArray(new String[0]); } - public String[] getRsaAlgos() { - List rsaAlgos = algos.stream().filter(p -> p.contains("RSA")).collect(Collectors.toList()); + public String[] getAlgos(String s) { + List rsaAlgos = algos.stream().filter(p -> p.contains(s)).collect(Collectors.toList()); return rsaAlgos.toArray(new String[0]); } + } diff --git a/src/main/java/de/usd/cstchef/operations/signature/SoapMultiSignature.java b/src/main/java/de/usd/cstchef/operations/signature/SoapMultiSignature.java index 93c7818..387eacc 100644 --- a/src/main/java/de/usd/cstchef/operations/signature/SoapMultiSignature.java +++ b/src/main/java/de/usd/cstchef/operations/signature/SoapMultiSignature.java @@ -43,6 +43,8 @@ import org.w3c.dom.Element; import org.w3c.dom.NodeList; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.view.ui.FormatTextField; @@ -87,7 +89,7 @@ private ArrayList getReferences(XMLSignatureFactory fac) throws Excep writer.println("test"); writer.close(); for( FormatTextField field : referenceFields ) { - String referenceString = new String(field.getText()); + String referenceString = field.getText().toString(); Reference ref = fac.newReference("#" + referenceString, fac.newDigestMethod(digestMethods.get(digMethod), null), Collections.singletonList (fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null); referenceList.add(ref); } @@ -99,7 +101,7 @@ private ArrayList getReferences(XMLSignatureFactory fac) throws Excep } private void validateIdAttributes(Document doc) throws Exception { - String idAttribute = new String(idIdentifier.getText()); + String idAttribute = idIdentifier.getText().toString(); XPathFactory xPathfactory = XPathFactory.newInstance(); XPath xpath = xPathfactory.newXPath(); XPathExpression expr = xpath.compile("descendant-or-self::*/@" + idAttribute); @@ -139,7 +141,7 @@ private KeyInfo getKeyInfo(XMLSignatureFactory fac, PrivateKeyEntry keyEntry) th } - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String signMethod = (String)signatureMethod.getSelectedItem(); PrivateKeyEntry keyEntry = this.selectedEntry; @@ -152,7 +154,7 @@ protected byte[] perform(byte[] input) throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); - Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(input)); + Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(input.getBytes())); try { validateIdAttributes(doc); } catch( Exception e ) { @@ -167,7 +169,7 @@ protected byte[] perform(byte[] input) throws Exception { TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.transform(source, result); - return bos.toByteArray(); + return factory.createByteArray(bos.toByteArray()); } public void createMyUI() { diff --git a/src/main/java/de/usd/cstchef/operations/signature/XmlFullSignature.java b/src/main/java/de/usd/cstchef/operations/signature/XmlFullSignature.java index 7f9071f..a2239f4 100644 --- a/src/main/java/de/usd/cstchef/operations/signature/XmlFullSignature.java +++ b/src/main/java/de/usd/cstchef/operations/signature/XmlFullSignature.java @@ -12,7 +12,9 @@ import org.w3c.dom.Document; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; @OperationInfos(name = "Xml Full Signature", category = OperationCategory.SIGNATURE, description = "Create a XML signature over the whole document.") @@ -22,12 +24,12 @@ public XmlFullSignature() { super(); } - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); - Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(input)); + Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(input.getBytes())); this.createSignature(doc); @@ -37,7 +39,7 @@ protected byte[] perform(byte[] input) throws Exception { TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.transform(source, result); - return bos.toByteArray(); + return factory.createByteArray(bos.toByteArray()); } } diff --git a/src/main/java/de/usd/cstchef/operations/signature/XmlMultiSignature.java b/src/main/java/de/usd/cstchef/operations/signature/XmlMultiSignature.java index 79bd83a..2164e63 100644 --- a/src/main/java/de/usd/cstchef/operations/signature/XmlMultiSignature.java +++ b/src/main/java/de/usd/cstchef/operations/signature/XmlMultiSignature.java @@ -11,7 +11,9 @@ import org.w3c.dom.Document; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation.OperationInfos; @OperationInfos(name = "Xml Multi Signature", category = OperationCategory.SIGNATURE, description = "Create a XML signature over specified fields.") @@ -22,12 +24,12 @@ public XmlMultiSignature() { this.addIdSelectors(); } - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); dbf.setXIncludeAware(false); dbf.setExpandEntityReferences(false); - Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(input)); + Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(input.getBytes())); this.createSignature(doc); @@ -37,7 +39,7 @@ protected byte[] perform(byte[] input) throws Exception { TransformerFactory transformerFactory = TransformerFactory.newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.transform(source, result); - return bos.toByteArray(); + return factory.createByteArray(bos.toByteArray()); } } diff --git a/src/main/java/de/usd/cstchef/operations/signature/XmlSignature.java b/src/main/java/de/usd/cstchef/operations/signature/XmlSignature.java index c25fb68..8cf18bb 100644 --- a/src/main/java/de/usd/cstchef/operations/signature/XmlSignature.java +++ b/src/main/java/de/usd/cstchef/operations/signature/XmlSignature.java @@ -78,7 +78,7 @@ protected ArrayList getReferences() throws Exception { ArrayList referenceList = new ArrayList(); if( referenceFields != null && referenceFields.size() > 0) { for( FormatTextField field : referenceFields ) { - String referenceString = new String(field.getText()); + String referenceString = field.getText().toString(); Reference ref = signatureFac.newReference("#" + referenceString, signatureFac.newDigestMethod(digestMethods.get(digMethod), null), Collections.singletonList (signatureFac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null); referenceList.add(ref); } @@ -91,7 +91,7 @@ protected ArrayList getReferences() throws Exception { protected void validateIdAttributes(Document doc) throws Exception { - String idAttribute = new String(idIdentifier.getText()); + String idAttribute = idIdentifier.getText().toString(); if( idAttribute == null || idAttribute.isEmpty() ) { return; } diff --git a/src/main/java/de/usd/cstchef/operations/string/Concatenate.java b/src/main/java/de/usd/cstchef/operations/string/Concatenate.java new file mode 100644 index 0000000..d212503 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/string/Concatenate.java @@ -0,0 +1,67 @@ +package de.usd.cstchef.operations.string; + +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.VariableStore; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.view.ui.VariableTextArea; +import de.usd.cstchef.view.ui.VariableTextField; + + +@OperationInfos(name = "Concatenate", category = OperationCategory.STRING, description = "Concatenate CSTC Input and/or your own. \"$input\" to work with CSTC Input.") +public class Concatenate extends Operation { + + protected VariableTextArea text; + protected VariableTextField delimiter; + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + + String delim = delimiter.getText(); + VariableStore.getInstance().setVariable("input", input); + + String[] components = text.getText().split(delim); + String trimed; + byte[][] value = new byte[components.length][]; + for (int i = 0; i < components.length; i++) { + trimed = components[i].trim(); + + if (trimed.startsWith("$")) { + value[i] = VariableStore.getInstance().getVariable(trimed).getBytes(); + } else { + value[i] = trimed.getBytes(); + } + } + + return factory.createByteArray(flatten_array(value)); + } + + public void createUI() { + this.text = new VariableTextArea(); + this.addUIElement("Strings", this.text); + this.delimiter = new VariableTextField(); + this.addUIElement("Delimiter", this.delimiter); + } + + private byte[] flatten_array(byte[][] arrays){ + + // Calculate the total length of the concatenated array + int totalLength = 0; + for (byte[] array : arrays) { + totalLength += array.length; + } + + // Create the concatenated array + byte[] result = new byte[totalLength]; + int currentIndex = 0; + for (byte[] array : arrays) { + System.arraycopy(array, 0, result, currentIndex, array.length); + currentIndex += array.length; + } + + return result; + } + +} diff --git a/src/main/java/de/usd/cstchef/operations/string/Length.java b/src/main/java/de/usd/cstchef/operations/string/Length.java index 77c41cf..ac9a06c 100644 --- a/src/main/java/de/usd/cstchef/operations/string/Length.java +++ b/src/main/java/de/usd/cstchef/operations/string/Length.java @@ -1,5 +1,7 @@ package de.usd.cstchef.operations.string; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -8,8 +10,8 @@ public class Length extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { - return String.valueOf(input.length).getBytes(); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + return factory.createByteArray(String.valueOf(input.length())); } } \ No newline at end of file diff --git a/src/main/java/de/usd/cstchef/operations/string/Lowercase.java b/src/main/java/de/usd/cstchef/operations/string/Lowercase.java index 1973b46..c2d601e 100644 --- a/src/main/java/de/usd/cstchef/operations/string/Lowercase.java +++ b/src/main/java/de/usd/cstchef/operations/string/Lowercase.java @@ -1,8 +1,8 @@ package de.usd.cstchef.operations.string; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -11,14 +11,14 @@ public class Lowercase extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { try { if(input != null) { - String inputStr = new String(input); - return inputStr.toLowerCase().getBytes(); + String inputStr = input.toString(); + return factory.createByteArray(inputStr.toLowerCase()); } else { - return "".getBytes(); + return factory.createByteArray(""); } } catch (Exception e) { diff --git a/src/main/java/de/usd/cstchef/operations/string/Prefix.java b/src/main/java/de/usd/cstchef/operations/string/Prefix.java index 108307f..8b3bdcf 100644 --- a/src/main/java/de/usd/cstchef/operations/string/Prefix.java +++ b/src/main/java/de/usd/cstchef/operations/string/Prefix.java @@ -2,6 +2,8 @@ import java.io.ByteArrayOutputStream; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -13,12 +15,12 @@ public class Prefix extends Operation { private FormatTextField prefixTxt; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - out.write(prefixTxt.getText()); - out.write(input); + out.write(prefixTxt.getText().getBytes()); + out.write(input.getBytes()); - return out.toByteArray(); + return factory.createByteArray(out.toByteArray()); } @Override diff --git a/src/main/java/de/usd/cstchef/operations/string/Replace.java b/src/main/java/de/usd/cstchef/operations/string/Replace.java index d328f74..b498095 100644 --- a/src/main/java/de/usd/cstchef/operations/string/Replace.java +++ b/src/main/java/de/usd/cstchef/operations/string/Replace.java @@ -3,8 +3,9 @@ import javax.swing.JCheckBox; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -19,28 +20,24 @@ public class Replace extends Operation { private VariableTextArea replacementTxt; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { - byte[] result = null; + ByteArray result = null; if( checkbox.isSelected() ) { - String inputStr = new String(input); - result = inputStr.replaceAll(exptTxt.getText(), replacementTxt.getText()).getBytes(); + String inputStr = input.toString(); + result = factory.createByteArray(inputStr.replaceAll(exptTxt.getText(), replacementTxt.getText())); } else { - IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = cbs.getHelpers(); - int start = helpers.indexOf(input, exptTxt.getBytes(), true, 0, input.length); + MontoyaApi api = BurpUtils.getInstance().getApi(); + int start = api.utilities().byteUtils().indexOf(input.getBytes(), exptTxt.getBytes().getBytes(), true, 0, input.length()); if(start < 0) return input; - byte[] replaced = exptTxt.getBytes(); - byte[] replacement = replacementTxt.getBytes(); + ByteArray replaced = exptTxt.getBytes(); + ByteArray replacement = replacementTxt.getBytes(); - byte[] newRequest = new byte[input.length + replacement.length - replaced.length]; - System.arraycopy(input, 0, newRequest, 0, start); - System.arraycopy(replacement, 0, newRequest, start, replacement.length); - System.arraycopy(input, start + replaced.length, newRequest, start + replacement.length, input.length - replaced.length - start); + ByteArray newRequest = BurpUtils.subArray(input, 0, start).withAppended(replacement).withAppended(BurpUtils.subArray(input, start + replaced.length(), input.length())); result = newRequest; } diff --git a/src/main/java/de/usd/cstchef/operations/string/Reverse.java b/src/main/java/de/usd/cstchef/operations/string/Reverse.java index 00b9e6c..a84222f 100644 --- a/src/main/java/de/usd/cstchef/operations/string/Reverse.java +++ b/src/main/java/de/usd/cstchef/operations/string/Reverse.java @@ -2,6 +2,8 @@ import org.bouncycastle.util.Arrays; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -10,8 +12,8 @@ public class Reverse extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { - return Arrays.reverse(input); + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + return factory.createByteArray(Arrays.reverse(input.getBytes())); } } \ No newline at end of file diff --git a/src/main/java/de/usd/cstchef/operations/string/SplitAndSelect.java b/src/main/java/de/usd/cstchef/operations/string/SplitAndSelect.java index deafc48..4c41ea2 100644 --- a/src/main/java/de/usd/cstchef/operations/string/SplitAndSelect.java +++ b/src/main/java/de/usd/cstchef/operations/string/SplitAndSelect.java @@ -3,8 +3,9 @@ import org.bouncycastle.util.Arrays; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -17,9 +18,9 @@ public class SplitAndSelect extends Operation { private VariableTextField delim; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { - byte[] delimmiter = delim.getBytes(); + ByteArray delimmiter = delim.getBytes(); int itemNumber = 0; try { @@ -32,35 +33,34 @@ protected byte[] perform(byte[] input) throws Exception { if( itemNumber < 0 ) return input; - IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = cbs.getHelpers(); - int length = input.length; + MontoyaApi api = BurpUtils.getInstance().getApi(); + int length = input.length(); int start = 0; int offset = 0; int counter = 0; while( counter < itemNumber ) { - offset = helpers.indexOf(input, delimmiter, false, start, length); + offset = api.utilities().byteUtils().indexOf(input.getBytes(), delimmiter.getBytes(), false, start, length); if( offset >= 0 ) { - start = offset + delimmiter.length; + start = offset + delimmiter.length(); counter++; } else { break; } } - int end = helpers.indexOf(input, delimmiter, false, start, length); + int end = api.utilities().byteUtils().indexOf(input.getBytes(), delimmiter.getBytes(), false, start, length); if( end < 0 ) end = length; - byte[] result = Arrays.copyOfRange(input, start, end); + ByteArray result = BurpUtils.subArray(input, start, end); return result; } @Override public void createUI() { this.delim = new VariableTextField(); - this.addUIElement("Delimmiter", this.delim); + this.addUIElement("Delimiter", this.delim); this.item = new VariableTextField(); this.addUIElement("Item number", this.item); } diff --git a/src/main/java/de/usd/cstchef/operations/string/StaticString.java b/src/main/java/de/usd/cstchef/operations/string/StaticString.java index 5da6d7c..5eb6f05 100644 --- a/src/main/java/de/usd/cstchef/operations/string/StaticString.java +++ b/src/main/java/de/usd/cstchef/operations/string/StaticString.java @@ -1,5 +1,7 @@ package de.usd.cstchef.operations.string; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -11,7 +13,7 @@ public class StaticString extends Operation { private VariableTextArea stringTxt; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { return this.stringTxt.getBytes(); } diff --git a/src/main/java/de/usd/cstchef/operations/string/Substring.java b/src/main/java/de/usd/cstchef/operations/string/Substring.java index 3980751..49bccd6 100644 --- a/src/main/java/de/usd/cstchef/operations/string/Substring.java +++ b/src/main/java/de/usd/cstchef/operations/string/Substring.java @@ -4,6 +4,9 @@ import org.bouncycastle.util.Arrays; +import burp.BurpUtils; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -15,19 +18,19 @@ public class Substring extends Operation { private JSpinner endSpinner; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { int start = (int) startSpinner.getValue(); int end = (int) endSpinner.getValue(); if( start < 0 ) - start = input.length + start; + start = input.length() + start; if( end < 0 ) - end = input.length + end; - if( end > input.length ) - end = input.length + 1; + end = input.length() + end; + if( end > input.length() ) + end = input.length() + 1; - byte[] slice = Arrays.copyOfRange(input, start, end); + ByteArray slice = BurpUtils.subArray(input, start, end); return slice; } diff --git a/src/main/java/de/usd/cstchef/operations/string/Suffix.java b/src/main/java/de/usd/cstchef/operations/string/Suffix.java index 69fea04..e43c097 100644 --- a/src/main/java/de/usd/cstchef/operations/string/Suffix.java +++ b/src/main/java/de/usd/cstchef/operations/string/Suffix.java @@ -2,6 +2,8 @@ import java.io.ByteArrayOutputStream; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -13,12 +15,12 @@ public class Suffix extends Operation { private FormatTextField suffixTxt; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); - out.write(input); - out.write(suffixTxt.getText()); + out.write(input.getBytes()); + out.write(suffixTxt.getText().getBytes()); - return out.toByteArray(); + return factory.createByteArray(out.toByteArray()); } @Override diff --git a/src/main/java/de/usd/cstchef/operations/string/Uppercase.java b/src/main/java/de/usd/cstchef/operations/string/Uppercase.java index 962912c..d329080 100644 --- a/src/main/java/de/usd/cstchef/operations/string/Uppercase.java +++ b/src/main/java/de/usd/cstchef/operations/string/Uppercase.java @@ -1,8 +1,8 @@ package de.usd.cstchef.operations.string; import burp.BurpUtils; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.Operation.OperationInfos; @@ -11,14 +11,14 @@ public class Uppercase extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { try { if(input != null) { - String inputStr = new String(input); - return inputStr.toUpperCase().getBytes(); + String inputStr = input.toString(); + return factory.createByteArray(inputStr.toUpperCase()); } else { - return "".getBytes(); + return factory.createByteArray(""); } } catch (Exception e) { return input; diff --git a/src/main/java/de/usd/cstchef/operations/utils/Counter.java b/src/main/java/de/usd/cstchef/operations/utils/Counter.java new file mode 100644 index 0000000..c038d45 --- /dev/null +++ b/src/main/java/de/usd/cstchef/operations/utils/Counter.java @@ -0,0 +1,57 @@ +package de.usd.cstchef.operations.utils; + +import java.math.BigInteger; +import java.nio.ByteBuffer; + +import javax.swing.JTextField; + +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.VariableStore; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; + +@OperationInfos(name = "Counter", category = OperationCategory.UTILS, description = "Increments a counter and stores it in a variable.") +public class Counter extends Operation { + + private JTextField varNameTxt; + private JTextField startValueTxt; + private JTextField stepSizeTxt; + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + String varName = this.varNameTxt.getText().trim(); + long value; + long stepSize = Long.valueOf(this.stepSizeTxt.getText()); + long startValue = Long.valueOf(this.startValueTxt.getText()); + if (VariableStore.getInstance().getVariable(varName) == null) { + value = startValue; + } else { + ByteArray currentValue = VariableStore.getInstance().getVariable(varName); + try { + value = Long.parseLong(currentValue.toString()); + } catch (NumberFormatException e) { + this.setErrorMessage(e); + value = startValue; + } + } + String storeValue = Long.toString(value + stepSize); + VariableStore.getInstance().setVariable(varName, factory.createByteArray(storeValue)); + return input; + } + + public void createUI() { + this.varNameTxt = new JTextField(); + this.addUIElement("Variable Name", this.varNameTxt); + + this.startValueTxt = new JTextField(); + this.startValueTxt.setText("0"); + this.addUIElement("Start Value", this.startValueTxt); + + this.stepSizeTxt = new JTextField(); + this.stepSizeTxt.setText("1"); + this.addUIElement("Step Size", this.stepSizeTxt); + } + +} diff --git a/src/main/java/de/usd/cstchef/operations/utils/GetVariable.java b/src/main/java/de/usd/cstchef/operations/utils/GetVariable.java index 66580c0..01ddb99 100644 --- a/src/main/java/de/usd/cstchef/operations/utils/GetVariable.java +++ b/src/main/java/de/usd/cstchef/operations/utils/GetVariable.java @@ -2,6 +2,8 @@ import javax.swing.JTextField; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.VariableStore; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; @@ -14,10 +16,10 @@ public class GetVariable extends Operation { private JTextField defaultTxt; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String varName = this.varNameTxt.getText().trim(); - byte[] var = VariableStore.getInstance().getVariable(varName); - return var == null ? this.defaultTxt.getText().getBytes() : var; + ByteArray var = VariableStore.getInstance().getVariable(varName); + return var == null ? factory.createByteArray(this.defaultTxt.getText()) : var; } public void createUI() { diff --git a/src/main/java/de/usd/cstchef/operations/utils/NoOperation.java b/src/main/java/de/usd/cstchef/operations/utils/NoOperation.java index e67a63e..3578fbd 100644 --- a/src/main/java/de/usd/cstchef/operations/utils/NoOperation.java +++ b/src/main/java/de/usd/cstchef/operations/utils/NoOperation.java @@ -1,5 +1,7 @@ package de.usd.cstchef.operations.utils; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -8,7 +10,7 @@ public class NoOperation extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { return input; } diff --git a/src/main/java/de/usd/cstchef/operations/utils/RandomNumber.java b/src/main/java/de/usd/cstchef/operations/utils/RandomNumber.java index d54c557..48b3a32 100644 --- a/src/main/java/de/usd/cstchef/operations/utils/RandomNumber.java +++ b/src/main/java/de/usd/cstchef/operations/utils/RandomNumber.java @@ -7,6 +7,8 @@ import javax.swing.JTextField; import javax.swing.SwingConstants; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -43,7 +45,7 @@ private int parseInt(String numberStr, int defaultValue) { } @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { // get Bounds from user input int boundMin = parseInt(this.textFieldMinimum.getText(), 0); @@ -68,11 +70,11 @@ protected byte[] perform(byte[] input) throws Exception { if(maxFracDigits == 0) { // use int mode int randomValue = secRand.nextInt(boundMax - boundMin + 1) + boundMin; - return numberFormatter.format(randomValue).getBytes(); + return factory.createByteArray(numberFormatter.format(randomValue)); } else { // use double mode double randomValue = boundMin + (boundMax - boundMin) * secRand.nextDouble(); - return numberFormatter.format(randomValue).getBytes(); + return factory.createByteArray(numberFormatter.format(randomValue)); } } diff --git a/src/main/java/de/usd/cstchef/operations/utils/RandomUUID.java b/src/main/java/de/usd/cstchef/operations/utils/RandomUUID.java index 7418932..cb6fbad 100644 --- a/src/main/java/de/usd/cstchef/operations/utils/RandomUUID.java +++ b/src/main/java/de/usd/cstchef/operations/utils/RandomUUID.java @@ -2,6 +2,8 @@ import java.util.UUID; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -10,9 +12,9 @@ public class RandomUUID extends Operation { @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { final String uuid = UUID.randomUUID().toString(); - return uuid.getBytes(); + return factory.createByteArray(uuid); } } diff --git a/src/main/java/de/usd/cstchef/operations/utils/SetIfEmpty.java b/src/main/java/de/usd/cstchef/operations/utils/SetIfEmpty.java index ab856af..4897604 100644 --- a/src/main/java/de/usd/cstchef/operations/utils/SetIfEmpty.java +++ b/src/main/java/de/usd/cstchef/operations/utils/SetIfEmpty.java @@ -2,6 +2,8 @@ import javax.swing.JCheckBox; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.Operation.OperationInfos; import de.usd.cstchef.operations.OperationCategory; @@ -14,10 +16,10 @@ public class SetIfEmpty extends Operation { private JCheckBox checkbox; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { - byte[] valueToSet = value.getText(); - if( input.length == 0 ) { + ByteArray valueToSet = value.getText(); + if( input.length() == 0 ) { return valueToSet; } @@ -25,8 +27,8 @@ protected byte[] perform(byte[] input) throws Exception { return input; int i = 0; - while( i < input.length ) { - if( input[i] != 32 ) { + while( i < input.length() ) { + if( input.getByte(i) != 32 ) { return input; } i++; diff --git a/src/main/java/de/usd/cstchef/operations/utils/StoreVariable.java b/src/main/java/de/usd/cstchef/operations/utils/StoreVariable.java index 3fabda9..d62e060 100644 --- a/src/main/java/de/usd/cstchef/operations/utils/StoreVariable.java +++ b/src/main/java/de/usd/cstchef/operations/utils/StoreVariable.java @@ -2,6 +2,8 @@ import javax.swing.JTextField; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.VariableStore; import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; @@ -14,7 +16,7 @@ public class StoreVariable extends Operation { private String oldVarName; @Override - protected byte[] perform(byte[] input) throws Exception { + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { String newVarName = this.varNameTxt.getText().trim(); VariableStore store = VariableStore.getInstance(); // remove old variable from hashmap diff --git a/src/main/java/de/usd/cstchef/view/BurpEditorWrapper.java b/src/main/java/de/usd/cstchef/view/BurpEditorWrapper.java index b5fd02f..7e85634 100644 --- a/src/main/java/de/usd/cstchef/view/BurpEditorWrapper.java +++ b/src/main/java/de/usd/cstchef/view/BurpEditorWrapper.java @@ -1,7 +1,9 @@ package de.usd.cstchef.view; import java.awt.Component; -import java.util.Arrays; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.util.Optional; import javax.swing.JScrollPane; import javax.swing.JTextArea; @@ -9,92 +11,148 @@ import javax.swing.event.DocumentListener; import burp.BurpUtils; -import burp.IMessageEditor; -import burp.IMessageEditorController; - -public class BurpEditorWrapper implements IMessageEditor, DocumentListener { - +import burp.CstcMessageEditorController; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.api.montoya.ui.Selection; +import burp.api.montoya.ui.editor.Editor; +import burp.api.montoya.ui.editor.HttpRequestEditor; +import burp.api.montoya.ui.editor.HttpResponseEditor; +import burp.api.montoya.ui.editor.RawEditor; +import de.usd.cstchef.Utils.MessageType; + +public class BurpEditorWrapper implements HttpRequestEditor, HttpResponseEditor, RawEditor{ + + private boolean isModified; + private boolean editable; + private MessageType messageType; + private MontoyaApi api; + private boolean fallbackMode; private JTextArea fallbackArea; - private IMessageEditor burpEditor; - public boolean fallbackMode; - boolean isModified; - byte[] lastContent; - - public BurpEditorWrapper(IMessageEditorController controller, boolean editable) { + private Editor burpEditor; + private ByteArray lastContent; + private RecipePanel recipePanel; + + public BurpEditorWrapper(CstcMessageEditorController controller, MessageType messageType, RecipePanel panel){ + this.api = BurpUtils.getInstance().getApi(); + this.messageType = messageType; + this.recipePanel = panel; + this.lastContent = ByteArray.byteArray(""); if (BurpUtils.inBurp()) { - this.burpEditor = BurpUtils.getInstance().getCallbacks().createMessageEditor(controller, editable); + switch(messageType){ + case REQUEST: burpEditor = api.userInterface().createHttpRequestEditor(); break; + case RESPONSE: burpEditor = api.userInterface().createHttpResponseEditor(); break; + case RAW: burpEditor = api.userInterface().createRawEditor(); break; + default: break; + } fallbackMode = false; } else { this.fallbackArea = new JTextArea(); - this.fallbackArea.getDocument().addDocumentListener(this); fallbackMode = true; } } @Override - public Component getComponent() { - if (fallbackMode) { - JScrollPane inputScrollPane = new JScrollPane(fallbackArea); - return inputScrollPane; - } - return burpEditor.getComponent(); + public void setEditable(boolean editable) { + this.editable = editable; } @Override - public byte[] getMessage() { - byte[] result; - result = fallbackMode ? fallbackArea.getText().getBytes() : burpEditor.getMessage(); - return result == null ? new byte[0] : result; + public ByteArray getContents() { + if(messageType == MessageType.RAW) + return ((RawEditor)burpEditor).getContents(); + else if(messageType == MessageType.REQUEST) + return ((HttpRequestEditor)burpEditor).getRequest().toByteArray(); + else if(messageType == MessageType.RESPONSE) + return ((HttpResponseEditor)burpEditor).getResponse().toByteArray(); + else + return ByteArray.byteArray(); } @Override - public byte[] getSelectedData() { - return null; + public void setContents(ByteArray contents) { + this.lastContent = contents; + if(messageType == MessageType.REQUEST) + ((HttpRequestEditor)burpEditor).setRequest(HttpRequest.httpRequest(contents)); + else if(messageType == MessageType.RESPONSE) + ((HttpResponseEditor)burpEditor).setResponse(HttpResponse.httpResponse(contents)); + else + ((RawEditor)burpEditor).setContents(contents); } @Override - public int[] getSelectionBounds() { - return null; + public HttpResponse getResponse() { + if(messageType != MessageType.RESPONSE){ + return null; + } + HttpResponse result; + result = fallbackMode ? HttpResponse.httpResponse(ByteArray.byteArray(fallbackArea.getText().getBytes())) : ((HttpResponseEditor)burpEditor).getResponse(); + return result == null ? HttpResponse.httpResponse() : result; } @Override - public boolean isMessageModified() { + public void setResponse(HttpResponse response) { if (fallbackMode) { - boolean state = this.isModified; - this.isModified = false; - return state; + fallbackArea.setText(response.toString()); + } else { + this.lastContent = response.toByteArray(); + ((HttpResponseEditor)burpEditor).setResponse(response); } - // TODO: a little hack here - if (!Arrays.equals(lastContent, getMessage())) { - lastContent = getMessage(); - return true; + } + + @Override + public HttpRequest getRequest() { + if(messageType != MessageType.REQUEST){ + return null; } - return false; + HttpRequest result; + result = fallbackMode ? HttpRequest.httpRequest(ByteArray.byteArray(fallbackArea.getText().getBytes())) : ((HttpRequestEditor)burpEditor).getRequest(); + return result == null ? HttpRequest.httpRequest() : result; } @Override - public void setMessage(byte[] arg0, boolean arg1) { + public void setRequest(HttpRequest request) { if (fallbackMode) { - fallbackArea.setText(new String(arg0)); + fallbackArea.setText(request.toString()); } else { - this.lastContent = arg0; - burpEditor.setMessage(arg0, arg1); //TODO fix second parameter + this.lastContent = request.toByteArray(); + ((HttpRequestEditor)burpEditor).setRequest(request); } } @Override - public void changedUpdate(DocumentEvent e) { - this.isModified = true; + public void setSearchExpression(String expression) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setSearchExpression'"); } @Override - public void insertUpdate(DocumentEvent e) { - this.isModified = true; + public boolean isModified() { + boolean result = this.getContents().equals(lastContent); + lastContent = this.getContents(); + return result; } @Override - public void removeUpdate(DocumentEvent e) { - this.isModified = true; + public int caretPosition() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'caretPosition'"); } + @Override + public Optional selection() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'selection'"); + } + + @Override + public Component uiComponent() { + if (fallbackMode) { + JScrollPane inputScrollPane = new JScrollPane(fallbackArea); + return inputScrollPane; + } + return burpEditor.uiComponent(); + } } diff --git a/src/main/java/de/usd/cstchef/view/FormatTab.java b/src/main/java/de/usd/cstchef/view/FormatTab.java deleted file mode 100644 index 5fee0cb..0000000 --- a/src/main/java/de/usd/cstchef/view/FormatTab.java +++ /dev/null @@ -1,70 +0,0 @@ -package de.usd.cstchef.view; - -import java.awt.Component; - -import burp.BurpUtils; -import burp.IMessageEditorTab; -import burp.ITextEditor; -import burp.Logger; - -public class FormatTab implements IMessageEditorTab { - private ITextEditor txtInput; - private boolean editable; - private RecipePanel responseFormatRecipePanel; - private RecipePanel requestFormatRecipePanel; - private byte[] currentMessage; - - public FormatTab(RecipePanel requestFormatRecipePanel, RecipePanel responseFormatRecipePanel, boolean editable) { - this.editable = editable; - this.responseFormatRecipePanel = responseFormatRecipePanel; - this.requestFormatRecipePanel = requestFormatRecipePanel; - txtInput = BurpUtils.getInstance().getCallbacks().createTextEditor(); - txtInput.setEditable(editable); - } - - @Override - public String getTabCaption() { - return "CSTC"; - } - - @Override - public Component getUiComponent() { - return txtInput.getComponent(); - } - - @Override - public boolean isEnabled(byte[] content, boolean isRequest) { - return true; - } - - @Override - public void setMessage(byte[] content, boolean isRequest) { - currentMessage = content; - - if (content == null) { - txtInput.setText("Nothing here".getBytes()); - txtInput.setEditable(false); - return; - } - RecipePanel recipe = isRequest ? this.requestFormatRecipePanel : this.responseFormatRecipePanel; - Logger.getInstance().log("baking new stuff"); - byte[] result = recipe.bake(content); - this.txtInput.setText(result); - } - - @Override - public byte[] getMessage() { - return currentMessage; - } - - @Override - public boolean isModified() { - return txtInput.isTextModified(); - } - - @Override - public byte[] getSelectedData() { - return txtInput.getSelectedText(); - } - -} \ No newline at end of file diff --git a/src/main/java/de/usd/cstchef/view/OperationsTree.java b/src/main/java/de/usd/cstchef/view/OperationsTree.java index 5824705..8a75bd3 100644 --- a/src/main/java/de/usd/cstchef/view/OperationsTree.java +++ b/src/main/java/de/usd/cstchef/view/OperationsTree.java @@ -8,6 +8,7 @@ import javax.swing.ImageIcon; import javax.swing.JTree; +import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; @@ -30,7 +31,7 @@ public class OperationsTree extends JTree { public OperationsTree() { super(); - + this.setUI(new CustomTreeUI()); this.model = (DefaultTreeModel) this.getModel(); this.model.setRoot(this.createTree()); this.setToolTipText(""); @@ -67,7 +68,7 @@ public void search(String text) { } ArrayList nodesToRemove = new ArrayList<>(); - Enumeration e = root.breadthFirstEnumeration(); + Enumeration e = root.breadthFirstEnumeration(); while (e.hasMoreElements()) { DefaultMutableTreeNode nextNode = (DefaultMutableTreeNode) e.nextElement(); if (!nextNode.toString().toLowerCase().contains(text.toLowerCase())) { @@ -96,7 +97,7 @@ public void search(String text) { this.expandAll(new TreePath(root)); } - private void removeNode(TreeNode selNode) { + private void removeNode(TreeNode selNode) { if (selNode == null) { return; } @@ -109,7 +110,7 @@ private void removeNode(TreeNode selNode) { if (selNode.getChildCount() == 0) { this.model.removeNodeFromParent((MutableTreeNode) selNode); } - } + } private DefaultMutableTreeNode createTree() { DefaultMutableTreeNode root = new DefaultMutableTreeNode(); @@ -157,4 +158,12 @@ private void expandAll(TreePath path) { } this.expandPath(path); } + + public class CustomTreeUI extends BasicTreeUI { + @Override + protected boolean shouldPaintExpandControl(javax.swing.tree.TreePath path, int row, boolean isExpanded, + boolean hasBeenExpanded, boolean isLeaf) { + return true; // Always display expand/collapse control + } + } } diff --git a/src/main/java/de/usd/cstchef/view/PopupVariableMenu.java b/src/main/java/de/usd/cstchef/view/PopupVariableMenu.java index 86ce84b..5b576f3 100644 --- a/src/main/java/de/usd/cstchef/view/PopupVariableMenu.java +++ b/src/main/java/de/usd/cstchef/view/PopupVariableMenu.java @@ -12,10 +12,12 @@ import javax.swing.event.PopupMenuListener; import javax.swing.text.JTextComponent; +import burp.api.montoya.core.ByteArray; + public class PopupVariableMenu extends JPopupMenu implements ActionListener, PopupMenuListener { private JTextComponent parent; - private static SortedMap variableMap; + private static SortedMap variableMap; public PopupVariableMenu(JTextComponent parent) { super(); @@ -34,11 +36,11 @@ public void refreshMenu() { } } - public static void refresh(HashMap variables) { + public static void refresh(HashMap variables) { if (variables == null) { - variableMap = new TreeMap(); + variableMap = new TreeMap(); } else { - variableMap = new TreeMap(variables); + variableMap = new TreeMap(variables); } } diff --git a/src/main/java/de/usd/cstchef/view/RecipePanel.java b/src/main/java/de/usd/cstchef/view/RecipePanel.java index 315f146..8f4cd84 100644 --- a/src/main/java/de/usd/cstchef/view/RecipePanel.java +++ b/src/main/java/de/usd/cstchef/view/RecipePanel.java @@ -3,6 +3,7 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; +import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.GridLayout; @@ -23,6 +24,7 @@ import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFileChooser; +import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; @@ -40,16 +42,27 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; +import burp.BurpExtender; import burp.BurpUtils; import burp.CstcMessageEditorController; -import burp.IBurpExtenderCallbacks; -import burp.IExtensionHelpers; -import burp.IHttpRequestResponse; -import burp.IParameter; -import burp.IRequestInfo; import burp.Logger; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.BurpSuiteEdition; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.HttpHeader; +import burp.api.montoya.http.message.HttpMessage; +import burp.api.montoya.http.message.HttpRequestResponse; +import burp.api.montoya.http.message.params.HttpParameter; +import burp.api.montoya.http.message.params.ParsedHttpParameter; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.api.montoya.persistence.PersistedObject; +import de.usd.cstchef.Utils; import de.usd.cstchef.VariableStore; +import de.usd.cstchef.Utils.MessageType; import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.view.filter.FilterState; +import de.usd.cstchef.view.filter.FilterState.BurpOperation; public class RecipePanel extends JPanel implements ChangeListener { @@ -57,26 +70,28 @@ public class RecipePanel extends JPanel implements ChangeListener { private int operationSteps = 10; private boolean autoBake = true; - private boolean isRequest = true; + private MessageType messageType; private int bakeThreshold = 400; private String recipeName; - private int filterMask; + private BurpOperation operation; private BurpEditorWrapper inputText; private BurpEditorWrapper outputText; private JPanel operationLines; - private RequestFilterDialog requestFilterDialog; private CstcMessageEditorController controllerOrig; private CstcMessageEditorController controllerMod; private Timer bakeTimer; - public RecipePanel(String recipeName, boolean isRequest) { + private JLabel inactiveWarning; - this.recipeName = recipeName; - this.isRequest = isRequest; + public RecipePanel(BurpOperation operation, MessageType messageType) { + + this.operation = operation; + this.messageType = messageType; + this.recipeName = operation.toString(); ToolTipManager tooltipManager = ToolTipManager.sharedInstance(); tooltipManager.setInitialDelay(0); @@ -89,13 +104,13 @@ public RecipePanel(String recipeName, boolean isRequest) { // create input panel JPanel inputPanel = new LayoutPanel("Input"); - inputText = new BurpEditorWrapper(controllerOrig, true); - inputPanel.add(inputText.getComponent()); + inputText = new BurpEditorWrapper(controllerOrig, messageType, this); + inputPanel.add(inputText.uiComponent()); // create output panel JPanel outputPanel = new LayoutPanel("Output"); - outputText = new BurpEditorWrapper(controllerMod, false); - outputPanel.add(outputText.getComponent()); + outputText = new BurpEditorWrapper(controllerMod, messageType, this); + outputPanel.add(outputText.uiComponent()); JPanel searchTreePanel = new JPanel(); searchTreePanel.setLayout(new BorderLayout()); @@ -103,7 +118,6 @@ public RecipePanel(String recipeName, boolean isRequest) { searchTreePanel.add(searchText, BorderLayout.PAGE_START); OperationsTree operationsTree = new OperationsTree(); - operationsTree.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); operationsTree.setRootVisible(false); searchTreePanel.add(new JScrollPane(operationsTree)); searchText.getDocument().addDocumentListener(new DocumentListener() { @@ -134,16 +148,31 @@ public void changedUpdate(DocumentEvent e) { // create active operations (middle) panel LayoutPanel activeOperationsPanel = new LayoutPanel("Recipe"); + + inactiveWarning = new JLabel(this.operation.toString() + " Operations currently inactive!"); + inactiveWarning.setForeground(Color.RED); + inactiveWarning.setFont(inactiveWarning.getFont().deriveFont(inactiveWarning.getFont().getStyle() | Font.BOLD)); + if(!this.operation.equals(BurpOperation.FORMAT)) + activeOperationsPanel.addActionComponent(inactiveWarning); + // add action items JButton filters = new JButton("Filter"); - this.requestFilterDialog = new RequestFilterDialog(); - activeOperationsPanel.addActionComponent(filters); + if(this.operation != BurpOperation.FORMAT) + activeOperationsPanel.addActionComponent(filters); filters.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - int result = JOptionPane.showConfirmDialog(null, requestFilterDialog, "Request Filter", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); + int result = JOptionPane.showConfirmDialog(null, RequestFilterDialog.getInstance(), "Request Filter", + JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); if (result == JOptionPane.OK_OPTION) { - filterMask = requestFilterDialog.getFilterMask(); + BurpUtils.getInstance().getFilterState().setFilterMask( + RequestFilterDialog.getInstance().getFilterMask(BurpOperation.INCOMING), + RequestFilterDialog.getInstance().getFilterMask(BurpOperation.OUTGOING)); + } + BurpUtils.getInstance().getView().updateInactiveWarnings(); + if (!BurpUtils.getInstance().getApi().burpSuite().version().edition() + .equals(BurpSuiteEdition.COMMUNITY_EDITION)) { + saveFilterState(); } } }); @@ -157,7 +186,7 @@ public void actionPerformed(ActionEvent arg0) { } }); - JButton saveButton = new JButton("Save"); + JButton saveButton = new JButton("Save to File"); activeOperationsPanel.addActionComponent(saveButton); saveButton.addActionListener(new ActionListener() { @Override @@ -216,14 +245,14 @@ public void actionPerformed(ActionEvent arg0) { } }); - JButton clearButton = new JButton("Clear"); - activeOperationsPanel.addActionComponent(clearButton); - clearButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent arg0) { - clear(); - } - }); + JButton clearButton = new JButton("Clear"); + activeOperationsPanel.addActionComponent(clearButton); + clearButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent arg0) { + clear(); + } + }); operationLines = new JPanel(); operationLines.setLayout(new GridBagLayout()); @@ -244,13 +273,13 @@ public void actionPerformed(ActionEvent arg0) { operationLines.add(dummyPanel, gbc); // this is the magic!11!! for (int i = operationSteps; i > 0; i--) { - RecipeStepPanel opPanel = new RecipeStepPanel(String.valueOf(i), this); + RecipeStepPanel opPanel = new RecipeStepPanel("Lane " + String.valueOf(i), this); operationLines.add(opPanel, co, 0); JPanel panel = opPanel.getOperationsPanel(); MoveOperationMouseAdapter moma = new MoveOperationMouseAdapter(opPanel, operationLines); - panel.addMouseListener(moma ); - panel.addMouseMotionListener(moma ); + panel.addMouseListener(moma); + panel.addMouseMotionListener(moma); } JScrollPane activeOperationsScrollPane = new JScrollPane(operationLines, JScrollPane.VERTICAL_SCROLLBAR_NEVER, @@ -274,55 +303,29 @@ public void actionPerformed(ActionEvent arg0) { operationsTree.addMouseListener(dma); operationsTree.addMouseMotionListener(dma); - loadRecipeFromBurp(); startAutoBakeTimer(); } - private void loadRecipeFromBurp() { - logger.log("[" + this.recipeName + "] Autoloading..."); - boolean inBurp = BurpUtils.inBurp(); - //Check if we run inside a burp - if (inBurp) { - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - String jsonState = callbacks.loadExtensionSetting("cstc_" + this.recipeName); - if (jsonState != null && jsonState != "") { - try { - logger.log("[" + this.recipeName + "] Restoring state."); - //We remove the setting and set it again to be safe in an error case - callbacks.saveExtensionSetting("cstc_" + this.recipeName, ""); - restoreState(jsonState); - callbacks.saveExtensionSetting("cstc_" + this.recipeName, jsonState); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IOException e) { - Logger.getInstance().err("There was an error restoring the state of RecipePanel " + this.recipeName); - } - } - } - else { - logger.log("[" + this.recipeName + "] Autoloading aborted. Not running inside Burp."); - } + public void hideInactiveWarning(){ + this.inactiveWarning.setVisible(false); } - private void autoSaveToBurp() { - boolean inBurp = BurpUtils.inBurp(); - //Check if we run inside a burp - if (inBurp) { - try { - String jsonState = getStateAsJSON(); - BurpUtils.getInstance().getCallbacks().saveExtensionSetting("cstc_" + this.recipeName, jsonState); - } catch (IOException e) { - Logger.getInstance().err("There was an error persisting the current state of the recipe panel."); - } - } + public void showInactiveWarning(){ + this.inactiveWarning.setVisible(true); } - public void setInput(IHttpRequestResponse requestResponse) { - if( isRequest ) - this.inputText.setMessage(requestResponse.getRequest(), true); - else { - byte[] responseBytes = requestResponse.getResponse(); - if( responseBytes == null ) - responseBytes = "Your request has no server response yet :(".getBytes(); - this.inputText.setMessage(responseBytes, false); + public void setInput(HttpRequestResponse requestResponse) { + if(messageType == MessageType.REQUEST){ + HttpRequest request = requestResponse.request(); + if(request == null) + request = HttpRequest.httpRequest(ByteArray.byteArray("The message you have sent via the context menu is not a valid HTML request. Try using the formatting tab.")); + this.inputText.setRequest(request); + } + else if(messageType == MessageType.RESPONSE) { + HttpResponse response = requestResponse.response(); + if(response == null) + response = HttpResponse.httpResponse(ByteArray.byteArray("The message you have sent via the context menu does not have a valid HTML response. Try including a response to a request or use the formatting tab.")); + this.inputText.setResponse(response); } this.controllerOrig.setHttpRequestResponse(requestResponse); @@ -331,9 +334,24 @@ public void setInput(IHttpRequestResponse requestResponse) { this.bake(false); } - private void restoreState(String jsonState) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { + public void setFormatMessage(HttpRequestResponse requestResponse, MessageType messageType){ + ByteArray message; + if(messageType == MessageType.REQUEST){ + message = requestResponse.request().toByteArray(); + } + else{ + message = requestResponse.response().toByteArray(); + } + if(message == null){ + message = ByteArray.byteArray("Message could not be parsed as a request or response."); + } + this.inputText.setContents(message); + this.bake(false); + } + + public void restoreState(String jsonState) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { // TODO do we want to remove all existing operations before loading here? - this.clear(); // Yes! + this.clear(); // Yes! ObjectMapper mapper = new ObjectMapper(); JsonNode stepNodes = mapper.readTree(jsonState); if (!stepNodes.isArray()) { @@ -354,7 +372,7 @@ private void restoreState(String jsonState) throws IOException, ClassNotFoundExc // check if it is an operation Operation op = cls.newInstance(); op.load(parameters); - op.setDisabled(!operationNode.get("is_enabled").asBoolean()); + op.setDisabled(!operationNode.get("is_enabled").asBoolean()); RecipeStepPanel panel = (RecipeStepPanel) this.operationLines.getComponent(step); panel.addComponent(op, i); } @@ -375,7 +393,7 @@ private String getStateAsJSON() throws IOException { operationNode.put("operation", op.getClass().getName()); operationsNode.add(operationNode); operationNode.putPOJO("parameters", op.getState()); - operationNode.putPOJO("is_enabled", !op.isDisabled()); + operationNode.putPOJO("is_enabled", !op.isDisabled()); } stepsNode.add(operationsNode); } @@ -388,12 +406,12 @@ private void save(File file) throws IOException { fw.close(); } - private byte[] doBake(byte[] input) { - if (input == null || input.length == 0) { - return new byte[0]; + private ByteArray doBake(ByteArray input, MessageType messageType) { + if (input == null || input.length() == 0) { + return ByteArray.byteArrayOfLength(0); } - byte[] result = input.clone(); - byte[] intermediateResult = input; + ByteArray result = input.copy(); + ByteArray intermediateResult = input; boolean outputChanged; VariableStore store = VariableStore.getInstance(); out: for (int j = 0; j < this.operationLines.getComponentCount(); j++) { @@ -417,7 +435,7 @@ private byte[] doBake(byte[] input) { continue; } - intermediateResult = op.performOperation(intermediateResult); + intermediateResult = op.performOperation(intermediateResult, messageType); outputChanged = true; if (op.isBreakpoint()) { @@ -437,32 +455,28 @@ private byte[] doBake(byte[] input) { } if (BurpUtils.inBurp()) { - IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks(); - IExtensionHelpers helpers = callbacks.getHelpers(); - - IRequestInfo info; + MontoyaApi api = BurpUtils.getInstance().getApi(); + HttpRequest req; + List headers; + int offset; try { - info = helpers.analyzeRequest(result); + req = HttpRequest.httpRequest(result); + headers = req.headers(); + offset = req.bodyOffset(); } catch( IllegalArgumentException e ) { // In this case there is no valid HTTP request and no Content-Length update is requried. return result; } - List headers = info.getHeaders(); - int offset = info.getBodyOffset(); - - if( result.length == offset ) { + if( result.length() == offset ) { // In this case there is no body and we do not need to update the content length header. return result; } - for(String header : headers) { - if(header.startsWith("Content-Length:")) { - // To update the content-length header, we just add a dummy parameter and remove it right away. - // Burps extension helpers will care about updating the length without any string transformations. - IParameter dummy = helpers.buildParameter("dummy", "dummy", IParameter.PARAM_BODY); - result = helpers.addParameter(result, dummy); - result = helpers.removeParameter(result, dummy); + for(HttpHeader header : headers) { + if(header.toString().startsWith("Content-Length:")) { + HttpParameter dummy = HttpParameter.bodyParameter("dummy", "dummy"); + result = HttpRequest.httpRequest(result).withAddedParameters(dummy).withRemovedParameters(dummy).toByteArray(); break; } } @@ -481,17 +495,22 @@ private void bake(boolean spamProtection) { TimerTask tt = new TimerTask() { @Override public void run() { - byte[] result = doBake(inputText.getMessage()); - HashMap variables = VariableStore.getInstance().getVariables(); + ByteArray result = doBake(inputText.getRequest() == null ? inputText.getContents() /* inputText.getResponse().toByteArray() */ : inputText.getRequest().toByteArray(), messageType); + HashMap variables = VariableStore.getInstance().getVariables(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - if( isRequest) { - outputText.setMessage(result, true); - controllerMod.setRequest(result); - } else { - outputText.setMessage(result, false); - controllerMod.setResponse(result); + if( messageType == MessageType.REQUEST) { + outputText.setRequest(HttpRequest.httpRequest(result)); + controllerMod.setRequest(HttpRequest.httpRequest(result)); + } else if (messageType == MessageType.RESPONSE){ + outputText.setResponse(HttpResponse.httpResponse(result)); + controllerMod.setResponse(HttpResponse.httpResponse(result)); + } + else{ + outputText.setContents(result); + // TODO: MessageEditorController? + } VariablesWindow vw = VariablesWindow.getInstance(); if (vw.isVisible()) { @@ -500,18 +519,17 @@ public void run() { PopupVariableMenu.refresh(variables); } }); - autoSaveToBurp(); } }; int threshold = spamProtection ? this.bakeThreshold : 0; this.bakeTimer.schedule(tt, threshold); } - public byte[] bake(byte[] input) { + public ByteArray bake(ByteArray input, MessageType messageType) { VariableStore store = VariableStore.getInstance(); try { store.lock(); - return this.doBake(input); + return this.doBake(input, messageType); } finally { store.unlock(); } @@ -520,8 +538,7 @@ public byte[] bake(byte[] input) { private void startAutoBakeTimer() { TimerTask repeatedTask = new TimerTask() { public void run() { - if (inputText.isMessageModified()) { - logger.log("autobaking"); + if (inputText.isModified()) { autoBake(); } } @@ -532,7 +549,7 @@ public void run() { timer.scheduleAtFixedRate(repeatedTask, delay, period); } - private void autoBake() { + public void autoBake() { if (!this.autoBake) { return; } @@ -543,21 +560,44 @@ private void autoBake() { } finally { store.unlock(); } - } + } - private void clear() { - for (int step = 0; step < this.operationSteps; step++) { - RecipeStepPanel stepPanel = (RecipeStepPanel) this.operationLines.getComponent(step); - stepPanel.clearOperations(); - } + private void saveRecipe() { + PersistedObject savedState = BurpUtils.getInstance().getApi().persistence().extensionData(); + try { + savedState.setString(this.operation + "Recipe", getStateAsJSON()); + } catch (IOException e) { + Logger.getInstance().err( + "Could not save recipes to the Burp project. If you are running Burp Suite Community Edition, this behavior is expected since saving project files is exclusive to BurpSuite Pro users."); + } + } + + private void saveFilterState() { + PersistedObject savedState = BurpUtils.getInstance().getApi().persistence().extensionData(); + try { + savedState.setString("FilterState", + new ObjectMapper().writeValueAsString(BurpUtils.getInstance().getFilterState())); + } catch (Exception e) { + Logger.getInstance().err( + "Could not save the filter state to the Burp project. If you are running Burp Suite Community Edition, this behavior is expected since saving project files is exclusive to BurpSuite Pro users.\n" + + e.getMessage()); + } + } + + private void clear() { + for (int step = 0; step < this.operationSteps; step++) { + RecipeStepPanel stepPanel = (RecipeStepPanel) this.operationLines.getComponent(step); + stepPanel.clearOperations(); + } } @Override public void stateChanged(ChangeEvent e) { this.autoBake(); - } - public boolean shouldProcess(int tool) { - return (this.filterMask & tool) != 0; + if (!BurpUtils.getInstance().getApi().burpSuite().version().edition().equals(BurpSuiteEdition.COMMUNITY_EDITION)) { + saveRecipe(); + } } + } diff --git a/src/main/java/de/usd/cstchef/view/RequestFilterDialog.java b/src/main/java/de/usd/cstchef/view/RequestFilterDialog.java index 1a8c6bb..60caa44 100644 --- a/src/main/java/de/usd/cstchef/view/RequestFilterDialog.java +++ b/src/main/java/de/usd/cstchef/view/RequestFilterDialog.java @@ -3,80 +3,89 @@ import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.Arrays; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; -import burp.IBurpExtenderCallbacks; +import burp.BurpUtils; +import burp.api.montoya.core.ToolType; +import de.usd.cstchef.view.filter.Filter; +import de.usd.cstchef.view.filter.FilterState.BurpOperation; public class RequestFilterDialog extends JPanel { - private LinkedHashMap filterSettings; - public RequestFilterDialog() { - this.filterSettings = new LinkedHashMap<>(); - this.filterSettings.put(new Filter("Proxy", IBurpExtenderCallbacks.TOOL_PROXY), false); - this.filterSettings.put(new Filter("Repeater", IBurpExtenderCallbacks.TOOL_REPEATER), false); - this.filterSettings.put(new Filter("Spider", IBurpExtenderCallbacks.TOOL_SPIDER), false); - this.filterSettings.put(new Filter("Scanner", IBurpExtenderCallbacks.TOOL_SCANNER), false); - this.filterSettings.put(new Filter("Intruder", IBurpExtenderCallbacks.TOOL_INTRUDER), false); - this.filterSettings.put(new Filter("Extender", IBurpExtenderCallbacks.TOOL_EXTENDER), false); + private static RequestFilterDialog instance = null; - this.setLayout(new GridLayout(0, 2)); + public static RequestFilterDialog getInstance() { + if (RequestFilterDialog.instance == null) { + RequestFilterDialog.instance = new RequestFilterDialog(); + } + return RequestFilterDialog.instance; + } + + private RequestFilterDialog() { + this.setLayout(new GridLayout(0, 3)); + + JPanel incomingPanel = createPanel(BurpOperation.INCOMING); + JPanel outgoingPanel = createPanel(BurpOperation.OUTGOING); + + JPanel labelPanel = new JPanel(); + labelPanel.setLayout(new GridLayout(7, 0)); + labelPanel.add(new JLabel("")); + List labels = Arrays.asList("Proxy", "Repeater", "Scanner", "Intruder", "Extender"); + for (String label : labels) { + labelPanel.add(new JLabel(label)); + } + + this.removeAll(); + this.add(labelPanel); + this.add("Outgoing", outgoingPanel); + this.add("Incoming", incomingPanel); - for (Map.Entry entry : this.filterSettings.entrySet()) { + } + + private JPanel createPanel(BurpOperation operation) { + if (BurpUtils.getInstance().getFilterState().getFilterMask(operation).isEmpty()) { + BurpUtils.getInstance().getFilterState().getFilterMask(operation).put(new Filter(ToolType.PROXY, ToolType.PROXY.ordinal()), false); + BurpUtils.getInstance().getFilterState().getFilterMask(operation).put(new Filter(ToolType.REPEATER, ToolType.REPEATER.ordinal()), + false); + BurpUtils.getInstance().getFilterState().getFilterMask(operation).put(new Filter(ToolType.SCANNER, ToolType.SCANNER.ordinal()), false); + BurpUtils.getInstance().getFilterState().getFilterMask(operation).put(new Filter(ToolType.INTRUDER, ToolType.INTRUDER.ordinal()), + false); + BurpUtils.getInstance().getFilterState().getFilterMask(operation).put(new Filter(ToolType.EXTENSIONS, ToolType.EXTENSIONS.ordinal()), + false); + } + + JPanel panel = new JPanel(); + panel.add(new JLabel(operation.toString())); + for (Map.Entry entry : BurpUtils.getInstance().getFilterState().getFilterMask(operation).entrySet()) { Filter filter = entry.getKey(); boolean selected = entry.getValue(); - this.add(new JLabel(filter.getName() + ": ")); JCheckBox box = new JCheckBox(); box.setSelected(selected); box.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - filterSettings.put(filter, box.isSelected()); + BurpUtils.getInstance().getFilterState().getFilterMask(operation).put(filter, box.isSelected()); } }); - this.add(box); + panel.add(box); } - } - public int getFilterMask() { - int filterMask = 0; - for (Map.Entry entry : this.filterSettings.entrySet()) { - Filter filter = entry.getKey(); - boolean selected = entry.getValue(); - if (selected) { - filterMask |= filter.getValue(); - } - } - return filterMask; + panel.setLayout(new GridLayout(7, 0)); + return panel; } - class Filter { - private String name; - private int value; - - public Filter(String name, int value) { - this.name = name; - this.value = value; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getValue() { - return value; - } + public void updateFilterSettings(){ + RequestFilterDialog.instance = new RequestFilterDialog(); + } - public void setValue(int value) { - this.value = value; - } + public LinkedHashMap getFilterMask(BurpOperation operation) { + return BurpUtils.getInstance().getFilterState().getFilterMask(operation); } } diff --git a/src/main/java/de/usd/cstchef/view/VariablesWindow.java b/src/main/java/de/usd/cstchef/view/VariablesWindow.java index b977237..1a8b8b9 100644 --- a/src/main/java/de/usd/cstchef/view/VariablesWindow.java +++ b/src/main/java/de/usd/cstchef/view/VariablesWindow.java @@ -19,6 +19,8 @@ import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; +import burp.api.montoya.core.ByteArray; + public class VariablesWindow extends JFrame { private static VariablesWindow instance; @@ -68,14 +70,14 @@ public void componentResized(ComponentEvent e) { this.add(scrollPane); } - public void refresh(HashMap variables) { + public void refresh(HashMap variables) { DefaultTableModel model = (DefaultTableModel) this.table.getModel(); model.setRowCount(0); this.emptyLbl.setVisible(variables.isEmpty()); - SortedMap sortedMap = new TreeMap(variables); + SortedMap sortedMap = new TreeMap(variables); for (String key : sortedMap.keySet()) { - model.addRow(new String[] { key, new String(sortedMap.get(key)) }); + model.addRow(new String[] { key, sortedMap.get(key).toString() }); } } diff --git a/src/main/java/de/usd/cstchef/view/View.java b/src/main/java/de/usd/cstchef/view/View.java index 5a9f8b8..b24947d 100644 --- a/src/main/java/de/usd/cstchef/view/View.java +++ b/src/main/java/de/usd/cstchef/view/View.java @@ -10,25 +10,34 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; +import burp.BurpUtils; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.view.filter.FilterState; +import de.usd.cstchef.view.filter.FilterState.BurpOperation; + public class View extends JPanel { private RecipePanel incomingRecipePanel; private RecipePanel outgoingRecipePanel; private RecipePanel formatRecipePanel; - public View() { + public View(){ + this(new FilterState()); + } + + public View(FilterState state) { Security.addProvider(new BouncyCastleProvider()); this.setLayout(new BorderLayout()); JTabbedPane tabbedPane = new JTabbedPane(); - incomingRecipePanel = new RecipePanel("Incomming", false); - outgoingRecipePanel = new RecipePanel("Outgoing", true); - formatRecipePanel = new RecipePanel("Formatting", true); + incomingRecipePanel = new RecipePanel(BurpOperation.INCOMING, MessageType.RESPONSE); + outgoingRecipePanel = new RecipePanel(BurpOperation.OUTGOING, MessageType.REQUEST); + formatRecipePanel = new RecipePanel(BurpOperation.FORMAT, MessageType.RAW); tabbedPane.addTab("Outgoing Requests", null, outgoingRecipePanel, "Outgoing requests from the browser, the repeater or another tool."); tabbedPane.addTab("Incoming Responses", null, incomingRecipePanel, "Responses from the server."); - tabbedPane.addTab("Formating", null, formatRecipePanel, "Formating for messages."); + tabbedPane.addTab("Formatting", null, formatRecipePanel, "Formatting for messages."); this.add(tabbedPane); } @@ -54,4 +63,18 @@ public static void main(String[] args) { frame.setVisible(true); // frame.setExtendedState(java.awt.Frame.MAXIMIZED_BOTH); } + + public void updateInactiveWarnings() { + incomingRecipePanel.showInactiveWarning(); + for(Boolean b : BurpUtils.getInstance().getFilterState().getIncomingFilterSettings().values()){ + if(b == true) + incomingRecipePanel.hideInactiveWarning(); + } + + outgoingRecipePanel.showInactiveWarning(); + for(Boolean b : BurpUtils.getInstance().getFilterState().getOutgoingFilterSettings().values()){ + if(b == true) + outgoingRecipePanel.hideInactiveWarning(); + } + } } diff --git a/src/main/java/de/usd/cstchef/view/filter/Filter.java b/src/main/java/de/usd/cstchef/view/filter/Filter.java new file mode 100644 index 0000000..d564687 --- /dev/null +++ b/src/main/java/de/usd/cstchef/view/filter/Filter.java @@ -0,0 +1,44 @@ +package de.usd.cstchef.view.filter; + +import com.fasterxml.jackson.annotation.JsonValue; + +import burp.Logger; +import burp.api.montoya.core.ToolType; + +public class Filter { + private ToolType toolType; + private int value; + + public Filter(ToolType toolType, int value) { + this.toolType = toolType; + this.value = value; + } + + public Filter(String s) { + String[] pairs = s.split(":"); + this.toolType = ToolType.valueOf(pairs[0].trim().toUpperCase()); + this.value = Integer.parseInt(pairs[1].trim()); + } + + public ToolType getToolType() { + return toolType; + } + + public void setToolType(ToolType name) { + this.toolType = name; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + @Override + @JsonValue + public String toString() { + return toolType.toString() + ": " + value; + } + } diff --git a/src/main/java/de/usd/cstchef/view/filter/FilterState.java b/src/main/java/de/usd/cstchef/view/filter/FilterState.java new file mode 100644 index 0000000..78e2b76 --- /dev/null +++ b/src/main/java/de/usd/cstchef/view/filter/FilterState.java @@ -0,0 +1,115 @@ +package de.usd.cstchef.view.filter; + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import burp.api.montoya.core.ToolSource; + +public class FilterState implements Serializable{ + @JsonDeserialize(keyUsing = FilterStateDeserializer.class) + private LinkedHashMap incomingFilterSettings; + @JsonDeserialize(keyUsing = FilterStateDeserializer.class) + private LinkedHashMap outgoingFilterSettings; + + public FilterState(LinkedHashMap incomingFilterSettings, + LinkedHashMap outgoingFilterSettings) { + this.incomingFilterSettings = incomingFilterSettings; + this.outgoingFilterSettings = outgoingFilterSettings; + } + + public FilterState() { + this(new LinkedHashMap(), new LinkedHashMap()); + } + + public void setFilterMask(LinkedHashMap filterMask, BurpOperation operation) { + switch (operation) { + case INCOMING: + incomingFilterSettings = filterMask; + break; + case OUTGOING: + outgoingFilterSettings = filterMask; + break; + default: + break; + } + } + + public LinkedHashMap getFilterMask(BurpOperation operation) { + switch (operation) { + case INCOMING: + return incomingFilterSettings; + case OUTGOING: + return outgoingFilterSettings; + default: + return new LinkedHashMap(); + } + } + + public void setFilterMask(LinkedHashMap incomingFilterMask, + LinkedHashMap outgoingFilterMask) { + this.incomingFilterSettings = incomingFilterMask; + this.outgoingFilterSettings = outgoingFilterMask; + } + + public boolean shouldProcess(BurpOperation operation, ToolSource toolSource) { + LinkedHashMap filterSettings; + int filterMask = 0; + switch (operation) { + case INCOMING: + filterSettings = incomingFilterSettings; + break; + case OUTGOING: + filterSettings = outgoingFilterSettings; + break; + default: + filterSettings = new LinkedHashMap<>(); + break; + } + + for (Map.Entry entry : filterSettings.entrySet()) { + Filter filter = entry.getKey(); + if(filter.getToolType().equals(toolSource.toolType())){ + return entry.getValue() == true; + } + } + return false; + } + + public LinkedHashMap getIncomingFilterSettings() { + return this.incomingFilterSettings; + } + + public void setIncomingFilterSettings(LinkedHashMap incomingFilterSettings) { + this.incomingFilterSettings = incomingFilterSettings; + } + + public LinkedHashMap getOutgoingFilterSettings() { + return this.outgoingFilterSettings; + } + + public void setOutgoingFilterSettings(LinkedHashMap outgoingFilterSettings) { + this.outgoingFilterSettings = outgoingFilterSettings; + } + + public String toString(){ + return "Incoming: " + this.incomingFilterSettings.toString() + "\nOutgoing: " + this.outgoingFilterSettings.toString(); + } + + public enum BurpOperation { + INCOMING, + OUTGOING, + FORMAT; + + public String toString(){ + switch(this){ + case INCOMING: return "Incoming"; + case OUTGOING: return "Outgoing"; + case FORMAT: return "Formatting"; + default: return ""; + } + } + } +} diff --git a/src/main/java/de/usd/cstchef/view/filter/FilterStateDeserializer.java b/src/main/java/de/usd/cstchef/view/filter/FilterStateDeserializer.java new file mode 100644 index 0000000..df6684d --- /dev/null +++ b/src/main/java/de/usd/cstchef/view/filter/FilterStateDeserializer.java @@ -0,0 +1,18 @@ +package de.usd.cstchef.view.filter; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.KeyDeserializer; + +public class FilterStateDeserializer extends KeyDeserializer { + + @Override + public Filter deserializeKey( + String key, + DeserializationContext ctxt) throws IOException, + JsonProcessingException { + return new Filter(key); + } +} \ No newline at end of file diff --git a/src/main/java/de/usd/cstchef/view/filter/FilterStateSerializer.java b/src/main/java/de/usd/cstchef/view/filter/FilterStateSerializer.java new file mode 100644 index 0000000..abddb47 --- /dev/null +++ b/src/main/java/de/usd/cstchef/view/filter/FilterStateSerializer.java @@ -0,0 +1,22 @@ +package de.usd.cstchef.view.filter; + +import java.io.IOException; +import java.io.StringWriter; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializerProvider; + +public class FilterStateSerializer extends JsonSerializer{ + + private ObjectMapper mapper = new ObjectMapper(); + + @Override + public void serialize(Filter value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + StringWriter writer = new StringWriter(); + mapper.writeValue(writer, value); + gen.writeFieldName(writer.toString()); + } + +} diff --git a/src/main/java/de/usd/cstchef/view/ui/FormatTextField.java b/src/main/java/de/usd/cstchef/view/ui/FormatTextField.java index 9eea88c..30e21c8 100644 --- a/src/main/java/de/usd/cstchef/view/ui/FormatTextField.java +++ b/src/main/java/de/usd/cstchef/view/ui/FormatTextField.java @@ -15,6 +15,8 @@ import javax.swing.event.DocumentListener; import org.bouncycastle.util.encoders.Hex; +import burp.api.montoya.core.ByteArray; + public class FormatTextField extends JPanel implements ActionListener { public VariableTextField txtField; @@ -25,7 +27,7 @@ public FormatTextField() { this.setLayout(new BorderLayout()); this.setBackground(new Color(0, 0, 0, 0)); this.txtField = new VariableTextField(); - this.formatBox = new JComboBox<>(new String[] {"Raw", "UTF-8", "Hex", "Latin1", "Base64"}); + this.formatBox = new JComboBox<>(new String[] { "Raw", "UTF-8", "Hex", "Latin1", "Base64" }); this.formatBox.addActionListener(this); Box box = Box.createHorizontalBox(); @@ -36,9 +38,22 @@ public FormatTextField() { this.add(box); } + public void addOption(String option) { + this.formatBox.addItem(option); + } + + public void setDefault(String option) { + for (int i = 0; i < this.formatBox.getItemCount(); i++) { + if (this.formatBox.getItemAt(i).equals(option)) { + this.formatBox.setSelectedItem(this.formatBox.getItemAt(i)); + } + } + } + public Map getValues() { Map values = new HashMap<>(); - values.put("text", this.txtField.getText()); + //values.put("text", this.txtField.getText()); + values.put("text", this.txtField.getRawText()); values.put("encoding", this.formatBox.getSelectedItem().toString()); return values; } @@ -50,27 +65,30 @@ public void setValues(Map values) { this.formatBox.setSelectedItem(encoding); } - public byte[] getText() throws UnsupportedEncodingException { + public ByteArray getText() throws UnsupportedEncodingException { - byte[] raw = this.txtField.getBytes(); - byte[] result = null; + ByteArray raw = this.txtField.getBytes(); + ByteArray result = null; switch ((String) this.formatBox.getSelectedItem()) { - case "Raw": - result = raw; - break; - case "Hex": - result = Hex.decode(raw); - break; - case "Base64": - result = Base64.getDecoder().decode(raw); - break; - case "Latin1": - result = this.txtField.getText().getBytes("ISO-8859-1"); - break; - case "UTF-8": - result = this.txtField.getText().getBytes("UTF-8"); - break; + case "Raw": + result = raw; + break; + case "Hex": + result = ByteArray.byteArray(Hex.decode(raw.getBytes())); + break; + case "Base64": + result = ByteArray.byteArray(Base64.getDecoder().decode(raw.getBytes())); + break; + case "Latin1": + result = ByteArray.byteArray(this.txtField.getText().getBytes("ISO-8859-1")); + break; + case "UTF-8": + result = ByteArray.byteArray(this.txtField.getText().getBytes("UTF-8")); + break; + case "Empty": + result = ByteArray.byteArray(16); + break; } return result; } @@ -85,6 +103,12 @@ public void actionPerformed(ActionEvent e) { if (this.docListener != null) { this.docListener.changedUpdate(null); } + if (this.formatBox.getSelectedItem().equals("Empty")) { + this.txtField.setEnabled(false); + this.txtField.setDisabledTextColor(Color.GRAY); + } else { + this.txtField.setEnabled(true); + } } } diff --git a/src/main/java/de/usd/cstchef/view/ui/VariableTextArea.java b/src/main/java/de/usd/cstchef/view/ui/VariableTextArea.java index 891ffcb..07c34ae 100644 --- a/src/main/java/de/usd/cstchef/view/ui/VariableTextArea.java +++ b/src/main/java/de/usd/cstchef/view/ui/VariableTextArea.java @@ -4,6 +4,7 @@ import javax.swing.JTextArea; import javax.swing.event.DocumentListener; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.Utils; import de.usd.cstchef.view.PopupVariableMenu; @@ -24,8 +25,8 @@ public String getText() { return Utils.replaceVariables(text); } - public byte[] getBytes() { - byte[] bytes = this.txtArea.getText().getBytes(); + public ByteArray getBytes() { + ByteArray bytes = ByteArray.byteArray(this.txtArea.getText()); return Utils.replaceVariablesByte(bytes); } diff --git a/src/main/java/de/usd/cstchef/view/ui/VariableTextField.java b/src/main/java/de/usd/cstchef/view/ui/VariableTextField.java index 7df0a47..363f01a 100644 --- a/src/main/java/de/usd/cstchef/view/ui/VariableTextField.java +++ b/src/main/java/de/usd/cstchef/view/ui/VariableTextField.java @@ -2,6 +2,7 @@ import javax.swing.JTextField; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.Utils; import de.usd.cstchef.view.PopupVariableMenu; @@ -18,8 +19,8 @@ public String getText() { return Utils.replaceVariables(text); } - public byte[] getBytes() { - byte[] bytes = super.getText().getBytes(); + public ByteArray getBytes() { + ByteArray bytes = ByteArray.byteArray(super.getText()); return Utils.replaceVariablesByte(bytes); } diff --git a/src/test/java/de/usd/cstcchecf/operations/AdditionTest.java b/src/test/java/de/usd/cstchef/operations/arithmetic/AdditionTest.java similarity index 55% rename from src/test/java/de/usd/cstcchecf/operations/AdditionTest.java rename to src/test/java/de/usd/cstchef/operations/arithmetic/AdditionTest.java index 506b2ba..c2bff87 100644 --- a/src/test/java/de/usd/cstcchecf/operations/AdditionTest.java +++ b/src/test/java/de/usd/cstchef/operations/arithmetic/AdditionTest.java @@ -1,16 +1,22 @@ -package de.usd.cstcchecf.operations; +package de.usd.cstchef.operations.arithmetic; +import org.junit.Before; import org.junit.Test; +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.arithmetic.Addition; +import de.usd.cstchef.utils.UnitTestObjectFactory; @OperationInfos(name = "Test", category = OperationCategory.ARITHMETIC, description = "Test class") public class AdditionTest extends Addition { private String number; private boolean isFloat; + private CstcObjectFactory factory; protected double getNumber() { @@ -29,9 +35,9 @@ public void SimpleAdditionTest() throws Exception isFloat = false; String testValue = "22"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("32"); + assert result.toString().equals("32"); } @Test @@ -41,9 +47,9 @@ public void AdditionFloatTest() throws Exception isFloat = true; String testValue = "2.2"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("4.4"); + assert result.toString().equals("4.4"); } @Test @@ -53,8 +59,15 @@ public void AdditionFloatRoundTest() throws Exception isFloat = false; String testValue = "2.2"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("4"); + assert result.toString().equals("4"); + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; } } \ No newline at end of file diff --git a/src/test/java/de/usd/cstcchecf/operations/DivideListTest.java b/src/test/java/de/usd/cstchef/operations/arithmetic/DivideListTest.java similarity index 61% rename from src/test/java/de/usd/cstcchecf/operations/DivideListTest.java rename to src/test/java/de/usd/cstchef/operations/arithmetic/DivideListTest.java index 8731f7b..c79200a 100644 --- a/src/test/java/de/usd/cstcchecf/operations/DivideListTest.java +++ b/src/test/java/de/usd/cstchef/operations/arithmetic/DivideListTest.java @@ -1,11 +1,16 @@ -package de.usd.cstcchecf.operations; +package de.usd.cstchef.operations.arithmetic; +import org.junit.Before; import org.junit.Test; +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.Delimiter; import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.arithmetic.DivideList; +import de.usd.cstchef.utils.UnitTestObjectFactory; @OperationInfos(name = "Test", category = OperationCategory.ARITHMETIC, description = "Test class") public class DivideListTest extends DivideList @@ -35,9 +40,9 @@ public void CommaDivideTest() throws Exception isFloat = false; String testValue = "8,2,4"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("1"); + assert result.toString().equals("1"); } @Test @@ -47,9 +52,9 @@ public void CommaDivideFloatTest() throws Exception isFloat = true; String testValue = "8,2,4,2"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("0.5"); + assert result.toString().equals("0.5"); } @Test @@ -59,9 +64,9 @@ public void SpaceDivideTest() throws Exception isFloat = false; String testValue = "8 2 4 0.5"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("2"); + assert result.toString().equals("2"); } @Test @@ -71,8 +76,15 @@ public void SpaceDivideFloatTest() throws Exception isFloat = true; String testValue = "8 2 4 4 0.5"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("0.5"); + assert result.toString().equals("0.5"); + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; } } \ No newline at end of file diff --git a/src/test/java/de/usd/cstcchecf/operations/DivideTest.java b/src/test/java/de/usd/cstchef/operations/arithmetic/DivideTest.java similarity index 60% rename from src/test/java/de/usd/cstcchecf/operations/DivideTest.java rename to src/test/java/de/usd/cstchef/operations/arithmetic/DivideTest.java index 6759619..04221a1 100644 --- a/src/test/java/de/usd/cstcchecf/operations/DivideTest.java +++ b/src/test/java/de/usd/cstchef/operations/arithmetic/DivideTest.java @@ -1,10 +1,15 @@ -package de.usd.cstcchecf.operations; +package de.usd.cstchef.operations.arithmetic; +import org.junit.Before; import org.junit.Test; +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.arithmetic.Divide; +import de.usd.cstchef.utils.UnitTestObjectFactory; @OperationInfos(name = "Test", category = OperationCategory.ARITHMETIC, description = "Test class") public class DivideTest extends Divide @@ -36,9 +41,9 @@ public void SimpleDivideTest() throws Exception isReverse = false; String testValue = "4"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("2"); + assert result.toString().equals("2"); } @Test @@ -49,8 +54,15 @@ public void ReverseDivideTest() throws Exception isReverse = true; String testValue = "4"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("0.5"); + assert result.toString().equals("0.5"); + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; } } \ No newline at end of file diff --git a/src/test/java/de/usd/cstcchecf/operations/MeanTest.java b/src/test/java/de/usd/cstchef/operations/arithmetic/MeanTest.java similarity index 61% rename from src/test/java/de/usd/cstcchecf/operations/MeanTest.java rename to src/test/java/de/usd/cstchef/operations/arithmetic/MeanTest.java index f7f2a49..d2a754b 100644 --- a/src/test/java/de/usd/cstcchecf/operations/MeanTest.java +++ b/src/test/java/de/usd/cstchef/operations/arithmetic/MeanTest.java @@ -1,11 +1,16 @@ -package de.usd.cstcchecf.operations; +package de.usd.cstchef.operations.arithmetic; +import org.junit.Before; import org.junit.Test; +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.Delimiter; import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.arithmetic.Mean; +import de.usd.cstchef.utils.UnitTestObjectFactory; @OperationInfos(name = "Test", category = OperationCategory.ARITHMETIC, description = "Test class") public class MeanTest extends Mean @@ -35,9 +40,9 @@ public void CommaMeanTest() throws Exception isFloat = false; String testValue = "8,2,5"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("5"); + assert result.toString().equals("5"); } @Test @@ -47,9 +52,9 @@ public void CommaMeanFloatTest() throws Exception isFloat = true; String testValue = "0.2,0.3,0.4,0.1"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("0.25"); + assert result.toString().equals("0.25"); } @Test @@ -59,9 +64,9 @@ public void SpaceMeanTest() throws Exception isFloat = false; String testValue = "8 2 5"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("5"); + assert result.toString().equals("5"); } @Test @@ -71,8 +76,15 @@ public void SpaceDivideFloatTest() throws Exception isFloat = true; String testValue = "0.2 0.3 0.4 0.1"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("0.25"); + assert result.toString().equals("0.25"); + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; } } \ No newline at end of file diff --git a/src/test/java/de/usd/cstcchecf/operations/MedianTest.java b/src/test/java/de/usd/cstchef/operations/arithmetic/MedianTest.java similarity index 62% rename from src/test/java/de/usd/cstcchecf/operations/MedianTest.java rename to src/test/java/de/usd/cstchef/operations/arithmetic/MedianTest.java index 59bca42..914d57c 100644 --- a/src/test/java/de/usd/cstcchecf/operations/MedianTest.java +++ b/src/test/java/de/usd/cstchef/operations/arithmetic/MedianTest.java @@ -1,11 +1,16 @@ -package de.usd.cstcchecf.operations; +package de.usd.cstchef.operations.arithmetic; +import org.junit.Before; import org.junit.Test; +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.Delimiter; import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.arithmetic.Median; +import de.usd.cstchef.utils.UnitTestObjectFactory; @OperationInfos(name = "Test", category = OperationCategory.ARITHMETIC, description = "Test class") public class MedianTest extends Median @@ -35,9 +40,9 @@ public void CommaMedianTest() throws Exception isFloat = false; String testValue = "1,2,3,4,5"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("3"); + assert result.toString().equals("3"); } @Test @@ -47,8 +52,15 @@ public void CommaMedianFloatTest() throws Exception isFloat = true; String testValue = "1,2,3.5,4,5"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("3.5"); + assert result.toString().equals("3.5"); + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; } } \ No newline at end of file diff --git a/src/test/java/de/usd/cstcchecf/operations/MultiplyListTest.java b/src/test/java/de/usd/cstchef/operations/arithmetic/MultiplyListTest.java similarity index 60% rename from src/test/java/de/usd/cstcchecf/operations/MultiplyListTest.java rename to src/test/java/de/usd/cstchef/operations/arithmetic/MultiplyListTest.java index 860fb68..00e99bd 100644 --- a/src/test/java/de/usd/cstcchecf/operations/MultiplyListTest.java +++ b/src/test/java/de/usd/cstchef/operations/arithmetic/MultiplyListTest.java @@ -1,11 +1,16 @@ -package de.usd.cstcchecf.operations; +package de.usd.cstchef.operations.arithmetic; +import org.junit.Before; import org.junit.Test; +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.Delimiter; import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.arithmetic.MultiplyList; +import de.usd.cstchef.utils.UnitTestObjectFactory; @OperationInfos(name = "Test", category = OperationCategory.ARITHMETIC, description = "Test class") public class MultiplyListTest extends MultiplyList @@ -35,9 +40,9 @@ public void CommaMultiplyTest() throws Exception isFloat = false; String testValue = "1,2,3,4,5,6"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("720"); + assert result.toString().equals("720"); } @Test @@ -47,9 +52,9 @@ public void CommaMultiplyFloatTest() throws Exception isFloat = true; String testValue = "3,0.5,0.5"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("0.75"); + assert result.toString().equals("0.75"); } @Test @@ -59,10 +64,10 @@ public void SpaceMultiplyTest() throws Exception isFloat = false; String testValue = "1 2 3 4 5 6"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - System.out.println(new String(result)); - assert new String(result).equals("720"); + System.out.println(result.toString()); + assert result.toString().equals("720"); } @Test @@ -72,8 +77,15 @@ public void SpaceMultiplyFloatTest() throws Exception isFloat = true; String testValue = "3 0.5 0.5"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("0.75"); + assert result.toString().equals("0.75"); + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; } } \ No newline at end of file diff --git a/src/test/java/de/usd/cstcchecf/operations/MultiplyTest.java b/src/test/java/de/usd/cstchef/operations/arithmetic/MultiplyTest.java similarity index 56% rename from src/test/java/de/usd/cstcchecf/operations/MultiplyTest.java rename to src/test/java/de/usd/cstchef/operations/arithmetic/MultiplyTest.java index 066df18..0b3a385 100644 --- a/src/test/java/de/usd/cstcchecf/operations/MultiplyTest.java +++ b/src/test/java/de/usd/cstchef/operations/arithmetic/MultiplyTest.java @@ -1,10 +1,15 @@ -package de.usd.cstcchecf.operations; +package de.usd.cstchef.operations.arithmetic; +import org.junit.Before; import org.junit.Test; +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.arithmetic.Multiply; +import de.usd.cstchef.utils.UnitTestObjectFactory; @OperationInfos(name = "Test", category = OperationCategory.ARITHMETIC, description = "Test class") public class MultiplyTest extends Multiply @@ -29,9 +34,9 @@ public void SimpleMultiplyTest() throws Exception isFloat = false; String testValue = "22"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("220"); + assert result.toString().equals("220"); } @Test @@ -41,9 +46,9 @@ public void MultiplyFloatTest() throws Exception isFloat = true; String testValue = "2.2"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).startsWith("4.84"); + assert result.toString().startsWith("4.84"); } @Test @@ -53,8 +58,15 @@ public void MultiplyRoundTest() throws Exception isFloat = false; String testValue = "2.2"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("5"); + assert result.toString().equals("5"); + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; } } \ No newline at end of file diff --git a/src/test/java/de/usd/cstcchecf/operations/SubtractionTest.java b/src/test/java/de/usd/cstchef/operations/arithmetic/SubtractionTest.java similarity index 53% rename from src/test/java/de/usd/cstcchecf/operations/SubtractionTest.java rename to src/test/java/de/usd/cstchef/operations/arithmetic/SubtractionTest.java index 533a13b..3421c68 100644 --- a/src/test/java/de/usd/cstcchecf/operations/SubtractionTest.java +++ b/src/test/java/de/usd/cstchef/operations/arithmetic/SubtractionTest.java @@ -1,11 +1,19 @@ -package de.usd.cstcchecf.operations; +package de.usd.cstchef.operations.arithmetic; +import org.junit.Before; import org.junit.Test; +import burp.BurpUtils; +import burp.CstcObjectFactory; +import burp.api.montoya.MontoyaApi; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.internal.MontoyaObjectFactory; import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.arithmetic.Addition; import de.usd.cstchef.operations.arithmetic.Subtraction; +import de.usd.cstchef.utils.UnitTestObjectFactory; @OperationInfos(name = "Test", category = OperationCategory.ARITHMETIC, description = "Test class") public class SubtractionTest extends Subtraction @@ -30,9 +38,9 @@ public void SimpleSubtraction() throws Exception isFloat = false; String testValue = "22"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("12"); + assert result.toString().equals("12"); } @Test @@ -42,10 +50,10 @@ public void SubtractionFloatTest() throws Exception isFloat = true; String testValue = "2.2"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - System.out.println(new String(result)); - assert new String(result).startsWith("0.1"); + System.out.println(result.toString()); + assert result.toString().startsWith("0.1"); } @Test @@ -55,8 +63,15 @@ public void SubractionRoundTest() throws Exception isFloat = false; String testValue = "2.8"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("1"); + assert result.toString().equals("1"); + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; } } \ No newline at end of file diff --git a/src/test/java/de/usd/cstcchecf/operations/SumTest.java b/src/test/java/de/usd/cstchef/operations/arithmetic/SumTest.java similarity index 61% rename from src/test/java/de/usd/cstcchecf/operations/SumTest.java rename to src/test/java/de/usd/cstchef/operations/arithmetic/SumTest.java index 69f5357..b2ded63 100644 --- a/src/test/java/de/usd/cstcchecf/operations/SumTest.java +++ b/src/test/java/de/usd/cstchef/operations/arithmetic/SumTest.java @@ -1,11 +1,16 @@ -package de.usd.cstcchecf.operations; +package de.usd.cstchef.operations.arithmetic; +import org.junit.Before; import org.junit.Test; +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; import de.usd.cstchef.Delimiter; import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.operations.Operation; import de.usd.cstchef.operations.OperationCategory; import de.usd.cstchef.operations.arithmetic.Sum; +import de.usd.cstchef.utils.UnitTestObjectFactory; @OperationInfos(name = "Test", category = OperationCategory.ARITHMETIC, description = "Test class") public class SumTest extends Sum @@ -35,9 +40,9 @@ public void CommaSumTest() throws Exception isFloat = false; String testValue = "1,2,3,4,5,6"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("21"); + assert result.toString().equals("21"); } @Test @@ -47,9 +52,9 @@ public void CommaSumFloatTest() throws Exception isFloat = true; String testValue = "1,2,3,4,5,6"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("21.0"); + assert result.toString().equals("21.0"); } @Test @@ -59,9 +64,9 @@ public void SpacesumTest() throws Exception isFloat = false; String testValue = "1.0 2.1 3.2 4.3 5.4 6.5"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("23"); + assert result.toString().equals("23"); } @Test @@ -71,8 +76,15 @@ public void SpacesumFloatTest() throws Exception isFloat = true; String testValue = "1.0 2.1 3.2 4.3 5.4 6.5"; - byte[] result = perform(testValue.getBytes()); + ByteArray result = perform(factory.createByteArray(testValue), null); - assert new String(result).equals("22.5"); + assert result.toString().equals("22.5"); + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; } } \ No newline at end of file diff --git a/src/test/java/de/usd/cstchef/operations/extractors/HttpBodyExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/HttpBodyExtractorTest.java new file mode 100755 index 0000000..c752be5 --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/HttpBodyExtractorTest.java @@ -0,0 +1,93 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; + +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.OperationCategory; + + +@OperationInfos(name = "HttpBodyExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class HttpBodyExtractorTest extends HttpBodyExtractor { + + HashMap inputs = new HashMap(); + + @Test + public void extractionTest() throws Exception + { + for(String res : inputs.keySet()){ + assertArrayEquals(factory.createByteArray(inputs.get(res)).getBytes(), perform(factory.createByteArray(res), MessageType.RESPONSE).getBytes()); + } + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + // param=value + String reqIn1 = """ + POST / HTTP/2 + Header1: value1 + Header2: value2 + + param=value + """; + String reqOut1 = """ + param=value + """; + + // empty body + String reqIn2 = """ + GET / HTTP/2 + Header1: value1 + Header2: value2 + + + """; + String reqOut2 = ""; + + // HTTP Response - html body + String resIn1 = """ + HTTP/2 200 Ok + Header1: value1 + Header2: value2 + + + +

Example body

+ + """; + String resOut1 = """ + + +

Example body

+ + """; + + // HTTP Response - empty body + String resIn2 = """ + HTTP/2 200 Ok + Header1: value1 + + + """; + String resOut2 = ""; + + + inputs.put(reqIn1, reqOut1); + inputs.put(reqIn2, reqOut2); + inputs.put(resIn1, resOut1); + inputs.put(resIn2, resOut2); + } +} \ No newline at end of file diff --git a/src/test/java/de/usd/cstchef/operations/extractors/HttpCookieExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/HttpCookieExtractorTest.java new file mode 100755 index 0000000..4122950 --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/HttpCookieExtractorTest.java @@ -0,0 +1,153 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.HashMap; + +import org.javatuples.Triplet; +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.Utils; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.OperationCategory; + +@OperationInfos(name = "HttpCookieExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class HttpCookieExtractorTest extends HttpCookieExtractor { + + // HashMap> + HashMap> inputs = new HashMap<>(); + + @Test + public void extractionTest() throws Exception { + for (String inp : inputs.keySet()) { + Triplet res = inputs.get(inp); + ByteArray inputArray = factory.createByteArray(inp); + ByteArray outputArray = factory.createByteArray(res.getValue0()); + MessageType messageType = parseMessageType(inputArray); + this.cookieNameField.setText(res.getValue1()); + if (res.getValue2()) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Parameter name not found.", exception.getMessage()); + } + else{ + assertArrayEquals(outputArray.getBytes(), perform(inputArray, messageType).getBytes()); + } + } + } + + @Before + public void setup() { + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + Utils.factory = new UnitTestObjectFactory(); + + // cookie1 + String reqIn1 = """ + GET / HTTP/2 + Header1: a + Cookie: cookie1=value1; cookie2=value2 + + + """; + String reqOut1 = "value1"; + String reqCookie1 = "cookie1"; + Triplet reqTriplet1 = new Triplet(reqOut1, reqCookie1, false); + + // cookie2 + String reqIn2 = """ + GET / HTTP/2 + Header1: b + Cookie: cookie1=value1; cookie2=value2 + + + """; + String reqOut2 = "value2"; + String reqCookie2 = "cookie2"; + Triplet reqTriplet2 = new Triplet(reqOut2, reqCookie2, false); + + // Exception + String reqIn3 = """ + GET / HTTP/2 + Header1: c + Cookie: cookie1=value1; cookie2=value2 + + + """; + String reqOut3 = ""; + String reqCookie3 = "cookie3"; + Triplet reqTriplet3 = new Triplet(reqOut3, reqCookie3, true); + + // Empty cookieName + String reqIn4 = """ + GET / HTTP/2 + Header1: d + Cookie: cookie1=value1; cookie2=value2 + + + """; + String reqOut4 = ""; + String reqCookie4 = ""; + Triplet reqTriplet4 = new Triplet(reqOut4, reqCookie4, false); + + // cookie1 + String resIn1 = """ + HTTP/2 200 Ok + Header1: a + Set-Cookie: cookie1=value1; cookie2=value2 + + """; + String resOut1 = "value1"; + String resCookie1 = "cookie1"; + Triplet resTriplet1 = new Triplet(resOut1, resCookie1, false); + + // cookie2 + String resIn2 = """ + HTTP/2 200 Ok + Header1: b + Set-Cookie: cookie1=value1; cookie2=value2 + + """; + String resOut2 = "value2"; + String resCookie2 = "cookie2"; + Triplet resTriplet2 = new Triplet(resOut2, resCookie2, false); + + // Exception + String resIn3 = """ + HTTP/2 200 Ok + Header1: c + Set-Cookie: cookie1=value1; cookie2=value2 + + """; + String resOut3 = ""; + String resCookie3 = "cookie3"; + Triplet resTriplet3 = new Triplet(resOut3, resCookie3, false); + + // empty cookieName + String resIn4 = """ + HTTP/2 200 Ok + Header1: d + Set-Cookie: cookie1=value1; cookie2=value2 + + """; + String resOut4 = ""; + String resCookie4 = ""; + Triplet resTriplet4 = new Triplet(resOut4, resCookie4, false); + + inputs.put(reqIn1, reqTriplet1); + inputs.put(reqIn2, reqTriplet2); + inputs.put(reqIn3, reqTriplet3); + inputs.put(reqIn4, reqTriplet4); + inputs.put(resIn1, resTriplet1); + inputs.put(resIn2, resTriplet2); + inputs.put(resIn3, resTriplet3); + inputs.put(resIn4, resTriplet4); + } +} diff --git a/src/test/java/de/usd/cstchef/operations/extractors/HttpGetExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/HttpGetExtractorTest.java new file mode 100755 index 0000000..17a0b34 --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/HttpGetExtractorTest.java @@ -0,0 +1,119 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.HashMap; + +import org.javatuples.Triplet; +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.OperationCategory; + + +@OperationInfos(name = "HttpGetExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class HttpGetExtractorTest extends HttpGetExtractor { + + // HashMap> + HashMap> inputs = new HashMap<>(); + + @Test + public void extractionTest() throws Exception { + for (String inp : inputs.keySet()) { + Triplet res = inputs.get(inp); + ByteArray inputArray = factory.createByteArray(inp); + ByteArray outputArray = factory.createByteArray(res.getValue0()); + MessageType messageType = parseMessageType(inputArray); + this.parameter.setText(res.getValue1()); + if (res.getValue2()) { + if(messageType == MessageType.REQUEST) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Parameter name not found.", exception.getMessage()); + } + else{ + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Input is not a valid HTTP Request", exception.getMessage()); + } + } + else{ + assertArrayEquals(outputArray.getBytes(), perform(inputArray, messageType).getBytes()); + } + } + } + + @Before + public void setup() { + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + // param1 + String reqIn1 = """ + GET /?param1=value1¶m2=value2 HTTP/2 + Header1: a + + + """; + String reqOut1 = "value1"; + String reqParam1 = "param1"; + Triplet reqTriplet1 = new Triplet(reqOut1, reqParam1, false); + + // param2 + String reqIn2 = """ + GET /?param1=value1¶m2=value2 HTTP/2 + Header1: b + + + """; + String reqOut2 = "value2"; + String reqParam2 = "param2"; + Triplet reqTriplet2 = new Triplet(reqOut2, reqParam2, false); + + // param3 - Exception + String reqIn3 = """ + GET /?param1=value1¶m2=value2 HTTP/2 + Header1: c + + + """; + String reqOut3 = ""; + String reqParam3 = "param3"; + Triplet reqTriplet3 = new Triplet(reqOut3, reqParam3, true); + + // empty paramName + String reqIn4 = """ + GET /?param1=value1¶m2=value2 HTTP/2 + Header1: d + + + """; + String reqOut4 = ""; + String reqParam4 = ""; + Triplet reqTriplet4 = new Triplet(reqOut4, reqParam4, false); + + // HTTP Response + String resIn1 = """ + HTTP/2 200 Ok + Header: value + + + """; + String resOut1 = ""; + String resParam1 = "param1"; + Triplet resTriplet1 = new Triplet(resOut1, resParam1, true); + + inputs.put(reqIn1, reqTriplet1); + inputs.put(reqIn2, reqTriplet2); + inputs.put(reqIn3, reqTriplet3); + inputs.put(reqIn4, reqTriplet4); + inputs.put(resIn1, resTriplet1); + + } +} diff --git a/src/test/java/de/usd/cstchef/operations/extractors/HttpHeaderExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/HttpHeaderExtractorTest.java new file mode 100755 index 0000000..78f636f --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/HttpHeaderExtractorTest.java @@ -0,0 +1,117 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.HashMap; + +import org.javatuples.Triplet; +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.OperationCategory; + +@OperationInfos(name = "HttpHeaderExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class HttpHeaderExtractorTest extends HttpHeaderExtractor { + + // HashMap> + HashMap> inputs = new HashMap<>(); + + @Test + public void extractionTest() throws Exception { + for (String inp : inputs.keySet()) { + Triplet res = inputs.get(inp); + ByteArray inputArray = factory.createByteArray(inp); + ByteArray outputArray = factory.createByteArray(res.getValue0()); + MessageType messageType = parseMessageType(inputArray); + this.headerNameField.setText(res.getValue1()); + if (res.getValue2()) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Parameter name not found.", exception.getMessage()); + } + else{ + //assertEquals(perform(inputArray, messageType), outputArray); + assertArrayEquals(outputArray.getBytes(), perform(inputArray, messageType).getBytes()); + } + } + } + + @Before + public void setup() { + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + // Header1 + String reqIn1 = """ + GET / HTTP/2 + Header1: value1 + Header2: value2 + + a + """; + String reqOut1 = "value1"; + String reqHeader1 = "Header1"; + Triplet reqTriplet1 = new Triplet(reqOut1, reqHeader1, false); + + // Header2 + String reqIn2 = """ + GET / HTTP/2 + Header1: value1 + Header2: value2 + + b + """; + String reqOut2 = "value2"; + String reqHeader2 = "Header2"; + Triplet reqTriplet2 = new Triplet(reqOut2, reqHeader2, false); + + // Header3 - Exception + String reqIn3 = """ + GET / HTTP/2 + Header1: value1 + Header2: value2 + + c + """; + String reqOut3 = ""; + String reqHeader3 = "Header3"; + Triplet reqTriplet3 = new Triplet(reqOut3, reqHeader3, true); + + // empty headerName + String reqIn4 = """ + GET / HTTP/2 + Header1: value1 + Header2: value2 + + d + """; + String reqOut4 = ""; + String reqHeader4 = ""; + Triplet reqTriplet4 = new Triplet(reqOut4, reqHeader4, false); + + // HTTP Response - Header2 + String resIn1 = """ + HTTP/2 200 Ok + Header1: value1 + Header2: value2 + + + """; + String resOut1 = "value2"; + String resHeader1 = "Header2"; + Triplet resTriplet1 = new Triplet(resOut1, resHeader1, false); + + inputs.put(reqIn1, reqTriplet1); + inputs.put(reqIn2, reqTriplet2); + inputs.put(reqIn3, reqTriplet3); + inputs.put(reqIn4, reqTriplet4); + inputs.put(resIn1, resTriplet1); + } +} diff --git a/src/test/java/de/usd/cstchef/operations/extractors/HttpMethodExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/HttpMethodExtractorTest.java new file mode 100755 index 0000000..5f7d1f2 --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/HttpMethodExtractorTest.java @@ -0,0 +1,73 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.HashMap; + +import org.javatuples.Pair; +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.OperationCategory; + +@OperationInfos(name = "HttpMethodExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class HttpMethodExtractorTest extends HttpMethodExtractor { + + // HashMap> + HashMap> inputs = new HashMap<>(); + + @Test + public void extractionTest() throws Exception { + for (String inp : inputs.keySet()) { + Pair res = inputs.get(inp); + ByteArray inputArray = factory.createByteArray(inp); + ByteArray outputArray = factory.createByteArray(res.getValue0()); + MessageType messageType = parseMessageType(inputArray); + if (res.getValue1()) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Input is not a valid HTTP Request", exception.getMessage()); + } + else{ + assertArrayEquals(outputArray.getBytes(), perform(inputArray, messageType).getBytes()); + } + } + } + + @Before + public void setup() { + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + // GET + String reqIn1 = """ + GET / HTTP/2 + Header1: value1 + Header2: value2 + + a + """; + String reqOut1 = "GET"; + Pair reqPair1 = new Pair(reqOut1, false); + + // HTTP Response - Exception + String reqIn2 = """ + HTTP/2 200 Ok + Header: value + + + """; + String reqOut2 = ""; + Pair reqPair2 = new Pair(reqOut2, true); + + inputs.put(reqIn1, reqPair1); + inputs.put(reqIn2, reqPair2); + } +} diff --git a/src/test/java/de/usd/cstchef/operations/extractors/HttpMultipartExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/HttpMultipartExtractorTest.java new file mode 100644 index 0000000..2d237d2 --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/HttpMultipartExtractorTest.java @@ -0,0 +1,198 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.HashMap; + +import org.javatuples.Triplet; +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.OperationCategory; + +@OperationInfos(name = "HttpMultipartExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class HttpMultipartExtractorTest extends HttpMultipartExtractor { + + // HashMap> + HashMap> inputs = new HashMap<>(); + + @Test + public void extractionTest() throws Exception { + for (String inp : inputs.keySet()) { + Triplet res = inputs.get(inp); + ByteArray inputArray = factory.createByteArray(inp); + ByteArray outputArray = factory.createByteArray(res.getValue0()); + MessageType messageType = parseMessageType(inputArray); + this.parameter.setText(res.getValue1()); + if (res.getValue2()) { + if(messageType == MessageType.REQUEST) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Input is not a valid request", exception.getMessage()); + } + if(messageType == MessageType.RESPONSE) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Input is not a valid HTTP Request", exception.getMessage()); + } + } + else{ + assertArrayEquals(outputArray.getBytes(), perform(inputArray, messageType).getBytes()); + } + } + } + + @Before + public void setup() { + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + // default case + String reqIn1 = """ + GET / HTTP/2 + Header1: a + Content-Type: multipart/form-data; boundary=-----2a8ae6ad + + -----2a8ae6ad + Content-Disposition: form-data; name="parameter1"; filename="file1" + + value1 + -----2a8ae6ad + Content-Disposition: form-data; name="parameter2"; + + value2 + + -----2a8ae6ad + Content-Disposition: form-data; name="parameter3"; filename="file1" + + + -----2a8ae6ad + """; + String reqOut1 = "value1"; + String reqParam1 = "parameter1"; + Triplet reqTriplet1 = new Triplet(reqOut1, reqParam1, false); + + // default case + newline + String reqIn2 = """ + GET / HTTP/2 + Header1: a + Content-Type: multipart/form-data; boundary=-----2a8ae6ad + + -----2a8ae6ad + Content-Disposition: form-data; name="parameter1"; filename="file1" + + value1 + -----2a8ae6ad + Content-Disposition: form-data; name="parameter2"; + + value2 + + -----2a8ae6ad + Content-Disposition: form-data; name="parameter3"; filename="file1" + + + -----2a8ae6ad + """; + String reqOut2 = "value2\n"; + String reqParam2 = "parameter2"; + Triplet reqTriplet2 = new Triplet(reqOut2, reqParam2, false); + + // empty value + String reqIn3 = """ + GET / HTTP/2 + Header1: c + Content-Type: multipart/form-data; boundary=-----2a8ae6ad + + -----2a8ae6ad + Content-Disposition: form-data; name="parameter1"; filename="file1" + + value1 + -----2a8ae6ad + Content-Disposition: form-data; name="parameter2"; + + value2 + + -----2a8ae6ad + Content-Disposition: form-data; name="parameter3"; filename="file1" + + + -----2a8ae6ad + """; + String reqOut3 = ""; + String reqParam3 = "parameter3"; + Triplet reqTriplet3 = new Triplet(reqOut3, reqParam3, false); + + // param not found + String reqIn4 = """ + GET / HTTP/2 + Header1: d + Content-Type: multipart/form-data; boundary=-----2a8ae6ad + + -----2a8ae6ad + Content-Disposition: form-data; name="parameter1"; filename="file1" + + value1 + -----2a8ae6ad + Content-Disposition: form-data; name="parameter2"; + + value2 + + -----2a8ae6ad + Content-Disposition: form-data; name="parameter3"; filename="file1" + + + -----2a8ae6ad + """; + String reqOut4 = ""; + String reqParam4 = "parameter4"; + Triplet reqTriplet4 = new Triplet(reqOut4, reqParam4, true); + + // empty param + String reqIn5 = """ + GET / HTTP/2 + Header1: e + Content-Type: multipart/form-data; boundary=-----2a8ae6ad + + -----2a8ae6ad + Content-Disposition: form-data; name="parameter1"; filename="file1" + + value1 + -----2a8ae6ad + Content-Disposition: form-data; name="parameter2"; + + value2 + + -----2a8ae6ad + Content-Disposition: form-data; name="parameter3"; filename="file1" + + + -----2a8ae6ad + """; + String reqOut5 = ""; + String reqParam5 = ""; + Triplet reqTriplet5 = new Triplet(reqOut5, reqParam5, false); + + // HTTP Response + String resIn1 = """ + HTTP/2 200 Ok + Header1: value1 + Header2: value2 + """; + String resOut1 = ""; + String resParam1 = "abc"; + Triplet resTriplet1 = new Triplet(resOut1, resParam1, true); + + inputs.put(reqIn1, reqTriplet1); + inputs.put(reqIn2, reqTriplet2); + inputs.put(reqIn3, reqTriplet3); + inputs.put(reqIn4, reqTriplet4); + inputs.put(reqIn5, reqTriplet5); + inputs.put(resIn1, resTriplet1); + } +} diff --git a/src/test/java/de/usd/cstchef/operations/extractors/HttpPostExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/HttpPostExtractorTest.java new file mode 100644 index 0000000..696c26c --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/HttpPostExtractorTest.java @@ -0,0 +1,107 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.HashMap; + +import org.javatuples.Triplet; +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.OperationCategory; + + +@OperationInfos(name = "HttpPostExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class HttpPostExtractorTest extends HttpPostExtractor { + + // HashMap> + HashMap> inputs = new HashMap<>(); + + @Test + public void extractionTest() throws Exception { + for (String inp : inputs.keySet()) { + Triplet res = inputs.get(inp); + ByteArray inputArray = factory.createByteArray(inp); + ByteArray outputArray = factory.createByteArray(res.getValue0()); + MessageType messageType = parseMessageType(inputArray); + this.parameter.setText(res.getValue1()); + if (res.getValue2()) { + if(messageType == MessageType.REQUEST) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Input is not a valid request", exception.getMessage()); + } + if(messageType == MessageType.RESPONSE) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Input is not a valid HTTP Request", exception.getMessage()); + } + } + else{ + assertArrayEquals(outputArray.getBytes(), perform(inputArray, messageType).getBytes()); + } + } + } + + @Before + public void setup() { + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + // default case + String reqIn1 = """ + POST / HTTP/2 + Header1: a + Content-Type: application/x-www-form-urlencoded + + parameter1=value1¶meter2=value2 + """; + String reqOut1 = "value1"; + String reqParam1 = "parameter1"; + Triplet reqTriplet1 = new Triplet(reqOut1, reqParam1, false); + + // empty param + String reqIn2 = """ + POST / HTTP/2 + Header1: b + Content-Type: application/x-www-form-urlencoded + + param1=value1¶m2=value2 + """; + String reqOut2 = ""; + String reqParam2 = ""; + Triplet reqTriplet2 = new Triplet(reqOut2, reqParam2, false); + + // param not found + String reqIn3 = """ + POST / HTTP/2 + Header1: c + Content-Type: application/x-www-form-urlencoded + + param1=value1¶m2=value2 + """; + String reqOut3 = ""; + String reqParam3 = "parameter3"; + Triplet reqTriplet3 = new Triplet(reqOut3, reqParam3, true); + + // HTTP Response + String resIn1 = """ + HTTP/2 200 Ok + Header1: value1 + """; + String resOut1 = ""; + String resParam1 = "abc"; + Triplet resTriplet1 = new Triplet(resOut1, resParam1, true); + + inputs.put(reqIn1, reqTriplet1); + inputs.put(reqIn2, reqTriplet2); + inputs.put(reqIn3, reqTriplet3); + inputs.put(resIn1, resTriplet1); + } +} diff --git a/src/test/java/de/usd/cstchef/operations/extractors/HttpUriExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/HttpUriExtractorTest.java new file mode 100644 index 0000000..78b655b --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/HttpUriExtractorTest.java @@ -0,0 +1,91 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.HashMap; + +import org.javatuples.Triplet; +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.OperationCategory; + + +@OperationInfos(name = "HttpUriExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class HttpUriExtractorTest extends HttpUriExtractor { + + // HashMap> + HashMap> inputs = new HashMap<>(); + + @Test + public void extractionTest() throws Exception { + for (String inp : inputs.keySet()) { + Triplet res = inputs.get(inp); + ByteArray inputArray = factory.createByteArray(inp); + ByteArray outputArray = factory.createByteArray(res.getValue0()); + MessageType messageType = parseMessageType(inputArray); + this.checkbox.setSelected(res.getValue1()); + if (res.getValue2()) { + if(messageType == MessageType.REQUEST) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Input is not a valid request", exception.getMessage()); + } + if(messageType == MessageType.RESPONSE) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Input is not a valid HTTP Request", exception.getMessage()); + } + } + else{ + assertArrayEquals(outputArray.getBytes(), perform(inputArray, messageType).getBytes()); + } + } + } + + @Before + public void setup() { + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + // with params + String reqIn1 = """ + GET /uri?param=value HTTP/2 + Header1: a + + + """; + String reqOut1 = "/uri?param=value"; + Triplet reqTriplet1 = new Triplet(reqOut1, true, false); + + // without params + String reqIn2 = """ + GET /uri?param=value HTTP/2 + Header1: b + + + """; + String reqOut2 = "/uri"; + Triplet reqTriplet2 = new Triplet(reqOut2, false, false); + + // HTTP Response + String reqIn3 = """ + HTTP/2 200 Ok + Header1: value1 + """; + String reqOut3 = ""; + Triplet reqTriplet3 = new Triplet(reqOut3, false, true); + + + inputs.put(reqIn1, reqTriplet1); + inputs.put(reqIn2, reqTriplet2); + inputs.put(reqIn3, reqTriplet3); + + } +} diff --git a/src/test/java/de/usd/cstchef/operations/extractors/HttpXmlExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/HttpXmlExtractorTest.java new file mode 100644 index 0000000..069bd55 --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/HttpXmlExtractorTest.java @@ -0,0 +1,208 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.HashMap; + +import org.javatuples.Triplet; +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.OperationCategory; + + +@OperationInfos(name = "HttpXmlExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class HttpXmlExtractorTest extends HttpXmlExtractor { + + // HashMap> + HashMap> inputs = new HashMap<>(); + + @Test + public void extractionTest() throws Exception { + for (String inp : inputs.keySet()) { + Triplet res = inputs.get(inp); + ByteArray inputArray = factory.createByteArray(inp); + ByteArray outputArray = factory.createByteArray(res.getValue0()); + MessageType messageType = parseMessageType(inputArray); + this.fieldTxt.setText(res.getValue1()); + if (res.getValue2()) { + Exception exception = assertThrows(IllegalArgumentException.class, () -> perform(inputArray, messageType)); + assertEquals("Input is not a valid request", exception.getMessage()); + } + else{ + assertArrayEquals(outputArray.getBytes(), perform(inputArray, messageType).getBytes()); + } + } + } + + @Before + public void setup() { + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + // outer tag (HTTP Request && param correct) + String reqIn1 = """ + GET / HTTP/2 + Header1: a + Content-Type: application/xml + + + + tag1 + + tag3 + + + """; + String reqOut1 = "tag1"; + String reqTag1 = "Tag1"; + Triplet reqTriplet1 = new Triplet(reqOut1, reqTag1, false); + + // inner tag (HTTP Request && param correct) + String reqIn2 = """ + GET / HTTP/2 + Header1: b + Content-Type: application/xml + + + + tag1 + + tag3 + + + """; + String reqOut2 = "tag3"; + String reqTag2 = "Tag3"; + Triplet reqTriplet2 = new Triplet(reqOut2, reqTag2, false); + + + // HTTP Request && param correct) + String resIn1 = """ + POST /echo/post/xml HTTP/1.1 + Host: reqbin.com + Content-Type: application/xml + Accept: application/xml + Content-Length: 118 + + + + tag1 + + tag3 + + + """; + String resOut1 = "tag1"; + String resTag1 = "Tag1"; + Triplet resTriplet1 = new Triplet(resOut1, resTag1, false); + + // HTTP Response && param correct + String resIn2 = """ + HTTP/2 200 Ok + Header1: b + Content-Type: application/xml + + + + tag1 + + tag3 + + + """; + String resOut2 = "tag3"; + String resTag2 = "Tag3"; + Triplet resTriplet2 = new Triplet(resOut2, resTag2, false); + + + // HTTP Request && param empty + String reqIn3 = """ + GET / HTTP/2 + Header1: c + Content-Type: application/xml + + + + tag1 + + tag3 + + + """; + String reqOut3 = ""; + String reqTag3 = ""; + Triplet reqTriplet3 = new Triplet(reqOut3, reqTag3, false); + + // HTTP Response && param empty + String resIn3 = """ + GET / HTTP/2 + Header1: c + Content-Type: application/xml + + + + tag1 + + tag3 + + + """; + String resOut3 = ""; + String resTag3 = ""; + Triplet resTriplet3 = new Triplet(resOut3, resTag3, false); + + // HTTP Request && param incorrect + String reqIn4 = """ + GET / HTTP/2 + Header1: a + Content-Type: application/xml + + + + tag1 + + tag3 + + + """; + String reqOut4 = ""; + String reqTag4 = "ImaginaryTag"; + Triplet reqTriplet4 = new Triplet(reqOut4, reqTag4, true); + + // HTTP Response && param incorrect + String resIn4 = """ + HTTP/2 200 Ok + Header1: b + Content-Type: application/xml + + + + tag1 + + tag3 + + + """; + String resOut4 = ""; + String resTag4 = "ImaginaryTag"; + Triplet resTriplet4 = new Triplet(resOut4, resTag4, true); + + inputs.put(reqIn1, reqTriplet1); + inputs.put(reqIn2, reqTriplet2); + inputs.put(resIn1, resTriplet1); + inputs.put(resIn2, resTriplet2); + inputs.put(reqIn3, reqTriplet3); + inputs.put(resIn3, resTriplet3); + inputs.put(reqIn4, reqTriplet4); + inputs.put(resIn4, resTriplet4); + } +} diff --git a/src/test/java/de/usd/cstchef/operations/extractors/JsonExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/JsonExtractorTest.java new file mode 100644 index 0000000..d7e2d8b --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/JsonExtractorTest.java @@ -0,0 +1,108 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.util.HashMap; + +import org.javatuples.Quartet; +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.operations.OperationCategory; + + +@OperationInfos(name = "JsonExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class JsonExtractorTest extends JsonExtractor { + + // HashMap> + HashMap> inputs = new HashMap<>(); + + @Test + public void extractionTest() throws Exception { + for (String inp : inputs.keySet()) { + Quartet res = inputs.get(inp); + ByteArray inputArray = factory.createByteArray(inp); + ByteArray outputArray = factory.createByteArray(res.getValue0()); + this.fieldTxt.setText(res.getValue1()); + if (res.getValue2()) { + Exception exception = assertThrows(com.jayway.jsonpath.PathNotFoundException.class, () -> perform(inputArray, null)); + assertEquals(res.getValue3(), exception.getMessage()); + } + else{ + assertArrayEquals(outputArray.getBytes(), perform(inputArray, null).getBytes()); + } + } + } + + @Before + public void setup() { + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + // outer + String in1 = """ + { + "key1":"value1", + "key2":{ + "key3":"value3" + } + } + """; + String out1 = "value1"; + String key1 = "key1"; + Quartet quartet1 = new Quartet(out1, key1, false, null); + + // inner + String in2 = """ + { + "key1":"a", + "key2":{ + "key3":"value3" + } + } + """; + String out2 = "value3"; + String key2 ="key2.key3"; + Quartet quartet2 = new Quartet(out2, key2, false, null); + + // nested + String in3 = """ + { + "key1":"b", + "key2":{ + "key3":"value3" + } + } + """; + String out3 = "{key3=value3}"; + String key3 = "key2"; + Quartet quartet3 = new Quartet(out3, key3, false, null); + + // path not found exception + String in4 = """ + { + "key1":"c", + "key2":{ + "key3":"value3" + } + } + """; + String out4 = ""; + String key4 = "key3"; + String excMess4 = "No results for path: $['key3']"; + Quartet quartet4 = new Quartet(out4, key4, true, excMess4); + + inputs.put(in1, quartet1); + inputs.put(in2, quartet2); + inputs.put(in3, quartet3); + inputs.put(in4, quartet4); + + } +} diff --git a/src/test/java/de/usd/cstchef/operations/extractors/RegexExtractorTest.java b/src/test/java/de/usd/cstchef/operations/extractors/RegexExtractorTest.java new file mode 100644 index 0000000..8c0f7dd --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/extractors/RegexExtractorTest.java @@ -0,0 +1,61 @@ +package de.usd.cstchef.operations.extractors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertArrayEquals; + +import java.util.HashMap; + +import org.javatuples.Triplet; +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.OperationCategory; + + +@OperationInfos(name = "RegexExtractorTest", category = OperationCategory.EXTRACTORS, description = "Test class") +public class RegexExtractorTest extends RegexExtractor { + + // HashMap> + HashMap> inputs = new HashMap>(); + + @Test + public void extractionTest() throws Exception + { + for (String inp : inputs.keySet()) { + Triplet res = inputs.get(inp); + ByteArray inputArray = factory.createByteArray(inp); + ByteArray outputArray = factory.createByteArray(res.getValue0()); + this.regexTxt.setText(res.getValue1()); + this.outputBox.setSelectedItem(res.getValue2() ? "List matches" : "List capture groups"); + assertArrayEquals(outputArray.getBytes(), perform(inputArray, null).getBytes()); + } + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + // list matches + String input1 = "uuid=545687-147953-996348"; + String output1 = "545687-147953-996348"; + String regex1 = "[0-9\\-]{20}"; + Triplet triplet1 = new Triplet(output1, regex1, true); + + // list capture groups + String input2 = "key=545687-147953-996348"; + String output2 = "545687\n147953\n996348"; + String regex2 = "([0-9]{6})-([0-9]{6})-([0-9]{6})"; + Triplet triplet2 = new Triplet(output2, regex2, false); + + inputs.put(input1, triplet1); + inputs.put(input2, triplet2); + } +} \ No newline at end of file diff --git a/src/test/java/de/usd/cstchef/operations/utils/MessageParsingTest.java b/src/test/java/de/usd/cstchef/operations/utils/MessageParsingTest.java new file mode 100755 index 0000000..21e0eb6 --- /dev/null +++ b/src/test/java/de/usd/cstchef/operations/utils/MessageParsingTest.java @@ -0,0 +1,79 @@ +package de.usd.cstchef.operations.utils; + +import java.util.HashMap; + +import org.junit.Before; +import org.junit.Test; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import de.usd.cstchef.Utils.MessageType; +import de.usd.cstchef.operations.Operation; +import de.usd.cstchef.operations.OperationCategory; +import de.usd.cstchef.operations.Operation.OperationInfos; +import de.usd.cstchef.utils.UnitTestObjectFactory; + +@OperationInfos(name = "Test", category = OperationCategory.ARITHMETIC, description = "Test class") +public class MessageParsingTest extends Operation +{ + HashMap correctInputs = new HashMap(); + HashMap wrongInputs = new HashMap(); + + @Test + public void correctMessageTest() throws Exception + { + for(String s : correctInputs.keySet()){ + assert parseMessageType(factory.createByteArray(s)).equals(correctInputs.get(s)); + } + } + + public void wrongMessageTest() throws Exception + { + for(String s : wrongInputs.keySet()){ + assert !parseMessageType(factory.createByteArray(s)).equals(wrongInputs.get(s)); + } + } + + @Before + public void setup(){ + CstcObjectFactory factory = new UnitTestObjectFactory(); + this.factory = factory; + super.factory = factory; + + correctInputs.put("GET / HTTP/1.1", MessageType.REQUEST); + correctInputs.put("POST /asd HTTP/2", MessageType.REQUEST); + correctInputs.put("HEAD / HTTP/2", MessageType.REQUEST); + correctInputs.put("PUT /new.html HTTP/2", MessageType.REQUEST); + correctInputs.put("DELETE /old.html HTTP/2", MessageType.REQUEST); + correctInputs.put("CONNECT /www.example.com HTTP/2", MessageType.REQUEST); // CONNECT requests without leading slash? + correctInputs.put("OPTIONS /dir/index.html HTTP/2", MessageType.REQUEST); + correctInputs.put("TRACE /reflect HTTP/2", MessageType.REQUEST); + correctInputs.put("PATCH /file.txt HTTP/2", MessageType.REQUEST); + + correctInputs.put("HTTP/1.1 200 Ok", MessageType.RESPONSE); + correctInputs.put("HTTP/2 301 Moved Permanently", MessageType.RESPONSE); + + wrongInputs.put("ABC / HTTP/2", MessageType.REQUEST); + wrongInputs.put("GET abc HTTP/2", MessageType.REQUEST); + wrongInputs.put("POST / HTT/2", MessageType.REQUEST); + wrongInputs.put("OPTIONS / HTTP2", MessageType.REQUEST); + wrongInputs.put("GET / HTTP/", MessageType.REQUEST); + wrongInputs.put("GET / HTTP/1.", MessageType.REQUEST); + wrongInputs.put("HTTP/2 200 Ok", MessageType.REQUEST); + + wrongInputs.put("HTTP/2", MessageType.RESPONSE); + wrongInputs.put("HTT/2 301 Moved Permanently", MessageType.RESPONSE); + wrongInputs.put("HTTP2 301 Moved Permanently", MessageType.RESPONSE); + wrongInputs.put("HTTP/1. 301 Moved Permanently", MessageType.RESPONSE); + wrongInputs.put("HTTP/ 301 Moved Permanently", MessageType.RESPONSE); + wrongInputs.put("HTTP/2 30 Moved Permanently", MessageType.RESPONSE); + wrongInputs.put("GET / HTTP/2", MessageType.RESPONSE); + + } + + @Override + protected ByteArray perform(ByteArray input, MessageType messageType) throws Exception { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'perform'"); + } +} \ No newline at end of file diff --git a/src/test/java/de/usd/cstchef/utils/UnitTestObjectFactory.java b/src/test/java/de/usd/cstchef/utils/UnitTestObjectFactory.java new file mode 100644 index 0000000..22c2da1 --- /dev/null +++ b/src/test/java/de/usd/cstchef/utils/UnitTestObjectFactory.java @@ -0,0 +1,48 @@ +package de.usd.cstchef.utils; + +import burp.CstcObjectFactory; +import burp.api.montoya.core.ByteArray; +import burp.api.montoya.http.message.requests.HttpRequest; +import burp.api.montoya.http.message.responses.HttpResponse; +import burp.objects.CstcByteArray; +import burp.objects.CstcHttpRequest; +import burp.objects.CstcHttpResponse; + +public class UnitTestObjectFactory implements CstcObjectFactory{ + + @Override + public ByteArray createByteArray(String s) { + return CstcByteArray.byteArray(s); + } + + @Override + public ByteArray createByteArray(byte[] bytes) { + return CstcByteArray.byteArray(bytes); + } + + @Override + public ByteArray createByteArray(int i) { + return CstcByteArray.byteArray(i); + } + + @Override + public ByteArray getHttpRequestBody(ByteArray request) { + return new CstcHttpRequest(request).body(); + } + + @Override + public ByteArray getHttpResponseBody(ByteArray response) { + return new CstcHttpResponse(response).body(); + } + + @Override + public HttpRequest createHttpRequest(ByteArray request) { + return new CstcHttpRequest(request); + } + + @Override + public HttpResponse createHttpResponse(ByteArray response) { + return new CstcHttpResponse(response); + } + +}