From 07eae0b6c6d038370414ffd3e8b03f11856c8cbd Mon Sep 17 00:00:00 2001 From: Matteo Bitussi Date: Thu, 9 Nov 2023 10:57:35 +0100 Subject: [PATCH] Edit Operation message fix + rm dependency from helpers - Almost completely removed dependency from burp helpers - Edit Operation add in message url and head now append value if param not present --- doc/language.md | 2 +- tool/src/main/java/migt/BurpExtender.java | 2 - tool/src/main/java/migt/DecodeOperation.java | 40 +++++++----------- tool/src/main/java/migt/ExecutePassives.java | 9 +--- tool/src/main/java/migt/GUI.java | 8 ++-- tool/src/main/java/migt/HTTPReqRes.java | 41 +++++++++++++++++-- tool/src/main/java/migt/MessageOperation.java | 10 +---- tool/src/main/java/migt/Module.java | 6 --- tool/src/main/java/migt/Operation.java | 10 +---- tool/src/main/java/migt/Tools.java | 35 +++++----------- tool/src/main/java/migt/Var.java | 3 -- tool/src/test/java/EditOperation_test.java | 26 ++++++++++++ 12 files changed, 99 insertions(+), 93 deletions(-) diff --git a/doc/language.md b/doc/language.md index 2816164..cc2a0c1 100644 --- a/doc/language.md +++ b/doc/language.md @@ -150,7 +150,7 @@ By using an Edit Operation inside an Operation, you are able to edit the interce - `from` to select the section of the message you need - `edit` to edit the value of the given parameter. (only for url and head sections) use `value` to specify the new value. - `edit regex` to edit with a regex the section of the message you selected. use `value` to specify the new value -- `add` to add some content to the given section. in case of url and head, you need to specify the name of the parameter in this tag, and the value with `value`. For the body section, the content will be always appended to the end of the body, so you can leave this tag value empty and put the content to append in the `value` tag. +- `add` to add some content to the given section. in case of url and head, you need to specify the name of the parameter in this tag, and the value with `value`. If the parameter is not found, a new parameter is added, if the parameter is already present, the new value will be appended to the old one. For the body section, the content will be always appended to the end of the body, so you can leave this tag value empty and put the content to append in the `value` tag. - `remove` used to specify the name of a parameter to remove in url and head. Not available on body. - `value` used to specify the new value for the edit operations - `use` used in place of `value` to use the given variable value as new value. You should give a variable name to this tag. diff --git a/tool/src/main/java/migt/BurpExtender.java b/tool/src/main/java/migt/BurpExtender.java index ae2efbe..818d1c8 100644 --- a/tool/src/main/java/migt/BurpExtender.java +++ b/tool/src/main/java/migt/BurpExtender.java @@ -56,7 +56,6 @@ public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { errorStream = new PrintStream(stdErr); mainPane = new GUI(); - mainPane.helpers = callbacks.getHelpers(); mainPane.callbacks = callbacks; mainPane.messageViewer = callbacks.createMessageEditor(mainPane.controller, false); mainPane.splitPane.setRightComponent(mainPane.messageViewer.getComponent()); @@ -163,7 +162,6 @@ private void processMatchedMsg(MessageType msg_type, HTTPReqRes message) { messageInfo.setHighlight("red"); - mainPane.act_active_op.helpers = helpers; mainPane.act_active_op.setAPI(new Operation_API(message, msg_type.msg_to_process_is_request)); mainPane.act_active_op.execute(); diff --git a/tool/src/main/java/migt/DecodeOperation.java b/tool/src/main/java/migt/DecodeOperation.java index 74d6601..fde3ebf 100644 --- a/tool/src/main/java/migt/DecodeOperation.java +++ b/tool/src/main/java/migt/DecodeOperation.java @@ -1,6 +1,5 @@ package migt; -import burp.IExtensionHelpers; import com.jayway.jsonpath.JsonPath; import org.json.JSONArray; import org.json.JSONObject; @@ -106,7 +105,6 @@ public DecodeOperation(JSONObject decode_op_json) throws ParsingException { * Decodes a parameter from a message, given the message section and the list of encodings to be applied during * decoding * - * @param helpers IExtensionHelpers helpers object from Burp * @param ms The message section that contains the parameter to be decoded * @param encodings The list of encodings to be applied to decode the parameter * @param messageInfo The message to be decoded @@ -115,25 +113,25 @@ public DecodeOperation(JSONObject decode_op_json) throws ParsingException { * @return The decoded parameter as a string * @throws ParsingException If problems are encountered during decoding */ - public static String decodeParam(IExtensionHelpers helpers, - DecodeOperationFrom ms, + public static String decodeParam(DecodeOperationFrom ms, List encodings, HTTPReqRes messageInfo, Boolean isRequest, String decode_param) throws ParsingException { String decoded_param = ""; + // TODO add regex selection switch (ms) { case HEAD: decoded_param = decode( - encodings, messageInfo.getHeadParam(isRequest, decode_param), helpers); + encodings, messageInfo.getHeadParam(isRequest, decode_param)); break; case BODY: decoded_param = decode( - encodings, messageInfo.getBodyRegex(isRequest, decode_param), helpers); + encodings, messageInfo.getBodyRegex(isRequest, decode_param)); break; case URL: decoded_param = decode( - encodings, messageInfo.getUrlParam(decode_param), helpers); + encodings, messageInfo.getUrlParam(decode_param)); break; } @@ -152,13 +150,12 @@ public static String decodeParam(IExtensionHelpers helpers, * @return the decoded string * @throws ParsingException if the decoding fails */ - public static String decode(List encodings, String encoded, IExtensionHelpers helpers) throws ParsingException { - // TODO: remove dependency from helpers + public static String decode(List encodings, String encoded) throws ParsingException { String actual = encoded; byte[] actual_b = null; boolean isActualString = true; - if (encoded.length() == 0) { + if (encoded.isEmpty()) { return ""; } @@ -166,18 +163,17 @@ public static String decode(List encodings, String encoded, IExtension switch (e) { case BASE64: if (isActualString) { - actual_b = helpers.base64Decode(actual); + actual_b = Base64.getDecoder().decode(actual); isActualString = false; } else { - actual_b = helpers.base64Decode(actual_b); + actual_b = Base64.getDecoder().decode(actual_b); } break; case URL: - if (isActualString) { - actual = helpers.urlDecode(actual); + actual = java.net.URLDecoder.decode(actual, StandardCharsets.UTF_8); } else { - actual = helpers.urlDecode(new String(actual_b)); + actual = java.net.URLDecoder.decode(new String(actual_b), StandardCharsets.UTF_8); isActualString = true; } break; @@ -393,12 +389,10 @@ public void setAPI(DecodeOperation_API dop_api) { * Loads an Operation API * * @param api - * @param helpers * @throws ParsingException */ - public void loader(Operation_API api, IExtensionHelpers helpers) { + public void loader(Operation_API api) { // load api, extract needed things - this.helpers = helpers; this.imported_api = api; } @@ -407,10 +401,8 @@ public void loader(Operation_API api, IExtensionHelpers helpers) { * * @param api */ - public void loader(DecodeOperation_API api, IExtensionHelpers helpers) { + public void loader(DecodeOperation_API api) { this.imported_api = api; - this.helpers = helpers; - } /** @@ -426,7 +418,6 @@ public API exporter() throws ParsingException { if (imported_api instanceof Operation_API) { Tools.editMessageParam( - helpers, decode_target, from, ((Operation_API) imported_api).message, @@ -450,7 +441,6 @@ public API exporter() throws ParsingException { public void execute(List vars) throws ParsingException { if (imported_api instanceof Operation_API) { decoded_content = decodeParam( - helpers, from, encodings, ((Operation_API) imported_api).message, @@ -474,7 +464,7 @@ public void execute(List vars) throws ParsingException { result = false; return; } - decoded_content = decode(encodings, found, helpers); + decoded_content = decode(encodings, found); break; default: @@ -503,7 +493,7 @@ public void execute(List vars) throws ParsingException { // executes recursive decode operations if (decodeOperations.size() != 0) { - executeDecodeOps(this, helpers, vars); + executeDecodeOps(this, vars); } // execute checks diff --git a/tool/src/main/java/migt/ExecutePassives.java b/tool/src/main/java/migt/ExecutePassives.java index 427fecf..4eec746 100644 --- a/tool/src/main/java/migt/ExecutePassives.java +++ b/tool/src/main/java/migt/ExecutePassives.java @@ -1,7 +1,5 @@ package migt; -import burp.IExtensionHelpers; - import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -13,7 +11,6 @@ public class ExecutePassives implements Runnable { final Object lock = new Object(); public List passives; - IExtensionHelpers helpers; ExecutePassiveListener listener; List messageTypes; boolean finished; @@ -22,17 +19,14 @@ public class ExecutePassives implements Runnable { /** * Used to instantiate an ExecutePassives object * - * @param helpers IExtensionHelpers instance of Burp * @param passiveTests The list of passive tests to execute * @param listener the listener for this ExecutePassives Object, used to communicate with the thread * @param msg_types the list of message types needed by the tests */ - public ExecutePassives(IExtensionHelpers helpers, - List passiveTests, + public ExecutePassives(List passiveTests, ExecutePassiveListener listener, List msg_types) { this.passives = passiveTests; - this.helpers = helpers; this.listener = listener; this.messageTypes = msg_types; this.finished = false; @@ -104,7 +98,6 @@ public void run() { res = Tools.executePassiveTest( actual_test, executedSession.messages, - helpers, messageTypes); } catch (ParsingException e) { actual_test.applicable = false; diff --git a/tool/src/main/java/migt/GUI.java b/tool/src/main/java/migt/GUI.java index 832c1a4..c5051e6 100644 --- a/tool/src/main/java/migt/GUI.java +++ b/tool/src/main/java/migt/GUI.java @@ -1,6 +1,9 @@ package migt; -import burp.*; +import burp.IBurpExtenderCallbacks; +import burp.IHttpService; +import burp.IMessageEditor; +import burp.IMessageEditorController; import com.google.gson.Gson; import org.json.JSONArray; import org.json.JSONException; @@ -94,7 +97,6 @@ public class GUI extends JSplitPane { JTabbedPane bot_tabbed; Map bot_tabs_index; HTTPReqRes viewedMessage; - IExtensionHelpers helpers; IBurpExtenderCallbacks callbacks; List sessions_names; Map session_port; @@ -681,7 +683,7 @@ public ArrayList onTrackExecuteDone() { } }; - ExecutePassives expa = new ExecutePassives(helpers, + ExecutePassives expa = new ExecutePassives( passives, listener, messageTypes); diff --git a/tool/src/main/java/migt/HTTPReqRes.java b/tool/src/main/java/migt/HTTPReqRes.java index d591ba3..d4fe70f 100644 --- a/tool/src/main/java/migt/HTTPReqRes.java +++ b/tool/src/main/java/migt/HTTPReqRes.java @@ -489,7 +489,7 @@ public void removeUrlParam(String name) throws ParsingException { } /** - * Adds an url query parameter to the request url + * Adds an url query parameter to the request url. If parameter already present, concatenate new value to old. * * @param name the name of the new parameter * @param value the value of the new parameter @@ -509,7 +509,23 @@ public void addUrlParam(String name, String value) { throw new RuntimeException(e); } - params.add(new BasicNameValuePair(name, value)); + int c = 0; + boolean found = false; + for (NameValuePair p : params) { + if (p.getName().equals(name)) { + found = true; + break; + } + c += 1; + } + + if (found) { + String old_value = params.get(c).getValue(); + old_value += value; + params.set(c, new BasicNameValuePair(name, old_value)); + } else { + params.add(new BasicNameValuePair(name, value)); + } String new_query = URLEncodedUtils.format(params, "utf-8"); @@ -581,7 +597,26 @@ public void editHeadParam(Boolean isRequest, String param, String new_value) { * @param value the value of the new header */ public void addHeadParameter(boolean isRequest, String name, String value) { - (isRequest ? this.headers_req : this.headers_resp).add(name + ": " + value); + List headers = isRequest ? this.headers_req : this.headers_resp; + + int c = 0; + boolean found = false; + + for (String h : headers) { + if (h.startsWith(name + ":")) { + found = true; + break; + } + c += 1; + } + + if (found) { + String old_header = headers.get(c); + old_header += value; + headers_req.set(c, old_header); + } else { + headers.add(name + ": " + value); + } } /** diff --git a/tool/src/main/java/migt/MessageOperation.java b/tool/src/main/java/migt/MessageOperation.java index 5a83b1f..9d6d724 100644 --- a/tool/src/main/java/migt/MessageOperation.java +++ b/tool/src/main/java/migt/MessageOperation.java @@ -1,6 +1,5 @@ package migt; -import burp.IExtensionHelpers; import org.json.JSONObject; import java.io.File; @@ -133,8 +132,7 @@ public Operation_API exporter() { * @return the updated Operation with the result * @throws ParsingException if parsing of names is not successfull */ - public Operation execute(Operation op, - IExtensionHelpers helpers) throws ParsingException { + public Operation execute(Operation op) throws ParsingException { for (MessageOperation mop : op.getMessageOperations()) { Pattern pattern; Matcher matcher; @@ -243,7 +241,6 @@ public Operation execute(Operation op, case EDIT: op.processed_message = Tools.editMessageParam( - helpers, mop.what, mop.from, op.api.message, @@ -254,7 +251,6 @@ public Operation execute(Operation op, case EDIT_REGEX: op.processed_message = Tools.editMessage( - helpers, mop.what, mop, op.api.message, @@ -388,10 +384,6 @@ public Operation execute(Operation op, } else { op.api.message.setResponse(op.processed_message); } - if (op.processed_message_service != null) { - // TODO: check if ok to remove - //op.api.message.setHttpService(op.processed_message_service); - } } } catch (StackOverflowError e) { e.printStackTrace(); diff --git a/tool/src/main/java/migt/Module.java b/tool/src/main/java/migt/Module.java index e17bcc1..4cb1785 100644 --- a/tool/src/main/java/migt/Module.java +++ b/tool/src/main/java/migt/Module.java @@ -1,6 +1,5 @@ package migt; -import burp.IExtensionHelpers; import org.json.JSONObject; /** @@ -11,7 +10,6 @@ public class Module { // These variables should be present in each module boolean result = true; boolean applicable = false; - IExtensionHelpers helpers; API api; // the api of this module API imported_api; // the api imported from a previous module @@ -28,10 +26,6 @@ public Module(JSONObject json_module) { // Parse } - public Module(IExtensionHelpers helpers) { - this.helpers = helpers; - } - /** * This function should be called to check that after an initialization of a module all the necessary parameters * are set correctly. And the JSON has been parsed correctly with all the required tags present. diff --git a/tool/src/main/java/migt/Operation.java b/tool/src/main/java/migt/Operation.java index b143353..33c7221 100644 --- a/tool/src/main/java/migt/Operation.java +++ b/tool/src/main/java/migt/Operation.java @@ -1,6 +1,5 @@ package migt; -import burp.IHttpService; import burp.IInterceptedProxyMessage; import org.json.JSONArray; import org.json.JSONObject; @@ -27,7 +26,6 @@ public class Operation extends Module { public boolean isSessionOp = false; public List matchedMessages; public byte[] processed_message; - public IHttpService processed_message_service; // null if it is not changed public List log_messages; public List session_operations; // Decode operations @@ -164,7 +162,6 @@ private void init() { this.replace_request_name = ""; this.messageType = ""; this.session = ""; - this.processed_message_service = null; this.processed_message = null; } @@ -425,7 +422,6 @@ public void execute() { try { applicable = true; processed_message = getVariableByName(replace_request_name, api.vars).message; - processed_message_service = getVariableByName(replace_request_name, api.vars).service_info; //return op; } catch (ParsingException e) { e.printStackTrace(); @@ -438,7 +434,6 @@ public void execute() { try { applicable = true; processed_message = getVariableByName(replace_response_name, api.vars).message; - processed_message_service = getVariableByName(replace_response_name, api.vars).service_info; //return op; } catch (ParsingException e) { e.printStackTrace(); @@ -451,13 +446,13 @@ public void execute() { // execute the message operations and the decode ops try { applicable = true; - executeMessageOperations(this, helpers); + executeMessageOperations(this); if (!applicable | !result) return; executeEditOps(this, api.vars); if (!applicable | !result) return; - executeDecodeOps(this, helpers, api.vars); + executeDecodeOps(this, api.vars); if (!applicable | !result) return; executeChecks(this, api.vars); @@ -475,7 +470,6 @@ public void execute() { v.name = save_name; v.isMessage = true; v.message = api.is_request ? api.message.getRequest() : api.message.getResponse(); - v.service_info = api.message.getHttpService(helpers); api.vars.add(v); } } diff --git a/tool/src/main/java/migt/Tools.java b/tool/src/main/java/migt/Tools.java index 1c1444e..030fab2 100644 --- a/tool/src/main/java/migt/Tools.java +++ b/tool/src/main/java/migt/Tools.java @@ -1,6 +1,5 @@ package migt; -import burp.IExtensionHelpers; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.jayway.jsonpath.Configuration; @@ -26,13 +25,12 @@ public class Tools { * * @param test a Test element, it has to be a passive test * @param messageList a list of HTTPReqRes messages - * @param helpers an istance of IExtensionHelpers * @param msg_types the message types used by the test + * @param helpers * @return true if a test is passed, false otherwise */ public static boolean executePassiveTest(Test test, List messageList, - IExtensionHelpers helpers, List msg_types) throws ParsingException { int i, j; boolean res = true; @@ -50,8 +48,6 @@ public static boolean executePassiveTest(Test test, } if (messageList.get(i).matches_msg_type(msg_type)) { - currentOP.helpers = helpers; - currentOP.setAPI(new Operation_API(messageList.get(i), msg_type.msg_to_process_is_request)); currentOP.execute(); res = currentOP.getResult(); @@ -128,17 +124,15 @@ public static Operation executeChecks(Operation op, List vars) throws Parsi /** * Executes the decode operations in an operation. Uses APIs. Sets the result to the operation * - * @param op the operation to execute the decode operations from - * @param helpers the Burp helpers + * @param op the operation to execute the decode operations from * @return The operation (edited) * @throws ParsingException if something goes wrong */ public static Operation executeDecodeOps(Operation op, - IExtensionHelpers helpers, List vars) throws ParsingException { Operation_API api = op.getAPI(); for (DecodeOperation dop : op.getDecodeOperations()) { - dop.loader(api, helpers); + dop.loader(api); dop.execute(vars); if (!op.setResult(dop)) break; @@ -151,17 +145,15 @@ public static Operation executeDecodeOps(Operation op, /** * Executes the decode operations in a decode operation. This is the recursive step. * - * @param op the decode operation executing its child decode operations - * @param helpers the burp helpers + * @param op the decode operation executing its child decode operations * @return The operation (edited) * @throws ParsingException if something goes wrong */ public static DecodeOperation executeDecodeOps(DecodeOperation op, - IExtensionHelpers helpers, List vars) throws ParsingException { DecodeOperation_API api = op.getAPI(); for (DecodeOperation dop : op.decodeOperations) { - dop.loader(api, helpers); + dop.loader(api); dop.execute(vars); if (!op.setResult(dop)) break; @@ -212,10 +204,10 @@ public static Operation executeEditOps(Operation op, List vars) throws Pars return op; } - public static Operation executeMessageOperations(Operation op, IExtensionHelpers helpers) throws ParsingException { + public static Operation executeMessageOperations(Operation op) throws ParsingException { for (MessageOperation mop : op.messageOperations) { mop.loader(op.api); - mop.execute(op, helpers); + mop.execute(op); op.setAPI(mop.exporter()); if (op.setResult(op)) break; @@ -657,7 +649,6 @@ public static List debatchPassive(HashMap> batch) { /** * Edit a message treating it as a string using a regex * - * @param helpers an instance of Burp's IExtensionHelper * @param regex the regex used to match the things to change * @param mop the message operation containing information about the section to match the regex * @param messageInfo the message as IHttpRequestResponse object @@ -666,13 +657,11 @@ public static List debatchPassive(HashMap> batch) { * @return the edited message as byte array * @throws ParsingException if problems are encountered in editing the message */ - public static byte[] editMessage(IExtensionHelpers helpers, - String regex, + public static byte[] editMessage(String regex, MessageOperation mop, HTTPReqRes messageInfo, boolean isRequest, String new_value) throws ParsingException { - // TODO: remove dependency from Helpers Pattern pattern = null; Matcher matcher = null; switch (mop.from) { @@ -714,7 +703,6 @@ public static byte[] editMessage(IExtensionHelpers helpers, /** * Edit a message parameter * - * @param helpers an instance of Burp's IExtensionHelper * @param param_name the name of the parameter to edit * @param message_section the message section to edit * @param messageInfo the message as IHttpRequestResponse object @@ -725,8 +713,7 @@ public static byte[] editMessage(IExtensionHelpers helpers, * @return the edited message as byte array * @throws ParsingException if problems are encountered in editing the message */ - public static byte[] editMessageParam(IExtensionHelpers helpers, - String param_name, + public static byte[] editMessageParam(String param_name, HTTPReqRes.MessageSection message_section, HTTPReqRes messageInfo, boolean isRequest, @@ -770,8 +757,7 @@ public static byte[] editMessageParam(IExtensionHelpers helpers, return null; } - public static byte[] editMessageParam(IExtensionHelpers helpers, - String param_name, + public static byte[] editMessageParam(String param_name, DecodeOperation.DecodeOperationFrom decodeOperationFrom, HTTPReqRes messageInfo, boolean isRequest, @@ -797,7 +783,6 @@ public static byte[] editMessageParam(IExtensionHelpers helpers, } return editMessageParam( - helpers, param_name, ms, messageInfo, diff --git a/tool/src/main/java/migt/Var.java b/tool/src/main/java/migt/Var.java index e6e1575..b09ef77 100644 --- a/tool/src/main/java/migt/Var.java +++ b/tool/src/main/java/migt/Var.java @@ -1,7 +1,5 @@ package migt; -import burp.IHttpService; - /** * The class storing the variables used in the test and sessions */ @@ -10,7 +8,6 @@ public class Var { public String value; public byte[] message; public boolean isMessage; // tells if a variable contains a message - public IHttpService service_info; /** * Istantiate a Var object diff --git a/tool/src/test/java/EditOperation_test.java b/tool/src/test/java/EditOperation_test.java index 35db897..4be558a 100644 --- a/tool/src/test/java/EditOperation_test.java +++ b/tool/src/test/java/EditOperation_test.java @@ -103,6 +103,19 @@ public void test_add_url_param() throws ParsingException { assertEquals("12345", res.message.getUrlParam("codechallenge")); } + @Test + public void test_add_url_param_already_present() throws ParsingException { + String input = "{\"from\": \"url\", \"add\": \"authuser\"," + "\"value\": \"1\"}"; + EditOperation eop = new EditOperation(new JSONObject(input)); + Operation_API api = new Operation_API(message, true); + + eop.setAPI(api); + eop.execute(null); + assertTrue(eop.getResult()); + Operation_API res = (Operation_API) eop.exporter(); + assertEquals("01", res.message.getUrlParam("authuser")); + } + @Test public void test_add_head_param() throws ParsingException { String input = "{\"from\": \"head\", \"add\": \"Magicheader\"," + "\"value\": \"123123\"}"; @@ -116,6 +129,19 @@ public void test_add_head_param() throws ParsingException { assertEquals("123123", res.message.getHeadParam(true,"Magicheader")); } + @Test + public void test_add_head_param_already_present() throws ParsingException { + String input = "{\"from\": \"head\", \"add\": \"Accept\"," + "\"value\": \"1\"}"; + EditOperation eop = new EditOperation(new JSONObject(input)); + Operation_API api = new Operation_API(message, true); + + eop.setAPI(api); + eop.execute(null); + assertTrue(eop.getResult()); + Operation_API res = (Operation_API) eop.exporter(); + assertEquals("*/*1", res.message.getHeadParam(true,"Accept")); + } + @Test public void test_add_body() throws ParsingException { String input = "{\"from\": \"body\", \"add\": \"anything\"," + "\"value\": \"&appended\"}";