result = null;
- try {
- result = executePassiveOperation(currentOP, messageList.get(i), i, first_message_index, helpers, msg_types);
- } catch (ParsingException e) {
- e.printStackTrace();
- res = false;
- currentOP.applicable = false;
- break;
- }
- res = result.get(0);
- actisreq = result.get(1);
- actisresp = result.get(2);
- currentOP.applicable = result.get(3);
- j++;
- }
- if (!res) {
- test.operations.get(--j).matchedMessages.add(new Operation.MatchedMessage(messageList.get(i), i, actisreq, actisresp, true));
- break;
- }
- }
-
- for (Operation op : test.operations) {
- if (!op.applicable) {
- res = false;
- test.applicable = false;
- break;
- }
- }
-
- return res;
- }
-
- /**
- * Còass that executes a passive operation
- *
- * @param op The operation to be executed
- * @param message the message to be used in the execution
- * @param messageIndex the index of that message w.r.t. the list of messages ( if present ) otherwise write 0
- * @param first_message_index the index of the first OAuth message, to enable the filtering for oauth messages
- * @param helpers An istance of the IExtensionHelpers
- * @param msg_types the list of msg_types available
- * @return a list of booleans, containing in order: the result of the operation, if the actual message is a request,
- * if the actual message is a response, if the operation is applicable
- *
- * Note that this function is used also to validate active checks.
- */
- public static List executePassiveOperation(Operation op,
- HTTPReqRes message,
- int messageIndex,
- int first_message_index,
- IExtensionHelpers helpers,
- List msg_types) throws ParsingException {
- boolean res = true;
- boolean actisreq = false;
- boolean actisresp = false;
- switch (op.getMessageType()) {
- case "request":
- op.applicable = true;
- actisreq = true;
- res = processOperation(op, message, messageIndex, helpers, true, false);
- break;
- case "response":
- op.applicable = true;
- actisreq = true;
- res = processOperation(op, message, messageIndex, helpers, false, true);
- break;
- case "oauth request":
- if (messageIndex >= first_message_index) {
- op.applicable = true;
- actisreq = true;
- res = processOperation(op, message, messageIndex, helpers, true, false);
- }
- break;
- case "oauth response":
- if (messageIndex >= first_message_index) {
- op.applicable = true;
- actisreq = true;
- res = processOperation(op, message, messageIndex, helpers, false, true);
- }
- break;
- default:
- try {
- MessageType msg_type = MessageType.getFromList(msg_types, op.getMessageType());
-
- /* If the response message name is searched, the getByResponse will be true.
- * so messageIndex have to search for the request, and then evaluate the response*/
- Boolean matchedMessage = false;
-
- if (msg_type.getByResponse) {
- if (msg_type.isRegex) {
- matchedMessage = Tools.findInMessage(msg_type.messageSection,
- msg_type.regex,
- message,
- helpers,
- true);
- } else {
- matchedMessage = Tools.executeChecks(msg_type.checks,
- message,
- helpers,
- true);
- }
- if (matchedMessage) {
- op.applicable = true;
- actisreq = false;
- actisresp = true;
-
- res = processOperation(op, message, messageIndex, helpers, false, true);
- }
- } else if (msg_type.getByRequest) {
- if (msg_type.isRegex) {
- matchedMessage = Tools.findInMessage(msg_type.messageSection,
- msg_type.regex,
- message,
- helpers, false);
- } else {
- matchedMessage = Tools.executeChecks(msg_type.checks,
- message,
- helpers, false);
- }
- if (matchedMessage) {
- op.applicable = true;
- actisreq = false;
- actisresp = true;
-
- res = processOperation(op, message, messageIndex, helpers, true, false);
- }
- } else {
- if (msg_type.isRegex) {
- matchedMessage = Tools.findInMessage(msg_type.messageSection,
- msg_type.regex,
- message,
- helpers,
- msg_type.isRequest);
- } else {
- matchedMessage = Tools.executeChecks(msg_type.checks,
- message,
- helpers,
- msg_type.isRequest);
- }
- if (matchedMessage) {
- op.applicable = true;
- actisreq = msg_type.isRequest;
- actisresp = !msg_type.isRequest;
-
- res = processOperation(op, message, messageIndex, helpers, msg_type.isRequest, !msg_type.isRequest);
- }
- }
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- break;
- }
- List tmp = new ArrayList<>();
- tmp.add(res);
- tmp.add(actisreq);
- tmp.add(actisresp);
- tmp.add(op.applicable);
- return tmp;
- }
-
- /**
- * Function that processes an operation over a message
- *
- * @param currentOP the Operation
to be processed
- * @param act_message the message over which the operation has to be executed
- * @param message_index the index of the act_message
in the messages list
- * @param helpers An istance of the helpers
- * @param request set true if the request has to be processed
- * @param response set true if the response has to be processed
- * @return the result of the operation
- */
- public static boolean processOperation(Operation currentOP,
- HTTPReqRes act_message,
- int message_index,
- IExtensionHelpers helpers,
- boolean request,
- boolean response) throws ParsingException {
- String decode_param = currentOP.decode_param;
- String decoded_param = "";
-
- HTTPReqRes message = null;
- boolean res = true;
-
- try {
- message = (HTTPReqRes) act_message.clone();
- } catch (CloneNotSupportedException e) {
- e.printStackTrace();
- currentOP.applicable = false;
- return false;
- }
-
- if (!decode_param.equals("")) {
- try {
- if (!currentOP.decode_param.equals("")) {
-
- decoded_param = Encoding.decodeParam(
- helpers,
- currentOP.getMessageSection(),
- currentOP.encodings,
- act_message,
- request,
- decode_param);
-
- if (!currentOP.isRegex) {
- throw new ParsingException("Checks cannot be executed on decode parameters");
- }
-
- Pattern p = Pattern.compile(currentOP.getRegex());
- Matcher m = p.matcher(decoded_param);
-
- boolean match = m.find();
-
- if (request)
- currentOP.matchedMessages.add(
- new Operation.MatchedMessage(
- message,
- message_index,
- true,
- false,
- false));
- if (!request || response)
- currentOP.matchedMessages.add(
- new Operation.MatchedMessage(
- message,
- message_index,
- false,
- true,
- false));
-
- return match;
- }
- } catch (StackOverflowError e) {
- e.printStackTrace();
- currentOP.applicable = false;
- return false;
- }
- }
-
- if (currentOP.isRegex) {
- try {
- res = !request || Tools.findInMessage(
- currentOP.getMessageSection(), currentOP.getRegex(), message, helpers, true);
- if (!res) {
- if (request)
- currentOP.matchedMessages.add(
- new Operation.MatchedMessage(
- message, message_index, true, false, true));
- return false;
- }
- res = !response || Tools.findInMessage(
- currentOP.getMessageSection(), currentOP.getRegex(), message, helpers, false);
- if (!res) {
- if (response)
- currentOP.matchedMessages.add(
- new Operation.MatchedMessage(
- message, message_index, false, true, true));
- return false;
- }
-
- if (request)
- currentOP.matchedMessages.add(
- new Operation.MatchedMessage(
- message, message_index, true, false, false));
- if (response)
- currentOP.matchedMessages.add(
- new Operation.MatchedMessage(
- message, message_index, false, true, false));
- } catch (ParsingException e) {
- currentOP.applicable = false;
- System.err.println(e);
- }
-
- } else {
- try {
- res = !request || Tools.executeChecks(
- currentOP.getChecks(), message, helpers, true);
- if (!res) {
- if (request)
- currentOP.matchedMessages.add(
- new Operation.MatchedMessage(
- message, message_index, true, false, true));
- return false;
- }
- res = !response || Tools.executeChecks(
- currentOP.getChecks(), message, helpers, false);
- if (!res) {
- if (response)
- currentOP.matchedMessages.add(
- new Operation.MatchedMessage(
- message, message_index, false, true, true));
- return false;
- }
-
- if (request)
- currentOP.matchedMessages.add(
- new Operation.MatchedMessage(
- message, message_index, true, false, false));
- if (response)
- currentOP.matchedMessages.add(
- new Operation.MatchedMessage(
- message, message_index, false, true, false));
- } catch (ParsingException e) {
- currentOP.applicable = false;
- System.err.println(e);
- }
- }
-
- return res;
- }
-
- /**
- * Function that check a regex over a given message and section
- *
- * @param section the section of the message to be checked with the regex
- * @param regex the regex
- * @param message the message to be checked
- * @param helpers an istance of the helpers
- * @return the result of the check, if the regex matches 1 or more time it returns true
- */
- public static boolean findInMessage(Utils.MessageSection section,
- String regex,
- HTTPReqRes message,
- IExtensionHelpers helpers,
- boolean isRequest) throws ParsingException {
- int occ = 0;
-
- Pattern pattern;
- Matcher matcher;
-
- switch (section) {
- case URL:
- if (!isRequest) {
- throw new ParsingException("Searching URL in response");
- }
- String url = message.getRequest_url();
- pattern = Pattern.compile(regex);
- matcher = pattern.matcher(url);
-
- while (matcher.find()) {
- occ++;
- }
- break;
-
- case HEAD:
- pattern = Pattern.compile(regex);
- List header = isRequest ? helpers.analyzeRequest(message.getRequest()).getHeaders() :
- helpers.analyzeResponse(message.getResponse()).getHeaders();
-
- String headersString = getAllHeaders(header);
-
- matcher = pattern.matcher(headersString);
-
- while (matcher.find()) {
- occ++;
- }
- break;
-
- case BODY:
- pattern = Pattern.compile(regex, Pattern.DOTALL);
- int index = isRequest ?
- helpers.analyzeRequest(message.getRequest()).getBodyOffset()
- : helpers.analyzeResponse(message.getResponse()).getBodyOffset();
-
- byte[] body = isRequest ?
- Arrays.copyOfRange(message.getRequest(), index, message.getRequest().length)
- : Arrays.copyOfRange(message.getResponse(), index, message.getResponse().length);
-
- String rawBody = new String(body);
- matcher = pattern.matcher(rawBody);
-
- while (matcher.find()) {
- occ++;
- }
- break;
-
- case RAW:
- String raw_msg = isRequest ?
- new String(message.getRequest(), StandardCharsets.UTF_8) :
- new String(message.getResponse(), StandardCharsets.UTF_8);
-
- pattern = Pattern.compile(regex, Pattern.DOTALL);
- matcher = pattern.matcher(raw_msg);
-
- while (matcher.find()) {
- occ++;
- }
- break;
-
- default:
- System.out.println("No right message section selected");
- }
-
- return (occ > 0);
- }
-
- /**
- * Function that given a list of headers, concatenate them in a single string
- *
- * @param headers the list of headers
- * @return the string
- */
- public static String getAllHeaders(List headers) {
- StringBuilder out = new StringBuilder();
- for (Object o : headers) {
- out.append(o.toString());
- out.append("\n");
- }
- return out.toString();
- }
-
- /**
- * This method retrieves Authorization Grant (Response) - Start of OAuth flow
- *
- * @param messageList the list of HTTPReqRes
messages over search to
- * @param helpers an instance of the helpers
- * @return the index of the Authorization GRant message in the list of messages
- */
- public static int getInitMessageIndex(List messageList, IExtensionHelpers helpers) {
- int result = -1;
- for (int i = 0; i < messageList.size() && result < 0; i++)
- if (helpers.bytesToString(messageList.get(i).getResponse()).contains("response_type"))
- result = i;
-
- return result;
- }
-
- /**
- * This method checks if the give message is an Authorization Grant Response
- *
- * @param messageInfo the message to check
- * @param helpers an istance of the helpers
- * @return a boolean true or false
- */
- public static boolean isFirstMessage(HTTPReqRes messageInfo, IExtensionHelpers helpers) {
- return helpers.bytesToString(messageInfo.getResponse()).contains("response_type=code");
- }
-
- /**
- * This method checks if the messageInfo is the last message of the OAuth flow
- *
- * @param messageInfo the message
- * @param helpers the helpers
- * @return a boolean true or false
- */
- public static boolean isLastMessage(HTTPReqRes messageInfo, IExtensionHelpers helpers) {
- String rawBody = helpers.bytesToString(messageInfo.getResponse());
- boolean result = false;
- List requestParams = helpers.analyzeRequest(messageInfo.getRequest()).getParameters();
- for (int i = 0; i < requestParams.size(); i++) {
-
- if (requestParams.get(i).getName().equals("code")) {
- result = true;
- }
- }
- //return result && rawBody.contains("access_token");
- return result;
- }
-
- /**
- * This function execute a list of checks over a message, returning true if all the checks are successful
- *
- * @param checks a List of checks
- * @param message the message to be checked
- * @param helpers an istance of the helpers
- * @param isRequest set true if the request has to be checked, false for the response
- * @return returns the result of the checks (true if all the tests are successful)
- */
- public static boolean executeChecks(List checks,
- HTTPReqRes message,
- IExtensionHelpers helpers,
- boolean isRequest) throws ParsingException {
- for (Check c : checks) {
- String msg_str = "";
- IRequestInfo req_info = null;
- IResponseInfo res_info = null;
- if (isRequest) req_info = helpers.analyzeRequest(message.getRequest());
- if (!isRequest) res_info = helpers.analyzeResponse(message.getResponse());
- if (c.in == null) {
- throw new ParsingException("from tag in checks is null");
- }
- switch (c.in) {
- case URL:
- if (!isRequest) {
- throw new ParsingException("Searching URL in response");
- }
- msg_str = req_info.getHeaders().get(0);
- break;
- case BODY:
- if (isRequest) {
- int offset = req_info.getBodyOffset();
- byte[] body = Arrays.copyOfRange(message.getRequest(), offset, message.getRequest().length);
- msg_str = new String(body);
- } else {
- int offset = res_info.getBodyOffset();
- byte[] body = Arrays.copyOfRange(message.getResponse(), offset, message.getResponse().length);
- msg_str = new String(body);
- }
- break;
- case HEAD:
- if (isRequest) {
- int offset = req_info.getBodyOffset();
- byte[] head = Arrays.copyOfRange(message.getRequest(), 0, offset);
- msg_str = new String(head);
- } else {
- int offset = res_info.getBodyOffset();
- byte[] head = Arrays.copyOfRange(message.getResponse(), 0, offset);
- msg_str = new String(head);
- }
- break;
- default:
- System.err.println("no valid \"in\" specified in check");
- return false;
- }
-
- if (msg_str.length() == 0) {
- return false;
- }
-
- if (c.isParamCheck) {
- try {
- Pattern p = c.in == Utils.MessageSection.URL ?
- Pattern.compile("(?<=[?&]" + c.what + "=)[^\\n&]*") :
- Pattern.compile("(?<=" + c.what + ":\\s?)[^\\n]*");
- Matcher m = p.matcher(msg_str);
-
- String val = "";
- if (m.find()) {
- val = m.group();
- } else {
- return false;
- }
-
- if (c.op == null && val.length() != 0) {
- // if it passed all the splits without errors, the param is present, but no checks are specified
- // so result is true
- continue;
- }
-
- switch (c.op) {
- case IS:
- if (!c.op_val.equals(val)) {
- return false;
- }
- break;
- case IS_NOT:
- if (!!c.op_val.equals(val)) {
- return false;
- }
- break;
- case CONTAINS:
- if (!val.contains(c.op_val)) {
- return false;
- }
- break;
- case NOT_CONTAINS:
- if (!!val.contains(c.op_val)) {
- return false;
- }
- break;
- case IS_PRESENT:
- continue; // if it gets to this, the searched param is already found
- case IS_NOT_PRESENT:
- return false;
- }
- } catch (ArrayIndexOutOfBoundsException e) {
- //e.printStackTrace();
- if (c.op != null) {
- if (c.op != Utils.CheckOps.IS_NOT_PRESENT) {
- return false;
- }
- } else {
- return false;
- }
- }
- } else {
- if (!msg_str.contains(c.what)) {
- if (c.op != null) {
- if (c.op != Utils.CheckOps.IS_NOT_PRESENT) {
- return false;
- }
- } else {
- return false;
- }
- } else {
- if (c.op != null) {
- if (c.op == Utils.CheckOps.IS_NOT_PRESENT) return false;
- }
- }
- }
- }
- return true;
- }
-
- /**
- * Function that gets all the parameters of an url
- * (stackoverflow )
- *
- * @param url the url from which extract the parameters
- * @return all the parameters
- */
- public static Map getUrlParams(URL url) {
- String query = url.getQuery();
- String[] params = query.split("&");
- Map map = new HashMap();
-
- for (String param : params) {
- String name = param.split("=")[0];
- String value = param.split("=")[1];
- map.put(name, value);
- }
- return map;
- }
-}
diff --git a/tool/src/main/java/burp/Utils.java b/tool/src/main/java/burp/Utils.java
deleted file mode 100644
index 199ae99..0000000
--- a/tool/src/main/java/burp/Utils.java
+++ /dev/null
@@ -1,1773 +0,0 @@
-package burp;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.zip.DataFormatException;
-import java.util.zip.Deflater;
-import java.util.zip.Inflater;
-
-/**
- * class containing useful methods and enums to be used in other classes
- *
- * @author Matteo Bitussi
- */
-public class Utils {
- /**
- * Function that checks if a message's url is an authorization request
- *
- * @param url the url of the request message to be checked
- * @return true if the message is an authorization request
- */
- public static boolean isAuthRequest(String url) {
- boolean d = url.contains("response_type");
- return d;
- }
-
- /**
- * Function that parses checks from a JSON array
- *
- * @param checks_array the JSONarray that should contain checks
- * @return a List of Check elements
- * @throws ParsingException if the input is malformed
- */
- public static List parseChecksFromJSON(JSONArray checks_array) throws ParsingException {
- List res = new ArrayList<>();
- for (int k = 0; k < checks_array.length(); k++) {
- JSONObject act_check = checks_array.getJSONObject(k);
- Check check = new Check();
- Iterator keys = act_check.keys();
- while (keys.hasNext()) {
- String key = keys.next();
-
-
- switch (key) {
- case "in":
- if (key.equals("in")) {
- check.in = Utils.MessageSection.fromString(act_check.getString("in"));
- }
- case "check param":
- if (key.equals("check param")) {
- check.isParamCheck = true;
- check.setWhat(act_check.getString("check param"));
- break;
- }
- case "check":
- if (key.equals("check")) {
- check.setWhat(act_check.getString("check"));
- break;
- }
- case "is":
- if (key.equals("is")) {
- check.setOp(Utils.CheckOps.IS);
- check.op_val = act_check.getString("is");
- break;
- }
- case "is not":
- if (key.equals("is not")) {
- check.setOp(Utils.CheckOps.IS_NOT);
- check.op_val = act_check.getString("is not");
- break;
- }
- case "contains":
- if (key.equals("contains")) {
- check.setOp(Utils.CheckOps.CONTAINS);
- check.op_val = act_check.getString("contains");
- break;
- }
- case "not contains":
- if (key.equals("not contains")) {
- check.setOp(Utils.CheckOps.NOT_CONTAINS);
- check.op_val = act_check.getString("not contains");
- break;
- }
- case "is present":
- if (key.equals("is present")) {
- check.op = act_check.getBoolean("is present") ? Utils.CheckOps.IS_PRESENT :
- Utils.CheckOps.IS_NOT_PRESENT;
- check.op_val = act_check.getBoolean("is present") ?
- "is present" : "is not present";
- }
- }
- }
- if (check.in == null) {
- throw new ParsingException("In tag cannot be empty");
- }
- res.add(check);
- }
- return res;
- }
-
- /**
- * Given a list of message parts url, head, body (in this order), build a message
- *
- * @param parts the list of the parts to build the message, has to be url, head, body
- * @return the builded message
- */
- public static byte[] buildMessage(List parts, IExtensionHelpers helpers) {
- String tmp = parts.get(0) + parts.get(1) + parts.get(2);
-
- //return helpers.stringToBytes(tmp);
- return tmp.getBytes(StandardCharsets.UTF_8); // verify if is the same as burp
- }
-
- /**
- * Given a message, split it in 3 parts, url, head, body
- *
- * @param message the message to be splitted
- * @param helpers an istance of IExtensionHelpers
- * @param isRequest true if the message is a request
- * @return a List of Strings containing the three parts
- */
- public static List splitMessage(IHttpRequestResponse message, IExtensionHelpers helpers, boolean isRequest) {
- int body_offset = isRequest ?
- helpers.analyzeRequest(message.getRequest()).getBodyOffset() :
- helpers.analyzeResponse(message.getResponse()).getBodyOffset();
-
- String head = new String(isRequest ?
- Arrays.copyOfRange(message.getRequest(), 0, body_offset) :
- Arrays.copyOfRange(message.getResponse(), 0, body_offset));
- String body = new String(isRequest ?
- Arrays.copyOfRange(message.getRequest(), body_offset, message.getRequest().length) :
- Arrays.copyOfRange(message.getResponse(), body_offset, message.getResponse().length));
-
- String url = head.split("\n")[0] + "\n";
- //String url = isRequest ? helpers.analyzeRequest(message).getUrl().toString() : "";
- String[] head_splitted = head.split("\n");
- String[] head_ok = Arrays.copyOfRange(head_splitted, 1, head_splitted.length);
-
- head = "";
- for (String act : head_ok) {
- head += act + "\n";
- }
-
- List res = new ArrayList<>();
- res.add(url);
- res.add(head);
- res.add(body);
-
- return res;
- }
-
- /**
- * Given a message, split it in 3 parts, url, head, body
- *
- * @param message the message to be splitted
- * @param helpers an istance of IExtensionHelpers
- * @param isRequest true if the message is a request
- * @return a List of Strings containing the three parts
- */
- public static List splitMessage(HTTPReqRes message, IExtensionHelpers helpers, boolean isRequest) {
- int body_offset = isRequest ?
- helpers.analyzeRequest(message.getRequest()).getBodyOffset() :
- helpers.analyzeResponse(message.getResponse()).getBodyOffset();
-
- String head = new String(isRequest ?
- Arrays.copyOfRange(message.getRequest(), 0, body_offset) :
- Arrays.copyOfRange(message.getResponse(), 0, body_offset));
- String body = new String(isRequest ?
- Arrays.copyOfRange(message.getRequest(), body_offset, message.getRequest().length) :
- Arrays.copyOfRange(message.getResponse(), body_offset, message.getResponse().length));
-
- String url = head.split("\n")[0] + "\n";
- //String url = isRequest ? helpers.analyzeRequest(message).getUrl().toString() : "";
- String[] head_splitted = head.split("\n");
- String[] head_ok = Arrays.copyOfRange(head_splitted, 1, head_splitted.length);
-
- head = "";
- for (String act : head_ok) {
- head += act + "\n";
- }
-
- List res = new ArrayList<>();
- res.add(url);
- res.add(head);
- res.add(body);
-
- return res;
- }
-
- /**
- * Set an url to a message, note that
- * with url is intended the entire first row of the head (GET ...path... HTTP)
- *
- * @param url the url to be substituted to the message
- * @param message the message
- * @param helpers the helpers
- * @param isRequest true if the message is a request
- * @return the edited message
- */
- public static byte[] setUrl(String url, IHttpRequestResponse message, IExtensionHelpers helpers, boolean isRequest) {
- List mes_split = splitMessage(message, helpers, isRequest);
-
- if (!url.endsWith("\n")) url += "\n";
- mes_split.set(0, url);
-
- return buildMessage(mes_split, helpers);
- }
-
- /**
- * Function used to parse the message types from a string
- *
- * @param input a string containing the msg types in JSON
- * @return a List of messagetype objects
- * @throws ParsingException if the input is malformed
- */
- public static List readMsgTypeFromJson(String input) throws ParsingException {
- List msg_types = new ArrayList<>();
-
- JSONObject obj = new JSONObject(input);
- JSONArray message_types = obj.getJSONArray("message_types");
-
- for (int i = 0; i < message_types.length(); i++) {
- JSONObject act_msg_type = message_types.getJSONObject(i);
-
- String name = act_msg_type.getString("name");
- Boolean isRequest = act_msg_type.getBoolean("is request");
-
- MessageType msg_obj = new MessageType(name, isRequest);
-
- if (act_msg_type.has("response name")) {
- msg_obj.responseName = act_msg_type.getString("response name");
- }
- if (act_msg_type.has("request name")) {
- msg_obj.requestName = act_msg_type.getString("request name");
- }
-
- if (act_msg_type.has("checks")) {
- msg_obj.isRegex = false;
- msg_obj.checks = parseChecksFromJSON(act_msg_type.getJSONArray("checks"));
- } else if (act_msg_type.has("regex")) {
- msg_obj.isRegex = true;
- msg_obj.regex = act_msg_type.getString("regex");
- msg_obj.messageSection = Utils.MessageSection.fromString(act_msg_type.getString("message section"));
- } else {
- throw new ParsingException("message type definition is invalid, no checks or regex found");
- }
- msg_types.add(msg_obj);
- }
-
- return msg_types;
- }
-
- /**
- * Returns the default string that contains the default message types that fill a msg_def.json file
- *
- * @return the string
- */
- public static String getDefaultJSONMsgType() {
- return "{\n" +
- " \"message_types\": [\n" +
- " {\n" +
- " \"name\": \"authorization request\",\n" +
- " \"is request\": true,\n" +
- " \"response name\": \"authorization response\",\n" +
- " \"checks\": [\n" +
- " {\n" +
- " \"in\": \"url\",\n" +
- " \"check param\": \"response_type\",\n" +
- " \"is present\": \"true\"\n" +
- " }\n" +
- " ]\n" +
- " },\n" +
- " {\n" +
- " \"name\": \"token request\",\n" +
- " \"is request\": true,\n" +
- " \"response name\": \"token response\",\n" +
- " \"checks\": [\n" +
- " {\n" +
- " \"in\": \"url\",\n" +
- " \"check param\": \"code\",\n" +
- " \"is present\": \"true\"\n" +
- " }\n" +
- " ]\n" +
- " },\n" +
- " {\n" +
- " \"name\": \"coda landing request\",\n" +
- " \"is request\": true,\n" +
- " \"response name\": \"coda landing response\",\n" +
- " \"checks\": [\n" +
- " {\n" +
- " \"in\": \"url\",\n" +
- " \"check\": \"/welcome\",\n" +
- " \"is present\": \"true\"\n" +
- " },\n" +
- " {\n" +
- " \"in\": \"head\",\n" +
- " \"check\": \"Host\",\n" +
- " \"is\": \"coda.io\"\n" +
- " }\n" +
- " ]\n" +
- " },\n" +
- " {\n" +
- " \"name\": \"saml request\",\n" +
- " \"is request\": true,\n" +
- " \"checks\": [\n" +
- " {\n" +
- " \"in\": \"url\",\n" +
- " \"check param\": \"SAMLRequest\",\n" +
- " \"is present\": true\n" +
- " }\n" +
- " ]\n" +
- " },\n" +
- " {\n" +
- " \"name\": \"saml response\",\n" +
- " \"is request\": true,\n" +
- " \"checks\": [\n" +
- " {\n" +
- " \"in\": \"body\",\n" +
- " \"check param\": \"SAMLResponse\",\n" +
- " \"is present\": true\n" +
- " }\n" +
- " ]\n" +
- " }\n" +
- " ]\n" +
- "}";
- }
-
- /**
- * Returns the default string that contains the default config for the config.json file
- *
- * @return the string
- */
- public static String getDefaultJSONConfig() {
- return "{\n" +
- " \"last_driver_path\":\"\",\n" +
- " \"last_browser_used\": \"\"\n" +
- "}";
- }
-
- /**
- * Set the body to a message
- *
- * @return the edited message
- */
- public static String removeNewline(String input) {
- Pattern p = Pattern.compile("\n");
- Matcher m = p.matcher(input);
-
- String out = m.replaceAll("");
- return out;
- }
-
- /**
- * Get the headers of an HTTP message
- * @param message the message
- * @param isRequest true if it is a request
- * @param helpers The Burp IExtensionHelpers
- * @return a list of Strings, each one is a header
- */
- public static List getHeaders(IHttpRequestResponse message, boolean isRequest, IExtensionHelpers helpers) {
- if (isRequest) {
- return helpers.analyzeRequest(message.getRequest()).getHeaders();
- } else {
- return helpers.analyzeResponse(message.getResponse()).getHeaders();
- }
- }
-
- /**
- * Get the body of a message
- * @param message the message
- * @param isRequest true if the message is a request
- * @param helpers The Burp IExtensionHelpers
- * @return the body of the message as byte array
- */
- public static byte[] getBody(IHttpRequestResponse message, boolean isRequest, IExtensionHelpers helpers) {
- int body_offset = isRequest ?
- helpers.analyzeRequest(message.getRequest()).getBodyOffset() :
- helpers.analyzeResponse(message.getResponse()).getBodyOffset();
-
- byte[] body = isRequest ?
- Arrays.copyOfRange(message.getRequest(), body_offset, message.getRequest().length) :
- Arrays.copyOfRange(message.getResponse(), body_offset, message.getResponse().length);
-
- return body;
- }
-
- /**
- * Removes a head parameter from a list of headers
- * @param headers the list of headers
- * @param param_name the name of the header to remove
- * @return The list without the removed header
- */
- public static List removeHeadParameter(List headers, String param_name) {
- for (String s : headers) {
- if (s.contains(param_name)) {
- headers.remove(s);
- break;
- }
- }
- return headers;
- }
-
- /**
- * Add a header to a list of headers
- * @param headers the header list
- * @param param_name the name of the header to add
- * @param value the value of the header to add
- * @return the edited header list
- */
- public static List addHeadParameter(List headers, String param_name, String value) {
- if (value.equals("")) return headers;
-
- for (String s : headers) {
- if (s.contains(param_name)) {
- headers.set(headers.indexOf(s), param_name + ": " + value);
- return headers;
- }
- }
- headers.add(param_name + ": " + value);
- return headers;
- }
-
- /**
- * Edit a header ina a list of headers
- * @param headers the header list
- * @param param_name the name of the header to edit
- * @param value the value of the header to add
- * @return the edited header list
- */
- public static List editHeadParameter(List headers, String param_name, String value) {
- if (value.equals("")) return headers;
-
- for (String s : headers) {
- if (s.contains(param_name)) {
- headers.set(headers.indexOf(s), param_name + ": " + value);
- return headers;
- }
- }
- return headers;
- }
-
- /**
- * Get the value of a header from a header list
- * @param headers the list of headers
- * @param param_name the name of the header to get the value from
- * @return the value of the header
- */
- public static String getHeadParameterValue(List headers, String param_name) {
- for (String s : headers) {
- if (s.contains(param_name)) {
- String[] splitted = s.split(":");
-
- String value = s.substring(s.indexOf(":") + 1);
- return value;
- }
- }
- return "";
- }
-
- /**
- * Builds a string, substituting variables names with values
- * @param vars the list of variables to use
- * @param s the string
- * @return the builded string
- * @throws ParsingException if a variable is not found
- */
- public static String buildStringWithVars(List vars, String s) throws ParsingException {
- Pattern p = Pattern.compile("\\$[^\\$]*\\$");
- Matcher m = p.matcher(s);
-
- String res = s;
-
- HashMap req_var = new HashMap<>();
-
- while (m.find()) {
- String act_match = m.group();
- act_match = act_match.replaceAll("\\$", "");
- req_var.put(act_match, getVariableByName(act_match, vars).value);
- }
-
- if (req_var.size() == 0) {
- return s;
- }
-
- for (String key : req_var.keySet()) {
- res = res.replaceAll("\\$" + key + "\\$", Matcher.quoteReplacement(req_var.get(key)));
- }
- return res;
- }
-
- /**
- * Given a name, returns the corresponding variable
- *
- * @param name the name of the variable
- * @return the Var object
- * @throws ParsingException if the variable cannot be found
- */
- public static Var getVariableByName(String name, List vars) throws ParsingException {
- for (Var act : vars) {
- if (act.name.equals(name)) {
- return act;
- }
- }
- throw new ParsingException("variable \"" + name + "\" not defined");
- }
-
- /**
- * Used to process session operations of a given operation
- *
- * @param op the operation containing the session operation
- * @return An array of Object elements, the first is the edited operation, the second is the updated variables
- */
- public static Object[] executeSessionOps(Test t,
- Operation op,
- List vars) throws ParsingException {
- Object[] res = new Object[2];
- List updated_vars = vars;
- for (SessionOperation sop : op.session_operations) {
-/*
- List vars_new = eal.onBeforeExSessionOps();
-
- for (Var v : vars_new) {
- if (!updated_vars.contains(v)) {
- updated_vars.inse
- }
- }
-
-
- */
- Session session = t.getSession(sop.from_session);
- Track track = session.track;
-
- switch (sop.action) {
- case SAVE:
- Var v = new Var();
- v.name = sop.as;
- v.isMessage = false;
- v.value = "";
- switch (sop.target) {
- case TRACK:
- for (SessionTrackAction sa : t.getSession(sop.from_session).track
- .getStasFromMarkers(sop.at, sop.to, sop.is_from_included, sop.is_to_included)) {
- v.value += sa.toString() + "\n";
- }
- break;
- case LAST_ACTION:
- v.value = session.last_action.toString();
- break;
- case LAST_ACTION_ELEM:
- v.value = session.last_action.elem;
- break;
- case LAST_ACTION_ELEM_PARENT:
- v.value = findParentDiv(session.last_action.elem);
- break;
- case LAST_CLICK:
- v.value = session.last_click.toString();
- break;
- case LAST_CLICK_ELEM:
- v.value = session.last_click.elem;
- break;
- case LAST_CLICK_ELEM_PARENT:
- v.value = findParentDiv(session.last_click.elem);
- break;
- case LAST_OPEN:
- v.value = session.last_open.toString();
- break;
- case LAST_OPEN_ELEM:
- v.value = session.last_open.elem;
- break;
- case LAST_URL:
- v.value = session.last_url;
- break;
- case ALL_ASSERT:
- for (SessionTrackAction sa : t.getSession(sop.from_session).track.getTrack()) {
- if (sa.isAssert) {
- v.value += sa + "\n";
- }
- }
- break;
- }
- updated_vars.add(v);
- break;
-
- case INSERT:
- String to_be_added = buildStringWithVars(updated_vars, sop.what);
- track.insert(new Marker(sop.at), to_be_added);
- break;
-
- case MARKER:
- switch (sop.target) {
- case LAST_ACTION:
- case LAST_ACTION_ELEM:
- track.mark(session.last_action, sop.mark_name);
- break;
- case LAST_CLICK:
- case LAST_CLICK_ELEM:
- track.mark(session.last_click, sop.mark_name);
- break;
- case LAST_OPEN:
- case LAST_OPEN_ELEM:
- track.mark(session.last_open, sop.mark_name);
- break;
- case ALL_ASSERT:
- for (SessionTrackAction sa : t.getSession(sop.from_session).track.getTrack()) {
- if (sa.isAssert) {
- track.mark(sa, sop.mark_name);
- }
- }
- break;
- case TRACK:
- case LAST_URL:
- throw new ParsingException("Invalid session operation target: " + sop.target);
- default:
- throw new ParsingException("Invalid session operation target");
- }
- break;
- case REMOVE:
- if (sop.to != null && !sop.to.equals("")) {
- // TODO: remove interval of indices instead of using the remove construct of lists, because it
- // removes duplicated things
-
- int[] range = t.getSession(sop.from_session).track.
- getStasIndexFromRange(sop.at, sop.to, sop.is_from_included, sop.is_to_included);
-
-
- t.getSession(sop.from_session).track.getTrack().subList(range[0], range[1]+1).clear();
- } else {
- track.remove(new Marker(sop.at));
- }
- break;
- }
- }
- res[0] = op;
- res[1] = updated_vars;
- return res;
- }
-
- /**
- * Finds the parent div of an http element
- * @param in the http element in xpath format
- * @return the xpath of the parent div
- * @throws ParsingException if no parent div present or input is malformed
- */
- public static String findParentDiv(String in) throws ParsingException {
- String[] split1 = in.split("=");
- if (split1.length != 2) {
- throw new ParsingException("invalid input \"" + in + "\" for finding parent div");
- }
- String[] split = split1[1].split("/");
- if (split.length == 0) {
- return in;
- }
-
- int cut_indx = -1;
-
- //-2 because if the last element is a div i don't take it, otherwise is not a div, so i don't take it
- for (int i = split.length - 2; i > 0; i--) {
- if (split[i].contains("div")) {
- cut_indx = i;
- break;
- }
- }
- if (cut_indx == -1) return in;
-
- String res = split1[0] + "=";
-
- for (int i = 1; i <= cut_indx; i++) {
- if (i == cut_indx) {
- // removes the index of the div element
- res += "/" + split[i].replaceAll("\\[.*\\]", "");
- } else {
- res += "/" + split[i];
- }
- }
- return res;
- }
-
- /**
- * An enum representing the possible message sections
- */
- public enum MessageSection {
- HEAD,
- BODY,
- URL,
- RAW;
-
- /**
- * Function that given a message section in form of String, returns the corresponding MessageSection enum value
- *
- * @param input the input string
- * @return the MessageSection enum value
- * @throws ParsingException if the input does not correspond to any of the possible messagesections
- */
- public static MessageSection fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "head":
- return HEAD;
- case "body":
- return BODY;
- case "url":
- return URL;
- case "raw":
- return RAW;
- default:
- throw new ParsingException("message section not valid");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * enum containing all the possible check operations
- */
- public enum CheckOps {
- IS,
- IS_NOT,
- CONTAINS,
- NOT_CONTAINS,
- IS_PRESENT,
- IS_NOT_PRESENT;
-
- /**
- * Function that given a String, returns the corresponding CheckOps enum's value
- *
- * @param input the input string
- * @return the CheckOps enum value
- * @throws ParsingException if the input string does not correspond to any of the possible check operations
- */
- public static CheckOps fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "is":
- return IS;
- case "is not":
- return IS_NOT;
- case "contains":
- return CONTAINS;
- case "not contains":
- return NOT_CONTAINS;
- default:
- throw new ParsingException("invalid check operation");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * Enum containing all the possible Active operation actions
- */
- public enum Action {
- INTERCEPT,
- VALIDATE;
-
- /**
- * From a string get the corresponding enum value
- *
- * @param input the string
- * @return the enum value
- * @throws ParsingException if the input is malformed
- */
- public static Action fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "intercept":
- return INTERCEPT;
- case "validate":
- return VALIDATE;
- default:
- throw new ParsingException("invalid check operation");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * Enum containing all the possible session operation actions
- */
- public enum SessionAction {
- START,
- PAUSE,
- RESUME,
- STOP,
- CLEAR_COOKIES;
-
- /**
- * From a string get the corresponding enum value
- *
- * @param input the string
- * @return the enum value
- * @throws ParsingException if the input is malformed
- */
- public static SessionAction fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "start":
- return START;
- case "pause":
- return PAUSE;
- case "resume":
- return RESUME;
- case "stop":
- return STOP;
- case "clear cookies":
- return CLEAR_COOKIES;
- default:
- throw new ParsingException("invalid Session action");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * Enum that contains all the possible action to do after a message is received
- */
- public enum Then {
- FORWARD,
- DROP;
-
- /**
- * From a string get the corresponding enum value
- *
- * @param input the string
- * @return the enum value
- * @throws ParsingException if the input is malformed
- */
- public static Then fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "forward":
- return FORWARD;
- case "drop":
- return DROP;
- default:
- throw new ParsingException("invalid check operation");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * All the possible actions of a MessageOperation
- */
- public enum MessageOperationActions {
- REMOVE_PARAMETER,
- REMOVE_MATCH_WORD,
- EDIT,
- EDIT_REGEX,
- ADD,
- SAVE,
- SAVE_MATCH;
-
- /**
- * From a string get the corresponding enum value
- *
- * @param input the string
- * @return the enum value
- * @throws ParsingException if the input is malformed
- */
- public static MessageOperationActions fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "remove parameter":
- return REMOVE_PARAMETER;
- case "remove match word":
- return REMOVE_MATCH_WORD;
- case "edit":
- return EDIT;
- case "edit regex":
- return EDIT_REGEX;
- case "add":
- return ADD;
- case "save":
- return SAVE;
- case "save match":
- return SAVE_MATCH;
- default:
- throw new ParsingException("invalid check operation");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * The result type of (also the oracle) of an Active test
- */
- public enum ResultType {
- CORRECT_FLOW,
- INCORRECT_FLOW,
- ASSERT_ONLY;
-
- /**
- * From a string get the corresponding enum value
- *
- * @param input the string
- * @return the enum value
- * @throws ParsingException if the input is malformed
- */
- public static ResultType fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "correct":
- return CORRECT_FLOW;
- case "incorrect":
- return INCORRECT_FLOW;
- case "assert_only":
- return ASSERT_ONLY;
- default:
- throw new ParsingException("invalid result");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * The possible encodings to be used
- */
- public enum Encoding {
- BASE64,
- URL,
- JWT,
- DEFLATE;
-
- /**
- * From a string get the corresponding enum value
- *
- * @param input the string
- * @return the enum value
- * @throws ParsingException if the input is malformed
- */
- public static Encoding fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "base64":
- return BASE64;
- case "url":
- return URL;
- case "jwt":
- return JWT;
- case "deflate":
- return DEFLATE;
- default:
- throw new ParsingException("invalid encoding");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * The possible types of messageOps
- */
- public enum MessageOpType {
- XML,
- JWT,
- HTTP,
- TXT,
- GENERATE_POC;
-
- /**
- * From a string get the corresponding enum value
- *
- * @param input the string
- * @return the enum value
- * @throws ParsingException if the input is malformed
- */
- public static MessageOpType fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "xml":
- return XML;
- case "jwt":
- return JWT;
- case "http":
- return HTTP;
- case "txt":
- return TXT;
- case "generate_poc":
- return GENERATE_POC;
- default:
- throw new ParsingException("invalid message Op Type");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * The possible XML actions are the ones described in this enum
- */
- public enum XmlAction {
- ADD_TAG,
- ADD_ATTR,
- EDIT_TAG,
- EDIT_ATTR,
- REMOVE_TAG,
- REMOVE_ATTR,
- SAVE_TAG,
- SAVE_ATTR;
-
- /**
- * From a string get the corresponding value
- *
- * @param input the input string
- * @return the enum value
- * @throws ParsingException if the string does not correspond to any of the values
- */
- public static XmlAction fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "add tag":
- return ADD_TAG;
- case "add attribute":
- return ADD_ATTR;
- case "edit tag":
- return EDIT_TAG;
- case "edit attribute":
- return EDIT_ATTR;
- case "remove tag":
- return REMOVE_TAG;
- case "remove attribute":
- return REMOVE_ATTR;
- case "save tag":
- return SAVE_TAG;
- case "save attribute":
- return SAVE_ATTR;
- default:
- throw new ParsingException("invalid xml action");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * Defines the possible actions to be done on a decoded parameter interpreted as plain text
- */
- public enum TxtAction {
- REMOVE,
- EDIT,
- ADD,
- SAVE;
-
- /**
- * From a string get the corresponding value
- *
- * @param input the input string
- * @return the enum value
- * @throws ParsingException if the string does not correspond to any of the values
- */
- public static TxtAction fromString(String input) throws ParsingException {
- if (input != null) {
- switch (input) {
- case "txt remove":
- return REMOVE;
- case "txt edit":
- return EDIT;
- case "txt add":
- return ADD;
- case "txt save":
- return SAVE;
- default:
- throw new ParsingException("invalid xml action");
- }
- } else {
- throw new NullPointerException();
- }
- }
- }
-
- /**
- * Defines the possible JWT token sections
- */
- public enum Jwt_section {
- HEADER,
- PAYLOAD,
- SIGNATURE,
- RAW_HEADER,
- RAW_PAYLOAD,
- RAW_SIGNATURE;
-
- /**
- * Get the JWT section enum value from a string
- * @param s the string to parse
- * @return the enum value
- * @throws ParsingException if the string is invalid
- */
- public static Jwt_section getFromString(String s) throws ParsingException {
- switch (s) {
- case "header":
- return HEADER;
- case "payload":
- return PAYLOAD;
- case "signature":
- return SIGNATURE;
- case "raw_header":
- return RAW_HEADER;
- case "raw_payload":
- return RAW_PAYLOAD;
- case "raw_signature":
- return RAW_SIGNATURE;
- default:
- throw new ParsingException("Invalid jwt section");
- }
- }
- }
-
- /**
- * Defines the possible actions to be done on a JWT token
- */
- public enum Jwt_action {
- REMOVE,
- EDIT,
- ADD,
- SAVE
- }
-
- /**
- * Defines the action of a session action
- */
- public enum SessAction {
- CLICK,
- OPEN,
- TYPE,
- SNAPSHOT,
- DIFF,
- EQUALS,
- WAIT,
- SET_VAR,
- CLEAR_COOKIES,
- ASSERT_CLICKABLE,
- ASSERT_NOT_CLICKABLE,
- ASSERT_VISIBLE,
- ASSERT_NOT_VISIBLE,
- ASSERT_ELEM_CONTENT_IS,
- ASSERT_ELEM_CONTENT_HAS,
- ASSERT_ELEM_CLASS_IS,
- ASSERT_ELEM_CLASS_HAS,
- ASSERT_ELEM_HAS_ATTRIBUTE,
- ASSERT_ELEM_NOT_HAS_ATTRIBUTE,
- ALERT;
-
- /**
- * Get a session action enum value from a string
- * @param s the string
- * @return the enum value
- * @throws ParsingException if the string is invalid
- */
- public static SessAction getFromString(String s) throws ParsingException {
- switch (s) {
- case "assert click":
- case "click":
- return CLICK;
- case "open":
- case "assert open": // just an alias of open
- return OPEN;
- case "type":
- return TYPE;
- case "snapshot":
- return SNAPSHOT;
- case "diff":
- return DIFF;
- case "equals":
- return EQUALS;
- case "wait":
- return WAIT;
- case "set var":
- return SET_VAR;
- case "clear cookies":
- return CLEAR_COOKIES;
- case "assert clickable":
- return ASSERT_CLICKABLE;
- case "assert not clickable":
- return ASSERT_NOT_CLICKABLE;
- case "assert visible":
- return ASSERT_VISIBLE;
- case "assert not visible":
- return ASSERT_NOT_VISIBLE;
- case "assert element content is":
- return ASSERT_ELEM_CONTENT_IS;
- case "assert element content has":
- return ASSERT_ELEM_CONTENT_HAS;
- case "assert element class is":
- return ASSERT_ELEM_CLASS_IS;
- case "assert element class has":
- return ASSERT_ELEM_CLASS_HAS;
- case "assert element has attribute":
- return ASSERT_ELEM_HAS_ATTRIBUTE;
- case "assert element not has attribute":
- return ASSERT_ELEM_NOT_HAS_ATTRIBUTE;
- case "alert":
- return ALERT;
- default:
- throw new ParsingException("Invalid session action \"" + s + "\"");
- }
- }
- }
-
- /**
- * Defines the action of a session operation
- */
- public enum SessOperationAction {
- SAVE,
- INSERT,
- MARKER,
- REMOVE
- }
-
- /**
- * Defines the target of a session operation.
- * Is it better to use js or just build a form? if a form is used, body has to be interpreted
- */
- public enum SessOperationTarget {
- LAST_ACTION,
- LAST_ACTION_ELEM,
- LAST_ACTION_ELEM_PARENT,
- LAST_CLICK,
- LAST_CLICK_ELEM,
- LAST_CLICK_ELEM_PARENT,
- LAST_OPEN,
- LAST_OPEN_ELEM,
- LAST_URL,
- ALL_ASSERT,
- TRACK;
-
- /**
- * Parse a string containing a session operation target
- * @param s the string to parse
- * @throws ParsingException if the string is malformed, or no session operation target is found
- */
- public static SessOperationTarget getFromString(String s) throws ParsingException {
-
- if (s.contains(".")) {
- String[] splitted;
- splitted = s.split("\\.");
- String left = splitted[0];
- boolean parent = false;
- if (splitted.length == 3) {
- if (splitted[2].equals("parent")) {
- parent = true;
- }
- }
-
- switch (s) {
- case "last_action.elem":
- case "last_action.elem.parent":
- return parent ? LAST_ACTION_ELEM_PARENT : LAST_ACTION_ELEM;
- case "last_click.elem":
- case "last_click.elem.parent":
- return parent ? LAST_CLICK_ELEM_PARENT : LAST_CLICK_ELEM;
- case "last_open.elem":
- return LAST_OPEN_ELEM;
- case "last_url":
- return LAST_URL;
- case "all_assert":
- return ALL_ASSERT;
- default:
- throw new ParsingException("invalid target in session operation");
- }
- } else {
- switch (s) {
- case "track":
- return TRACK;
- case "last_action":
- return LAST_ACTION;
- case "last_click":
- return LAST_CLICK;
- case "last_open":
- return LAST_OPEN;
- case "last_url":
- return LAST_URL;
- case "all_assert":
- return ALL_ASSERT;
- default:
- throw new ParsingException("invalid target in session operation");
- }
- }
- }
- }
-
- /**
- * Generates a CSRF POC from an HTTP request message
- * @param message the message to generate the POC from
- * @param helpers Burp's IExtensionHelper instance
- * @return the html poc as a string
- */
- public static String generate_CSRF_POC(IHttpRequestResponse message,
- IExtensionHelpers helpers){
-
- String CSFR_TEMPLATE= "\n" +
- "\n" +
- " \n" +
- " Attack Page \n" +
- " Service Provider (SP) is your service.
\n" +
- " Identity Provider (IdP) is the provider with which the SP allows to associate the account.
\n" +
- " These are the steps to reproduce the attack:
\n" +
- " 1. The victim clicks on button to initiate force-login to IdP and victim logs in as the attacker because IdP suffering of Pre-Authentication Login CSRF. To simulate this step, the victim logs in with the attacker's IdP credentials.
\n" +
- " 2. The victim logins at SP with victim credentials.
\n" +
- " 3. The victim clicks on following link which suffers of CSRF, to associate attacker IdP account with the Victim SP account.
\n" +
- " $INSERT_HERE$\n" +
- " \n" +
- " 4. If the IdP attacker account has been associated with the victim SP account then the vulnerability has been properly exploited.
\n" +
- " \n" +
- "";
-
- String POST_TEMPLATE =
- " \n";
-
- String TEMPLATE_BODY_PARAMS = "" +
- " \n" +
- " $PARAM_NAME$ \n" +
- " \n" +
- " \n" +
- " \n" +
- " ";
-
- List headers = Utils.getHeaders(message, true, helpers);
- String encoding = getHeadParameterValue(headers, "Content-Type").strip();
- String body = splitMessage(message,helpers,true).get(2);
- String url = helpers.analyzeRequest(message).getUrl().toString();
- String method = splitMessage(message,helpers,true).get(0).split(" ")[0];
-
- Pattern p = Pattern.compile("");
- Matcher m = p.matcher(body);
-
- String res = "";
-
- res = POST_TEMPLATE;
- p = Pattern.compile("\\$ENCODING_TYPE\\$");
- m = p.matcher(res);
- res = m.replaceAll(encoding);
-
- p = Pattern.compile("\\$METHOD\\$");
- m = p.matcher(res);
- res = m.replaceAll(method);
-
- if (method.equals("POST")) {
- p = Pattern.compile("([^=]*)=([^&\\n$]*)(&|\\n|$)");
- m = p.matcher(body);
- String out_body_params = "";
-
- if (body.length() != 0) {
- Map body_params = new HashMap<>();
- while (m.find()) {
- String name = m.group(1);
- String value = m.group(2);
- if (name.length() != 0 ) {
- body_params.put(name,
- value.length() != 0 ? value : "");
- }
- }
- for (String key : body_params.keySet()) {
- String tmp = TEMPLATE_BODY_PARAMS;
- p = Pattern.compile("\\$PARAM_NAME\\$");
- m = p.matcher(tmp);
-
- tmp = m.replaceAll(key);
-
- p = Pattern.compile("\\$PARAM_VALUE\\$");
- m = p.matcher(tmp);
-
- tmp = m.replaceAll(body_params.get(key));
-
- out_body_params += tmp;
- }
- }
-
- p = Pattern.compile("\\$URL\\$");
- m = p.matcher(res);
- res = m.replaceAll(url);
-
- p = Pattern.compile("\\$BODY_PARAMETERS\\$");
- m = p.matcher(res);
- res = m.replaceAll(out_body_params);
- } else {
- boolean has_query_params = url.split("\\?").length > 1;
- String out_query_params = "";
-
- if (has_query_params) {
- String raw_query_params = url.split("\\?")[1];
-
- p = Pattern.compile("([^=\\n&]*)=([^=\\n&]*)");
- m = p.matcher(raw_query_params);
-
- Map query_params = new HashMap<>();
- while (m.find()) {
- String name = m.group(1);
- String value = m.group(2);
- if (name.length() != 0) {
- query_params.put(name, value.length() != 0 ? value : "");
- }
- }
- for (String key : query_params.keySet()) {
- String tmp = TEMPLATE_BODY_PARAMS;
- p = Pattern.compile("\\$PARAM_NAME\\$");
- m = p.matcher(tmp);
-
- tmp = m.replaceAll(key);
-
- p = Pattern.compile("\\$PARAM_VALUE\\$");
- m = p.matcher(tmp);
-
- tmp = m.replaceAll(query_params.get(key));
-
- out_query_params += tmp;
- }
- }
-
- p = Pattern.compile("\\$URL\\$");
- m = p.matcher(res);
- res = m.replaceAll( has_query_params ? url.split("\\?")[0] : url);
-
- p = Pattern.compile("\\$BODY_PARAMETERS\\$");
- m = p.matcher(res);
- res = m.replaceAll(out_query_params);
- }
-
- String tmp = CSFR_TEMPLATE;
- p = Pattern.compile("\\$INSERT_HERE\\$");
- m = p.matcher(tmp);
- tmp = m.replaceAll(res);
-
- return tmp;
- }
-
- /**
- * Create batches of passive tests, grouping them by the session they need to execute.
- * @return An HashMap object having as keys, Strings representing the sessions names, and as value a list of tests
- * that need to execute that session
- */
- public static HashMap> batchPassivesFromSession(List testList) throws ParsingException {
- HashMap> batch = new HashMap<>();
- for (Test t : testList) {
- if (t.sessions.size() == 0) {
- throw new ParsingException("Undefined session in test " + t.name);
- }
-
- if(!batch.containsKey(t.sessions.get(0).name)){
- List n = new ArrayList<>();
- n.add(t);
- batch.put(t.sessions.get(0).name, n);
- } else {
- List tmp = batch.get(t.sessions.get(0).name);
- tmp.add(t);
- batch.put(t.sessions.get(0).name, tmp);
- }
- }
- return batch;
- }
-
- /**
- * From a batch of tests grouped by sessions, return a list containing all the tests
- * @param batch the batch of tests in the form of a MAP>
- * @return
- * @throws ParsingException
- */
- public static List debatchPassive(HashMap> batch) {
- List res = new ArrayList<>();
- for (String sessionName : batch.keySet()){
- for (Test t : batch.get(sessionName)) {
- res.add(t);
- }
- }
- return res;
- }
-
- /**
- * Given a message, get the given parameter value from the url
- *
- * @param message the message to search into
- * @param isRequest if the message is a request
- * @param param the parameter name to be searched
- * @return the value of the parameter
- */
- public static String getUrlParam(IExtensionHelpers helpers, IHttpRequestResponse message, Boolean isRequest, String param) {
- List parts = Utils.splitMessage(message, helpers, isRequest);
- //Pattern pattern = Pattern.compile("(?=&?)" + param + "=[^& ]*((?=&)|(?= ))");
-
- Pattern pattern = Pattern.compile("(?<=" + param + "=)[^$\\n&\\s]*");
- Matcher matcher = pattern.matcher(parts.get(0));
- String res = "";
- while (matcher.find()) {
- res = matcher.group();
- break;
- }
- return res;
- }
-
- /**
- * Given a message, get the given parameter value from the url
- *
- * @param message the message to search into
- * @param isRequest if the message is a request
- * @param param the parameter name to be searched
- * @return the value of the parameter
- */
- public static String getUrlParam(IExtensionHelpers helpers, HTTPReqRes message, Boolean isRequest, String param) {
- List parts = Utils.splitMessage(message, helpers, isRequest);
- //Pattern pattern = Pattern.compile("(?=&?)" + param + "=[^& ]*((?=&)|(?= ))");
-
- Pattern pattern = Pattern.compile("(?<=" + param + "=)[^$\\n&\\s]*");
- Matcher matcher = pattern.matcher(parts.get(0));
- String res = "";
- while (matcher.find()) {
- res = matcher.group();
- break;
- }
- return res;
- }
-
- /**
- * Given a message, get the given parameter value from the head
- *
- * @param message the message to search into
- * @param isRequest if the message is a request
- * @param param the parameter name to be searched
- * @return the value of the parameter
- */
- public static String getHeadParam(IExtensionHelpers helpers, IHttpRequestResponse message, Boolean isRequest, String param) {
- List parts = Utils.splitMessage(message, helpers, isRequest);
-
- Pattern pattern = Pattern.compile("(?<=" + param + ":)[^$\\n]*", Pattern.MULTILINE);
- Matcher matcher = pattern.matcher(parts.get(1));
- String res = "";
- while (matcher.find()) {
- res = matcher.group();
- res = res;
- break;
- }
- return res;
- }
-
- /**
- * Given a message, get the given parameter value from the head
- *
- * @param message the message to search into
- * @param isRequest if the message is a request
- * @param param the parameter name to be searched
- * @return the value of the parameter
- */
- public static String getHeadParam(IExtensionHelpers helpers, HTTPReqRes message, Boolean isRequest, String param) {
- List parts = Utils.splitMessage(message, helpers, isRequest);
-
- Pattern pattern = Pattern.compile("(?<=" + param + ":)[^$\\n]*", Pattern.MULTILINE);
- Matcher matcher = pattern.matcher(parts.get(1));
- String res = "";
- while (matcher.find()) {
- res = matcher.group();
- res = res;
- break;
- }
- return res;
- }
-
- /**
- * Given a message, get the given parameter value from the body, note that it accepts a regular expression, and
- * everything matched will be returned as a value
- *
- * @param message the message to search into
- * @param isRequest if the message is a request
- * @param param the parameter to be searched as a regex, everything matched by this will be returned as a value
- * @return the value of the parameter
- */
- public static String getBodyParam(IExtensionHelpers helpers, IHttpRequestResponse message, Boolean isRequest, String param) {
- List parts = Utils.splitMessage(message, helpers, isRequest);
-
- //Pattern pattern = Pattern.compile("(?<=" + param + "=)[^$\\n&]*");
- Pattern pattern = Pattern.compile(param);
- Matcher matcher = pattern.matcher(parts.get(2));
- //parts.set(2, matcher.replaceAll(""));
-
- String res = "";
- while (matcher.find()) {
- res = matcher.group();
- break;
- }
- return res;
- }
-
- /**
- * Given a message, get the given parameter value from the body, note that it accepts a regular expression, and
- * everything matched will be returned as a value
- *
- * @param message the message to search into
- * @param isRequest if the message is a request
- * @param param the parameter to be searched as a regex, everything matched by this will be returned as a value
- * @return the value of the parameter
- */
- public static String getBodyParam(IExtensionHelpers helpers, HTTPReqRes message, Boolean isRequest, String param) {
- List parts = Utils.splitMessage(message, helpers, isRequest);
-
- //Pattern pattern = Pattern.compile("(?<=" + param + "=)[^$\\n&]*");
- Pattern pattern = Pattern.compile(param);
- Matcher matcher = pattern.matcher(parts.get(2));
- //parts.set(2, matcher.replaceAll(""));
-
- String res = "";
- while (matcher.find()) {
- res = matcher.group();
- break;
- }
- return res;
- }
-
- /**
- * 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
- * @param isRequest specify if the message to consider is the request or response
- * @param new_value the new value to substitute to the message section
- * @param isBodyRegex not used, to remove
- * @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,
- MessageOperation mop,
- IHttpRequestResponse messageInfo,
- boolean isRequest,
- String new_value,
- boolean isBodyRegex) throws ParsingException {
- List splitted = null;
- Pattern pattern = null;
- Matcher matcher = null;
- switch (mop.from) {
- case HEAD:
- splitted = Utils.splitMessage(messageInfo, helpers, isRequest);
-
- pattern = Pattern.compile(regex);
- matcher = pattern.matcher(splitted.get(1));
- splitted.set(1, matcher.replaceAll(new_value));
-
- return Utils.buildMessage(splitted, helpers);
-
- case BODY:
- splitted = Utils.splitMessage(messageInfo, helpers, isRequest);
-
- pattern = Pattern.compile(regex);
-
- matcher = pattern.matcher(splitted.get(2));
- splitted.set(2, matcher.replaceAll(new_value));
-
- List head = Utils.getHeaders(messageInfo, isRequest, helpers);
- //Automatically update content-lenght
- return helpers.buildHttpMessage(head, helpers.stringToBytes(splitted.get(2)));
-
- case URL:
- if (!isRequest) {
- throw new ParsingException("Encoding URL in response");
- }
- splitted = Utils.splitMessage(messageInfo, helpers, isRequest);
-
- pattern = Pattern.compile(regex);
- matcher = pattern.matcher(splitted.get(0));
-
- String replaced = matcher.replaceAll(new_value);
-
- splitted.set(0, replaced); // problema
-
- return Utils.buildMessage(splitted, helpers);
- }
-
- return null;
- }
-
- /**
- * Edit a message parameter
- * @param helpers an instance of Burp's IExtensionHelper
- * @param param_name the name of the parameter to edit
- * @param mop the message operation containing information about the section containing the parameter to edit
- * @param messageInfo the message as IHttpRequestResponse object
- * @param isRequest specify if the message to consider is the request or response
- * @param new_value the new value of the parameter
- * @param isBodyRegex when the section is body, set it to true if you want to use a regex to substitute the value,
- * otherwise a parameter param=... is searched
- * @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,
- MessageOperation mop,
- IHttpRequestResponse messageInfo,
- boolean isRequest,
- String new_value,
- boolean isBodyRegex) throws ParsingException {
- List splitted = null;
- Pattern pattern = null;
- Matcher matcher = null;
- switch (mop.from) {
- case HEAD:
- List headers = Utils.getHeaders(messageInfo, isRequest, helpers);
- headers = Utils.editHeadParameter(headers, mop.what, new_value);
- byte[] message = helpers.buildHttpMessage(
- headers,
- Utils.getBody(messageInfo, isRequest, helpers));
-
- if (param_name.equals("Host")) {
- messageInfo.setHttpService(
- helpers.buildHttpService(
- new_value,
- messageInfo.getHttpService().getPort(),
- messageInfo.getHttpService().getProtocol()
- )
- );
- }
-
- return message;
-
- case BODY:
- splitted = Utils.splitMessage(messageInfo, helpers, isRequest);
-
- if (!isBodyRegex) {
- pattern = Pattern.compile("(?<=" + param_name + "=)[^$\\n& ]*");
- } else {
- pattern = Pattern.compile(param_name);
- }
-
- matcher = pattern.matcher(splitted.get(2));
- splitted.set(2, matcher.replaceAll(new_value));
-
- List head = Utils.getHeaders(messageInfo, isRequest, helpers);
- //Automatically update content-lenght
- return helpers.buildHttpMessage(head, helpers.stringToBytes(splitted.get(2)));
-
- case URL:
- if (!isRequest) {
- throw new ParsingException("Encoding URL in response");
- }
- splitted = Utils.splitMessage(messageInfo, helpers, isRequest);
-
- pattern = Pattern.compile(param_name + "=[^& ]*((?=&)|(?= ))");
- matcher = pattern.matcher(splitted.get(0));
-
- splitted.set(0, matcher.replaceAll(param_name + "=" + new_value)); // problema
-
- return Utils.buildMessage(splitted, helpers);
- }
-
- return null;
- }
-}
diff --git a/tool/src/main/java/migt/API.java b/tool/src/main/java/migt/API.java
new file mode 100644
index 0000000..a62f80a
--- /dev/null
+++ b/tool/src/main/java/migt/API.java
@@ -0,0 +1,7 @@
+package migt;
+
+/**
+ * The API class, this class is inherited by all the other APIs
+ */
+public class API {
+}
diff --git a/tool/src/main/java/migt/BurpExtender.java b/tool/src/main/java/migt/BurpExtender.java
new file mode 100644
index 0000000..425c0fa
--- /dev/null
+++ b/tool/src/main/java/migt/BurpExtender.java
@@ -0,0 +1,200 @@
+package migt;
+
+import burp.*;
+
+import javax.swing.*;
+import java.awt.*;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Objects;
+
+/**
+ * Main class executed by Burp
+ *
+ * @author Matteo Bitussi
+ */
+public class BurpExtender implements IBurpExtender, ITab, IProxyListener {
+
+ public static IExtensionHelpers helpers;
+ public static PrintStream printStream;
+ public static PrintStream errorStream;
+ public IBurpExtenderCallbacks callbacks;
+ private GUI mainPane; // The GUI
+
+ /**
+ * Main function creating the extension
+ *
+ * @param callbacks The callbacks received by Burp
+ */
+ public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) {
+ /*
+ try {
+ System.setOut(new PrintStream("output_log.txt")); // Changes the default outstream with this file
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ try {
+ System.setErr(new PrintStream("error_log.txt"));
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ */
+
+ System.out.println("Initializing extension");
+
+ this.callbacks = callbacks;
+ helpers = callbacks.getHelpers();
+
+ callbacks.setExtensionName("MIG Testing tool");
+
+ //The UI is created
+ SwingUtilities.invokeLater(() -> {
+ // setup output stream
+ OutputStream stdOut = callbacks.getStdout();
+ OutputStream stdErr = callbacks.getStderr();
+ printStream = new PrintStream(stdOut);
+ 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());
+
+ // add the custom tab to Burp's UI
+ callbacks.addSuiteTab(BurpExtender.this);
+
+ // register ourselves as an HTTP listener
+ callbacks.registerProxyListener(BurpExtender.this);
+ //callbacks.registerHttpListener(BurpExtender.this);
+ });
+ }
+
+ @Override
+ public String getTabCaption() {
+ return "MIG-T";
+ }
+
+ @Override
+ public Component getUiComponent() {
+ return mainPane;
+ }
+
+ /**
+ * Proxy's listener function which is called wheter a new message arrives. Note that if the received message is a
+ * request, you cannot access the response
+ *
+ * @param messageIsRequest Indicates whether the HTTP message is a request
+ * or a response.
+ * @param proxy_message An
+ * IInterceptedProxyMessage
object that extensions can use to
+ * query and update details of the message, and control whether the message
+ * should be intercepted and displayed to the user for manual review or
+ */
+ @Override
+ public void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage proxy_message) {
+ String port = proxy_message.getListenerInterface().split(":")[1];
+ IHttpRequestResponse messageInfo = proxy_message.getMessageInfo();
+
+ HTTPReqRes message = new HTTPReqRes(
+ messageInfo,
+ helpers,
+ messageIsRequest,
+ proxy_message.getMessageReference()
+ );
+
+ if (mainPane.ACTIVE_ENABLED) {
+ if (!port.equals(mainPane.act_active_op.session_port)) {
+ return;
+ }
+
+ log_message(messageIsRequest, proxy_message);
+
+ MessageType msg_type = null;
+ try {
+ msg_type = MessageType.getFromList(mainPane.messageTypes,
+ mainPane.act_active_op.getMessageType());
+ } catch (Exception e) {
+ e.printStackTrace();
+ mainPane.act_active_op.applicable = false;
+ }
+
+ boolean matchMessage = message.matches_msg_type(msg_type);
+
+ if (matchMessage) {
+ // If the operation's action is an intercept
+ if (Objects.requireNonNull(mainPane.act_active_op.getAction()) == Operation.Action.INTERCEPT) {
+ try {
+ processMatchedMsg(msg_type, messageInfo, message);
+ if (mainPane.act_active_op.then != null &
+ mainPane.act_active_op.then == Operation.Then.DROP) {
+ proxy_message.setInterceptAction(IInterceptedProxyMessage.ACTION_DROP);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ mainPane.act_active_op.applicable = false;
+ }
+ }
+ }
+ }
+
+ if (mainPane.recording) {
+ if (!messageIsRequest) { // do not remove
+ synchronized (mainPane.interceptedMessages) {
+ IHttpRequestResponsePersisted actual = callbacks.saveBuffersToTempFiles(messageInfo);
+ mainPane.interceptedMessages.add(
+ new HTTPReqRes(actual, helpers)
+ );
+ if (mainPane.defaultSession != null) {
+ mainPane.defaultSession.addMessage(actual, helpers, mainPane.FILTERING);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @param msg_type the message type to be used
+ * @param messageInfo the original intercepted messageInfo to being able to edit the message
+ * @param message a custom parsed message to be used in opeations
+ */
+ private void processMatchedMsg(MessageType msg_type,
+ IHttpRequestResponse messageInfo,
+ 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();
+
+ // if message has been edited inside operation update the value
+ try {
+ if (mainPane.act_active_op.processed_message != null) {
+ if (msg_type.msg_to_process_is_request) {
+ messageInfo.setRequest(mainPane.act_active_op.processed_message);
+ } else {
+ messageInfo.setResponse(mainPane.act_active_op.processed_message);
+ }
+ }
+ } catch (UnsupportedOperationException e) {
+ // This is thrown when an already issued request is being substituted
+ System.err.println("Warning, edited message that has already been sent");
+ }
+ resume();
+ }
+
+ /**
+ * Tells the lock on the Execute Actives process to resume the execution
+ */
+ private void resume() {
+ // Resume the execution thread
+ synchronized (mainPane.waiting) {
+ mainPane.waiting.notify();
+ }
+ }
+
+ private void log_message(boolean isRequest, IInterceptedProxyMessage message) {
+ mainPane.act_active_op.log_messages.add(message);
+ }
+}
diff --git a/tool/src/main/java/migt/Check.java b/tool/src/main/java/migt/Check.java
new file mode 100644
index 0000000..0216f01
--- /dev/null
+++ b/tool/src/main/java/migt/Check.java
@@ -0,0 +1,685 @@
+package migt;
+
+import com.jayway.jsonpath.JsonPath;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.URLDecoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static migt.Check.CheckOps.*;
+
+/**
+ * Check Object class. This object is used in Operations to check that a parameter or some text is in as specified.
+ *
+ * @author Matteo Bitussi
+ */
+public class Check extends Module {
+ String what; // what to search
+ CheckOps op; // the check operations
+ CheckIn in; // the section over which to search
+ String op_val;
+ List value_list; // the eventual list of values to check between
+ boolean isParamCheck; // specifies if what is declared in what is a parameter name
+ String regex; // the eventual regex to use
+ boolean use_variable; // if a variable name will be used in the check operation
+ boolean url_decode = true; // this can be used to disable url decoding
+
+ public Check() {
+ init();
+ }
+
+ /**
+ * Instantiate a new Check object given its parsed JSONObject
+ *
+ * @param json_check the check as JSONObject
+ * @throws ParsingException if the input is not compliant with the language
+ */
+ public Check(JSONObject json_check) throws ParsingException {
+ init();
+ Iterator keys = json_check.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ try {
+ switch (key) {
+ case "in":
+ this.in = CheckIn.fromString(json_check.getString("in"));
+ break;
+ case "check param":
+ this.isParamCheck = true;
+ this.setWhat(json_check.getString("check param"));
+ break;
+ case "check":
+ this.setWhat(json_check.getString("check"));
+ break;
+ case "check regex":
+ regex = json_check.getString("check regex");
+ break;
+ case "use variable":
+ use_variable = json_check.getBoolean("use variable");
+ break;
+ case "is":
+ this.setOp(CheckOps.IS);
+ this.op_val = json_check.getString("is");
+ break;
+ case "is not":
+ this.setOp(CheckOps.IS_NOT);
+ this.op_val = json_check.getString("is not");
+ break;
+ case "contains":
+ this.setOp(CheckOps.CONTAINS);
+ try {
+ this.op_val = json_check.getString("contains");
+ } catch (JSONException e) {
+ // if not a string try an array
+ JSONArray jsonArr = json_check.getJSONArray("contains");
+ Iterator it = jsonArr.iterator();
+
+ while (it.hasNext()) {
+ String act_enc = (String) it.next();
+ value_list.add(act_enc);
+ }
+ }
+ break;
+ case "not contains":
+ this.setOp(CheckOps.NOT_CONTAINS);
+ try {
+ this.op_val = json_check.getString("not contains");
+ } catch (JSONException e) {
+ // if not a string try an array
+ JSONArray jsonArr = json_check.getJSONArray("not contains");
+ Iterator it = jsonArr.iterator();
+
+ while (it.hasNext()) {
+ String act_enc = (String) it.next();
+ value_list.add(act_enc);
+ }
+ }
+ break;
+ case "is present":
+ this.op = json_check.getBoolean("is present") ? CheckOps.IS_PRESENT :
+ IS_NOT_PRESENT;
+ this.op_val = json_check.getBoolean("is present") ?
+ "is present" : "is not present";
+ break;
+ case "is in":
+ this.op = CheckOps.IS_IN;
+ JSONArray jsonArr = json_check.getJSONArray("is in");
+ Iterator it = jsonArr.iterator();
+
+ while (it.hasNext()) {
+ String act_enc = (String) it.next();
+ value_list.add(act_enc);
+ }
+ break;
+ case "is not in":
+ this.op = CheckOps.IS_NOT_IN;
+ JSONArray jsonArr2 = json_check.getJSONArray("is not in");
+ Iterator it2 = jsonArr2.iterator();
+
+ while (it2.hasNext()) {
+ String act_enc = (String) it2.next();
+ value_list.add(act_enc);
+ }
+ break;
+ case "is subset of":
+ this.op = IS_SUBSET_OF;
+ JSONArray jsonArr3 = json_check.getJSONArray("is subset of");
+ Iterator it3 = jsonArr3.iterator();
+
+ while (it3.hasNext()) {
+ String act_enc = (String) it3.next();
+ value_list.add(act_enc);
+ }
+ break;
+ case "matches regex":
+ this.op = MATCHES_REGEX;
+ this.op_val = json_check.getString("matches regex");
+ break;
+ case "not matches regex":
+ this.op = NOT_MATCHES_REGEX;
+ this.op_val = json_check.getString("not matches regex");
+ break;
+ case "url decode":
+ url_decode = json_check.getBoolean("url decode");
+ break;
+ }
+ } catch (JSONException e) {
+ throw new ParsingException("error in parsing check: " + e);
+ } catch (ClassCastException e) {
+ throw new ParsingException("Only allowed values in arrays are Strings, if you are using integers or " +
+ "floats, please convert them as strings");
+ }
+
+ }
+
+ if (regex.equals("") && what.equals(""))
+ throw new ParsingException("Error in parsing check");
+ }
+
+ public void init() {
+ what = "";
+ op_val = "";
+ isParamCheck = false;
+ regex = "";
+ value_list = new ArrayList<>();
+ use_variable = false;
+ }
+
+ /**
+ * Loads a Decode operation's API into the check
+ *
+ * @param api the Decode operation's api to load
+ */
+ public void loader(DecodeOperation_API api) {
+ this.imported_api = api;
+ }
+
+ /**
+ * Loads an Operation's API into the check
+ *
+ * @param api the Operation's API to load
+ */
+ public void loader(Operation_API api) {
+ this.imported_api = api;
+ }
+
+ /**
+ * Executes the regex version of the check
+ *
+ * @param input the input content
+ * @return the result of the check
+ */
+ private boolean execute_regex(String input) throws ParsingException {
+ Pattern p = Pattern.compile(regex);
+ Matcher m = p.matcher(input);
+ applicable = true;
+
+ String val = "";
+ if (m.find()) {
+ val = m.group();
+ }
+
+ if (this.op == null) {
+ // Return result based on matched or not
+ return (val.length() > 0);
+ } else {
+ // execute op against matched value
+ return do_check(val);
+ }
+ }
+
+ /**
+ * Execute the check over a message (in an Operation)
+ *
+ * @param message the message to check
+ * @param isRequest tells if the message is a request or a response
+ * @return the result of the check
+ * @throws ParsingException if something wrong is found wrt the language
+ */
+ private boolean execute_http(HTTPReqRes message,
+ boolean isRequest) throws ParsingException {
+ String msg_str = "";
+ if (this.in == null) {
+ throw new ParsingException("from tag in checks is null");
+ }
+
+ switch (this.in) {
+ case URL:
+ if (!isRequest) {
+ throw new ParsingException("Searching URL in response");
+ }
+ msg_str = message.getUrlHeader();
+ break;
+ case BODY:
+ msg_str = new String(message.getBody(isRequest), StandardCharsets.UTF_8);
+ break;
+ case HEAD:
+ msg_str = String.join("\r\n", message.getHeaders(isRequest));
+ break;
+ default:
+ System.err.println("no valid \"in\" specified in check");
+ return false;
+ }
+
+ if (msg_str.length() == 0) {
+ return false;
+ }
+
+ // URL-decode matched content
+ if (url_decode)
+ msg_str = URLDecoder.decode(msg_str, StandardCharsets.UTF_8);
+
+ // if a regex is present, execute it
+ if (!regex.equals("")) {
+ return execute_regex(msg_str);
+ }
+
+ if (this.isParamCheck) {
+ if (in == CheckIn.BODY) {
+ applicable = false;
+ throw new ParsingException("Invalid check operation, cannot do \"check param\" over body, " +
+ "use \"check_regex instead\"");
+ }
+
+ Pattern p = this.in == CheckIn.URL ?
+ Pattern.compile("(?<=[?&]" + this.what + "=)[^\\r\\n&]*") :
+ Pattern.compile("(?<=" + this.what + ":\\s?)[^\\r\\n]*");
+ // TODO: this could be done better by using message methods
+ Matcher m = p.matcher(msg_str);
+
+ applicable = true;
+
+ String val = "";
+ if (m.find()) {
+ val = m.group();
+ val = val.trim();
+ } else {
+ //return false; // TODO: check if correct, is not present?
+ }
+
+ return do_check(val);
+ } else {
+ applicable = true;
+ if (!msg_str.contains(this.what)) {
+ if (this.op != null) {
+ return this.op == IS_NOT_PRESENT;
+ } else {
+ return false;
+ }
+ } else {
+ if (this.op != null) {
+ return this.op != IS_NOT_PRESENT;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Execute the json version of the check
+ *
+ * @return the result of the execution
+ * @throws ParsingException if something wrong is found wrt the language
+ */
+ private boolean execute_json() throws ParsingException {
+ DecodeOperation_API tmp = ((DecodeOperation_API) this.imported_api);
+
+ if (isParamCheck) {
+ throw new ParsingException("Cannot execute a 'check param' in a json, please use 'check'");
+ }
+
+ String j = "";
+
+ switch (in) {
+ case JWT_HEADER: {
+ j = tmp.jwt.header;
+ break;
+ }
+ case JWT_PAYLOAD: {
+ j = tmp.jwt.payload;
+ break;
+ }
+ case JWT_SIGNATURE: {
+ j = tmp.jwt.signature;
+ break;
+ }
+ }
+
+ // if a regex is present, execute it
+ if (!regex.equals("")) {
+ return execute_regex(j);
+ }
+
+ String found = "";
+ List found_array = null;
+ boolean value_is_array = false;
+ // https://github.com/json-path/JsonPath
+ try {
+ Object found_obj = JsonPath.read(j, what);
+
+ if (op == IS_PRESENT | op == IS_NOT_PRESENT) {
+ // whatever is the type of the value, if it is found return the result
+ applicable = true;
+ return op == IS_PRESENT;
+ }
+
+ if (found_obj instanceof net.minidev.json.JSONArray) {
+ // the value is a list, allowed ops are: contains/not-contains
+ if (!(op == CONTAINS | op == NOT_CONTAINS | op == IS_SUBSET_OF)) {
+ throw new ParsingException("Check error, used " + op.toString() + " over a matched list");
+ }
+
+ Iterator i = ((net.minidev.json.JSONArray) found_obj).iterator();
+
+ List new_array = new ArrayList<>();
+ while (i.hasNext()) {
+ try {
+ String elem = String.valueOf(i.next());
+ new_array.add(elem);
+ } catch (java.lang.ClassCastException e) {
+ throw new ParsingException("Cannot convert element in jwt matched array to string");
+ }
+ }
+ found_array = new_array;
+ value_is_array = true;
+
+ } else if (found_obj instanceof java.lang.String) {
+ // the value is a string, can do all ops
+ found = (String) found_obj;
+
+ } else if (found_obj instanceof java.lang.Double |
+ found_obj instanceof java.lang.Integer) {
+ // the value is an double or integer, convert to string
+ found = String.valueOf(found_obj);
+ }
+
+ } catch (com.jayway.jsonpath.PathNotFoundException e) {
+ applicable = true;
+ return op == IS_NOT_PRESENT;
+ } catch (java.lang.ClassCastException e) {
+ throw new ParsingException("Error in check, json matched value cast exception: " + e);
+ }
+
+ applicable = true; // at this point the path has been found so the check is applicable
+
+ switch (op) {
+ case IS:
+ return op_val.equals(found);
+ case IS_NOT:
+ return !op_val.equals(found);
+ case CONTAINS:
+ if (!value_is_array)
+ return found.contains(op_val);
+ else {
+ // the matched value is an array
+ if (!value_list.isEmpty()) {
+ // check against a value array
+ for (String elem : value_list) {
+ if (!found_array.contains(elem)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ // check against single string value
+ return found_array.contains(op_val);
+ }
+ }
+ case NOT_CONTAINS:
+ if (!value_is_array)
+ return !found.contains(op_val);
+ else {
+ //the matched value is an array
+ if (!value_list.isEmpty()) {
+ // check against a value array
+ for (String elem : value_list) {
+ if (found_array.contains(elem)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ // check against single string value
+ return found_array.contains(op_val);
+ }
+ }
+ case IS_PRESENT:
+ return !found.isEmpty();
+ case IS_NOT_PRESENT:
+ return found.isEmpty();
+ case IS_IN:
+ return value_list.contains(found);
+ case IS_NOT_IN:
+ return !value_list.contains(found);
+ case IS_SUBSET_OF:
+ if (!value_is_array)
+ throw new ParsingException("Matched single element in jwt, but should be an array when using IS SUBSET OF");
+
+ return value_list.containsAll(found_array);
+ case MATCHES_REGEX: {
+ if (value_is_array) throw new ParsingException("Check error: cannot execute a regex over a list");
+ Pattern p = Pattern.compile(op_val);
+ Matcher m = p.matcher(found);
+ return m.find();
+ }
+ case NOT_MATCHES_REGEX: {
+ if (value_is_array) throw new ParsingException("Check error: cannot execute a regex over a list");
+ Pattern p = Pattern.compile(op_val);
+ Matcher m = p.matcher(found);
+ return !m.find();
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Executes check operations over the selected value, and returns the result
+ *
+ * @param val_to_check the value to check
+ * @return the result of the check
+ */
+ public boolean do_check(String val_to_check) throws ParsingException {
+ try {
+ if (this.op == null && val_to_check.length() != 0) {
+ // if it passed all the splits without errors, the param is present, but no checks are specified
+ // so result is true
+ return true;
+ }
+ switch (this.op) {
+ case IS:
+ if (!this.op_val.equals(val_to_check)) {
+ return false;
+ }
+ break;
+ case IS_NOT:
+ if (this.op_val.equals(val_to_check)) {
+ return false;
+ }
+ break;
+ case CONTAINS:
+ if (!val_to_check.contains(this.op_val)) {
+ return false;
+ }
+ break;
+ case NOT_CONTAINS:
+ if (val_to_check.contains(this.op_val)) {
+ return false;
+ }
+ break;
+ case IS_PRESENT:
+ return !val_to_check.isEmpty(); // if it gets to this, the searched param is already found
+ case IS_NOT_PRESENT:
+ return val_to_check.isEmpty();
+ case IS_IN:
+ return value_list.contains(val_to_check); // TODO check
+ case IS_NOT_IN:
+ return !value_list.contains(val_to_check);
+ case MATCHES_REGEX: {
+ Pattern p = Pattern.compile(op_val);
+ Matcher m = p.matcher(val_to_check);
+ return m.find();
+ }
+ case NOT_MATCHES_REGEX: {
+ Pattern p = Pattern.compile(op_val);
+ Matcher m = p.matcher(val_to_check);
+ return !m.find();
+ }
+ default:
+ throw new ParsingException("Unsupported operand for Check in a message: " + op);
+ }
+ } catch (ArrayIndexOutOfBoundsException e) {
+ //e.printStackTrace();
+ if (this.op != null) {
+ if (this.op != IS_NOT_PRESENT) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Executes the given check (without API). Used to match messages with msg_types usually.
+ *
+ * @param message the message to check
+ * @param isRequest if the message is a request or a response
+ * @return the result of the check (passed or not passed)
+ */
+ public boolean execute(HTTPReqRes message,
+ boolean isRequest,
+ List vars) throws ParsingException {
+
+ if (use_variable) {
+ // Substitute to the op_val variable (that contains the name), the value of the variable
+ op_val = Tools.getVariableByName(op_val, vars).value;
+ }
+ result = execute_http(message, isRequest);
+ return result;
+ }
+
+ /**
+ * Execute the check by using API
+ *
+ * @param vars the variables of the actual operation (test)
+ */
+ public void execute(List vars) throws ParsingException {
+ if (use_variable) {
+ // Substitute to the op_val variable (that contains the name), the value of the variable
+ op_val = Tools.getVariableByName(op_val, vars).value;
+ }
+
+ if (imported_api instanceof Operation_API) {
+ // If is inside a standard Operation
+ result = execute_http(
+ ((Operation_API) imported_api).message,
+ ((Operation_API) imported_api).is_request
+ );
+ } else if (imported_api instanceof DecodeOperation_API) {
+ // if inside a decode operation
+ switch (((DecodeOperation_API) imported_api).type) {
+ case JWT:
+ result = execute_json();
+ break;
+ case NONE:
+ break;
+ //TODO
+ case XML:
+ //TODO
+ break;
+ }
+ }
+ }
+
+ public void setWhat(String what) {
+ this.what = what;
+ }
+
+ public void setOp(CheckOps op) {
+ this.op = op;
+ }
+
+ @Override
+ public String toString() {
+ return "check: " + what + (op == null ? "" : " " + op + ": " + op_val);
+ }
+
+ /**
+ * enum containing all the possible check operations
+ */
+ public enum CheckOps {
+ IS,
+ IS_NOT,
+ CONTAINS,
+ NOT_CONTAINS,
+ IS_PRESENT,
+ IS_NOT_PRESENT,
+ IS_IN,
+ IS_NOT_IN,
+ IS_SUBSET_OF,
+ MATCHES_REGEX,
+ NOT_MATCHES_REGEX;
+
+ /**
+ * Function that given a String, returns the corresponding CheckOps enum's value
+ *
+ * @param input the input string
+ * @return the CheckOps enum value
+ * @throws ParsingException if the input string does not correspond to any of the possible check operations
+ */
+ public static CheckOps fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "is":
+ return IS;
+ case "is not":
+ return IS_NOT;
+ case "contains":
+ return CONTAINS;
+ case "not contains":
+ return NOT_CONTAINS;
+ case "is in":
+ return IS_IN;
+ case "is not in":
+ return IS_NOT_IN;
+ case "is subset of":
+ return IS_SUBSET_OF;
+ case "matches regex":
+ return MATCHES_REGEX;
+ case "not matches regex":
+ return NOT_MATCHES_REGEX;
+ default:
+ throw new ParsingException("invalid check operation");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ /**
+ * Used in the Check operation, to specify where is the content to check.
+ */
+ public enum CheckIn {
+ // standard message
+ HEAD,
+ BODY,
+ URL,
+ // jwt
+ JWT_HEADER,
+ JWT_PAYLOAD,
+ JWT_SIGNATURE;
+
+ public static CheckIn fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "head":
+ return HEAD;
+ case "body":
+ return BODY;
+ case "url":
+ return URL;
+ case "header":
+ return JWT_HEADER;
+ case "payload":
+ return JWT_PAYLOAD;
+ case "signature":
+ return JWT_SIGNATURE;
+ default:
+ throw new ParsingException("invalid in '" + input + "' for check");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+}
diff --git a/tool/src/main/java/migt/DecodeOperation.java b/tool/src/main/java/migt/DecodeOperation.java
new file mode 100644
index 0000000..7fa5ebe
--- /dev/null
+++ b/tool/src/main/java/migt/DecodeOperation.java
@@ -0,0 +1,626 @@
+package migt;
+
+import burp.IExtensionHelpers;
+import com.jayway.jsonpath.JsonPath;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.zip.DataFormatException;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+
+import static migt.Tools.executeDecodeOps;
+import static migt.Tools.executeEditOps;
+
+/**
+ * This class stores a decode operation
+ */
+public class DecodeOperation extends Module {
+ public String decoded_content; // the decoded content
+ public String decode_target; // aka decode_param how to decode the raw content
+ public DecodeOperationFrom from; // where the raw content is. Depending on the containing module, can be other things
+ public List encodings; // the list of encoding to decode and rencode
+ public DecodeOpType type; // the type of the decoded param (used only to edit its content)
+ public List checks; // the list of checks to be executed
+ public List decodeOperations; // a list of decode operations to execute them recursevly
+ public List editOperations; // a list of edit operations
+ public boolean check_jwt = false;
+ JWT jwt;
+ String what;
+
+ public DecodeOperation() {
+ init();
+ }
+
+ /**
+ * Instantiate a decode operation object parsing a json object
+ *
+ * @param decode_op_json the json object
+ * @throws ParsingException
+ */
+ public DecodeOperation(JSONObject decode_op_json) throws ParsingException {
+ init();
+ java.util.Iterator keys = decode_op_json.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+
+ switch (key) {
+ case "type":
+ type = DecodeOpType.fromString(decode_op_json.getString("type"));
+ break;
+ case "decode param":
+ decode_target = decode_op_json.getString("decode param");
+ break;
+ case "encodings":
+ JSONArray encodings = decode_op_json.getJSONArray("encodings");
+ Iterator it = encodings.iterator();
+
+ while (it.hasNext()) {
+ String act_enc = (String) it.next();
+ this.encodings.add(
+ Encoding.fromString(act_enc));
+ }
+ break;
+ case "from":
+ String f = decode_op_json.getString("from");
+ from = DecodeOperationFrom.fromString(f);
+ break;
+ case "decode operations":
+ // Recursion goes brr
+ JSONArray decode_ops = decode_op_json.getJSONArray("decode operations");
+ for (int k = 0; k < decode_ops.length(); k++) {
+ JSONObject act_decode_op = decode_ops.getJSONObject(k);
+ DecodeOperation decode_op = new DecodeOperation(act_decode_op);
+ decodeOperations.add(decode_op);
+ }
+ break;
+ case "checks":
+ checks = Tools.parseChecksFromJSON(decode_op_json.getJSONArray("checks"));
+ break;
+ case "edits":
+ editOperations = Tools.parseEditsFromJSON(decode_op_json.getJSONArray("edits"));
+ break;
+ case "jwt check sig":
+ check_jwt = true;
+ jwt.public_key_pem = decode_op_json.getString("jwt check sig");
+ break;
+ case "jwe decrypt":
+ jwt.decrypt = true;
+ jwt.private_key_pem_enc = decode_op_json.getString("jwe decrypt");
+ break;
+ case "jwe encrypt":
+ // if encrypt specified, also decrypt has to be specified
+ jwt.decrypt = true;
+ jwt.private_key_pem_enc = decode_op_json.getString("jwe encrypt");
+ jwt.public_key_pem_enc = decode_op_json.getString("jwe decrypt");
+ }
+ }
+ }
+
+ /**
+ * 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
+ * @param isRequest True if the message containing the parameter is a request
+ * @param decode_param The name of the parameter to be decoded
+ * @return The decoded parameter as a string
+ * @throws ParsingException If problems are encountered during decoding
+ */
+ public static String decodeParam(IExtensionHelpers helpers,
+ DecodeOperationFrom ms,
+ List encodings,
+ HTTPReqRes messageInfo,
+ Boolean isRequest,
+ String decode_param) throws ParsingException {
+ String decoded_param = "";
+ switch (ms) {
+ case HEAD:
+ decoded_param = decode(
+ encodings, messageInfo.getHeadParam(isRequest, decode_param), helpers);
+ break;
+ case BODY:
+ decoded_param = decode(
+ encodings, messageInfo.getBodyRegex(isRequest, decode_param), helpers);
+ break;
+ case URL:
+ decoded_param = decode(
+ encodings, messageInfo.getUrlParam(decode_param), helpers);
+ break;
+ }
+
+ decoded_param = Tools.removeNewline(decoded_param);
+
+ return decoded_param;
+ }
+
+ /**
+ * Decode the given string, with the given ordered encodings
+ * Example taken from
+ * Saml Raider
+ *
+ * @param encodings the ordered list of encodings to be applied
+ * @param encoded the string to be decoded
+ * @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
+ String actual = encoded;
+ byte[] actual_b = null;
+ boolean isActualString = true;
+
+ if (encoded.length() == 0) {
+ return "";
+ }
+
+ for (Encoding e : encodings) {
+ switch (e) {
+ case BASE64:
+ if (isActualString) {
+ actual_b = helpers.base64Decode(actual);
+ isActualString = false;
+ } else {
+ actual_b = helpers.base64Decode(actual_b);
+ }
+ break;
+ case URL:
+
+ if (isActualString) {
+ actual = helpers.urlDecode(actual);
+ } else {
+ actual = helpers.urlDecode(new String(actual_b));
+ isActualString = true;
+ }
+ break;
+ case DEFLATE:
+ boolean done = false;
+ if (isActualString) {
+ byte[] data = actual.getBytes();
+
+ try {
+ actual_b = decompress(data, true);
+ done = true;
+ isActualString = false;
+ } catch (IOException | DataFormatException ioException) {
+ ioException.printStackTrace();
+ //ioException.printStackTrace();
+ }
+
+ try {
+ if (!done) {
+ actual_b = decompress(data, false);
+ done = true;
+ isActualString = false;
+ }
+ } catch (IOException | DataFormatException ioException) {
+ //ioException.printStackTrace();
+
+ }
+ } else {
+ try {
+ actual_b = decompress(actual_b, true);
+ done = true;
+ } catch (IOException | DataFormatException ioException) {
+
+ //ioException.printStackTrace();
+ }
+
+ try {
+ if (!done) {
+ actual_b = decompress(actual_b, false);
+ done = true;
+ }
+ } catch (IOException | DataFormatException ioException) {
+
+ }
+ }
+ break;
+ }
+ }
+ if (isActualString) {
+ return actual;
+ } else {
+ return new String(actual_b, StandardCharsets.UTF_8);
+ }
+ }
+
+ /**
+ * Encode the given string, with the given encodings (in the specified order)
+ * Example taken from
+ * Saml Raider
+ *
+ * @param encodings the ordered list of encodings to be applied
+ * @param decoded the string to be encoded
+ * @return the encoded string
+ */
+ public static String encode(List encodings, String decoded, IExtensionHelpers helpers) {
+ String actual = decoded;
+ byte[] actual_b = null;
+ boolean isActualString = true;
+ for (Encoding e : encodings) {
+ switch (e) {
+ case BASE64:
+
+ if (isActualString) {
+ actual = helpers.base64Encode(actual);
+ } else {
+ actual = helpers.base64Encode(actual_b);
+ isActualString = true;
+ }
+ break;
+
+ case URL:
+
+ if (isActualString) {
+ actual = URLEncoder.encode(actual);
+ } else {
+ actual = new String(actual_b);
+ actual = URLEncoder.encode(actual);
+ isActualString = true;
+ }
+ break;
+
+ case DEFLATE:
+
+ if (isActualString) {
+ try {
+ actual_b = compress(actual.getBytes(StandardCharsets.UTF_8), true);
+ } catch (IOException ioException) {
+
+ //ioException.printStackTrace();
+ }
+ isActualString = false;
+ } else {
+ try {
+ actual_b = compress(actual_b, true);
+ } catch (IOException ioException) {
+
+ //ioException.printStackTrace();
+ }
+ isActualString = false;
+ }
+ }
+ }
+
+ if (isActualString) {
+ return actual;
+ } else {
+ return new String(actual_b, StandardCharsets.UTF_8);
+ }
+ }
+
+ /**
+ * Also named Inflate, taken from
+ * here
+ *
+ * @param data the data to be decompressed (inflated)
+ * @param gzip true to use gzip
+ * @return returns the decompressed data
+ * @throws IOException if something goes wrong
+ * @throws DataFormatException if something goes wrong
+ */
+ public static byte[] decompress(byte[] data, boolean gzip) throws IOException, DataFormatException {
+ Inflater inflater = new Inflater(true);
+ inflater.setInput(data);
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
+ byte[] buffer = new byte[1024];
+ while (!inflater.finished()) {
+ int count = inflater.inflate(buffer);
+ outputStream.write(buffer, 0, count);
+ }
+ outputStream.close();
+ byte[] output = outputStream.toByteArray();
+
+ inflater.end();
+
+ return output;
+ }
+
+ /**
+ * Also named Deflate, taken from
+ * here
+ *
+ * @param data data to be compressed (deflated)
+ * @param gzip true to use gzip
+ * @return the compressed data
+ * @throws IOException if the compression goes wrong
+ */
+ public static byte[] compress(byte[] data, boolean gzip) throws IOException {
+ Deflater deflater = new Deflater(5, gzip);
+ deflater.setInput(data);
+
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
+
+ deflater.finish();
+ byte[] buffer = new byte[1024];
+ while (!deflater.finished()) {
+ int count = deflater.deflate(buffer);
+ outputStream.write(buffer, 0, count);
+ }
+ outputStream.close();
+ byte[] output = outputStream.toByteArray();
+
+ deflater.end();
+
+ return output;
+ }
+
+ public void init() {
+ decoded_content = "";
+ decode_target = "";
+ checks = new ArrayList<>();
+ encodings = new ArrayList<>();
+ decodeOperations = new ArrayList<>();
+ what = "";
+ type = DecodeOpType.NONE;
+ editOperations = new ArrayList<>();
+ jwt = new JWT();
+ }
+
+ @Override
+ public DecodeOperation_API getAPI() {
+ api = new DecodeOperation_API(this);
+ return (DecodeOperation_API) api;
+ }
+
+ public void setAPI(DecodeOperation_API dop_api) {
+ this.api = dop_api;
+ // assign values returned from the api
+ switch (type) {
+ case JWT:
+ this.jwt = dop_api.jwt;
+ break;
+ case NONE:
+ this.decoded_content = dop_api.txt;
+ break;
+ case XML:
+ this.decoded_content = dop_api.xml;
+ break;
+ }
+ }
+
+ /**
+ * Loads an Operation API
+ *
+ * @param api
+ * @param helpers
+ * @throws ParsingException
+ */
+ public void loader(Operation_API api, IExtensionHelpers helpers) {
+ // load api, extract needed things
+ this.helpers = helpers;
+ this.imported_api = api;
+ }
+
+ /**
+ * Loads a decode operation API
+ *
+ * @param api
+ */
+ public void loader(DecodeOperation_API api, IExtensionHelpers helpers) {
+ this.imported_api = api;
+ this.helpers = helpers;
+
+ }
+
+ /**
+ * Exports the API of this decode operation to be used by another operation
+ *
+ * @return the API
+ * @throws ParsingException
+ */
+ @Override
+ public Operation_API exporter() throws ParsingException {
+ Collections.reverse(encodings); // Set the right order for encoding
+ String encoded = encode(encodings, decoded_content, helpers);
+
+ Tools.editMessageParam(
+ helpers,
+ decode_target,
+ from,
+ ((Operation_API) imported_api).message,
+ ((Operation_API) imported_api).is_request,
+ encoded,
+ true);
+
+ // the previous function should already have updated the message inside api
+ return ((Operation_API) imported_api);
+ }
+
+ /**
+ * Executes this decode operation
+ *
+ * @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,
+ ((Operation_API) imported_api).is_request,
+ decode_target);
+
+ } else if (imported_api instanceof DecodeOperation_API) {
+ switch (from) {
+ case JWT_HEADER:
+ case JWT_PAYLOAD:
+ case JWT_SIGNATURE:
+ // recursevly decode from a jwt
+ String j = ((DecodeOperation_API) imported_api).getDecodedContent(from);
+
+ String found = "";
+ // https://github.com/json-path/JsonPath
+ try {
+ found = JsonPath.read(j, what); // select what to decode
+ } catch (com.jayway.jsonpath.PathNotFoundException e) {
+ applicable = false;
+ result = false;
+ return;
+ }
+ decoded_content = decode(encodings, found, helpers);
+ break;
+ default:
+ throw new UnsupportedOperationException(
+ "the from you selected in the recursive decode operation is not yet supported");
+ //TODO implement missing
+ }
+ }
+
+ // If type is jwt, parse
+ if (Objects.requireNonNull(type) == DecodeOpType.JWT) {
+ jwt.parse(decoded_content);
+ if (check_jwt) {
+ if (!jwt.check_sig()) {
+ applicable = true;
+ result = false;
+ return;
+ }
+ }
+ }
+
+ // execute edit operations
+ if (editOperations.size() > 0) {
+ executeEditOps(this, vars);
+ }
+
+ // executes recursive decode operations
+ if (decodeOperations.size() != 0) {
+ executeDecodeOps(this, helpers, vars);
+ }
+
+ // execute checks
+ if (checks.size() != 0) {
+ executeChecks(vars);
+ }
+
+ // Rebuild JWT before encoding it
+ if (Objects.requireNonNull(type) == DecodeOpType.JWT) {
+ decoded_content = jwt.build();
+ }
+ applicable = true;
+ }
+
+ /**
+ * Execute a list of checks inside a decode operation. This function uses the APIs Sets also the result to the
+ * decode op
+ *
+ * @return the result, for convenience
+ * @throws ParsingException if errors are found
+ */
+ public boolean executeChecks(List vars) throws ParsingException {
+ for (Check c : checks) {
+ c.loader(getAPI());
+ c.execute(vars);
+ if (!setResult(c)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Used in decode operation to specify where to search for the content to decode
+ */
+ public enum DecodeOperationFrom {
+ // standard message
+ HEAD,
+ BODY,
+ URL,
+ // jwt
+ JWT_HEADER,
+ JWT_PAYLOAD,
+ JWT_SIGNATURE;
+
+ public static DecodeOperationFrom fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "head":
+ return HEAD;
+ case "body":
+ return BODY;
+ case "url":
+ return URL;
+ case "jwt header":
+ return JWT_HEADER;
+ case "jwt payload":
+ return JWT_PAYLOAD;
+ case "jwt signature":
+ return JWT_SIGNATURE;
+ default:
+ throw new ParsingException("invalid decode operation from '" + input + "'");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ /**
+ * The possible encodings to be used
+ */
+ public enum Encoding {
+ BASE64,
+ URL,
+ DEFLATE;
+
+ /**
+ * From a string get the corresponding enum value
+ *
+ * @param input the string
+ * @return the enum value
+ * @throws ParsingException if the input is malformed
+ */
+ public static Encoding fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "base64":
+ return BASE64;
+ case "url":
+ return URL;
+ case "deflate":
+ return DEFLATE;
+ default:
+ throw new ParsingException("invalid encoding");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ /**
+ * Used to specify the type of decoded content, only when that content has to be edited.
+ */
+ public enum DecodeOpType {
+ JWT,
+ NONE,
+ XML;
+
+ public static DecodeOpType fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "jwt":
+ return JWT;
+ case "xml":
+ return XML;
+ default:
+ throw new ParsingException("invalid message Op Type");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+}
diff --git a/tool/src/main/java/migt/DecodeOperation_API.java b/tool/src/main/java/migt/DecodeOperation_API.java
new file mode 100644
index 0000000..c95d164
--- /dev/null
+++ b/tool/src/main/java/migt/DecodeOperation_API.java
@@ -0,0 +1,60 @@
+package migt;
+
+public class DecodeOperation_API extends API {
+ public DecodeOperation.DecodeOpType type; // the type of the decoded param
+
+ public JWT jwt; // TODO: use this instead of single strings
+ public String txt;
+ public String xml;
+
+ public DecodeOperation_API() {
+ init();
+ }
+
+ public DecodeOperation_API(DecodeOperation dop) {
+ init();
+ type = dop.type;
+ switch (dop.type) {
+ case NONE:
+ txt = dop.decoded_content;
+ break;
+ case JWT:
+ jwt = dop.jwt;
+ break;
+ case XML:
+ xml = dop.decoded_content;
+ break;
+ }
+ }
+
+ public void init() {
+ jwt = new JWT();
+ }
+
+ public String getDecodedContent(DecodeOperation.DecodeOperationFrom dopfrom) throws ParsingException {
+ switch (dopfrom) {
+ case HEAD:
+ throw new ParsingException("cannot decode from header in a recursive decode");
+ case BODY:
+ throw new ParsingException("cannot decode from body in a recursive decode");
+ case URL:
+ throw new ParsingException("cannot decode from url in a recursive decode");
+ case JWT_HEADER:
+ if (type != DecodeOperation.DecodeOpType.JWT)
+ throw new ParsingException("cannot decode in a jwt header if previous decode was not a jwt");
+ return jwt.header;
+
+ case JWT_PAYLOAD:
+ if (type != DecodeOperation.DecodeOpType.JWT)
+ throw new ParsingException("cannot decode in a jwt payload if previous decode was not a jwt");
+ return jwt.payload;
+
+ case JWT_SIGNATURE:
+ if (type != DecodeOperation.DecodeOpType.JWT)
+ throw new ParsingException("cannot decode in a jwt signature if previous decode was not a jwt");
+ return jwt.signature;
+ default:
+ throw new UnsupportedOperationException("invalid Decode operation from");
+ }
+ }
+}
diff --git a/tool/src/main/java/migt/EditOperation.java b/tool/src/main/java/migt/EditOperation.java
new file mode 100644
index 0000000..7b70872
--- /dev/null
+++ b/tool/src/main/java/migt/EditOperation.java
@@ -0,0 +1,489 @@
+package migt;
+
+import com.jayway.jsonpath.PathNotFoundException;
+import org.json.JSONObject;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+import samlraider.application.SamlTabController;
+import samlraider.helpers.XMLHelpers;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static migt.Tools.getVariableByName;
+
+public class EditOperation extends Module {
+ // XML
+ XmlAction xml_action;
+ String xml_action_name;
+ String xml_tag;
+ String xml_attr;
+ String value;
+ Integer xml_occurrency;
+ Boolean self_sign;
+ Boolean remove_signature;
+ String saml_original_cert;
+ String edited_xml;
+
+ String use; // say which parameter use as value
+ String save_as; // say the name of the parameter to save the value
+
+ // JWT
+ Jwt_section jwt_section;
+ Jwt_action jwt_action;
+ boolean sign;
+ String jwt_private_key_pem;
+
+ String what;
+
+ // TXT
+ TxtAction txt_action;
+ String txt_action_name;
+
+ public EditOperation(JSONObject eop_json) throws ParsingException {
+ init();
+ java.util.Iterator keys = eop_json.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+
+ switch (key) {
+ case "use":
+ use = eop_json.getString("use");
+ break;
+ case "as":
+ save_as = eop_json.getString("as");
+ break;
+ case "value":
+ // value of xml or other edits
+ value = eop_json.getString("value");
+ break;
+ case "add tag":
+ xml_action = XmlAction.ADD_TAG;
+ xml_action_name = eop_json.getString(key);
+ break;
+ case "add attribute":
+ xml_action = XmlAction.ADD_ATTR;
+ xml_action_name = eop_json.getString(key);
+ break;
+ case "edit tag":
+ xml_action = XmlAction.EDIT_TAG;
+ xml_action_name = eop_json.getString(key);
+ break;
+ case "edit attribute":
+ xml_action = XmlAction.EDIT_ATTR;
+ xml_action_name = eop_json.getString(key);
+ break;
+ case "remove tag":
+ xml_action = XmlAction.REMOVE_TAG;
+ xml_action_name = eop_json.getString(key);
+ break;
+ case "remove attribute":
+ xml_action = XmlAction.REMOVE_ATTR;
+ xml_action_name = eop_json.getString(key);
+ break;
+ case "save tag":
+ xml_action = XmlAction.SAVE_TAG;
+ xml_action_name = eop_json.getString(key);
+ break;
+ case "save attribute":
+ xml_action = XmlAction.SAVE_ATTR;
+ xml_action_name = eop_json.getString(key);
+ break;
+ case "self-sign":
+ self_sign = eop_json.getBoolean("self-sign");
+ break;
+ case "remove signature":
+ remove_signature = eop_json.getBoolean("remove signature");
+ break;
+ case "xml tag":
+ xml_tag = eop_json.getString("xml tag");
+ break;
+ case "xml occurrency":
+ xml_occurrency = eop_json.getInt("xml occurrency");
+ break;
+ case "xml attribute":
+ xml_attr = eop_json.getString("xml attribute");
+ break;
+ // JWT
+ case "jwt from":
+ jwt_section = Jwt_section.getFromString(
+ eop_json.getString("jwt from"));
+ break;
+ case "jwt remove":
+ jwt_action = Jwt_action.REMOVE;
+ what = eop_json.getString("jwt remove");
+ break;
+ case "jwt edit":
+ jwt_action = Jwt_action.EDIT;
+ what = eop_json.getString("jwt edit");
+ break;
+ case "jwt add":
+ jwt_action = Jwt_action.ADD;
+ what = eop_json.getString("jwt add");
+ break;
+ case "jwt save":
+ jwt_action = Jwt_action.SAVE;
+ what = eop_json.getString("jwt save");
+ break;
+ case "jwt sign":
+ sign = true;
+ jwt_private_key_pem = eop_json.getString("jwt sign");
+ break;
+
+ case "txt remove":
+ txt_action = TxtAction.REMOVE;
+ txt_action_name = eop_json.getString("txt remove");
+ break;
+ case "txt edit":
+ txt_action = TxtAction.EDIT;
+ txt_action_name = eop_json.getString("txt edit");
+ break;
+ case "txt add":
+ txt_action = TxtAction.ADD;
+ txt_action_name = eop_json.getString("txt add");
+ break;
+ case "txt save":
+ txt_action = TxtAction.SAVE;
+ txt_action_name = eop_json.getString("txt save");
+ break;
+ }
+ }
+ }
+
+ public void init() {
+ use = "";
+ save_as = "";
+ xml_action_name = "";
+ xml_tag = "";
+ xml_attr = "";
+ value = "";
+ self_sign = false;
+ remove_signature = false;
+ saml_original_cert = "";
+ edited_xml = "";
+ sign = false;
+ txt_action_name = "";
+ what = "";
+ }
+
+ public void loader(DecodeOperation_API dop_api) {
+ // TODO
+ if (dop_api == null) {
+ throw new RuntimeException("loaded api is null");
+ }
+ this.imported_api = dop_api;
+ }
+
+ public DecodeOperation_API exporter() {
+ return (DecodeOperation_API) imported_api;
+ }
+
+ public void execute(List vars) throws ParsingException {
+ if (imported_api instanceof DecodeOperation_API) {
+ // the edit operation is being executed inside a Decode Operation
+ // If a variable value has to be used, read the value of the variable at execution time
+ if (!use.equals("")) {
+ Var v = getVariableByName(use, vars);
+ if (!v.isMessage) {
+ value = v.value;
+ } else {
+ throw new ParsingException("Error while using variable, expected text var, got message var");
+ }
+ }
+
+ DecodeOperation_API tmp_imported_api = (DecodeOperation_API) imported_api;
+
+ switch (((DecodeOperation_API) imported_api).type) {
+ case XML:
+ //SAML Remove signatures
+ if (self_sign | remove_signature) {
+ Document document = null;
+ try {
+ XMLHelpers xmlHelpers = new XMLHelpers();
+ document = xmlHelpers.getXMLDocumentOfSAMLMessage(((DecodeOperation_API) imported_api).xml);
+ saml_original_cert = xmlHelpers.getCertificate(document.getDocumentElement());
+ if (saml_original_cert == null) {
+ System.out.println("SAML Certificate not found in decoded parameter");
+ applicable = false;
+ }
+ edited_xml = SamlTabController.removeSignature_edit(((DecodeOperation_API) imported_api).xml);
+ } catch (SAXException e) {
+ e.printStackTrace();
+ }
+ }
+
+ switch (xml_action) {
+ case ADD_TAG:
+ edited_xml = XML.addTag(edited_xml,
+ xml_tag,
+ xml_action_name,
+ value,
+ xml_occurrency);
+ break;
+ case ADD_ATTR:
+ edited_xml = XML.addTagAttribute(edited_xml,
+ xml_tag,
+ xml_action_name,
+ value,
+ xml_occurrency);
+ break;
+ case EDIT_TAG:
+ edited_xml = XML.editTagValue(edited_xml,
+ xml_action_name,
+ value,
+ xml_occurrency);
+ break;
+ case EDIT_ATTR:
+ edited_xml = XML.editTagAttributes(edited_xml,
+ xml_tag,
+ xml_action_name,
+ value,
+ xml_occurrency);
+ break;
+ case REMOVE_TAG:
+ edited_xml = XML.removeTag(edited_xml,
+ xml_action_name,
+ xml_occurrency);
+ break;
+ case REMOVE_ATTR:
+ edited_xml = XML.removeTagAttribute(edited_xml,
+ xml_tag,
+ xml_action_name,
+ xml_occurrency);
+ break;
+ case SAVE_TAG: {
+ String to_save = XML.getTagValaue(edited_xml,
+ xml_action_name,
+ xml_occurrency);
+ Var v = new Var();
+ v.name = save_as;
+ v.isMessage = false;
+ v.value = to_save;
+ vars.add(v);
+ break;
+ }
+ case SAVE_ATTR:
+ String to_save = XML.getTagAttributeValue(edited_xml,
+ xml_tag, xml_action_name,
+ xml_occurrency);
+ Var v = new Var();
+ v.name = save_as;
+ v.isMessage = false;
+ v.value = to_save;
+ vars.add(v);
+ break;
+ }
+
+ if (self_sign && !edited_xml.equals("")) {
+ // SAML re-sign
+ edited_xml = SamlTabController.resignAssertion_edit(edited_xml, saml_original_cert);
+ }
+
+ tmp_imported_api.xml = edited_xml;
+ applicable = true;
+ break;
+
+ case JWT:
+ if (jwt_section != null) { // if only sign, there will be no jwt section
+ try {
+ switch (jwt_section) {
+ case HEADER:
+ tmp_imported_api.jwt.header = Tools.editJson(
+ jwt_action, tmp_imported_api.jwt.header, what, vars, save_as, value);
+ break;
+ case PAYLOAD:
+ // TODO: pass newvalue
+ tmp_imported_api.jwt.payload = Tools.editJson(
+ jwt_action, tmp_imported_api.jwt.payload, what, vars, save_as, value);
+ break;
+ case SIGNATURE:
+ tmp_imported_api.jwt.signature = Tools.editJson(
+ jwt_action, tmp_imported_api.jwt.signature, what, vars, save_as, value);
+ break;
+ }
+ } catch (PathNotFoundException e) {
+ this.applicable = false;
+ this.result = false;
+ return;
+ }
+ }
+ applicable = true;
+
+ if (sign) {
+ tmp_imported_api.jwt.sign = true;
+ tmp_imported_api.jwt.private_key_pem = jwt_private_key_pem;
+ }
+
+ break;
+
+ case NONE:
+ Pattern p = Pattern.compile(txt_action_name);
+ Matcher m = p.matcher(tmp_imported_api.txt);
+
+ if (txt_action == null) {
+ throw new ParsingException("txt action not specified");
+ }
+
+ switch (txt_action) {
+ case REMOVE:
+ tmp_imported_api.txt = m.replaceAll("");
+
+ break;
+ case EDIT:
+ tmp_imported_api.txt = m.replaceAll(value);
+
+ break;
+ case ADD:
+ while (m.find()) {
+ int index = m.end();
+ String before = tmp_imported_api.txt.substring(0, index);
+ String after = tmp_imported_api.txt.substring(index);
+ tmp_imported_api.txt = before + value + after;
+ break;
+ }
+ break;
+ case SAVE:
+ String val = "";
+ while (m.find()) {
+ val = m.group();
+ break;
+ }
+
+ Var v = new Var();
+ v.name = save_as;
+ v.isMessage = false;
+ v.value = val;
+ vars.add(v);
+ break;
+ }
+ applicable = true;
+ break;
+ }
+ imported_api = tmp_imported_api;
+ }
+ }
+
+ /**
+ * The possible XML actions are the ones described in this enum
+ */
+ public enum XmlAction {
+ ADD_TAG,
+ ADD_ATTR,
+ EDIT_TAG,
+ EDIT_ATTR,
+ REMOVE_TAG,
+ REMOVE_ATTR,
+ SAVE_TAG,
+ SAVE_ATTR;
+
+ /**
+ * From a string get the corresponding value
+ *
+ * @param input the input string
+ * @return the enum value
+ * @throws ParsingException if the string does not correspond to any of the values
+ */
+ public static XmlAction fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "add tag":
+ return ADD_TAG;
+ case "add attribute":
+ return ADD_ATTR;
+ case "edit tag":
+ return EDIT_TAG;
+ case "edit attribute":
+ return EDIT_ATTR;
+ case "remove tag":
+ return REMOVE_TAG;
+ case "remove attribute":
+ return REMOVE_ATTR;
+ case "save tag":
+ return SAVE_TAG;
+ case "save attribute":
+ return SAVE_ATTR;
+ default:
+ throw new ParsingException("invalid xml action");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ /**
+ * Defines the possible actions to be done on a decoded parameter interpreted as plain text
+ */
+ public enum TxtAction {
+ REMOVE,
+ EDIT,
+ ADD,
+ SAVE;
+
+ /**
+ * From a string get the corresponding value
+ *
+ * @param input the input string
+ * @return the enum value
+ * @throws ParsingException if the string does not correspond to any of the values
+ */
+ public static TxtAction fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "txt remove":
+ return REMOVE;
+ case "txt edit":
+ return EDIT;
+ case "txt add":
+ return ADD;
+ case "txt save":
+ return SAVE;
+ default:
+ throw new ParsingException("invalid xml action");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ /**
+ * Defines the possible actions to be done on a JWT token
+ */
+ public enum Jwt_action {
+ REMOVE,
+ EDIT,
+ ADD,
+ SAVE
+ }
+
+ /**
+ * Defines the possible JWT token sections
+ */
+ public enum Jwt_section {
+ HEADER,
+ PAYLOAD,
+ SIGNATURE;
+
+ /**
+ * Get the JWT section enum value from a string
+ *
+ * @param s the string to parse
+ * @return the enum value
+ * @throws ParsingException if the string is invalid
+ */
+ public static Jwt_section getFromString(String s) throws ParsingException {
+ switch (s) {
+ case "header":
+ return HEADER;
+ case "payload":
+ return PAYLOAD;
+ case "signature":
+ return SIGNATURE;
+ default:
+ throw new ParsingException("Invalid jwt section");
+ }
+ }
+ }
+}
diff --git a/tool/src/main/java/burp/ExecuteActiveListener.java b/tool/src/main/java/migt/ExecuteActiveListener.java
similarity index 69%
rename from tool/src/main/java/burp/ExecuteActiveListener.java
rename to tool/src/main/java/migt/ExecuteActiveListener.java
index e5f9a96..3487a4c 100644
--- a/tool/src/main/java/burp/ExecuteActiveListener.java
+++ b/tool/src/main/java/migt/ExecuteActiveListener.java
@@ -1,6 +1,4 @@
-package burp;
-
-import java.util.List;
+package migt;
/**
* Listener class for ExecuteActive class
@@ -44,39 +42,22 @@ public interface ExecuteActiveListener {
/**
* This method is called during an ExecuteActive execution, when a new test is being executed
+ *
* @param actual_test The test that was being executed
*/
void onNewTest(Test actual_test);
/**
* This method is called during an ExecuteActive execution, when a test has finished
+ *
* @param actual_test The test that was being executed
*/
void onTestDone(Test actual_test);
/**
* This method is called whether an error occurs during the execution of the active tests
+ *
* @param actual_test The test that was being executed
*/
void onError(Test actual_test);
-
- /**
- * This method is called before the execution of the SessionOperations in a test. It is used to update the list
- * of variables with the thread
- * @return the updated list of variables
- */
- List onBeforeExSessionOps();
-
- /**
- * This method is called after the execution of the SessionOperations in a test. It is used to updated the list of
- * variables with the thread
- * @param re the list of variables to update
- */
- void onAfterExSessionOps(List re);
-
- /**
- * This method is called when a variable has to been set during the test
- * @param v the new set variable
- */
- void onAddVar(Var v);
}
diff --git a/tool/src/main/java/burp/ExecuteActives.java b/tool/src/main/java/migt/ExecuteActives.java
similarity index 89%
rename from tool/src/main/java/burp/ExecuteActives.java
rename to tool/src/main/java/migt/ExecuteActives.java
index 7d5f9d7..6ef2ff7 100644
--- a/tool/src/main/java/burp/ExecuteActives.java
+++ b/tool/src/main/java/migt/ExecuteActives.java
@@ -1,4 +1,4 @@
-package burp;
+package migt;
import java.util.HashMap;
import java.util.List;
@@ -69,7 +69,7 @@ public void run() {
case START: {
Session selected = actual_test.getSession(op.getSession());
if (selected == null) {
- actual_test.error_srt = "Invalid session name, maybe you didn't declare it?";
+ actual_test.error_str = "Invalid session name, maybe you didn't declare it?";
actual_test.error = true;
break;
}
@@ -79,14 +79,13 @@ public void onExecuteDone(boolean errors, String current_url, String sessionName
if (actual_test.resultSession.equals("") ||
actual_test.resultSession.equals(sessionName)) {
- if (actual_test.result == Utils.ResultType.CORRECT_FLOW) {
+ if (actual_test.result == Test.ResultType.CORRECT_FLOW) {
if (errors || current_url.contains("error")) {
actual_test.success = false;
}
- } else if (actual_test.result == Utils.ResultType.INCORRECT_FLOW) {
+ } else if (actual_test.result == Test.ResultType.INCORRECT_FLOW) {
actual_test.success = errors; // Difficult to read
- } else if (actual_test.result == Utils.ResultType.ASSERT_ONLY) {
- actual_test.success = true;
+ } else if (actual_test.result == Test.ResultType.ASSERT_ONLY) {
//at this point, all the asserts have been executed, and if they failed
// they already returned a false result
}
@@ -172,7 +171,7 @@ public Track onUpdateTrack(String sessionName) throws ParsingException {
@Override
public void onSetVar(Var v) {
- listener.onAddVar(v);
+ actual_test.vars.add(v);
}
});
@@ -195,7 +194,7 @@ public void onSetVar(Var v) {
case STOP: {
Session selected = actual_test.getSession(op.getSession());
if (selected == null) {
- actual_test.error_srt = "Invalid session name, maybe you didn't declare it?";
+ actual_test.error_str = "Invalid session name, maybe you didn't declare it?";
actual_test.error = true;
break;
}
@@ -206,7 +205,7 @@ public void onSetVar(Var v) {
case CLEAR_COOKIES:
Session selected = actual_test.getSession(op.getSession());
if (selected == null) {
- actual_test.error_srt = "Invalid session name, maybe you didn't declare it?";
+ actual_test.error_str = "Invalid session name, maybe you didn't declare it?";
actual_test.error = true;
break;
}
@@ -214,10 +213,9 @@ public void onSetVar(Var v) {
break;
}
- List act_vars = listener.onBeforeExSessionOps();
- Object[] res = Utils.executeSessionOps(actual_test, op, act_vars);
- op = (Operation) res[0];
- listener.onAfterExSessionOps((List) res[1]);
+ List act_vars = actual_test.vars;
+ List updated_vars = op.executeSessionOps(actual_test, act_vars);
+ actual_test.vars = updated_vars;
} else {
//if it is a normal operation
@@ -230,6 +228,12 @@ public void onSetVar(Var v) {
op.session_port = "8080";
}
+ if (op.api == null) {
+ op.api = new Operation_API(actual_test.vars);
+ } else {
+ op.api.vars = actual_test.vars;
+ }
+
listener.onNewProcessOperation(op);
synchronized (this.waiting) {
@@ -242,23 +246,13 @@ public void onSetVar(Var v) {
op = listener.onOperationDone(); // Take the operation from the caller
- List act_vars = listener.onBeforeExSessionOps();
- if (act_vars.size() == 0) {
- try {
- Thread.sleep(500);
- act_vars = listener.onBeforeExSessionOps();
- } catch (InterruptedException e) {
- }
- }
-
- Object[] res = Utils.executeSessionOps(actual_test, op, act_vars);
- op = (Operation) res[0];
- listener.onAfterExSessionOps((List) res[1]);
+ actual_test.vars = op.api.vars;
+ actual_test.vars = op.executeSessionOps(actual_test, actual_test.vars);
if (op.applicable) {
- actual_test.success = op.passed;
+ actual_test.success = op.result;
actual_test.applicable = true;
- if (!op.passed) {
+ if (!op.result) {
for (String key : executions.keySet()) {
executions.get(key).interrupt();
}
diff --git a/tool/src/main/java/burp/ExecutePassiveListener.java b/tool/src/main/java/migt/ExecutePassiveListener.java
similarity index 97%
rename from tool/src/main/java/burp/ExecutePassiveListener.java
rename to tool/src/main/java/migt/ExecutePassiveListener.java
index 3eeac4b..dbb4652 100644
--- a/tool/src/main/java/burp/ExecutePassiveListener.java
+++ b/tool/src/main/java/migt/ExecutePassiveListener.java
@@ -1,4 +1,4 @@
-package burp;
+package migt;
import java.util.ArrayList;
import java.util.List;
@@ -10,6 +10,7 @@ public interface ExecutePassiveListener {
/**
* This method is called when the ExecutePassive thread is started, it is used to tell when the thread should start
* executing the passives test. if it returns true, the thread will start
+ *
* @return true if you want the thread to start
*/
boolean onWaitToStart();
@@ -21,12 +22,14 @@ public interface ExecutePassiveListener {
/**
* Called when the ExecutePassives thread has ended
+ *
* @param passives_test The list of executed passive tests
*/
void onExecuteDone(List passives_test);
/**
* Called when there is an error in the execution
+ *
* @param msg the error message
*/
void onError(String msg);
@@ -34,6 +37,7 @@ public interface ExecutePassiveListener {
/**
* When a new session has to be executed this method is called. This is thought to fill the session with
* the right values from the GUI class.
+ *
* @param s the session to be initiated
* @return the session with the filled values
*/
diff --git a/tool/src/main/java/burp/ExecutePassives.java b/tool/src/main/java/migt/ExecutePassives.java
similarity index 88%
rename from tool/src/main/java/burp/ExecutePassives.java
rename to tool/src/main/java/migt/ExecutePassives.java
index 1483cf9..411832b 100644
--- a/tool/src/main/java/burp/ExecutePassives.java
+++ b/tool/src/main/java/migt/ExecutePassives.java
@@ -1,4 +1,6 @@
-package burp;
+package migt;
+
+import burp.IExtensionHelpers;
import java.util.ArrayList;
import java.util.HashMap;
@@ -11,20 +13,21 @@
* @author Matteo Bitussi
*/
public class ExecutePassives implements Runnable {
+ final Object lock = new Object();
public List passives;
IExtensionHelpers helpers;
ExecutePassiveListener listener;
List messageTypes;
- final Object lock = new Object();
boolean finished;
boolean execution_error;
/**
* Used to instantiate an ExecutePassives object
- * @param helpers IExtensionHelpers instance of Burp
+ *
+ * @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
+ * @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,
@@ -58,7 +61,7 @@ public void run() {
HashMap> batch = null;
try {
- batch = Utils.batchPassivesFromSession(passives);
+ batch = Tools.batchPassivesFromSession(passives);
} catch (ParsingException e) {
e.printStackTrace();
//lblOutput.setText(e.getMessage());
@@ -86,7 +89,7 @@ public void run() {
executedSession = executePassiveTestSession(act_session);
for (Test t : actual_batch) {
// TODO: limit one session for active tests
- t.sessions.set(0,executedSession);
+ t.sessions.set(0, executedSession);
}
batch.put(sessionName, actual_batch);
if (execution_error) {
@@ -98,11 +101,16 @@ public void run() {
for (Test actual_test : actual_batch) {
System.out.println("Actual test name: " + actual_test.getName());
- boolean res = Tools.executePassiveTest(
- actual_test,
- executedSession.messages,
- helpers,
- messageTypes);
+ boolean res = false;
+ try {
+ res = Tools.executePassiveTest(
+ actual_test,
+ executedSession.messages,
+ helpers,
+ messageTypes);
+ } catch (ParsingException e) {
+ actual_test.applicable = false;
+ }
System.out.println("Actual test result: " + res);
actual_test.success = res;
@@ -110,13 +118,14 @@ public void run() {
// TODO: Remove used session
executedSession = null;
}
- passives = Utils.debatchPassive(batch);
+ passives = Tools.debatchPassive(batch);
listener.onExecuteDone(passives);
}
/**
* Executes a passive test's session, to gather messages needed to execute the passive
* tests.
+ *
* @param session the session to be executed
* @return the same session, executed, that will contain the intercepted messages
*/
diff --git a/tool/src/main/java/burp/ExecuteTrack.java b/tool/src/main/java/migt/ExecuteTrack.java
similarity index 93%
rename from tool/src/main/java/burp/ExecuteTrack.java
rename to tool/src/main/java/migt/ExecuteTrack.java
index aa7b042..4094273 100644
--- a/tool/src/main/java/burp/ExecuteTrack.java
+++ b/tool/src/main/java/migt/ExecuteTrack.java
@@ -1,4 +1,4 @@
-package burp;
+package migt;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
@@ -33,12 +33,13 @@ public class ExecuteTrack implements Runnable {
/**
* Instantiate the ExecuteTrack Object
+ *
* @param isHeadless
* @param chrome_selected tells if chrome is selected as a browser to be used, otherwise firefox is used
- * @param driver_path the path to the chosen browser's driver
- * @param track the track to be executed
- * @param port the port set in the browser that the HTTP(S) proxy is listening to
- * @param sessionName The name of the session to be executed
+ * @param driver_path the path to the chosen browser's driver
+ * @param track the track to be executed
+ * @param port the port set in the browser that the HTTP(S) proxy is listening to
+ * @param sessionName The name of the session to be executed
*/
public ExecuteTrack(boolean isHeadless,
boolean chrome_selected,
@@ -76,6 +77,7 @@ public void run() {
options.addArguments("ignore-certificate-errors");
options.addArguments("window-size=1280,1400");
options.addArguments("--proxy-bypass-list=<-loopback>");
+ options.addArguments("--remote-allow-origins=*");
Proxy proxy = new Proxy();
proxy.setHttpProxy("localhost:" + port);
proxy.setSslProxy("localhost:" + port);
@@ -130,10 +132,10 @@ public void run() {
} catch (WebDriverException e) {
}
- if (track.getTrack().get(i).action == Utils.SessAction.CLICK) {
+ if (track.getTrack().get(i).action == SessionOperation.SessAction.CLICK) {
last_click = track.getTrack().get(i);
}
- if (track.getTrack().get(i).action == Utils.SessAction.OPEN) {
+ if (track.getTrack().get(i).action == SessionOperation.SessAction.OPEN) {
last_open = track.getTrack().get(i);
}
listener.onNextSessionAction(last_action, last_open, last_click, last_url, sessionName);
@@ -178,15 +180,14 @@ public void run() {
continue;
}
case ALERT: {
- if (action.elem != null ) {
+ if (action.elem != null) {
Alert alert = null;
- int c=0;
- while(c++<10) {
+ int c = 0;
+ while (c++ < 10) {
try {
alert = driver.switchTo().alert();
break;
- }
- catch(NoAlertPresentException e) {
+ } catch (NoAlertPresentException e) {
Thread.sleep(1000);
continue;
}
@@ -229,16 +230,15 @@ public void run() {
case ASSERT_ELEM_CLASS_HAS:
case ASSERT_ELEM_CLASS_IS:
case ASSERT_ELEM_HAS_ATTRIBUTE:
- case ASSERT_ELEM_NOT_HAS_ATTRIBUTE:
- {
+ case ASSERT_ELEM_NOT_HAS_ATTRIBUTE: {
String searchBy = action.elem_type;
String identifier = action.elem_source;
while (windows_checked != windows_count) {
try {
- boolean is_snapshot = action.action == Utils.SessAction.SNAPSHOT ||
- action.action == Utils.SessAction.DIFF ||
- action.action == Utils.SessAction.EQUALS;
+ boolean is_snapshot = action.action == SessionOperation.SessAction.SNAPSHOT ||
+ action.action == SessionOperation.SessAction.DIFF ||
+ action.action == SessionOperation.SessAction.EQUALS;
By by = null;
// Checks for the presence of a valid item to search
@@ -262,7 +262,7 @@ public void run() {
throw new ParsingException("invalid session track command");
}
- if (action.action == Utils.SessAction.ASSERT_VISIBLE) {
+ if (action.action == SessionOperation.SessAction.ASSERT_VISIBLE) {
new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)).until(
ExpectedConditions.visibilityOfElementLocated(by));
} else if (is_snapshot) {
@@ -291,8 +291,8 @@ public void run() {
if (currentElement != null) break;
}
if (currentElement == null) {
- if (action.action == Utils.SessAction.ASSERT_CLICKABLE
- || action.action == Utils.SessAction.ASSERT_VISIBLE) {
+ if (action.action == SessionOperation.SessAction.ASSERT_CLICKABLE
+ || action.action == SessionOperation.SessAction.ASSERT_VISIBLE) {
listener.onExecuteDone(false, sessionName);
driver.close();
return;
@@ -334,7 +334,7 @@ public void run() {
String diff = currentElement.getScreenshotAs(OutputType.BASE64);
File f2 = currentElement.getScreenshotAs(OutputType.FILE);
f2.renameTo(new File("./diff.png"));
- if (action.action == Utils.SessAction.DIFF) {
+ if (action.action == SessionOperation.SessAction.DIFF) {
if (diff.equals(snapshot)) {
listener.onExecuteDone(true, current_url, sessionName);
driver.close();
diff --git a/tool/src/main/java/burp/ExecuteTrackListener.java b/tool/src/main/java/migt/ExecuteTrackListener.java
similarity index 90%
rename from tool/src/main/java/burp/ExecuteTrackListener.java
rename to tool/src/main/java/migt/ExecuteTrackListener.java
index 6cfba95..df85888 100644
--- a/tool/src/main/java/burp/ExecuteTrackListener.java
+++ b/tool/src/main/java/migt/ExecuteTrackListener.java
@@ -1,4 +1,4 @@
-package burp;
+package migt;
/**
* Listener for the ExectuteTrack Object
@@ -44,6 +44,7 @@ public interface ExecuteTrackListener {
/**
* The thread asks if it has to stop the execution, the listener responds
+ *
* @param sessionName The name of the session that is executing
* @return true if the execution should stop
*/
@@ -51,6 +52,7 @@ public interface ExecuteTrackListener {
/**
* The thread asks if it has to clear the cookies, the listener responds
+ *
* @param sessionName The name of the session that is executing
* @return true if the browser that is executing should clear the cookies
*/
@@ -59,10 +61,11 @@ public interface ExecuteTrackListener {
/**
* Called whether a new session action is executed. This is used to update the listener on the actions that are
* being executed
- * @param last_action the last User action executed at this point
- * @param last_open the last open User action executed to this point
- * @param last_click the last click User action executed to this point
- * @param last_url the last url User action executed to this point
+ *
+ * @param last_action the last User action executed at this point
+ * @param last_open the last open User action executed to this point
+ * @param last_click the last click User action executed to this point
+ * @param last_url the last url User action executed to this point
* @param session_name the name of the session that is executing
* @throws ParsingException If problems are encounter retrieving these parameters
*/
@@ -74,6 +77,7 @@ void onNextSessionAction(SessionTrackAction last_action,
/**
* With this method is possible to update the Session track during execution
+ *
* @param sessionName the name of the session that is executing
* @return the updated track
* @throws ParsingException if problems are encountered in updating the session track
@@ -82,6 +86,7 @@ void onNextSessionAction(SessionTrackAction last_action,
/**
* Called whether a variable is set using a User action
+ *
* @param v the variable that has been set
*/
void onSetVar(Var v);
diff --git a/tool/src/main/java/burp/GUI.java b/tool/src/main/java/migt/GUI.java
similarity index 66%
rename from tool/src/main/java/burp/GUI.java
rename to tool/src/main/java/migt/GUI.java
index 3c3af32..ef01a0d 100644
--- a/tool/src/main/java/burp/GUI.java
+++ b/tool/src/main/java/migt/GUI.java
@@ -1,5 +1,6 @@
-package burp;
+package migt;
+import burp.*;
import com.google.gson.Gson;
import org.json.JSONArray;
import org.json.JSONException;
@@ -16,6 +17,30 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+/**
+ * Sets a JTextArea as an output of an OutputStream, used by the redirect of the stdout and stderr
+ */
+class CustomOutputStream extends OutputStream {
+ private final JTextArea textArea;
+
+ public CustomOutputStream(JTextArea textArea) {
+ this.textArea = textArea;
+ }
+
+ @Override
+ public void write(int b) {
+ // Redirect the byte written to the OutputStream to the JTextArea
+ retry:
+ try {
+ textArea.append(String.valueOf((char) b));
+ textArea.setCaretPosition(textArea.getDocument().getLength()); // Auto-scroll to the bottom
+ } catch (java.lang.Error e) {
+ // for some reason write lock acquire is interrupted sometimes
+ break retry; // TODO: fix
+ }
+ }
+}
+
/**
* This class contains the GUI for the plugin, also a lot of functionality methods
*
@@ -23,12 +48,9 @@
* @author Wendy Barreto
*/
public class GUI extends JSplitPane {
-
private static DefaultTableModel resultTableModel;
private static DefaultTableModel testTableModel;
- public final ArrayList interceptedMessages;
final Object waiting = new Object();
- final Object lock = new Object();
final String LOG_FOLDER = "logs/";
private final String[] foundTableColNames = {"Op. num", "Message Type", "message section", "check/regex", "index", "result"};
private final String[] testSuiteColNames = {
@@ -40,9 +62,8 @@ public class GUI extends JSplitPane {
"Mitigations",
"Result"};
private final Object[][] foundData = {};
- private List passives;
- private final List actives;
- private final Map sessions_text;
+ private final Object lock2 = new Object();
+ public ArrayList interceptedMessages;
//GUI
JTable resultTable;
JTable testTable;
@@ -66,6 +87,8 @@ public class GUI extends JSplitPane {
JTextArea txtScript;
JTextArea txtSearch;
JTextArea txtSessionConfig;
+ JTextArea txt_out_debug_tab;
+ JTextArea txt_err_debug_tab;
JFileChooser driverSelector;
JSplitPane splitPane;
IMessageEditor messageViewer;
@@ -91,11 +114,12 @@ public class GUI extends JSplitPane {
String CONFIG_FILE_PATH = "config.json";
Operation act_active_op;
ExecuteActives ex;
- List act_test_vars;
List messageTypes;
+ private List actives;
+ private Map sessions_text;
+ private List passives;
private String DRIVER_PATH = "";
private Thread active_ex;
- private Object lock2 = new Object();
private boolean active_ex_finished = false;
/**
@@ -103,1926 +127,1608 @@ public class GUI extends JSplitPane {
*/
public GUI() {
super(JSplitPane.VERTICAL_SPLIT);
-
//initialize vars
- interceptedMessages = new ArrayList<>();
- testSuite = new TestSuite();
- passives = new ArrayList<>();
- actives = new ArrayList<>();
- sessions_names = new ArrayList<>();
- ACTIVE_ENABLED = false;
- act_test_vars = new ArrayList<>();
- sessions_text = new HashMap<>();
- messageTypes = new ArrayList<>();
- session_port = new HashMap<>();
- bot_tabs_index = new HashMap<>();
+ init();
+
+ set_std_out_redirect();
this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
// Top part of the UI ------------------------------------------------------------------------------------------
- GridBagLayout gridBagLayout = new GridBagLayout();
- gridBagLayout.columnWidths = new int[]{230, 230, 230, 230, 100, 100, 100};
- gridBagLayout.rowHeights = new int[]{20, 48, 48, 48, 48};
- gridBagLayout.columnWeights = new double[]{1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0};
- gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0};
+ GridBagLayout top_layout = new GridBagLayout();
+ top_layout.columnWidths = new int[]{230, 230, 230, 230, 100, 100, 100};
+ top_layout.rowHeights = new int[]{20, 48, 48, 48, 48};
+ top_layout.columnWeights = new double[]{1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0};
+ top_layout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0};
trackContainer = new JPanel();
- trackContainer.setLayout(gridBagLayout);
+ trackContainer.setLayout(top_layout);
- lblTrack = new JLabel("Session track ");
- GridBagConstraints gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.insets = new Insets(10, 0, 0, 0);
- gbc.gridx = 0;
- gbc.gridy = 0;
- trackContainer.add(lblTrack, gbc);
+ setup_tab_track();
+ setup_tab_butons();
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.EAST;
- gbc.insets = new Insets(10, 0, 0, 0);
- gbc.gridx = 1;
- gbc.gridy = 0;
- trackContainer.add(new JLabel("Download Driver for your browser"), gbc);
+ this.setTopComponent(trackContainer);
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(10, 0, 0, 0);
- gbc.gridx = 2;
- gbc.gridy = 0;
- trackContainer.add(new JLabel(" https://www.selenium.dev/downloads/"), gbc);
+ // Bottom part -------------------------------------------------------------------------------------------------
- txtScript = new JTextArea();
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.fill = GridBagConstraints.BOTH;
- gbc.insets = new Insets(10, 0, 0, 10);
- gbc.gridx = 0;
- gbc.gridy = 1;
- gbc.gridwidth = 4;
- gbc.gridheight = 4;
+ GridBagLayout bot_layout = new GridBagLayout();
+ bot_layout.columnWidths = new int[]{500, 500, 100};
+ bot_layout.rowHeights = new int[]{15, 20, 20, 20, 30};
+ bot_layout.columnWeights = new double[]{Double.MIN_VALUE, 0.0};
+ bot_layout.rowWeights = new double[]{0.0, Double.MIN_VALUE, 0.0, 0.0, 0.0};
- top_tabbed = new JTabbedPane();
+ bot_tabbed = new JTabbedPane();
- JScrollPane scrollPane1 = new JScrollPane(txtScript,
- JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
- JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
- top_tabbed.add("main", scrollPane1);
- trackContainer.add(top_tabbed, gbc);
+ JPanel tab_input_json = setup_tab_input_json(bot_layout);
+ bot_tabs_index.put("Input JSON", 0);
+ bot_tabbed.addTab("Input JSON", tab_input_json);
- driverSelector = new JFileChooser();
- btndriverSelector = new JButton("Select Driver");
+ JScrollPane tab_suite_result = setup_tab_suite_result(bot_layout);
+ bot_tabs_index.put("Test Suite Result", 1);
+ bot_tabbed.addTab("Test Suite Result", tab_suite_result);
- btndriverSelector.addActionListener(actionEvent -> {
- int returnVal = driverSelector.showOpenDialog(GUI.this);
- if (returnVal == JFileChooser.APPROVE_OPTION) {
- File file = driverSelector.getSelectedFile();
- DRIVER_PATH = file.getPath();
- editConfigFile("last_driver_path", DRIVER_PATH);
- lbldriver.setText("Driver Selected");
- btndriverSelector.setBackground(Color.GREEN);
- btnTestTrack.setEnabled(true);
- } else if ((returnVal == JFileChooser.ERROR) || (returnVal == JFileChooser.ERROR_OPTION)) {
- lbldriver.setText("Driver:error during file selection");
- System.out.println("error during file selection");
- btnTestTrack.setEnabled(false);
+ JSplitPane tab_test_result = setup_tab_test_result(bot_layout);
+ bot_tabs_index.put("Test Result", 2);
+ bot_tabbed.addTab("Test Result", tab_test_result);
- btndriverSelector.setBackground(Color.RED);
- } else {
- lbldriver.setText("Driver file still not selected");
- btnTestTrack.setEnabled(false);
- btndriverSelector.setBackground(Color.RED);
- }
- });
+ JPanel tab_session_config = setup_tab_session_config(bot_layout);
+ bot_tabs_index.put("session config", 3);
+ bot_tabbed.addTab("session config", tab_session_config);
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(0, 0, 0, 10);
- gbc.gridx = 4;
- gbc.gridy = 1;
- trackContainer.add(btndriverSelector, gbc);
+ JPanel tab_debug_panel = setub_tab_debug(bot_layout);
+ bot_tabs_index.put("debug tab", 4);
+ bot_tabbed.addTab("debug tab", tab_debug_panel);
- lbldriver = new JLabel("Driver file still not selected");
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.SOUTHEAST;
- gbc.insets = new Insets(10, 0, 0, 30);
- gbc.gridx = 3;
- gbc.gridy = 0;
- trackContainer.add(lbldriver, gbc);
+ //Set Bottom Part
+ this.setBottomComponent(bot_tabbed);
- btnTestTrack = new JButton("Test track");
- btnTestTrack.setEnabled(true);
+ readMsgDefFile();
+ readConfigFile();
+ if (!DRIVER_PATH.equals("")) {
+ lbldriver.setText("Driver Selected");
+ btndriverSelector.setBackground(Color.GREEN);
+ btnTestTrack.setEnabled(true);
+ }
+ }
- btnTestTrack.addActionListener(e -> {
- ExecuteTrackListener listener = new ExecuteTrackListener() {
- @Override
- public void onExecuteDone(boolean errors, String current_url, String sessionName) {
- if (errors) {
- lblOutput.setText("Error in executing track");
- } else {
- lblOutput.setText("Track Executed correctly");
- }
- }
+ /**
+ * Function used to add an item to the resultTableModel. Contains the results of the tests
+ *
+ * @param data the string array containing the data, also a row
+ */
+ private static void addItem(String[] data) {
+ resultTableModel.addRow(data);
+ }
- @Override
- public void onExecuteDone(boolean forceResult, String sessionName) {
- if (forceResult) {
- lblOutput.setText("Track Executed correctly");
- } else {
- lblOutput.setText("Error in executing track");
- }
- }
+ public void init() {
+ interceptedMessages = new ArrayList<>();
+ testSuite = new TestSuite();
+ passives = new ArrayList<>();
+ actives = new ArrayList<>();
+ sessions_names = new ArrayList<>();
+ ACTIVE_ENABLED = false;
+ sessions_text = new HashMap<>();
+ messageTypes = new ArrayList<>();
+ session_port = new HashMap<>();
+ bot_tabs_index = new HashMap<>();
+ txt_out_debug_tab = new JTextArea();
+ txt_err_debug_tab = new JTextArea();
+ }
- @Override
- public void onError(String sessionName) {
- lblOutput.setText("Error in executing track");
- }
+ /**
+ * Set a redirect of the stdout and stderr to the txtboxes in the GUI
+ */
+ private void set_std_out_redirect() {
+ // Stdout out redirect
+ PrintStream printStream = new PrintStream(
+ new CustomOutputStream(txt_out_debug_tab));
+ System.setOut(printStream);
+
+ // stderr out redirect
+ PrintStream printStream_err = new PrintStream(
+ new CustomOutputStream(txt_err_debug_tab));
+ System.setErr(printStream_err);
+ }
- @Override
- public Boolean onAskPause(String sessionName) {
- return false;
+ /**
+ * Function used to read the message definition file
+ */
+ private void readMsgDefFile() {
+ File msg_def_file = new File(MSG_DEF_PATH);
+ try {
+ if (!msg_def_file.createNewFile()) {
+ Scanner myReader = null;
+ String tmp = "";
+ try {
+ myReader = new Scanner(msg_def_file);
+ while (myReader.hasNextLine()) {
+ tmp += myReader.nextLine();
+ }
+ myReader.close();
+ messageTypes = Tools.readMsgTypeFromJson(tmp);
+ } catch (ParsingException e) {
+ lblOutput.setText("Invalid message type in message type definition file");
+ e.printStackTrace();
+ } catch (FileNotFoundException e) {
+ lblOutput.setText("Cannot find message definition file");
}
+ } else {
+ FileWriter w = new FileWriter(MSG_DEF_PATH);
+ w.write(Tools.getDefaultJSONMsgType());
+ w.close();
+ messageTypes = Tools.readMsgTypeFromJson(Tools.getDefaultJSONMsgType());
+ }
+ } catch (ParsingException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ lblOutput.setText("cannot create message definition file");
+ }
+ }
- @Override
- public Boolean onAskStop(String sessionName) {
- return false;
- }
+ /**
+ * Function used to read the json config file
+ */
+ private void readConfigFile() {
+ File config_file = new File(CONFIG_FILE_PATH);
+ try {
+ if (!config_file.createNewFile()) {
+ Scanner myReader = null;
+ String tmp = "";
+ try {
+ myReader = new Scanner(config_file);
+ while (myReader.hasNextLine()) {
+ tmp += myReader.nextLine();
+ }
+ myReader.close();
- @Override
- public Boolean onAskClearCookie(String sessionName) {
- return null;
- }
+ JSONObject obj = new JSONObject(tmp);
+ String last_driver_path = obj.getString("last_driver_path");
+ String last_used_browser = obj.getString("last_browser_used");
- @Override
- public void onNextSessionAction(SessionTrackAction last_action,
- SessionTrackAction last_open,
- SessionTrackAction last_click,
- String last_url,
- String session_name) {
+ if (!last_driver_path.equals("")) {
+ DRIVER_PATH = last_driver_path;
+ }
- }
+ switch (last_used_browser) {
+ case "firefox": {
+ btnselectChrome.setEnabled(true);
+ btnselectFirefox.setEnabled(false);
+ break;
+ }
+ case "chrome": {
+ btnselectChrome.setEnabled(false);
+ btnselectFirefox.setEnabled(true);
+ break;
+ }
+ }
- @Override
- public Track onUpdateTrack(String sessionName) throws ParsingException {
- return null;
+ } catch (JSONException e) {
+ lblOutput.setText("Invalid config file");
+ } catch (FileNotFoundException e) {
+ lblOutput.setText("Cannot find config file");
}
+ } else {
+ FileWriter w = new FileWriter(CONFIG_FILE_PATH);
+ w.write(Tools.getDefaultJSONConfig());
+ w.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ lblOutput.setText("cannot create message definition file");
+ }
+ }
- @Override
- public void onSetVar(Var v) {
- }
- };
- recording = false;
- defaultSession = new Session("temp");
-
- Track track = null;
- try {
- track = defaultSession.setTrackFromString(txtScript.getText());
- } catch (ParsingException exc) {
- lblOutput.setText("Error in parsing session track");
- }
-
- defaultSession = null;
- ExecuteTrack ex = new ExecuteTrack(false,
- !btnselectChrome.isEnabled(),
- DRIVER_PATH,
- track,
- "8080",
- "test");
- ex.registerExecuteTrackListener(listener);
- new Thread(ex).start();
- });
-
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(0, 0, 0, 0);
- gbc.gridx = 5;
- gbc.gridy = 1;
- btnTestTrack.setPreferredSize(new Dimension(100, 20));
- trackContainer.add(btnTestTrack, gbc);
-
- btnselectChrome = new JButton("Use Chrome");
- btnselectChrome.setEnabled(false);
- btnselectChrome.addActionListener(actionEvent -> {
- btnTestTrack.setEnabled(false);
- lbldriver.setText("Driver file still not selected");
- btndriverSelector.setBackground(Color.RED);
-
- btnselectChrome.setEnabled(false);
- btnselectFirefox.setEnabled(true);
- lblnextTestBrowser.setText("Chrome");
- });
-
- lblnextTestBrowser = new JLabel("Firefox");
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(0, 0, 0, 0);
- gbc.gridx = 4;
- gbc.gridy = 2;
- trackContainer.add(btnselectChrome, gbc);
-
- btnselectFirefox = new JButton("Use Firefox");
- btnselectFirefox.setEnabled(true);
- btnselectFirefox.addActionListener(actionEvent -> {
- btnTestTrack.setEnabled(false);
- lbldriver.setText("Driver file still not selected");
- btndriverSelector.setBackground(Color.RED);
-
- btnselectFirefox.setEnabled(false);
- btnselectChrome.setEnabled(true);
- lblnextTestBrowser.setText("Firefox");
- });
-
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(0, 0, 0, 0);
- gbc.gridx = 4;
- gbc.gridy = 3;
- btnselectFirefox.setPreferredSize(new Dimension(100, 20));
- trackContainer.add(btnselectFirefox, gbc);
-
- JFileChooser messageSaver = new JFileChooser();
- btnSetRecording = new JButton("Record");
- btnSetRecording.setEnabled(true);
-
- // Recording button listener
- btnSetRecording.addActionListener(actionEvent -> {
- if (btnSetRecording.isEnabled()) {
- int returnVal = messageSaver.showOpenDialog(GUI.this);
- if (returnVal == JFileChooser.APPROVE_OPTION) {
- File file = messageSaver.getSelectedFile();
- RECORD_FILE_PATH = file.getPath();
- lblOutput.setText("File selected");
-
- SAVE_TO_FILE = true;
-
- defaultSession = new Session("default");
- try {
- defaultSession.setTrackFromString(txtScript.getText());
- } catch (ParsingException e) {
- lblOutput.setText("Error in parsing session track");
- return;
+ /**
+ * Function that edits the config file.
+ *
+ * @param key the key of the config to change
+ * @param value the new value of the config
+ */
+ private void editConfigFile(String key, String value) {
+ File config_file = new File(CONFIG_FILE_PATH);
+ try {
+ if (!config_file.createNewFile()) {
+ Scanner myReader = null;
+ String tmp = "";
+ try {
+ myReader = new Scanner(config_file);
+ while (myReader.hasNextLine()) {
+ tmp += myReader.nextLine();
}
+ myReader.close();
- btnSetRecording.setBackground(new Color(255, 0, 0));
- btnSetRecording.setText("recording..");
-
- recording = true;
-
- ExecuteTrackListener listener = new ExecuteTrackListener() {
- @Override
- public void onExecuteDone(boolean errors, String current_url, String sessionName) {
- recording = false;
-
- if (errors) {
- lblOutput.setText("Errore nell'esecuzione della traccia");
- }
-
- lblOutput.setText("Track recorded");
-
- if (SAVE_TO_FILE) {
- FileWriter w;
- try {
- w = new FileWriter(RECORD_FILE_PATH);
-
- for (HTTPReqRes actual : defaultSession.messages) {
- Gson geson = new Gson();
- String serialized = geson.toJson(actual);
- w.write(serialized + "\n");
- }
- w.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- btnSetRecording.setBackground(Color.white);
- btnSetRecording.setText("record");
- }
-
- @Override
- public void onExecuteDone(boolean forceResult, String sessionName) {
-
- }
-
- @Override
- public void onError(String sessionName) {
- lblOutput.setText("Errore nell'esecuzione della traccia");
- }
-
- @Override
- public Boolean onAskPause(String sessionName) {
- return false;
- }
-
- @Override
- public Boolean onAskStop(String sessionName) {
- return false;
- }
-
- @Override
- public Boolean onAskClearCookie(String sessionName) {
- return false;
- }
-
- @Override
- public void onNextSessionAction(SessionTrackAction last_action,
- SessionTrackAction last_open,
- SessionTrackAction last_click,
- String last_url,
- String session_name) {
-
- }
-
- @Override
- public Track onUpdateTrack(String sessionName) throws ParsingException {
- return null;
- }
+ JSONObject obj = new JSONObject(tmp);
+ obj.remove(key);
+ obj.put(key, value);
- @Override
- public void onSetVar(Var v) {
- }
- };
+ FileWriter w = new FileWriter(CONFIG_FILE_PATH);
+ w.write(obj.toString());
+ w.close();
- ExecuteTrack ex = new ExecuteTrack(false,
- !btnselectChrome.isEnabled(),
- DRIVER_PATH,
- defaultSession.track,
- defaultSession.port,
- "main");
- ex.registerExecuteTrackListener(listener);
- new Thread(ex).start();
- } else if ((returnVal == JFileChooser.ERROR) || (returnVal == JFileChooser.ERROR_OPTION)) {
- lblOutput.setText("error in selecting output file");
- System.out.println("error in selecting output file");
- } else {
- lbldriver.setText("Messages still not loaded");
+ } catch (JSONException e) {
+ lblOutput.setText("Invalid config file");
+ } catch (FileNotFoundException e) {
+ lblOutput.setText("Cannot find config file");
}
+ } else {
+ FileWriter w = new FileWriter(CONFIG_FILE_PATH);
+ w.write(Tools.getDefaultJSONConfig());
+ w.close();
}
- });
-
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(0, 0, 0, 0);
- gbc.gridx = 5;
- gbc.gridy = 2;
- btnSetRecording.setPreferredSize(new Dimension(100, 20));
- trackContainer.add(btnSetRecording, gbc);
-
- // Button Execute track
- btnExecuteTrack = new JButton("execute track");
- btnExecuteTrack.addActionListener(actionEvent -> {
- defaultSession = new Session("default");
- try {
- defaultSession.setTrackFromString(txtScript.getText());
- } catch (ParsingException e) {
- lblOutput.setText("Error in parsing session track");
- return;
- }
- btnExecuteTrack.setText("executing..");
- interceptedMessages.clear();
- recording = true;
-
- ExecuteTrackListener listener = new ExecuteTrackListener() {
- @Override
- public void onExecuteDone(boolean errors, String current_url, String sessionName) {
- recording = false;
-
- if (errors) {
- lblOutput.setText("Errore nell'esecuzione della traccia");
- }
-
- lblOutput.setText("Track executed");
-
- btnExecuteTrack.setText("execute track");
- }
-
- @Override
- public void onExecuteDone(boolean forceResult, String sessionName) {
+ } catch (IOException e) {
+ e.printStackTrace();
+ lblOutput.setText("cannot create message definition file");
+ }
+ }
- }
+ /**
+ * This function parses the given jsonInput string of the language
+ *
+ * @param jsonInput the json input
+ */
+ private void readJSONinput(String jsonInput) {
+ sessions_names.clear();
+ txtSearch.setBorder(BorderFactory.createEmptyBorder());
+ setJSONError(false, "");
- @Override
- public void onError(String sessionName) {
- lblOutput.setText("Errore nell'esecuzione della traccia");
- }
+ try {
+ JSONObject obj = new JSONObject(jsonInput);
+ List tests = new ArrayList<>();
- @Override
- public Boolean onAskPause(String sessionName) {
- return false;
- }
+ //Getting Test suite data
+ String suite_name = obj.getJSONObject("test suite").getString("name");
+ String suite_description = obj.getJSONObject("test suite").getString("description");
- @Override
- public Boolean onAskStop(String sessionName) {
- return false;
- }
+ if (obj.getJSONObject("test suite").has("filter messages")) {
+ FILTERING = obj.getJSONObject("test suite").getBoolean("filter messages");
+ }
- @Override
- public Boolean onAskClearCookie(String sessionName) {
- return null;
- }
+ //Array of Tests
+ JSONArray arrTests = obj.getJSONArray("tests");
- @Override
- public void onNextSessionAction(SessionTrackAction last_action,
- SessionTrackAction last_open,
- SessionTrackAction last_click,
- String last_url,
- String session_name) {
- }
+ //scorro tutti i test
+ for (int i = 0; i < arrTests.length(); i++) {
+ JSONObject act_test = arrTests.getJSONObject(i).getJSONObject("test");
- @Override
- public Track onUpdateTrack(String sessionName) throws ParsingException {
- return null;
- }
+ Test test = new Test(act_test, messageTypes);
+ tests.add(test);
- @Override
- public void onSetVar(Var v) {
+ for (Session s : test.sessions) {
+ if (!sessions_names.contains(s.name)) {
+ sessions_names.add(s.name);
+ session_port.put(s.name, "8080"); // set default port to session
+ }
}
- };
-
- editConfigFile("last_browser_used", btnselectChrome.isEnabled() ? "firefox" : "chrome");
-
- ExecuteTrack ex = new ExecuteTrack(false,
- !btnselectChrome.isEnabled(),
- DRIVER_PATH,
- defaultSession.track,
- defaultSession.port,
- "main");
- ex.registerExecuteTrackListener(listener);
- new Thread(ex).start();
- });
-
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(0, 0, 0, 0);
- gbc.gridx = 5;
- gbc.gridy = 3;
- btnSetRecording.setPreferredSize(new Dimension(100, 20));
- trackContainer.add(btnExecuteTrack, gbc);
+ }
+ updateSessionTabs();
+ updateTxtSessionConfig();
+ //JSONArray result = obj.getJSONArray("Test Suite Result Table");
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(0, 0, 0, 0);
- gbc.gridx = 4;
- gbc.gridy = 4;
- trackContainer.add(new JLabel("Next test will use"), gbc);
+ this.testSuite = new TestSuite(suite_name, suite_description, tests);
+ lblInfo.setText("JSON read successfully, Test Suite Object has been created");
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(20, 0, 0, 0);
- gbc.gridx = 4;
- gbc.gridy = 4;
- trackContainer.add(lblnextTestBrowser, gbc);
+ } catch (ParsingException e) {
+ e.printStackTrace();
- JFileChooser messageLoader = new JFileChooser();
- btnLoadMessages = new JButton("load messages");
- btnLoadMessages.setEnabled(true);
- btnLoadMessages.addActionListener(actionEvent -> {
- if (btnLoadMessages.getBackground() == Color.GREEN) {
- btnSetOffline.setEnabled(false);
- btnSetOffline.setBackground(Color.white);
- btnLoadMessages.setBackground(Color.white);
- btnLoadMessages.setText("load messages");
- OFFLINE = false;
- SAVE_FILE_PATH = "";
+ setJSONError(true, "Problem in parsing JSON: " + e.getMessage());
+ } catch (Exception e) {
+ e.printStackTrace();
- } else {
- int returnVal = messageLoader.showOpenDialog(GUI.this);
- if (returnVal == JFileChooser.APPROVE_OPTION) {
- File file = messageLoader.getSelectedFile();
- SAVE_FILE_PATH = file.getPath();
- lblOutput.setText("Messages selected");
- btnLoadMessages.setBackground(Color.GREEN);
- btnLoadMessages.setText("unload");
- btnSetOffline.setEnabled(true);
- } else if ((returnVal == JFileChooser.ERROR) || (returnVal == JFileChooser.ERROR_OPTION)) {
- lblOutput.setText("error in selecting messages");
- System.out.println("error in selecting messages");
- btnSetOffline.setEnabled(false);
- btnLoadMessages.setBackground(Color.RED);
- } else {
- lbldriver.setText("Messages still not loaded");
- btnSetOffline.setEnabled(false);
- btnLoadMessages.setBackground(Color.RED);
- }
- }
- });
+ setJSONError(true, "PROBLEM IN READING JSON, check it please");
+ }
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(0, 0, 0, 0);
- gbc.gridx = 6;
- gbc.gridy = 1;
- trackContainer.add(btnLoadMessages, gbc);
+ }
- btnSetOffline = new JButton("offline mode");
- btnSetOffline.setEnabled(false);
- btnSetOffline.addActionListener(actionEvent -> {
- if (btnSetOffline.getBackground() == Color.green) {
- OFFLINE = false;
- btnSetOffline.setBackground(Color.white);
- } else {
- btnSetOffline.setBackground(Color.green);
- OFFLINE = true;
- }
- });
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(0, 0, 0, 0);
- gbc.gridx = 6;
- gbc.gridy = 2;
- trackContainer.add(btnSetOffline, gbc);
+ /**
+ * This function reads the selected file deserializing the messages and creating a new Session
+ *
+ * @return if the reading has been succesfull
+ */
+ private boolean readSavedMessages() {
+ if (!SAVE_FILE_PATH.isEmpty()) {
+ try {
+ if (defaultSession == null) {
+ defaultSession = new Session("default");
+ defaultSession.isOffline = true;
- btnSaveToFile = new JButton("save");
- btnSaveToFile.addActionListener(actionEvent -> {
- if (SAVE_TO_FILE) {
- FileWriter w;
- try {
- w = new FileWriter(RECORD_FILE_PATH);
+ File f = new File(SAVE_FILE_PATH);
+ Scanner r = new Scanner(f);
- for (HTTPReqRes actual : defaultSession.messages) {
- Gson geson = new Gson();
- String serialized = geson.toJson(actual);
- w.write(serialized + "\n");
+ Gson json = new Gson();
+ while (r.hasNextLine()) {
+ HTTPReqRes tmp = json.fromJson(r.nextLine(), HTTPReqRes.class);
+ defaultSession.messages.add(tmp);
}
- w.close();
- } catch (IOException e) {
- e.printStackTrace();
+ } else {
+ System.out.println("main session already created, skipping message reading from file");
}
+ return true;
+ } catch (FileNotFoundException fileNotFoundException) {
+ fileNotFoundException.printStackTrace();
+ return false;
}
- });
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(0, 0, 0, 0);
- gbc.gridx = 6;
- gbc.gridy = 3;
- trackContainer.add(btnSaveToFile, gbc);
-
- this.setTopComponent(trackContainer);
-
- // Bottom part -------------------------------------------------------------------------------------------------
+ } else {
+ return false;
+ }
+ }
- bot_tabbed = new JTabbedPane();
+ /**
+ * Method which executes the entire test suite
+ */
+ private void executeSuite() {
+ // clears all previously saved tests
+ actives.clear();
+ passives.clear();
+ act_active_op = null;
+ ex = null;
+ active_ex_finished = false;
- // Input Search Tabm
- GridBagLayout gridBagLayout3 = new GridBagLayout();
- gridBagLayout3.columnWidths = new int[]{1000, 100};
- gridBagLayout3.rowHeights = new int[]{15, 20, 20, 20, 30};
- gridBagLayout3.columnWeights = new double[]{Double.MIN_VALUE, 0.0};
- gridBagLayout3.rowWeights = new double[]{0.0, Double.MIN_VALUE, 0.0, 0.0, 0.0};
+ // clears the test suite result table
+ DefaultTableModel dm = (DefaultTableModel) resultTable.getModel();
+ dm.getDataVector().removeAllElements();
+ dm.fireTableDataChanged();
- inputContainer = new JPanel();
- inputContainer.setLayout(gridBagLayout3);
+ System.out.println("Number of test found: " + testSuite.getTests().size());
+ for (Test t : testSuite.getTests()) {
+ if (t.isActive) {
+ actives.add(t);
+ } else {
+ passives.add(t);
+ }
+ }
- lblInfo = new JLabel(" ");
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.insets = new Insets(10, 10, 10, 10);
- gbc.gridx = 0;
- gbc.gridy = 0;
- gbc.gridwidth = 2;
- inputContainer.add(lblInfo, gbc);
+ if (OFFLINE) {
+ if (!readSavedMessages()) {
+ System.err.println("Can't read message file");
+ lblOutput.setText("Can't read message file");
+ return;
+ }
+ }//TODO: re-enable OFFLINE mode
+ /* else if (passives.size() > 0 && defaultSession == null && actives.size() == 0) {
+ lblOutput.setText("Track need to be run for passive tests before executing tests");
+ return;
+ }
+ */
- lblOutput = new JLabel(" ");
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.insets = new Insets(10, 10, 10, 10);
- gbc.gridx = 0;
- gbc.gridy = 4;
- gbc.gridwidth = 2;
- inputContainer.add(lblOutput, gbc);
+ if (actives.size() == 0) {
+ synchronized (lock2) {
+ active_ex_finished = true;
+ }
+ }
- txtSearch = new JTextArea();
- txtSearch.append("");
+ //FIXME: Passives thread starts without waiting for the end of actives one
+ // Execute active tests
+ if (actives.size() != 0) {
+ try {
+ for (String key : session_port.keySet()) {
+ if (session_port.get(key).equals("")) {
+ lblOutput.setText("session port not configured");
+ return;
+ }
+ }
+ ex = new ExecuteActives(actives, waiting);
- JScrollPane scrollPane2 = new JScrollPane(txtSearch,
- JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.fill = GridBagConstraints.BOTH;
- gbc.insets = new Insets(10, 10, 10, 10);
- gbc.gridx = 0;
- gbc.gridy = 1;
- gbc.gridwidth = 1;
- gbc.gridheight = 3;
- inputContainer.add(scrollPane2, gbc);
+ editConfigFile("last_browser_used", btnselectChrome.isEnabled() ? "firefox" : "chrome");
- JButton btnReadJSON = new JButton("Read JSON");
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(10, 10, 10, 10);
- gbc.gridx = 1;
- gbc.gridy = 2;
- inputContainer.add(btnReadJSON, gbc);
+ ex.registerExecuteActivesListener(new ExecuteActiveListener() {
+ @Override
+ public void onExecuteStart() {
+ ACTIVE_ENABLED = false;
+ act_active_op = new Operation();
+ lblOutput.setText("Executing active tests");
+ }
- btnReadJSON.addActionListener(e -> {
- testSuite = new TestSuite();
+ @Override
+ public void onExecuteDone() {
+ if (passives.size() == 0) {
+ update_gui_test_results();
- readMsgDefFile(); // Updates the Message Definitions
+ lblOutput.setText("Done. Executed Passive Tests: "
+ + (passives.isEmpty() ? 0 : passives.size())
+ + " - Active Tests: "
+ + (testSuite.getTests().size() - (passives.isEmpty() ? 0 : passives.size())));
+ } else {
+ lblOutput.setText("Executed Active tests, now doing passives");
+ }
+ synchronized (lock2) {
+ active_ex_finished = true;
+ }
+ }
- readJSONinput(txtSearch.getText());
- // if there's only a session, set the default port
- // TODO: Check if this is useful or just call updateTxtSessionConfig
- /*
- if (session_port.size() > 0) {
- Integer port = 8080;
- for (String ses_name : session_port.keySet()) {
- if (session_port.get(ses_name).equals("")) {
- session_port.replace(ses_name, port.toString());
- port++;
+ @Override
+ public void onNewProcessOperation(Operation op) {
+ ACTIVE_ENABLED = true;
+ act_active_op = op;
}
- }
- String tmp = "";
- for (String key : session_port.keySet()) {
- tmp += key + ": " + session_port.get(key) + ";\n";
- }
+ @Override
+ public Operation onOperationDone() {
+ ACTIVE_ENABLED = false;
+ Operation tmp = act_active_op;
- txtSessionConfig.setText(tmp);
- }
- */
- try {
- updateTxtSessionConfig();
- } catch (ParsingException exc) {
- setJSONError(true, "error in updating the session config");
- }
+ act_active_op = new Operation();
+ return tmp;
+ }
- lblOutput.setText("Number of Tests: " + testSuite.getTests().size());
+ @Override
+ public Session onNewSession(Session s) {
+ Track track = null;
+ try {
+ track = s.setTrackFromString(getSessionTxt(s.name));
+ } catch (ParsingException e) {
+ lblOutput.setText("error in parsing session track");
+ return null;
+ }
+ String port = session_port.get(s.name);
+ s.port = port;
+ s.track = track;
- if (testSuite.getTests().size() > 0) {
- btnExecuteSuite.setEnabled(true);
- }
- });
+ s.ex = new ExecuteTrack(false,
+ !btnselectChrome.isEnabled(),
+ DRIVER_PATH,
+ track,
+ port,
+ s.name);
+ return s;
+ }
- JButton btnStop = new JButton("Stop");
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.insets = new Insets(10, 10, 10, 10);
- gbc.gridx = 1;
- gbc.gridy = 1;
- inputContainer.add(btnStop, gbc);
+ @Override
+ public void onNewTest(Test actual_test) {
+ act_active_op = null;
+ }
- btnStop.addActionListener(e -> {
- if (active_ex != null) {
- active_ex.interrupt();
- ACTIVE_ENABLED = false;
- active_ex.stop();
- }
- });
+ public void onTestDone(Test actual_test) {
+ int indx = testSuite.tests.indexOf(actual_test);
+ if (indx != -1) {
+ System.out.printf("Saving test %s in test results", actual_test.getName());
+ //TODO: add log of sessions
+ testSuite.tests.set(indx, actual_test);
+ actual_test.logTest(LOG_FOLDER);
+ }
+ }
- btnExecuteSuite = new JButton("Execute Test Suite");
- btnExecuteSuite.setEnabled(false);
+ @Override
+ public void onError(Test actual_test) {
+ System.err.println("Error executing the test:" + actual_test.name);
+ synchronized (lock2) {
+ active_ex_finished = true;
+ }
+ }
+ });
+ active_ex = new Thread(ex);
+ active_ex.start();
- btnExecuteSuite.addActionListener(e -> {
- executeSuite();
- });
+ } catch (Exception er) {
+ er.printStackTrace();
+ System.out.println(er.getLocalizedMessage() + "nad" + er.getMessage() + "2" + er);
- gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.SOUTHWEST;
- gbc.insets = new Insets(10, 10, 10, 10);
- gbc.gridx = 1;
- gbc.gridy = 3;
- inputContainer.add(btnExecuteSuite, gbc);
- bot_tabs_index.put("Input JSON", 0);
- // Add Input Search tab
- bot_tabbed.addTab("Input JSON", inputContainer);
+ lblOutput.setText("PROBLEM IN Executing Suite, check it please");
+ }
+ }
- // Test Suite Result Tab
- resultTableModel = new DefaultTableModel(foundData, testSuiteColNames) {
- @Override
- public boolean isCellEditable(int row, int column) {
- return false;
+ // Execute passive tests
+ if (passives.size() != 0) {
+ // TODO: Add offline clause
+ /*
+ if (defaultSession.messages.size() == 0) {
+ lblOutput.setText("no message found");
+ return;
}
- };
+ */
- resultTable = new JTable(resultTableModel) {
- //Implement table cell tool tips.
- public String getToolTipText(MouseEvent e) {
- String tip = null;
- java.awt.Point p = e.getPoint();
- int rowIndex = rowAtPoint(p);
- int colIndex = columnAtPoint(p);
+ ExecutePassiveListener listener = new ExecutePassiveListener() {
+ @Override
+ public boolean onWaitToStart() {
+ synchronized (lock2) {
+ return active_ex_finished;
+ }
+ }
- try {
- tip = getValueAt(rowIndex, colIndex).toString();
- } catch (RuntimeException e1) {
- //catch null pointer exception if mouse is over an empty line
+ @Override
+ public void onExecuteStart() {
+ lblOutput.setText("Executing passive tests");
}
- return tip;
- }
- };
- resultTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
- @Override
- public Component getTableCellRendererComponent(JTable table,
- Object value,
- boolean isSelected,
- boolean hasFocus,
- int row,
- int column) {
- final Component c = super.getTableCellRendererComponent(table,
- value,
- isSelected,
- hasFocus,
- row,
- column);
- if (value == null) return c;
- if (value.equals("failed")) {
- c.setBackground(Color.RED);
- } else {
- c.setBackground(Color.WHITE);
+ @Override
+ public void onExecuteDone(List passives_test) {
+ //TODO: Check if this is ok
+ lblOutput.setText("Done. Executed Passive Tests: "
+ + (passives.isEmpty() ? 0 : passives.size())
+ + " - Active Tests: "
+ + (testSuite.getTests().size() - (passives.isEmpty() ? 0 : passives.size())));
+
+ passives = passives_test;
+
+ update_gui_test_results();
}
- return c;
- }
- });
- JScrollPane scrollPane = new JScrollPane(resultTable,
- JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ @Override
+ public void onError(String msg) {
+ lblOutput.setText(msg);
+ }
- resultTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ @Override
+ public Session onNewSession(Session s) throws ParsingException {
+ //TODO: implement
+ s.setTrackFromString(getSessionTxt(s.name));
- // Adds all the test result to the result table
- resultTable.getSelectionModel().addListSelectionListener(event -> {
- if (resultTable.getSelectedRow() > -1) {
+ String port = session_port.get(s.name);
+ s.port = port;
- int row = resultTable.getSelectedRow();
- //BurpSuite.getTests.get(row).getTable();
+ s.ex = new ExecuteTrack(false,
+ !btnselectChrome.isEnabled(),
+ DRIVER_PATH,
+ s.track,
+ port,
+ s.name);
- DefaultTableModel dm = (DefaultTableModel) testTable.getModel();
- dm.getDataVector().removeAllElements();
- dm.fireTableDataChanged();
+ return s;
+ }
- for (String[] act : testSuite.getTests().get(row).getRows()) {
+ @Override
+ public void onBeforeExecuteTrack() {
+ //Clear previous interceptedMessages if any
+ interceptedMessages.clear();
+ //Tell Burp Extender class to record the intercepted messages from now on
+ recording = true;
+ }
- ((DefaultTableModel) testTable.getModel()).addRow(act);
+ @Override
+ public ArrayList onTrackExecuteDone() {
+ recording = false;
+ return interceptedMessages;
}
- }
- });
+ };
- //Add Search Result Tab
- bot_tabs_index.put("Test Suite Result", 1);
- bot_tabbed.addTab("Test Suite Result", scrollPane);
+ ExecutePassives expa = new ExecutePassives(helpers,
+ passives,
+ listener,
+ messageTypes);
- // Test Result Tab
- testTableModel = new DefaultTableModel(foundData, foundTableColNames) {
- @Override
- public boolean isCellEditable(int row, int column) {
- return false;
+ new Thread(expa).start();
+ }
+ }
+
+ /**
+ * Function used to get the text of the text area of a certain session using the session name
+ *
+ * @param session_name the name of the session to get the text
+ * @return the content of the session tab if it is not empty, otherwise the main session text tab
+ */
+ private String getSessionTxt(String session_name) {
+ if (sessions_text.containsKey(session_name)) {
+ JTextArea t = (JTextArea) sessions_text.get(session_name);
+ if (t.getText().equals("")) {
+ return txtScript.getText();
+ } else {
+ return t.getText();
}
- };
+ }
+ return null;
+ }
- testTable = new JTable(testTableModel);
- // Add all the operations result to the table
- testTable.getSelectionModel().addListSelectionListener(listSelectionEvent -> {
- if (!listSelectionEvent.getValueIsAdjusting()) {
- if (testTable.getSelectedRow() == -1) return;
- int index = Integer.parseInt((String) testTable.getModel().getValueAt(testTable.getSelectedRow(), 4));
- int op_index = Integer.parseInt((String) testTable.getModel().getValueAt(testTable.getSelectedRow(), 0));
+ /**
+ * Update the session config tab, using the session_port variable, at the same time reads if there are changes, and
+ * updates the session_port variable
+ */
+ private void updateTxtSessionConfig() throws ParsingException {
+ setSession_configError(false, "");
- Operation op = testSuite.tests.get(resultTable.getSelectedRow()).operations.get(op_index);
- for (Operation.MatchedMessage m : op.matchedMessages) {
- if (m.index == index) {
- if (m.isRequest) {
- messageViewer.setMessage(m.message.getRequest(), true);
- } else {
- messageViewer.setMessage(m.message.getResponse(), false);
- }
- break;
- }
- }
+ String text = txtSessionConfig.getText();
+ Pattern p = Pattern.compile("\\n");
+ Matcher m = p.matcher(text);
+ text = m.replaceAll("");
+
+ if (text.equals("")) {
+ String tmp = "";
+ for (String s : session_port.keySet()) {
+ tmp += s;
+ tmp += ":" + session_port.get(s) + ";\n";
}
- });
+ txtSessionConfig.setText(tmp);
+ return;
+ }
- JScrollPane scrollPane3 = new JScrollPane(testTable,
- JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ String[] text_list = text.split(";");
- splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
+ for (String row : text_list) {
+ String[] splitted = row.trim().split(":");
+ if (splitted.length == 0) continue;
+ if (splitted.length <= 1) {
+ String[] splitted2 = new String[]{"", ""};
+ splitted2[0] = splitted[0];
+ splitted = splitted2;
+ }
+ String port = splitted[1].trim();
+ p = Pattern.compile("^\\d+$");
+ m = p.matcher(splitted[1].trim());
+ if (!m.find()) {
+ setSession_configError(true, "invalid port");
- splitPane.setLeftComponent(scrollPane3);
+ throw new ParsingException("Invalid port");
+ }
+ if (session_port.containsKey(splitted[0].trim())) {
+ session_port.replace(splitted[0].trim(), splitted[1].trim());
+ } else {
+ session_port.put((splitted[0]).trim(), splitted[1].trim());
+ }
+ }
+ String tmp = "";
+ for (String key : session_port.keySet()) {
+ tmp += key + ": " + session_port.get(key) + ";\n";
+ }
- controller = new IMessageEditorController() {
- @Override
- public IHttpService getHttpService() {
- return new IHttpService() {
- @Override
- public String getHost() {
- return null;
- }
+ txtSessionConfig.setText(tmp);
+ }
- @Override
- public int getPort() {
- return 0;
- }
+ /**
+ * This function updates the session tabs in the gui to match the actual value in session_names
+ */
+ private void updateSessionTabs() {
+ List present = new ArrayList<>();
- @Override
- public String getProtocol() {
- return null;
- }
- };
+ for (int i = 1; i < top_tabbed.getTabCount(); i++) {
+ present.add(top_tabbed.getTitleAt(i));
+ if (!sessions_names.contains(top_tabbed.getTitleAt(i))) {
+ top_tabbed.remove(i);
}
-
- @Override
- public byte[] getRequest() {
- return viewedMessage.getRequest();
+ }
+ for (String name : sessions_names) {
+ if (!present.contains(name)) {
+ JTextArea tmp = new JTextArea();
+ JScrollPane sp = new JScrollPane(tmp,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ sessions_text.put(name, tmp);
+ top_tabbed.add(name, sp);
}
+ }
+ JTextArea tmp = new JTextArea();
+ JScrollPane sp = new JScrollPane(tmp,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ }
- @Override
- public byte[] getResponse() {
- return viewedMessage.getResponse();
+ /**
+ * Function used to set the JSON textbox with a red colour to highlight an error.
+ *
+ * @param isInError true to highlight an error, false to remove the highlight
+ * @param msg the error message to display
+ */
+ private void setJSONError(boolean isInError, String msg) {
+ if (isInError) {
+ txtSearch.setBorder(BorderFactory.createLineBorder(Color.RED, 3));
+ lblOutput.setText(msg);
+ bot_tabbed.setBackgroundAt(bot_tabs_index.get("Input JSON"), Color.RED);
+ } else {
+ txtSearch.setBorder(BorderFactory.createEmptyBorder());
+ lblOutput.setText("");
+ bot_tabbed.setBackgroundAt(bot_tabs_index.get("Input JSON"), Color.white);
+ }
+ }
+
+ /**
+ * Function used to set the session config textbox with a red border to highlight an error.
+ *
+ * @param isInError true to highlight an error, false to remove highlight
+ * @param msg The error message to display
+ */
+ private void setSession_configError(boolean isInError, String msg) {
+ if (isInError) {
+ txtSessionConfig.setBorder(BorderFactory.createLineBorder(Color.RED, 3));
+ lblOutput.setText(msg);
+ bot_tabbed.setBackgroundAt(bot_tabs_index.get("session config"), Color.RED);
+ } else {
+ txtSessionConfig.setBorder(BorderFactory.createEmptyBorder());
+ lblOutput.setText("");
+ bot_tabbed.setBackgroundAt(bot_tabs_index.get("session config"), Color.white);
+ }
+ }
+
+ /**
+ * Function used to update the gui test results after the tests are executed
+ */
+ private void update_gui_test_results() {
+ for (Test t : testSuite.getTests()) {
+ String esito = "";
+ if (t.applicable) {
+ esito = t.success ? "passed" : "failed";
+ } else {
+ esito = "not applicable";
}
- };
-
- //Add Search Result Tab
- bot_tabs_index.put("Test Result", 2);
- bot_tabbed.addTab("Test Result", splitPane);
+ String[] tmp = new String[]{t.getName(),
+ t.getDescription(),
+ t.references,
+ t.violated_properties,
+ t.affected_entity,
+ t.mitigations,
+ esito};
+ System.out.println(t.getName() + " " + esito);
+ addItem(tmp);
+ }
+ btnExecuteSuite.setEnabled(false);
+ }
- // Metadata tab
- JPanel sessionConfig = new JPanel();
- sessionConfig.setLayout(gridBagLayout3);
+ private void setup_tab_track() {
+ lblTrack = new JLabel("Session track ");
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.insets = new Insets(10, 0, 0, 0);
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ trackContainer.add(lblTrack, gbc);
- txtSessionConfig = new JTextArea();
- JScrollPane scrollPane5 = new JScrollPane(txtSessionConfig, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ txtScript = new JTextArea();
gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.fill = GridBagConstraints.BOTH;
- gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.insets = new Insets(10, 0, 0, 10);
gbc.gridx = 0;
gbc.gridy = 1;
- gbc.gridwidth = 1;
- gbc.gridheight = 3;
- sessionConfig.add(scrollPane5, gbc);
+ gbc.gridwidth = 4;
+ gbc.gridheight = 4;
- JButton btnSetSessionConfig = new JButton("save");
- btnSetSessionConfig.addActionListener(actionEvent -> {
- try {
- updateTxtSessionConfig();
- } catch (ParsingException e) {
- e.printStackTrace();
+ JScrollPane scrollPane1 = new JScrollPane(txtScript,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+
+ top_tabbed = new JTabbedPane();
+ top_tabbed.add("main", scrollPane1);
+ trackContainer.add(top_tabbed, gbc);
+ }
+
+ private void setup_tab_butons() {
+ driverSelector = new JFileChooser();
+ btndriverSelector = new JButton("Select Driver");
+
+ btndriverSelector.addActionListener(actionEvent -> {
+ int returnVal = driverSelector.showOpenDialog(GUI.this);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ File file = driverSelector.getSelectedFile();
+ DRIVER_PATH = file.getPath();
+ editConfigFile("last_driver_path", DRIVER_PATH);
+ lbldriver.setText("Driver Selected");
+ btndriverSelector.setBackground(Color.GREEN);
+ btnTestTrack.setEnabled(true);
+ } else if ((returnVal == JFileChooser.ERROR) || (returnVal == JFileChooser.ERROR_OPTION)) {
+ lbldriver.setText("Driver:error during file selection");
+ System.out.println("error during file selection");
+ btnTestTrack.setEnabled(false);
+
+ btndriverSelector.setBackground(Color.RED);
+ } else {
+ lbldriver.setText("Driver file still not selected");
+ btnTestTrack.setEnabled(false);
+ btndriverSelector.setBackground(Color.RED);
}
});
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(0, 0, 0, 10);
+ gbc.gridx = 4;
+ gbc.gridy = 1;
+ trackContainer.add(btndriverSelector, gbc);
+ lbldriver = new JLabel("Driver file still not selected");
gbc = new GridBagConstraints();
- gbc.anchor = GridBagConstraints.WEST;
- gbc.fill = GridBagConstraints.BOTH;
- gbc.insets = new Insets(10, 10, 10, 10);
- gbc.gridx = 1;
- gbc.gridy = 3;
- gbc.gridwidth = 1;
- gbc.gridheight = 1;
- sessionConfig.add(btnSetSessionConfig, gbc);
+ gbc.anchor = GridBagConstraints.SOUTHEAST;
+ gbc.insets = new Insets(10, 0, 0, 30);
+ gbc.gridx = 3;
+ gbc.gridy = 0;
+ trackContainer.add(lbldriver, gbc);
- bot_tabs_index.put("session config", 3);
- bot_tabbed.addTab("session config", sessionConfig);
+ btnTestTrack = new JButton("Test track");
+ btnTestTrack.setEnabled(true);
- //Set Bottom Part
- this.setBottomComponent(bot_tabbed);
+ btnTestTrack.addActionListener(e -> {
+ ExecuteTrackListener listener = new ExecuteTrackListener() {
+ @Override
+ public void onExecuteDone(boolean errors, String current_url, String sessionName) {
+ if (errors) {
+ lblOutput.setText("Error in executing track");
+ } else {
+ lblOutput.setText("Track Executed correctly");
+ }
+ }
- readMsgDefFile();
- readConfigFile();
- if (!DRIVER_PATH.equals("")) {
- lbldriver.setText("Driver Selected");
- btndriverSelector.setBackground(Color.GREEN);
- btnTestTrack.setEnabled(true);
- }
- }
+ @Override
+ public void onExecuteDone(boolean forceResult, String sessionName) {
+ if (forceResult) {
+ lblOutput.setText("Track Executed correctly");
+ } else {
+ lblOutput.setText("Error in executing track");
+ }
+ }
- /**
- * Function used to add an item to the resultTableModel. Contains the results of the tests
- *
- * @param data the string array containing the data, also a row
- */
- private static void addItem(String[] data) {
- resultTableModel.addRow(data);
- }
+ @Override
+ public void onError(String sessionName) {
+ lblOutput.setText("Error in executing track");
+ }
- /**
- * Function used to read the message definition file
- */
- private void readMsgDefFile() {
- File msg_def_file = new File(MSG_DEF_PATH);
- try {
- if (!msg_def_file.createNewFile()) {
- Scanner myReader = null;
- String tmp = "";
- try {
- myReader = new Scanner(msg_def_file);
- while (myReader.hasNextLine()) {
- tmp += myReader.nextLine();
- }
- myReader.close();
- messageTypes = Utils.readMsgTypeFromJson(tmp);
- } catch (ParsingException e) {
- lblOutput.setText("Invalid message type in message type definition file");
- e.printStackTrace();
- } catch (FileNotFoundException e) {
- lblOutput.setText("Cannot find message definition file");
+ @Override
+ public Boolean onAskPause(String sessionName) {
+ return false;
}
- } else {
- FileWriter w = new FileWriter(MSG_DEF_PATH);
- w.write(Utils.getDefaultJSONMsgType());
- w.close();
- messageTypes = Utils.readMsgTypeFromJson(Utils.getDefaultJSONMsgType());
- }
- } catch (ParsingException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- lblOutput.setText("cannot create message definition file");
- }
- }
- /**
- * Function used to read the json config file
- */
- private void readConfigFile() {
- File config_file = new File(CONFIG_FILE_PATH);
- try {
- if (!config_file.createNewFile()) {
- Scanner myReader = null;
- String tmp = "";
- try {
- myReader = new Scanner(config_file);
- while (myReader.hasNextLine()) {
- tmp += myReader.nextLine();
- }
- myReader.close();
+ @Override
+ public Boolean onAskStop(String sessionName) {
+ return false;
+ }
- JSONObject obj = new JSONObject(tmp);
- String last_driver_path = obj.getString("last_driver_path");
- String last_used_browser = obj.getString("last_browser_used");
+ @Override
+ public Boolean onAskClearCookie(String sessionName) {
+ return null;
+ }
- if (!last_driver_path.equals("")) {
- DRIVER_PATH = last_driver_path;
- }
+ @Override
+ public void onNextSessionAction(SessionTrackAction last_action,
+ SessionTrackAction last_open,
+ SessionTrackAction last_click,
+ String last_url,
+ String session_name) {
- switch (last_used_browser) {
- case "firefox": {
- btnselectChrome.setEnabled(true);
- btnselectFirefox.setEnabled(false);
- break;
- }
- case "chrome": {
- btnselectChrome.setEnabled(false);
- btnselectFirefox.setEnabled(true);
- break;
- }
- }
+ }
- } catch (JSONException e) {
- lblOutput.setText("Invalid config file");
- } catch (FileNotFoundException e) {
- lblOutput.setText("Cannot find config file");
+ @Override
+ public Track onUpdateTrack(String sessionName) throws ParsingException {
+ return null;
}
- } else {
- FileWriter w = new FileWriter(CONFIG_FILE_PATH);
- w.write(Utils.getDefaultJSONConfig());
- w.close();
+
+ @Override
+ public void onSetVar(Var v) {
+ }
+ };
+ recording = false;
+ defaultSession = new Session("temp");
+
+ Track track = null;
+ try {
+ track = defaultSession.setTrackFromString(txtScript.getText());
+ } catch (ParsingException exc) {
+ lblOutput.setText("Error in parsing session track");
}
- } catch (IOException e) {
- e.printStackTrace();
- lblOutput.setText("cannot create message definition file");
- }
- }
- /**
- * Function that edits the config file.
- * @param key the key of the config to change
- * @param value the new value of the config
- */
- private void editConfigFile(String key, String value) {
- File config_file = new File(CONFIG_FILE_PATH);
- try {
- if (!config_file.createNewFile()) {
- Scanner myReader = null;
- String tmp = "";
- try {
- myReader = new Scanner(config_file);
- while (myReader.hasNextLine()) {
- tmp += myReader.nextLine();
- }
- myReader.close();
+ defaultSession = null;
+ ExecuteTrack ex = new ExecuteTrack(false,
+ !btnselectChrome.isEnabled(),
+ DRIVER_PATH,
+ track,
+ "8080",
+ "test");
+ ex.registerExecuteTrackListener(listener);
+ new Thread(ex).start();
+ });
+
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ gbc.gridx = 5;
+ gbc.gridy = 1;
+ btnTestTrack.setPreferredSize(new Dimension(100, 20));
+ trackContainer.add(btnTestTrack, gbc);
- JSONObject obj = new JSONObject(tmp);
- obj.remove(key);
- obj.put(key, value);
+ btnselectChrome = new JButton("Use Chrome");
+ btnselectChrome.setEnabled(false);
+ btnselectChrome.addActionListener(actionEvent -> {
+ btnTestTrack.setEnabled(false);
+ lbldriver.setText("Driver file still not selected");
+ btndriverSelector.setBackground(Color.RED);
- FileWriter w = new FileWriter(CONFIG_FILE_PATH);
- w.write(obj.toString());
- w.close();
+ btnselectChrome.setEnabled(false);
+ btnselectFirefox.setEnabled(true);
+ lblnextTestBrowser.setText("Chrome");
+ });
+ lblnextTestBrowser = new JLabel("Firefox");
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ gbc.gridx = 4;
+ gbc.gridy = 2;
+ trackContainer.add(btnselectChrome, gbc);
- } catch (JSONException e) {
- lblOutput.setText("Invalid config file");
- } catch (FileNotFoundException e) {
- lblOutput.setText("Cannot find config file");
- }
- } else {
- FileWriter w = new FileWriter(CONFIG_FILE_PATH);
- w.write(Utils.getDefaultJSONConfig());
- w.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- lblOutput.setText("cannot create message definition file");
- }
- }
+ btnselectFirefox = new JButton("Use Firefox");
+ btnselectFirefox.setEnabled(true);
+ btnselectFirefox.addActionListener(actionEvent -> {
+ btnTestTrack.setEnabled(false);
+ lbldriver.setText("Driver file still not selected");
+ btndriverSelector.setBackground(Color.RED);
- /**
- * This function parses the given jsonInput string of the language
- *
- * @param jsonInput the json input
- */
- private void readJSONinput(String jsonInput) {
- sessions_names.clear();
- //BurpExtender.printStream.println("" + obj.getJSONObject("Search").getString("Delete"));
- txtSearch.setBorder(BorderFactory.createEmptyBorder());
- setJSONError(false, "");
- try {
- JSONObject obj = new JSONObject(jsonInput);
- List tests = new ArrayList<>();
+ btnselectFirefox.setEnabled(false);
+ btnselectChrome.setEnabled(true);
+ lblnextTestBrowser.setText("Firefox");
+ });
- //Getting Test suite data
- String suite_name = obj.getJSONObject("test suite").getString("name");
- String suite_description = obj.getJSONObject("test suite").getString("description");
- boolean metadata = false;
- if (obj.getJSONObject("test suite").has("metadata")) {
- metadata = obj.getJSONObject("test suite").getBoolean("metadata");
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ gbc.gridx = 4;
+ gbc.gridy = 3;
+ btnselectFirefox.setPreferredSize(new Dimension(100, 20));
+ trackContainer.add(btnselectFirefox, gbc);
- }
+ JFileChooser messageSaver = new JFileChooser();
+ btnSetRecording = new JButton("Record");
+ btnSetRecording.setEnabled(true);
- if (obj.getJSONObject("test suite").has("filter messages")) {
- FILTERING = obj.getJSONObject("test suite").getBoolean("filter messages");
- }
+ // Recording button listener
+ btnSetRecording.addActionListener(actionEvent -> {
+ if (btnSetRecording.isEnabled()) {
+ int returnVal = messageSaver.showOpenDialog(GUI.this);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ File file = messageSaver.getSelectedFile();
+ RECORD_FILE_PATH = file.getPath();
+ lblOutput.setText("File selected");
- //Array of Tests
- JSONArray arrTests = obj.getJSONArray("tests");
+ SAVE_TO_FILE = true;
- //scorro tutti i test
- for (int i = 0; i < arrTests.length(); i++) {
- JSONObject act_test = arrTests.getJSONObject(i).getJSONObject("test");
+ defaultSession = new Session("default");
+ try {
+ defaultSession.setTrackFromString(txtScript.getText());
+ } catch (ParsingException e) {
+ lblOutput.setText("Error in parsing session track");
+ return;
+ }
+
+ btnSetRecording.setBackground(new Color(255, 0, 0));
+ btnSetRecording.setText("recording..");
- Test test = new Test();
+ recording = true;
- test.setDescription(act_test.getString("description"));
- test.setName(act_test.getString("name"));
- test.setType(act_test.getString("type"));
+ ExecuteTrackListener listener = new ExecuteTrackListener() {
+ @Override
+ public void onExecuteDone(boolean errors, String current_url, String sessionName) {
+ recording = false;
- Iterator keys = act_test.keys();
- while (keys.hasNext()) {
- String key = keys.next();
+ if (errors) {
+ lblOutput.setText("Errore nell'esecuzione della traccia");
+ }
- switch (key) {
- case "name":
- case "type":
- case "description":
- case "result":
- case "operations":
- case "sessions":
- break;
- case "references":
- test.references = act_test.getString("references");
- break;
- case "violated_properties":
- test.violated_properties = act_test.getString("violated_properties");
- break;
- case "mitigations":
- test.mitigations = act_test.getString("mitigations");
- break;
- case "affected_entity":
- test.affected_entity = act_test.getString("affected_entity");
- break;
- default:
- throw new ParsingException("Invalid key \"" + key + "\"");
- }
- }
+ lblOutput.setText("Track recorded");
- if (test.isActive) {
- if (act_test.has("result")) {
- String tmp = act_test.getString("result");
- if (tmp.contains("assert_only")) {
- test.result = Utils.ResultType.fromString(tmp);
- } else {
- tmp = tmp.trim();
- String[] splitted = tmp.split("flow");
+ if (SAVE_TO_FILE) {
+ FileWriter w;
+ try {
+ w = new FileWriter(RECORD_FILE_PATH);
- if (splitted.length > 1) {
- test.resultSession = splitted[1].trim();
+ for (HTTPReqRes actual : defaultSession.messages) {
+ Gson geson = new Gson();
+ String serialized = geson.toJson(actual);
+ w.write(serialized + "\n");
+ }
+ w.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
}
- test.result = Utils.ResultType.fromString(splitted[0].trim());
+ btnSetRecording.setBackground(Color.white);
+ btnSetRecording.setText("record");
}
- }
- }
- if (act_test.has("sessions")) {
- JSONArray arrSess = act_test.getJSONArray("sessions");
- Iterator it = arrSess.iterator();
- while (it.hasNext()) {
- String act_sess_name = (String) it.next();
+ @Override
+ public void onExecuteDone(boolean forceResult, String sessionName) {
- if (!sessions_names.contains(act_sess_name)) {
- sessions_names.add(act_sess_name);
- // Default is 8080
- session_port.put(act_sess_name, "8080");
}
- test.sessions.add(new Session(act_sess_name));
- }
+ @Override
+ public void onError(String sessionName) {
+ lblOutput.setText("Errore nell'esecuzione della traccia");
+ }
- } else {
- test.sessions.add(defaultSession);
- }
+ @Override
+ public Boolean onAskPause(String sessionName) {
+ return false;
+ }
- //Array of Operations
- JSONArray arrOps = act_test.getJSONArray("operations");
+ @Override
+ public Boolean onAskStop(String sessionName) {
+ return false;
+ }
- //Reads all the operations
- for (int j = 0; j < arrOps.length(); j++) {
- JSONObject act_operation = arrOps.getJSONObject(j);
+ @Override
+ public Boolean onAskClearCookie(String sessionName) {
+ return false;
+ }
- Operation op = new Operation();
+ @Override
+ public void onNextSessionAction(SessionTrackAction last_action,
+ SessionTrackAction last_open,
+ SessionTrackAction last_click,
+ String last_url,
+ String session_name) {
- // Test non attivo
- if (!test.isActive) {
- if (act_operation.has("decode param")) {
- op.decode_param = act_operation.getString("decode param");
+ }
- JSONArray encodings = act_operation.getJSONArray("encoding");
- Iterator it = encodings.iterator();
+ @Override
+ public Track onUpdateTrack(String sessionName) throws ParsingException {
+ return null;
+ }
- while (it.hasNext()) {
- String act_enc = (String) it.next();
- op.encodings.add(
- Utils.Encoding.fromString(act_enc));
- }
+ @Override
+ public void onSetVar(Var v) {
}
- if (act_operation.has("regex")) {
- // regex version
- op.isRegex = true;
- op.setRegex(act_operation.getString("regex"));
- op.setMessageType(act_operation.getString("message type"), messageTypes);
- op.setMessageSection(Utils.MessageSection.fromString(act_operation.getString("message section")));
- } else {
- //non regex version
- op.setMessageType(act_operation.getString("message type"), messageTypes);
+ };
- JSONArray checks = act_operation.getJSONArray("checks");
+ ExecuteTrack ex = new ExecuteTrack(false,
+ !btnselectChrome.isEnabled(),
+ DRIVER_PATH,
+ defaultSession.track,
+ defaultSession.port,
+ "main");
+ ex.registerExecuteTrackListener(listener);
+ new Thread(ex).start();
- if (act_operation.has("message section")) {
- op.setMessageSection(Utils.MessageSection.fromString(act_operation.getString("message section")));
- }
- op.setChecks(Utils.parseChecksFromJSON(checks));
- }
- } else {
- // If the test is active
- if (act_operation.has("session")) {
- // If is an operation to control a session
- String session = act_operation.getString("session");
- String action = act_operation.getString("action");
-
- List lsop = SessionOperation.parseFromJson(act_operation);
- if (lsop != null) {
- for (SessionOperation sop : lsop) {
- op.session_operations.add(sop);
- }
- }
+ } else if ((returnVal == JFileChooser.ERROR) || (returnVal == JFileChooser.ERROR_OPTION)) {
+ lblOutput.setText("error in selecting output file");
+ System.out.println("error in selecting output file");
+ } else {
+ lbldriver.setText("Messages still not loaded");
+ }
+ }
+ });
- op.setSession(session);
- op.setSessionAction(action);
- op.isSessionOp = true;
- test.operations.add(op);
- continue;
- }
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ gbc.gridx = 5;
+ gbc.gridy = 2;
+ btnSetRecording.setPreferredSize(new Dimension(100, 20));
+ trackContainer.add(btnSetRecording, gbc);
- // If is a standard operation
- String action = act_operation.getString("action");
- op.setAction(action);
-
- if (op.getAction() == Utils.Action.VALIDATE) {
- if (act_operation.has("match")) {
- String toMatch = act_operation.getString("match");
- if (toMatch.equals("all")) op.to_match = -1;
- else op.to_match = Integer.parseInt(toMatch);
- } else {
- op.to_match = 1;
- }
+ // Button Execute track
+ btnExecuteTrack = new JButton("execute track");
+ btnExecuteTrack.addActionListener(actionEvent -> {
+ defaultSession = new Session("default");
+ try {
+ defaultSession.setTrackFromString(txtScript.getText());
+ } catch (ParsingException e) {
+ lblOutput.setText("Error in parsing session track");
+ return;
+ }
+ btnExecuteTrack.setText("executing..");
+ interceptedMessages.clear();
+ recording = true;
- if (act_operation.has("regex")) {
- // regex version
- op.isRegex = true;
- op.setRegex(act_operation.getString("regex"));
- op.setMessageType(act_operation.getString("message type"), messageTypes);
- op.setMessageSection(
- Utils.MessageSection.fromString(
- act_operation.getString("message section")));
- } else {
- //non regex version
- op.setMessageType(act_operation.getString("message type"), messageTypes);
- JSONArray checks = act_operation.getJSONArray("checks");
-
- op.setChecks(Utils.parseChecksFromJSON(checks));
- }
- }
+ ExecuteTrackListener listener = new ExecuteTrackListener() {
+ @Override
+ public void onExecuteDone(boolean errors, String current_url, String sessionName) {
+ recording = false;
+
+ if (errors) {
+ lblOutput.setText("Errore nell'esecuzione della traccia");
+ }
+
+ lblOutput.setText("Track executed");
+
+ btnExecuteTrack.setText("execute track");
+ }
+
+ @Override
+ public void onExecuteDone(boolean forceResult, String sessionName) {
- String message_type = act_operation.getString("message type");
+ }
- op.setMessageType(message_type, messageTypes);
+ @Override
+ public void onError(String sessionName) {
+ lblOutput.setText("Errore nell'esecuzione della traccia");
+ }
- if (act_operation.has("from session")) {
- op.from_session = act_operation.getString("from session");
- }
- if (act_operation.has("to session")) {
- op.to_session = act_operation.getString("to session");
- }
- if (act_operation.has("then")) {
- op.then = Utils.Then.fromString(act_operation.getString("then"));
- }
- if (act_operation.has("save")) {
- op.save_name = act_operation.getString("save");
- }
- if (act_operation.has("replace request")) {
- op.replace_request_name = act_operation.getString("replace request");
- } else if (act_operation.has("replace response")) {
- op.replace_response_name = act_operation.getString("replace response");
- }
+ @Override
+ public Boolean onAskPause(String sessionName) {
+ return false;
+ }
- // Preconditions
- if (act_operation.has("preconditions")) {
- JSONArray checks = act_operation.getJSONArray("preconditions");
- op.preconditions = Utils.parseChecksFromJSON(checks);
- }
+ @Override
+ public Boolean onAskStop(String sessionName) {
+ return false;
+ }
- // Message Operations
- if (act_operation.has("message operations")) {
- JSONArray message_ops = act_operation.getJSONArray("message operations");
- for (int k = 0; k < message_ops.length(); k++) {
- JSONObject act_message_op = message_ops.getJSONObject(k);
- MessageOperation message_op = new MessageOperation();
- keys = act_message_op.keys();
- while (keys.hasNext()) {
- String key = keys.next();
-
- switch (key) {
- case "from":
- message_op.from = Utils.MessageSection.fromString(act_message_op.getString("from"));
- break;
- case "remove parameter":
- message_op.what = act_message_op.getString("remove parameter");
- message_op.action = Utils.MessageOperationActions.REMOVE_PARAMETER;
- break;
- case "remove match word":
- message_op.what = act_message_op.getString("remove match word");
- message_op.action = Utils.MessageOperationActions.REMOVE_MATCH_WORD;
- break;
- case "edit":
- message_op.what = act_message_op.getString("edit");
- message_op.action = Utils.MessageOperationActions.EDIT;
- break;
- case "edit regex":
- message_op.what = act_message_op.getString("edit regex");
- message_op.action = Utils.MessageOperationActions.EDIT_REGEX;
- break;
- case "in":
- message_op.to = act_message_op.getString("in");
- break;
- case "add":
- message_op.what = act_message_op.getString("add");
- message_op.action = Utils.MessageOperationActions.ADD;
- break;
- case "this":
- message_op.to = act_message_op.getString("this");
- break;
- case "save":
- message_op.what = act_message_op.getString("save");
- message_op.action = Utils.MessageOperationActions.SAVE;
- break;
- case "save match":
- message_op.what = act_message_op.getString("save match");
- message_op.action = Utils.MessageOperationActions.SAVE_MATCH;
- break;
- case "as":
- message_op.save_as = act_message_op.getString("as");
- break;
- case "use":
- message_op.use = act_message_op.getString("use");
- break;
- case "type":
- message_op.type = Utils.MessageOpType.fromString(
- act_message_op.getString("type"));
- break;
- case "decode param":
- message_op.decode_param = act_message_op.getString("decode param");
- break;
-
- case "encoding":
- JSONArray encodings = act_message_op.getJSONArray("encoding");
- Iterator it = encodings.iterator();
-
- while (it.hasNext()) {
- String act_enc = (String) it.next();
- message_op.encodings.add(
- Utils.Encoding.fromString(act_enc));
- }
- break;
-
- case "value":
- // value of xml or other edits
- message_op.value = act_message_op.getString("value");
- break;
- case "add tag":
- message_op.xml_action = Utils.XmlAction.ADD_TAG;
- message_op.xml_action_name = act_message_op.getString(key);
- break;
- case "add attribute":
- message_op.xml_action = Utils.XmlAction.ADD_ATTR;
- message_op.xml_action_name = act_message_op.getString(key);
- break;
- case "edit tag":
- message_op.xml_action = Utils.XmlAction.EDIT_TAG;
- message_op.xml_action_name = act_message_op.getString(key);
- break;
- case "edit attribute":
- message_op.xml_action = Utils.XmlAction.EDIT_ATTR;
- message_op.xml_action_name = act_message_op.getString(key);
- break;
- case "remove tag":
- message_op.xml_action = Utils.XmlAction.REMOVE_TAG;
- message_op.xml_action_name = act_message_op.getString(key);
- break;
- case "remove attribute":
- message_op.xml_action = Utils.XmlAction.REMOVE_ATTR;
- message_op.xml_action_name = act_message_op.getString(key);
- break;
- case "save tag":
- message_op.xml_action = Utils.XmlAction.SAVE_TAG;
- message_op.xml_action_name = act_message_op.getString(key);
- break;
- case "save attribute":
- message_op.xml_action = Utils.XmlAction.SAVE_ATTR;
- message_op.xml_action_name = act_message_op.getString(key);
- break;
- case "self-sign":
- message_op.self_sign = act_message_op.getBoolean("self-sign");
- break;
- case "remove signature":
- message_op.remove_signature = act_message_op.getBoolean("remove signature");
- break;
- case "xml tag":
- message_op.xml_tag = act_message_op.getString("xml tag");
- break;
- case "xml occurrency":
- message_op.xml_occurrency = act_message_op.getInt("xml occurrency");
- break;
- case "xml attribute":
- message_op.xml_attr = act_message_op.getString("xml attribute");
- break;
- case "txt remove":
- message_op.txt_action = Utils.TxtAction.REMOVE;
- message_op.txt_action_name = act_message_op.getString("txt remove");
- break;
- case "txt edit":
- message_op.txt_action = Utils.TxtAction.EDIT;
- message_op.txt_action_name = act_message_op.getString("txt edit");
- break;
- case "txt add":
- message_op.txt_action = Utils.TxtAction.ADD;
- message_op.txt_action_name = act_message_op.getString("txt add");
- break;
- case "txt save":
- message_op.txt_action = Utils.TxtAction.SAVE;
- message_op.txt_action_name = act_message_op.getString("txt save");
- break;
- case "jwt from":
- message_op.jwt_section = Utils.Jwt_section.getFromString(
- act_message_op.getString("jwt from"));
- if (act_message_op.getString("jwt from").contains("raw")) {
- message_op.isRawJWT = true;
- }
- break;
- case "jwt remove":
- message_op.jwt_action = Utils.Jwt_action.REMOVE;
- message_op.what = act_message_op.getString("jwt remove");
- break;
- case "jwt edit":
- message_op.jwt_action = Utils.Jwt_action.EDIT;
- message_op.what = act_message_op.getString("jwt edit");
- break;
- case "jwt add":
- message_op.jwt_action = Utils.Jwt_action.ADD;
- message_op.what = act_message_op.getString("jwt add");
- break;
- case "jwt save":
- message_op.jwt_action = Utils.Jwt_action.SAVE;
- message_op.what = act_message_op.getString("jwt save");
- break;
- case "jwt sign":
- message_op.sign = act_message_op.getBoolean("jwt sign");
- break;
- case "template":
- message_op.template = act_message_op.getString("template");
- break;
- case "output_path":
- message_op.output_path = act_message_op.getString("output_path");
- break;
- default:
- System.err.println(key);
- throw new ParsingException("Message operation not valid");
- }
- }
- op.messageOerations.add(message_op);
- }
- }
+ @Override
+ public Boolean onAskClearCookie(String sessionName) {
+ return null;
+ }
- // Session Operations
- List lsop = SessionOperation.parseFromJson(act_operation);
- if (lsop != null) {
- for (SessionOperation sop : lsop) {
- op.session_operations.add(sop);
- }
- }
+ @Override
+ public void onNextSessionAction(SessionTrackAction last_action,
+ SessionTrackAction last_open,
+ SessionTrackAction last_click,
+ String last_url,
+ String session_name) {
+ }
- }
- test.operations.add(op);
+ @Override
+ public Track onUpdateTrack(String sessionName) throws ParsingException {
+ return null;
}
- tests.add(test);
- }
- updateSessionTabs();
- updateTxtSessionConfig();
- //JSONArray result = obj.getJSONArray("Test Suite Result Table");
- this.testSuite = new TestSuite(suite_name, suite_description, tests);
- this.testSuite.metadata = metadata;
- lblInfo.setText("JSON read successfully, Test Suite Object has been created");
+ @Override
+ public void onSetVar(Var v) {
+ }
+ };
- } catch (ParsingException e) {
- e.printStackTrace();
+ editConfigFile("last_browser_used", btnselectChrome.isEnabled() ? "firefox" : "chrome");
- setJSONError(true, "Problem in parsing JSON: " + e.getMessage());
- } catch (Exception e) {
- e.printStackTrace();
+ ExecuteTrack ex = new ExecuteTrack(false,
+ !btnselectChrome.isEnabled(),
+ DRIVER_PATH,
+ defaultSession.track,
+ defaultSession.port,
+ "main");
+ ex.registerExecuteTrackListener(listener);
+ new Thread(ex).start();
+ });
- setJSONError(true, "PROBLEM IN READING JSON, check it please");
- }
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ gbc.gridx = 5;
+ gbc.gridy = 3;
+ btnSetRecording.setPreferredSize(new Dimension(100, 20));
+ trackContainer.add(btnExecuteTrack, gbc);
- }
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ gbc.gridx = 4;
+ gbc.gridy = 4;
+ trackContainer.add(new JLabel("Next test will use"), gbc);
- /**
- * This function reads the selected file deserializing the messages and creating a new Session
- *
- * @return if the reading has been succesfull
- */
- private boolean readSavedMessages() {
- if (!SAVE_FILE_PATH.isEmpty()) {
- try {
- if (defaultSession == null) {
- defaultSession = new Session("default");
- defaultSession.isOffline = true;
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(20, 0, 0, 0);
+ gbc.gridx = 4;
+ gbc.gridy = 4;
+ trackContainer.add(lblnextTestBrowser, gbc);
- File f = new File(SAVE_FILE_PATH);
- Scanner r = new Scanner(f);
+ JFileChooser messageLoader = new JFileChooser();
+ btnLoadMessages = new JButton("load messages");
+ btnLoadMessages.setEnabled(true);
+ btnLoadMessages.addActionListener(actionEvent -> {
+ if (btnLoadMessages.getBackground() == Color.GREEN) {
+ btnSetOffline.setEnabled(false);
+ btnSetOffline.setBackground(Color.white);
+ btnLoadMessages.setBackground(Color.white);
+ btnLoadMessages.setText("load messages");
+ OFFLINE = false;
+ SAVE_FILE_PATH = "";
- Gson json = new Gson();
- while (r.hasNextLine()) {
- HTTPReqRes tmp = json.fromJson(r.nextLine(), HTTPReqRes.class);
- defaultSession.messages.add(tmp);
- }
+ } else {
+ int returnVal = messageLoader.showOpenDialog(GUI.this);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ File file = messageLoader.getSelectedFile();
+ SAVE_FILE_PATH = file.getPath();
+ lblOutput.setText("Messages selected");
+ btnLoadMessages.setBackground(Color.GREEN);
+ btnLoadMessages.setText("unload");
+ btnSetOffline.setEnabled(true);
+ } else if ((returnVal == JFileChooser.ERROR) || (returnVal == JFileChooser.ERROR_OPTION)) {
+ lblOutput.setText("error in selecting messages");
+ System.out.println("error in selecting messages");
+ btnSetOffline.setEnabled(false);
+ btnLoadMessages.setBackground(Color.RED);
} else {
- System.out.println("main session already created, skipping message reading from file");
+ lbldriver.setText("Messages still not loaded");
+ btnSetOffline.setEnabled(false);
+ btnLoadMessages.setBackground(Color.RED);
}
- return true;
- } catch (FileNotFoundException fileNotFoundException) {
- fileNotFoundException.printStackTrace();
- return false;
}
- } else {
- return false;
- }
- }
-
- /**
- * Method which executes the entire test suite
- */
- private void executeSuite() {
- // clears all previously saved tests
- actives.clear();
- passives.clear();
- act_active_op = null;
- ex = null;
- synchronized (lock) {
- act_test_vars = new ArrayList<>();
- }
- active_ex_finished = false;
+ });
- // clears the test suite result table
- DefaultTableModel dm = (DefaultTableModel) resultTable.getModel();
- dm.getDataVector().removeAllElements();
- dm.fireTableDataChanged();
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ gbc.gridx = 6;
+ gbc.gridy = 1;
+ trackContainer.add(btnLoadMessages, gbc);
- System.out.println("Number of test found: " + testSuite.getTests().size());
- for (Test t : testSuite.getTests()) {
- if (t.isActive) {
- actives.add(t);
+ btnSetOffline = new JButton("offline mode");
+ btnSetOffline.setEnabled(false);
+ btnSetOffline.addActionListener(actionEvent -> {
+ if (btnSetOffline.getBackground() == Color.green) {
+ OFFLINE = false;
+ btnSetOffline.setBackground(Color.white);
} else {
- passives.add(t);
- }
- }
-
- if (OFFLINE) {
- if (!readSavedMessages()) {
- System.err.println("Can't read message file");
- lblOutput.setText("Can't read message file");
- return;
+ btnSetOffline.setBackground(Color.green);
+ OFFLINE = true;
}
- }//TODO: re-enable OFFLINE mode
- /* else if (passives.size() > 0 && defaultSession == null && actives.size() == 0) {
- lblOutput.setText("Track need to be run for passive tests before executing tests");
- return;
- }
- */
+ });
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ gbc.gridx = 6;
+ gbc.gridy = 2;
+ trackContainer.add(btnSetOffline, gbc);
- if (actives.size() == 0) {
- synchronized (lock2) {
- active_ex_finished = true;
- }
- }
+ btnSaveToFile = new JButton("save");
+ btnSaveToFile.addActionListener(actionEvent -> {
+ if (SAVE_TO_FILE) {
+ FileWriter w;
+ try {
+ w = new FileWriter(RECORD_FILE_PATH);
- //FIXME: Passives thread starts without waiting for the end of actives one
- // Execute active tests
- if (actives.size() != 0) {
- try {
- for (String key : session_port.keySet()) {
- if (session_port.get(key).equals("")) {
- lblOutput.setText("session port not configured");
- return;
+ for (HTTPReqRes actual : defaultSession.messages) {
+ Gson geson = new Gson();
+ String serialized = geson.toJson(actual);
+ w.write(serialized + "\n");
}
+ w.close();
+ } catch (IOException e) {
+ e.printStackTrace();
}
- ex = new ExecuteActives(actives, waiting);
-
- editConfigFile("last_browser_used", btnselectChrome.isEnabled() ? "firefox" : "chrome");
+ }
+ });
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(0, 0, 0, 0);
+ gbc.gridx = 6;
+ gbc.gridy = 3;
+ trackContainer.add(btnSaveToFile, gbc);
+ }
- ex.registerExecuteActivesListener(new ExecuteActiveListener() {
- @Override
- public void onExecuteStart() {
- ACTIVE_ENABLED = false;
- act_active_op = new Operation();
- }
+ private JPanel setup_tab_input_json(GridBagLayout bottom_layout) {
+ inputContainer = new JPanel();
+ inputContainer.setLayout(bottom_layout);
- @Override
- public void onExecuteDone() {
- if (passives.size() == 0){
- update_gui_test_results();
+ lblInfo = new JLabel(" ");
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.gridwidth = 2;
+ inputContainer.add(lblInfo, gbc);
- lblOutput.setText("Passive Tests: "
- + (passives.isEmpty() ? 0 : passives.size())
- + " - Active Tests: "
- + (testSuite.getTests().size() - (passives.isEmpty() ? 0 : passives.size())));
- }
- lblOutput.setText("Executed Active tests, now doing passives");
- synchronized (lock2) {
- active_ex_finished = true;
- }
- }
+ lblOutput = new JLabel(" ");
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 0;
+ gbc.gridy = 4;
+ gbc.gridwidth = 2;
+ inputContainer.add(lblOutput, gbc);
+ txtSearch = new JTextArea();
- @Override
- public void onNewProcessOperation(Operation op) {
- ACTIVE_ENABLED = true;
- act_active_op = op;
- }
+ JScrollPane scrollPane2 = new JScrollPane(txtSearch,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.fill = GridBagConstraints.BOTH;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 0;
+ gbc.gridy = 1;
+ gbc.gridwidth = 2;
+ gbc.gridheight = 3;
+ inputContainer.add(scrollPane2, gbc);
- @Override
- public Operation onOperationDone() {
- ACTIVE_ENABLED = false;
- Operation tmp = act_active_op;
+ JButton btnReadJSON = new JButton("Read JSON");
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 2;
+ gbc.gridy = 2;
+ inputContainer.add(btnReadJSON, gbc);
- act_active_op = new Operation();
- return tmp;
- }
+ btnReadJSON.addActionListener(e -> {
+ testSuite = new TestSuite();
- @Override
- public Session onNewSession(Session s) {
- Track track = null;
- try {
- track = s.setTrackFromString(getSessionTxt(s.name));
- } catch (ParsingException e) {
- lblOutput.setText("error in parsing session track");
- return null;
- }
- String port = session_port.get(s.name);
- s.port = port;
- s.track = track;
+ readMsgDefFile(); // Updates the Message Definitions
+ readJSONinput(txtSearch.getText());
- s.ex = new ExecuteTrack(false,
- !btnselectChrome.isEnabled(),
- DRIVER_PATH,
- track,
- port,
- s.name);
- return s;
- }
+ try {
+ updateTxtSessionConfig();
+ } catch (ParsingException exc) {
+ setJSONError(true, "error in updating the session config");
+ }
- @Override
- public void onNewTest(Test actual_test) {
- synchronized (lock) {
- act_test_vars = new ArrayList<>();
- }
- act_active_op = null;
- }
+ lblOutput.setText("Number of Tests: " + testSuite.getTests().size());
- public void onTestDone(Test actual_test) {
- int indx = testSuite.tests.indexOf(actual_test);
- if (indx != -1) {
- System.out.printf("Saving test %s in test results", actual_test.getName());
- //TODO: add log of sessions
- testSuite.tests.set(indx, actual_test);
- actual_test.logTest(LOG_FOLDER);
- }
- }
+ if (testSuite.getTests().size() > 0) {
+ btnExecuteSuite.setEnabled(true);
+ }
+ });
- @Override
- public void onError(Test actual_test) {
- System.err.println("Error executing the test:" + actual_test.name);
- synchronized (lock2) {
- active_ex_finished = true;
- }
- }
+ JButton btnStop = new JButton("Stop");
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 2;
+ gbc.gridy = 1;
+ inputContainer.add(btnStop, gbc);
- @Override
- public List onBeforeExSessionOps() {
- synchronized (lock) {
- return act_test_vars;
- }
- }
+ btnStop.addActionListener(e -> {
+ if (active_ex != null) {
+ active_ex.interrupt();
+ ACTIVE_ENABLED = false;
+ active_ex.stop();
+ }
+ });
- @Override
- public void onAfterExSessionOps(List re) {
- synchronized (lock) {
- act_test_vars = re;
- }
- }
+ btnExecuteSuite = new JButton("Execute Test Suite");
+ btnExecuteSuite.setEnabled(false);
- @Override
- public void onAddVar(Var v) {
- synchronized (lock) {
- act_test_vars.add(v);
- }
- }
- });
- active_ex = new Thread(ex);
- active_ex.start();
+ btnExecuteSuite.addActionListener(e -> {
+ executeSuite();
+ });
- } catch (Exception er) {
- er.printStackTrace();
- System.out.println("" + er.getLocalizedMessage() + "nad" + er.getMessage() + "2" + er);
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.SOUTHWEST;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 2;
+ gbc.gridy = 3;
+ inputContainer.add(btnExecuteSuite, gbc);
+ return inputContainer;
+ }
- lblOutput.setText("PROBLEM IN Executing Suite, check it please");
+ private JScrollPane setup_tab_suite_result(GridBagLayout bottom_layout) {
+ resultTableModel = new DefaultTableModel(foundData, testSuiteColNames) {
+ @Override
+ public boolean isCellEditable(int row, int column) {
+ return false;
}
- }
+ };
- // Execute passive tests
- if (passives.size() != 0) {
- // TODO: Add offline clause
- /*
- if (defaultSession.messages.size() == 0) {
- lblOutput.setText("no message found");
- return;
+ resultTable = new JTable(resultTableModel) {
+ //Implement table cell tool tips.
+ public String getToolTipText(MouseEvent e) {
+ String tip = null;
+ java.awt.Point p = e.getPoint();
+ int rowIndex = rowAtPoint(p);
+ int colIndex = columnAtPoint(p);
+
+ try {
+ tip = getValueAt(rowIndex, colIndex).toString();
+ } catch (RuntimeException e1) {
+ //catch null pointer exception if mouse is over an empty line
+ }
+ return tip;
}
- */
+ };
- ExecutePassiveListener listener = new ExecutePassiveListener() {
- @Override
- public boolean onWaitToStart() {
- synchronized (lock2) {
- return active_ex_finished;
- }
+ resultTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
+ @Override
+ public Component getTableCellRendererComponent(JTable table,
+ Object value,
+ boolean isSelected,
+ boolean hasFocus,
+ int row,
+ int column) {
+ final Component c = super.getTableCellRendererComponent(table,
+ value,
+ isSelected,
+ hasFocus,
+ row,
+ column);
+ if (value == null) return c;
+ if (value.equals("failed")) {
+ c.setBackground(Color.RED);
+ } else {
+ c.setBackground(Color.WHITE);
}
+ return c;
+ }
+ });
- @Override
- public void onExecuteStart() {
+ JScrollPane scrollPane = new JScrollPane(resultTable,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
- }
+ resultTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
- @Override
- public void onExecuteDone(List passives_test) {
- //TODO: Check if this is ok
- lblOutput.setText("Passive Tests: "
- + (passives.isEmpty() ? 0 : passives.size())
- + " - Active Tests: "
- + (testSuite.getTests().size() - (passives.isEmpty() ? 0 : passives.size())));
+ // Adds all the test result to the result table
+ resultTable.getSelectionModel().addListSelectionListener(event -> {
+ if (resultTable.getSelectedRow() > -1) {
- passives = passives_test;
+ int row = resultTable.getSelectedRow();
+ //BurpSuite.getTests.get(row).getTable();
- update_gui_test_results();
- }
+ DefaultTableModel dm = (DefaultTableModel) testTable.getModel();
+ dm.getDataVector().removeAllElements();
+ dm.fireTableDataChanged();
- @Override
- public void onError(String msg) {
- lblOutput.setText(msg);
- }
+ for (String[] act : testSuite.getTests().get(row).getRows()) {
- @Override
- public Session onNewSession(Session s) throws ParsingException {
- //TODO: implement
- s.setTrackFromString(getSessionTxt(s.name));
+ ((DefaultTableModel) testTable.getModel()).addRow(act);
+ }
+ }
+ });
+ return scrollPane;
+ }
- String port = session_port.get(s.name);
- s.port = port;
+ private JSplitPane setup_tab_test_result(GridBagLayout bottom_layout) {
+ // Test Result Tab
+ testTableModel = new DefaultTableModel(foundData, foundTableColNames) {
+ @Override
+ public boolean isCellEditable(int row, int column) {
+ return false;
+ }
+ };
- s.ex = new ExecuteTrack(false,
- !btnselectChrome.isEnabled(),
- DRIVER_PATH,
- s.track,
- port,
- s.name);
+ testTable = new JTable(testTableModel);
+ // Add all the operations result to the table
+ testTable.getSelectionModel().addListSelectionListener(listSelectionEvent -> {
+ if (!listSelectionEvent.getValueIsAdjusting()) {
+ if (testTable.getSelectedRow() == -1) return;
+ int index = Integer.parseInt((String) testTable.getModel().getValueAt(testTable.getSelectedRow(), 4));
+ int op_index = Integer.parseInt((String) testTable.getModel().getValueAt(testTable.getSelectedRow(), 0));
- return s;
- }
+ Operation op = testSuite.tests.get(resultTable.getSelectedRow()).operations.get(op_index);
- @Override
- public void onBeforeExecuteTrack() {
- //Clear previous interceptedMessages if any
- interceptedMessages.clear();
- //Tell Burp Extender class to record the intercepted messages from now on
- recording = true;
+ MessageType msg_type;
+ try {
+ msg_type = MessageType.getFromList(messageTypes, op.getMessageType());
+ } catch (ParsingException e) {
+ throw new RuntimeException(e);
}
- @Override
- public ArrayList onTrackExecuteDone() {
- recording = false;
- return interceptedMessages;
+ for (HTTPReqRes m : op.matchedMessages) {
+ if (m.index == index) {
+ if (msg_type.msg_to_process_is_request) {
+ messageViewer.setMessage(m.getRequest(), true);
+ } else {
+ messageViewer.setMessage(m.getResponse(), false);
+ }
+ break;
+ }
}
- };
+ }
+ });
- ExecutePassives expa = new ExecutePassives(helpers,
- passives,
- listener,
- messageTypes);
+ JScrollPane scrollPane3 = new JScrollPane(testTable,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
- new Thread(expa).start();
- }
- }
+ splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
- /**
- * Function used to get the text of the text area of a certain session using the session name
- *
- * @param session_name the name of the session to get the text
- * @return the content of the session tab if it is not empty, otherwise the main session text tab
- */
- private String getSessionTxt(String session_name) {
- if (sessions_text.containsKey(session_name)) {
- JTextArea t = (JTextArea) sessions_text.get(session_name);
- if (t.getText().equals("")) {
- return txtScript.getText();
- } else {
- return t.getText();
- }
- }
- return null;
- }
+ splitPane.setLeftComponent(scrollPane3);
- /**
- * Update the session config tab, using the session_port variable, at the same time reads if there are changes, and
- * updates the session_port variable
- */
- private void updateTxtSessionConfig() throws ParsingException {
- setSession_configError(false, "");
+ controller = new IMessageEditorController() {
+ @Override
+ public IHttpService getHttpService() {
+ return new IHttpService() {
+ @Override
+ public String getHost() {
+ return null;
+ }
- String text = txtSessionConfig.getText();
- Pattern p = Pattern.compile("\\n");
- Matcher m = p.matcher(text);
- text = m.replaceAll("");
+ @Override
+ public int getPort() {
+ return 0;
+ }
- if (text.equals("")) {
- String tmp = "";
- for (String s : session_port.keySet()) {
- tmp += s;
- tmp += ":"+ session_port.get(s) +";\n";
+ @Override
+ public String getProtocol() {
+ return null;
+ }
+ };
}
- txtSessionConfig.setText(tmp);
- return;
- }
- String[] text_list = text.split(";");
-
- for (String row : text_list) {
- String[] splitted = row.trim().split(":");
- if (splitted.length == 0) continue;
- if (splitted.length <= 1) {
- String[] splitted2 = new String[]{"", ""};
- splitted2[0] = splitted[0];
- splitted = splitted2;
+ @Override
+ public byte[] getRequest() {
+ return viewedMessage.getRequest();
}
- String port = splitted[1].trim();
- p = Pattern.compile("^\\d+$");
- m = p.matcher(splitted[1].trim());
- if (!m.find()) {
- setSession_configError(true, "invalid port");
- throw new ParsingException("Invalid port");
+ @Override
+ public byte[] getResponse() {
+ return viewedMessage.getResponse();
}
- if (session_port.containsKey(splitted[0].trim())) {
- session_port.replace(splitted[0].trim(), splitted[1].trim());
- } else {
- session_port.put((splitted[0]).trim(), splitted[1].trim());
+ };
+ return splitPane;
+ }
+
+ private JPanel setup_tab_session_config(GridBagLayout bottom_layout) {
+ JPanel sessionConfig = new JPanel();
+ sessionConfig.setLayout(bottom_layout);
+
+ txtSessionConfig = new JTextArea();
+ JScrollPane scrollPane5 = new JScrollPane(txtSessionConfig, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.fill = GridBagConstraints.BOTH;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 0;
+ gbc.gridy = 1;
+ gbc.gridwidth = 2;
+ gbc.gridheight = 3;
+ sessionConfig.add(scrollPane5, gbc);
+
+ JButton btnSetSessionConfig = new JButton("save");
+ btnSetSessionConfig.addActionListener(actionEvent -> {
+ try {
+ updateTxtSessionConfig();
+ } catch (ParsingException e) {
+ e.printStackTrace();
}
- }
- String tmp = "";
- for (String key : session_port.keySet()) {
- tmp += key + ": " + session_port.get(key) + ";\n";
- }
+ });
- txtSessionConfig.setText(tmp);
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.fill = GridBagConstraints.BOTH;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 2;
+ gbc.gridy = 3;
+ gbc.gridwidth = 1;
+ gbc.gridheight = 1;
+ sessionConfig.add(btnSetSessionConfig, gbc);
+
+ return sessionConfig;
}
- /**
- * This function updates the session tabs in the gui to match the actual value in session_names
- */
- private void updateSessionTabs() {
- List present = new ArrayList<>();
+ private JPanel setub_tab_debug(GridBagLayout bottom_layout) {
+ // Debug Tab
+ JPanel debugPanel = new JPanel();
- for (int i = 1; i < top_tabbed.getTabCount(); i++) {
- present.add(top_tabbed.getTitleAt(i));
- if (!sessions_names.contains(top_tabbed.getTitleAt(i))) {
- top_tabbed.remove(i);
- }
- }
- for (String name : sessions_names) {
- if (!present.contains(name)) {
- JTextArea tmp = new JTextArea();
- JScrollPane sp = new JScrollPane(tmp,
- JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
- JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
- sessions_text.put(name, tmp);
- top_tabbed.add(name, sp);
- }
- }
- JTextArea tmp = new JTextArea();
- JScrollPane sp = new JScrollPane(tmp,
+ debugPanel.setLayout(bottom_layout);
+
+ txt_out_debug_tab.setEditable(false);
+ JScrollPane scrollPane6 = new JScrollPane(txt_out_debug_tab,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
- }
+ GridBagConstraints constraints1 = new GridBagConstraints();
+ constraints1.gridx = 0;
+ constraints1.gridy = 1;
+ constraints1.gridwidth = 1; // One cell wide
+ constraints1.gridheight = 3; // One cell high
+ constraints1.fill = GridBagConstraints.BOTH;
+ constraints1.weightx = 0.5; // Half of the horizontal space
+ constraints1.weighty = 0.5; // Half of the vertical space
+ debugPanel.add(scrollPane6, constraints1);
+
+ txt_err_debug_tab.setEditable(false);
+ JScrollPane scrollPane7 = new JScrollPane(txt_err_debug_tab,
+ JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ GridBagConstraints constraints2 = new GridBagConstraints();
+ constraints2.gridx = 1;
+ constraints2.gridy = 1;
+ constraints2.gridwidth = 1; // One cell wide
+ constraints2.gridheight = 3; // One cell high
+ constraints2.fill = GridBagConstraints.BOTH;
+ constraints2.weightx = 0.5; // Half of the horizontal space
+ constraints2.weighty = 0.5; // Half of the vertical space
+ debugPanel.add(scrollPane7, constraints2);
+
+ JButton btn_clear = new JButton("clear");
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 2;
+ gbc.gridy = 1;
+ debugPanel.add(btn_clear, gbc);
- /**
- * Function used to set the JSON textbox with a red colour to highlight an error.
- * @param isInError true to highlight an error, false to remove the highlight
- * @param msg the error message to display
- */
- private void setJSONError(boolean isInError, String msg) {
- if (isInError) {
- txtSearch.setBorder(BorderFactory.createLineBorder(Color.RED, 3));
- lblOutput.setText(msg);
- bot_tabbed.setBackgroundAt(bot_tabs_index.get("Input JSON"), Color.RED);
- } else {
- txtSearch.setBorder(BorderFactory.createEmptyBorder());
- lblOutput.setText("");
- bot_tabbed.setBackgroundAt(bot_tabs_index.get("Input JSON"), Color.white);
- }
- }
+ btn_clear.addActionListener(e -> {
+ txt_out_debug_tab.setText("");
+ txt_err_debug_tab.setText("");
+ });
- /**
- * Function used to set the session config textbox with a red border to highlight an error.
- * @param isInError true to highlight an error, false to remove highlight
- * @param msg The error message to display
- */
- private void setSession_configError(boolean isInError, String msg) {
- if (isInError) {
- txtSessionConfig.setBorder(BorderFactory.createLineBorder(Color.RED, 3));
- lblOutput.setText(msg);
- bot_tabbed.setBackgroundAt(bot_tabs_index.get("session config"), Color.RED);
- } else {
- txtSessionConfig.setBorder(BorderFactory.createEmptyBorder());
- lblOutput.setText("");
- bot_tabbed.setBackgroundAt(bot_tabs_index.get("session config"), Color.white);
- }
- }
+ JLabel lbl_left = new JLabel("Output log");
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 0;
+ gbc.gridy = 0;
+ gbc.gridwidth = 2;
+ debugPanel.add(lbl_left, gbc);
- /**
- * Function used to update the gui test results after the tests are executed
- */
- private void update_gui_test_results() {
- for (Test t : testSuite.getTests()) {
- String esito = "";
- if (t.applicable) {
- esito = t.success ? "passed" : "failed";
- } else {
- esito = "not applicable";
- }
- String[] tmp = new String[]{t.getName(),
- t.getDescription(),
- t.references,
- t.violated_properties,
- t.affected_entity,
- t.mitigations,
- esito};
- System.out.println(t.getName() + " " + esito);
- addItem(tmp);
- }
+ JLabel lbl_right = new JLabel("Error log");
+ gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.WEST;
+ gbc.fill = GridBagConstraints.HORIZONTAL;
+ gbc.insets = new Insets(10, 10, 10, 10);
+ gbc.gridx = 1;
+ gbc.gridy = 0;
+ gbc.gridwidth = 2;
+ debugPanel.add(lbl_right, gbc);
- btnExecuteSuite.setEnabled(false);
+ return debugPanel;
}
}
\ No newline at end of file
diff --git a/tool/src/main/java/migt/HTTPReqRes.java b/tool/src/main/java/migt/HTTPReqRes.java
new file mode 100644
index 0000000..d5aed22
--- /dev/null
+++ b/tool/src/main/java/migt/HTTPReqRes.java
@@ -0,0 +1,540 @@
+package migt;
+
+import burp.IExtensionHelpers;
+import burp.IHttpRequestResponse;
+import burp.IHttpRequestResponsePersisted;
+import burp.IHttpService;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Class which is intended to substitute the IHTTPRequestResponse
one, because of serialization support
+ *
+ * @author Matteo Bitussi
+ */
+public class HTTPReqRes implements Cloneable {
+ static public int instances;
+ public Integer index = -1; // index of the message wrt the burp proxy
+ public boolean isRequest = false;
+ public boolean isResponse = false;
+ public int body_offset_req; // identifies the index where the body ends in the request
+ public int body_offset_resp; // the index where teh body of the response starts
+ byte[] body_req = null; // the body of the request message
+ byte[] body_resp = null; // the body of the response message
+ // host data
+ private String host;
+ private int port = 0;
+ private String protocol;
+ // message data
+ private String request_url; // The url of the request (not the header)
+ private byte[] request;
+ private byte[] response;
+ private List headers_req; // the headers of the request
+ private List headers_resp; // the headers of the response
+
+ /**
+ * Instantiate an HTTPReqRes element
+ *
+ * @param request the request in byte[] form
+ * @param response the response in byte[] form
+ */
+ public HTTPReqRes(byte[] request, byte[] response) {
+ this.isRequest = true;
+ this.isResponse = true;
+ this.setRequest(request);
+ this.setResponse(response);
+ instances++;
+ }
+
+ /**
+ * Instantiate an HTTPReqRes element from a IHttpRequestResponsePersisted
message
+ *
+ * @param message the message to be created from
+ * @param helpers the helpers
+ */
+ public HTTPReqRes(IHttpRequestResponsePersisted message, IExtensionHelpers helpers) {
+ this.isRequest = true;
+ this.isResponse = true;
+ this.setRequest(message.getRequest());
+ this.setResponse(message.getResponse());
+ this.setRequest_url(helpers.analyzeRequest(message).getUrl().toString());
+ IHttpService service = message.getHttpService();
+ this.setHost(service.getHost());
+ this.setPort(service.getPort());
+ this.setProtocol(service.getProtocol());
+ this.body_offset_req = helpers.analyzeRequest(message.getRequest()).getBodyOffset();
+ this.body_offset_resp = helpers.analyzeResponse(message.getResponse()).getBodyOffset();
+ this.headers_req = helpers.analyzeRequest(message.getRequest()).getHeaders();
+ this.headers_resp = helpers.analyzeResponse(message.getResponse()).getHeaders();
+
+ instances++;
+ }
+
+ /**
+ * Instantiate an HTTPReqRes element. If a message is a request it does not gather the response
+ *
+ * @param message an IHTTPRequestResponse message
+ * @param helpers an istance of the IExtensionHelpers
+ * @param isRequest true if the message is a request, false otherwise
+ */
+ public HTTPReqRes(IHttpRequestResponse message, IExtensionHelpers helpers, Boolean isRequest, int index) {
+ if (!isRequest) {
+ this.isResponse = true;
+ this.setResponse(message.getResponse());
+ this.headers_resp = helpers.analyzeResponse(message.getResponse()).getHeaders();
+ this.body_offset_resp = helpers.analyzeRequest(message.getResponse()).getBodyOffset();
+ }
+
+ this.index = index;
+
+ // the request is always present in a IHTTPRequestResponse
+ this.isRequest = true;
+ this.setRequest(message.getRequest());
+ this.setRequest_url(helpers.analyzeRequest(message).getUrl().toString());
+ this.headers_req = helpers.analyzeRequest(message.getRequest()).getHeaders();
+ this.request_url = helpers.analyzeRequest(message).getUrl().toString();
+ this.body_offset_req = helpers.analyzeRequest(message.getRequest()).getBodyOffset();
+
+ // set host info
+ IHttpService service = message.getHttpService();
+ this.setHost(service.getHost());
+ this.setPort(service.getPort());
+ this.setProtocol(service.getProtocol());
+
+ instances++;
+ }
+
+ /**
+ * Function used to replace an IHttpRequestResponse message with the values contained in this object
+ *
+ * @param message the message to be replaced
+ * @param helpers the burp helpers
+ * @return the edited message with the request and/or response replaced
+ */
+ public IHttpRequestResponse replaceBurpMessage(IHttpRequestResponse message, IExtensionHelpers helpers) {
+ // TODO: eventually rebuild the message with the edited parts, here or when edited ?
+ if (isRequest) {
+ message.setRequest(request);
+ }
+ if (isResponse) {
+ message.setResponse(response);
+ }
+ if (host != null && port != 0 && protocol != null) {
+ message.setHttpService(
+ this.getHttpService(helpers)
+ );
+ }
+ return message;
+ }
+
+ public IHttpService getHttpService(IExtensionHelpers helpers) {
+ return helpers.buildHttpService(
+ host,
+ port,
+ protocol
+ );
+ }
+
+ public String getUrlHeader() {
+ if (!isRequest)
+ throw new RuntimeException("called getUrlHeader on a response message");
+
+ return this.headers_req.get(0);
+ }
+
+ public void setUrlHeader(String url_header) {
+ if (!isRequest)
+ throw new RuntimeException("called setUrlHeader on a response message");
+
+ this.headers_req.set(0, url_header);
+ }
+
+ public byte[] getBody(boolean isRequest) {
+ if (isRequest && (this.body_offset_req == 0 | this.request == null | this.request.length == 0)) {
+ throw new RuntimeException("called getBody, but class is not properly initialized");
+ }
+ if (!isRequest && (this.body_offset_resp == 0 | this.response == null | this.response.length == 0)) {
+ throw new RuntimeException("called getBody, but class is not properly initialized");
+ }
+
+ if (isRequest) {
+ // if asking for the first time, take the body from the message
+ if (this.body_req == null)
+ this.body_req = Arrays.copyOfRange(this.request, this.body_offset_req, this.request.length);
+ return this.body_req;
+ } else {
+ if (this.body_resp == null)
+ this.body_resp = Arrays.copyOfRange(this.response, this.body_offset_resp, this.response.length);
+ return this.body_resp;
+ }
+ }
+
+ public List getHeaders(boolean isRequest) {
+ return isRequest ? this.headers_req : this.headers_resp;
+ }
+
+ /**
+ * Used to build the message based on the changes made
+ */
+ private byte[] build_message(IExtensionHelpers helpers, boolean isRequest) {
+ // TODO: this could be written avoiding helpers class
+ // TODO: url is not updated
+ if (isRequest) {
+ this.request = helpers.buildHttpMessage(headers_req, getBody(true));
+ return this.request;
+ } else {
+ this.response = helpers.buildHttpMessage(headers_resp, getBody(false));
+ return this.response;
+ }
+ }
+
+ /**
+ * Builds the message taking the headers and the body, without using the burp's helpers.
+ *
+ * @param isRequest true if message is a request message
+ * @return
+ */
+ public byte[] build_message(boolean isRequest) {
+ String builded = "";
+ byte[] body = getBody(isRequest);
+
+ List headers = getHeaders(isRequest);
+ String content_header = "Content-Length: " + body.length;
+
+ for (String header : headers) {
+ if (header.contains("Content-Length:")) {
+ if (body.length == 0)
+ continue;// if Content-Length header found, but message has no body, remove.
+ builded += content_header;
+
+ } else {
+ builded += header;
+ }
+ builded += "\r\n";
+ }
+ builded += "\r\n"; // last row of header before body
+
+ if (body.length != 0)
+ builded += new String(body);
+
+ return builded.getBytes(StandardCharsets.UTF_8);
+ }
+
+ /**
+ * Get the message in bytes with the changes made
+ *
+ * @param isRequest to specify the request or the response
+ * @return the message
+ */
+ public byte[] getMessage(boolean isRequest, IExtensionHelpers helpers) {
+ if (isRequest && this.request == null) {
+ throw new RuntimeException("Called getMessage on a message that is not initialized");
+ } else if (!isRequest && this.response == null) {
+ throw new RuntimeException("Called getMessage on a message that is not initialized");
+ }
+ build_message(helpers, isRequest);
+
+ return isRequest ? request : response;
+ }
+
+ /**
+ * Get the message without updating it with the changes
+ *
+ * @param isRequest
+ * @return
+ */
+ public byte[] getMessage(boolean isRequest) {
+ // TODO: this is probably a source of bugs, called without noticing that it doesnt get the updated message
+ if (isRequest && this.request == null) {
+ throw new RuntimeException("Called getMessage on a message that is not initialized");
+ } else if (!isRequest && this.response == null) {
+ throw new RuntimeException("Called getMessage on a message that is not initialized");
+ }
+
+ return isRequest ? request : response;
+ }
+
+ public void setHeaders(boolean isRequest, List headers) {
+ if (isRequest) {
+ this.headers_req = headers;
+ } else {
+ this.headers_resp = headers;
+ }
+ }
+
+ /**
+ * Set the body of the request or response message with a new value
+ *
+ * @param isRequest
+ * @param body
+ */
+ public void setBody(boolean isRequest, byte[] body) {
+ if (isRequest) {
+ this.body_req = body;
+ } else {
+ this.body_resp = body;
+ }
+ }
+
+ /**
+ * Set the body of the request or response message with a new value
+ *
+ * @param isRequest
+ * @param body
+ */
+ public void setBody(boolean isRequest, String body) {
+ setBody(isRequest, body.getBytes());
+ }
+
+ /**
+ * Get the original un-edited request message
+ *
+ * @return
+ */
+ public byte[] getRequest() {
+ return request;
+ }
+
+ public void setRequest(byte[] request) {
+ this.request = request;
+ }
+
+ /**
+ * Get the original un-edited response message
+ *
+ * @return
+ */
+ public byte[] getResponse() {
+ return response;
+ }
+
+ public void setResponse(byte[] response) {
+ this.response = response;
+ }
+
+ public String getUrl() {
+ return this.request_url;
+ }
+
+ public String getRequest_url() {
+ return request_url;
+ }
+
+ public void setRequest_url(String request_url) {
+ this.request_url = request_url;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getProtocol() {
+ return protocol;
+ }
+
+ public void setProtocol(String protocol) {
+ this.protocol = protocol;
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ /**
+ * Get the given parameter value from the url of the request messsage
+ *
+ * @param param the parameter name to be searched
+ * @return the value of the parameter
+ */
+ public String getUrlParam(String param) {
+ if (!isRequest || request_url == null) {
+ throw new RuntimeException("Trying to access the url of a response message");
+ }
+
+ Pattern pattern = Pattern.compile("(?<=" + param + "=)[^$\\n&\\s]*");
+ Matcher matcher = pattern.matcher(this.request_url);
+ String res = "";
+ while (matcher.find()) {
+ res = matcher.group();
+ break;
+ }
+ return res;
+ }
+
+ /**
+ * Get the given parameter value from the head
+ *
+ * @param isRequest if the message is a request
+ * @param param the parameter name to be searched
+ * @return the value of the parameter
+ */
+ public String getHeadParam(Boolean isRequest, String param) {
+ List headers = isRequest ? this.headers_req : this.headers_resp;
+
+ for (String s : headers) {
+ if (s.contains(param)) {
+ String value = s.substring(s.indexOf(":") + 1);
+ return value.strip();
+ }
+ }
+ return "";
+ }
+
+
+ public void editHeadParam(Boolean isRequest, String param, String new_value) {
+ List headers = isRequest ? this.headers_req : this.headers_resp;
+
+ int indx = -1;
+
+ for (String s : headers) {
+ if (s.contains(param)) {
+ indx = headers.indexOf(s);
+ break;
+ }
+ }
+
+ if (isRequest) {
+ headers_req.set(indx, param + ": " + new_value);
+ } else {
+ headers_resp.set(indx, param + ": " + new_value);
+ }
+ }
+
+ public void addHeadParameter(boolean isRequest, String name, String value) {
+ (isRequest ? this.headers_req : this.headers_resp).add(name + ": " + value);
+ }
+
+ public void removeHeadParameter(boolean isRequest, String name) {
+ List headers = isRequest ? this.headers_req : this.headers_resp;
+
+ for (String h : headers) {
+ if (h.contains(name)) {
+ headers.remove(h);
+ break;
+ }
+ }
+
+ if (isRequest) {
+ this.headers_req = headers;
+ } else {
+ this.headers_resp = headers;
+ }
+ }
+
+ /**
+ * Given a message, get the given parameter value from the body, note that it accepts a regular expression, and
+ * everything matched will be returned as a value
+ *
+ * @param isRequest if the message is a request
+ * @param param the parameter to be searched as a regex, everything matched by this will be returned as a value
+ * @return the value of the parameter
+ */
+ public String getBodyRegex(Boolean isRequest, String param) {
+ Pattern pattern = Pattern.compile(param);
+ Matcher matcher = pattern.matcher(new String(getBody(isRequest), StandardCharsets.UTF_8));
+
+ String res = "";
+ while (matcher.find()) {
+ res = matcher.group();
+ break;
+ }
+ return res;
+ }
+
+ /**
+ * Function to check if the given message matches a message_type
+ *
+ * @param msg_type the message type to check against it
+ * @return true or false, if matched or not respectively
+ */
+ public boolean matches_msg_type(MessageType msg_type) {
+ boolean matchedMessage = false;
+ try {
+ /* If the response message name is searched, the getByResponse will be true.
+ * so messageIndex have to search for the request, and then evaluate the response
+ */
+ if (msg_type.getByResponse) {
+ if (!isResponse) return false; // both request and response have to be present
+ matchedMessage = Tools.executeChecks(
+ msg_type.checks,
+ this,
+ true,
+ new ArrayList<>() // TODO: fix
+ );
+ } else if (msg_type.getByRequest) {
+ if (!isResponse) return false; // both request and response have to be present
+ matchedMessage = Tools.executeChecks(
+ msg_type.checks,
+ this,
+ false,
+ new ArrayList<>() // TODO: fix
+ );
+ } else {
+ if (!msg_type.isRequest && !isResponse) return false; // this message is not containing a response
+ matchedMessage = Tools.executeChecks(
+ msg_type.checks,
+ this,
+ msg_type.isRequest,
+ new ArrayList<>() // TODO: fix
+ );
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return matchedMessage;
+ }
+
+ /**
+ * An enum representing the possible message sections
+ */
+ public enum MessageSection {
+ HEAD,
+ BODY,
+ URL,
+ RAW;
+
+ /**
+ * Function that given a message section in form of String, returns the corresponding MessageSection enum value
+ *
+ * @param input the input string
+ * @return the MessageSection enum value
+ * @throws ParsingException if the input does not correspond to any of the possible messagesections
+ */
+ public static MessageSection fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "head":
+ return HEAD;
+ case "body":
+ return BODY;
+ case "url":
+ return URL;
+ case "raw":
+ return RAW;
+ default:
+ throw new ParsingException("message section not valid");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+}
diff --git a/tool/src/main/java/migt/JWT.java b/tool/src/main/java/migt/JWT.java
new file mode 100644
index 0000000..c2083f6
--- /dev/null
+++ b/tool/src/main/java/migt/JWT.java
@@ -0,0 +1,295 @@
+package migt;
+
+import com.nimbusds.jose.*;
+import com.nimbusds.jose.crypto.*;
+import com.nimbusds.jose.jwk.JWK;
+import com.nimbusds.jwt.SignedJWT;
+import org.json.JSONException;
+
+import java.text.ParseException;
+import java.util.Base64;
+
+
+/**
+ * Class to manage JWT tokens
+ * Uses https://connect2id.com/products/nimbus-jose-jwt
+ * {@code @Author} Matteo Bitussi
+ */
+public class JWT {
+ public String header;
+ public String payload;
+ public String signature;
+ public String raw;
+ public boolean sign; // put to true if you want to sign the jwt after edit (need private key)
+ public String private_key_pem; // PEM the private key used to sign the jwt
+ public String public_key_pem; // PEM public key used to check the signature of the jwt
+ public boolean decrypt; // put to true if the raw is a JWE, and you want to decrypt it
+ public String private_key_pem_enc;
+ public String public_key_pem_enc;
+ public JWEObject jwe;
+ JOSEObject parsed_jwt;
+ EncryptingAlg e_alg;
+ SigningAlgs signing_alg;
+
+ /**
+ * Constructor that instantiate a JWT object
+ */
+ public JWT() {
+ raw = "";
+ signature = "";
+ header = "";
+ payload = "";
+ sign = false;
+ private_key_pem = "";
+ public_key_pem = "";
+ private_key_pem_enc = "";
+ public_key_pem_enc = "";
+ decrypt = false;
+ jwe = null;
+ e_alg = null;
+ }
+
+ /**
+ * Parse a JWT token from a string and stores header, payload, and signature string values inside the JWT object
+ *
+ * @param raw_jwt the raw JWT in string format
+ * @throws ParsingException if there are problems in the parsing
+ */
+ public void parse(String raw_jwt) throws ParsingException {
+ this.raw = raw_jwt;
+
+ if (decrypt) {
+ // it is a JWE containing a JWT
+ try {
+ jwe = JWEObject.parse(raw_jwt);
+ e_alg = EncryptingAlg.fromString(jwe.getHeader().getAlgorithm().getName());
+ JWK jwk_private_enc = JWK.parseFromPEMEncodedObjects(private_key_pem_enc);
+
+ switch (e_alg) {
+ case RSA_OAEP:
+ case RSA_OAEP_256:
+ jwe.decrypt(new RSADecrypter(jwk_private_enc.toRSAKey()));
+ break;
+ case ECDH_ES_A128KW:
+ case ECDH_ES_A256KW:
+ jwe.decrypt(new ECDHDecrypter(jwk_private_enc.toECKey()));
+ break;
+ }
+
+ parsed_jwt = jwe;
+
+ if (parsed_jwt == null) {
+ throw new ParsingException("Error, JWE payload is not a JWS");
+ }
+ } catch (ParseException | JOSEException e) {
+ throw new ParsingException("problem in decrypting JWE: " + e);
+ }
+ }
+
+ try {
+ if (!decrypt) // otherwise it is already parsed
+ parsed_jwt = SignedJWT.parse(raw_jwt);
+ if (parsed_jwt instanceof JWSObject) {
+ Header header = parsed_jwt.getHeader();
+ signing_alg = SigningAlgs.fromString(header.getAlgorithm().getName());
+ }
+ } catch (ParseException e) {
+ throw new ParsingException("Error while parsing jwt: " + e);
+ }
+
+ try {
+ header = parsed_jwt.getHeader().toString();
+ payload = parsed_jwt.getPayload().toString();
+ signature = parsed_jwt instanceof JWSObject ?
+ ((JWSObject) parsed_jwt).getSignature().toString() : null;
+ } catch (JSONException e) {
+ throw new ParsingException("Error parsing JWT tokens");
+ }
+ }
+
+ /**
+ * Check the signature of the jws using the public_key_pem
+ *
+ * @return true if the jwt signature is valid, false otherwise
+ * @throws ParsingException if something goes wrong while checking signature of the jwt
+ */
+ public boolean check_sig() throws ParsingException {
+ boolean res = false;
+ if (parsed_jwt == null) {
+ throw new RuntimeException("JWT need to be parsed before checking signature");
+ }
+
+ if (!(parsed_jwt instanceof JWSObject))
+ throw new RuntimeException("trying to check the signature of a JWE");
+
+ JWK pub_key_jwk = null;
+ try {
+ pub_key_jwk = JWK.parseFromPEMEncodedObjects(public_key_pem);
+ } catch (JOSEException e) {
+ throw new ParsingException("Problem in loading public key, " + e);
+ }
+ switch (signing_alg) {
+ case RS256:
+ case RS512:
+ JWSVerifier verifier = null;
+ try {
+ verifier = new RSASSAVerifier(pub_key_jwk.toRSAKey());
+ } catch (JOSEException e) {
+ throw new ParsingException("Invalid public key used do verify jwt. " + e);
+ }
+ try {
+ res = ((JWSObject) parsed_jwt).verify(verifier);
+ } catch (JOSEException e) {
+ throw new ParsingException("The jws could not be verified. " + e);
+ }
+ break;
+ }
+ return res;
+ }
+
+ /**
+ * Builds a JWT token from the string values in this class
+ *
+ * @return A JWT as a string
+ * @throws ParsingException if there are problems building the jwt
+ */
+ public String build() throws ParsingException {
+ String res = "";
+
+ if (this.parsed_jwt == null) {
+ throw new ParsingException("error in building jwt, no jwt have been parsed");
+ }
+
+ if (this.header.equals("") || this.payload.equals(""))
+ throw new ParsingException("error in building jwt, empty values");
+
+ res += Base64.getEncoder().encodeToString(header.getBytes());
+ res += "." + Base64.getEncoder().encodeToString(payload.getBytes());
+
+ if (sign) {
+ // sign the jwt with sk
+ JWSObject signed_jws = null;
+ JWK private_key_jwk = null;
+ try {
+ JWSHeader header_j = JWSHeader.parse(header);
+ Payload payload_j = new Payload(payload);
+ signed_jws = new JWSObject(header_j, payload_j);
+ private_key_jwk = JWK.parseFromPEMEncodedObjects(private_key_pem);
+ } catch (ParseException e) {
+ throw new ParsingException("unable to build jwt: " + e);
+ } catch (JOSEException e) {
+ throw new ParsingException("Unable to load public key to sign jwt" + e);
+ }
+ JWSSigner signer = null;
+
+ try {
+ switch (signing_alg) {
+ case RS256:
+ case RS512:
+ signer = new RSASSASigner(private_key_jwk.toRSAKey());
+ break;
+ default:
+ throw new ParsingException("unsupported signing algorithm" + signing_alg);
+ }
+ } catch (JOSEException e) {
+ throw new ParsingException("unable to use public key to sign jwt: " + e);
+ } catch (IllegalArgumentException e) {
+ throw new ParsingException("invalid private key: " + e);
+ }
+
+ try {
+ signed_jws.sign(signer);
+ } catch (JOSEException e) {
+ throw new ParsingException("unable to sign jwt: " + e);
+ }
+
+ res = signed_jws.serialize();
+ } else {
+ if (signature != null && !signature.equals("")) {
+ res += "." + signature;
+ }
+ }
+
+ if (decrypt) {
+ if (!(parsed_jwt instanceof JWEObject))
+ throw new RuntimeException("tried to encrypt a JWT");
+
+ if (public_key_pem_enc.length() != 0) {
+ // if the JWE has been decrypted, now it needs to be re-encrypted
+ try {
+ JWEObject editedJWE = new JWEObject(
+ JWEHeader.parse(header),
+ new Payload(payload)
+ );
+
+ switch (e_alg) {
+ case RSA_OAEP:
+ case RSA_OAEP_256:
+ editedJWE.encrypt(
+ new RSAEncrypter(JWK.parseFromPEMEncodedObjects(public_key_pem_enc).toRSAKey()));
+ break;
+ case ECDH_ES_A128KW:
+ case ECDH_ES_A256KW:
+ editedJWE.encrypt(
+ new ECDHEncrypter(JWK.parseFromPEMEncodedObjects(public_key_pem_enc).toECKey()));
+ break;
+ }
+
+ res = editedJWE.serialize();
+ } catch (JOSEException | java.text.ParseException e) {
+ throw new ParsingException("Unable to encrypt JWE " + e);
+ }
+ } else {
+ // if no public key is provided, the jwe will not be edited
+ res = raw;
+ }
+ }
+
+ res = res.replaceAll("=", "");
+ return res;
+ }
+
+ /**
+ * All JWS signing algs supported
+ */
+ public enum SigningAlgs {
+ RS256,
+ RS512;
+
+ public static SigningAlgs fromString(String algStr) throws ParsingException {
+ switch (algStr) {
+ case "RS256":
+ return RS256;
+ case "RS512":
+ return RS512;
+ default:
+ throw new ParsingException("Unsupported signing algorithm \"" + algStr + "\"");
+ }
+ }
+ }
+
+ /**
+ * All JWE encrypting algs supported
+ */
+ public enum EncryptingAlg {
+ RSA_OAEP,
+ RSA_OAEP_256,
+ ECDH_ES_A128KW,
+ ECDH_ES_A256KW;
+
+ public static EncryptingAlg fromString(String algStr) throws ParsingException {
+ switch (algStr) {
+ case "RSA-OAEP":
+ return RSA_OAEP;
+ case "RSA-OAEP-256":
+ return RSA_OAEP_256;
+ case "ECDH-ES+A128KW":
+ return ECDH_ES_A128KW;
+ case "ECDH-ES+A256KW":
+ return ECDH_ES_A256KW;
+ default:
+ throw new ParsingException("Encrypting algorithm " + algStr + " not supported");
+ }
+ }
+ }
+}
diff --git a/tool/src/main/java/burp/Marker.java b/tool/src/main/java/migt/Marker.java
similarity index 84%
rename from tool/src/main/java/burp/Marker.java
rename to tool/src/main/java/migt/Marker.java
index 468b649..2309f5a 100644
--- a/tool/src/main/java/burp/Marker.java
+++ b/tool/src/main/java/migt/Marker.java
@@ -1,4 +1,6 @@
-package burp;
+package migt;
+
+import java.util.Objects;
/**
* Class used to mark User Actions to be managed by session actions
@@ -10,6 +12,7 @@ public class Marker {
/**
* Constructor to instantiate a new marker object
+ *
* @param _name name of the marker
*/
public Marker(String _name) {
@@ -23,6 +26,6 @@ public boolean equals(Object o) {
Marker marker = (Marker) o;
- return name != null ? name.equals(marker.name) : marker.name == null;
+ return Objects.equals(name, marker.name);
}
}
\ No newline at end of file
diff --git a/tool/src/main/java/migt/MessageOperation.java b/tool/src/main/java/migt/MessageOperation.java
new file mode 100644
index 0000000..24e2b20
--- /dev/null
+++ b/tool/src/main/java/migt/MessageOperation.java
@@ -0,0 +1,479 @@
+package migt;
+
+import burp.IExtensionHelpers;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static migt.Tools.getAdding;
+
+/**
+ * The class storing a MessageOperation object
+ *
+ * @author Matteo Bitussi
+ */
+public class MessageOperation extends Module {
+ HTTPReqRes.MessageSection from;
+ String what;
+ String to;
+ MessageOperationActions action;
+ String save_as; // The name of the variable to save the parameter's value
+ String use;
+ MessageOpType type;
+
+ // GENERATE POC
+ String template;
+ String output_path;
+
+ /**
+ * Used to Instantiate the class
+ */
+ public MessageOperation() {
+ init();
+ }
+
+ public MessageOperation(JSONObject message_op_json) throws ParsingException {
+ init();
+
+ java.util.Iterator keys = message_op_json.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+
+ switch (key) {
+ case "from":
+ from = HTTPReqRes.MessageSection.fromString(message_op_json.getString("from"));
+ break;
+ case "remove parameter":
+ what = message_op_json.getString("remove parameter");
+ action = MessageOperationActions.REMOVE_PARAMETER;
+ break;
+ case "remove match word":
+ what = message_op_json.getString("remove match word");
+ action = MessageOperationActions.REMOVE_MATCH_WORD;
+ break;
+ case "edit":
+ what = message_op_json.getString("edit");
+ action = MessageOperationActions.EDIT;
+ break;
+ case "edit regex":
+ what = message_op_json.getString("edit regex");
+ action = MessageOperationActions.EDIT_REGEX;
+ break;
+ case "in":
+ to = message_op_json.getString("in");
+ break;
+ case "add":
+ what = message_op_json.getString("add");
+ action = MessageOperationActions.ADD;
+ break;
+ case "this":
+ to = message_op_json.getString("this");
+ break;
+ case "save":
+ what = message_op_json.getString("save");
+ action = MessageOperationActions.SAVE;
+ break;
+ case "save match":
+ what = message_op_json.getString("save match");
+ action = MessageOperationActions.SAVE_MATCH;
+ break;
+ case "as":
+ save_as = message_op_json.getString("as");
+ break;
+ case "use":
+ use = message_op_json.getString("use");
+ break;
+ case "type":
+ type = MessageOpType.fromString(
+ message_op_json.getString("type"));
+ break;
+ case "template":
+ template = message_op_json.getString("template");
+ break;
+ case "output_path":
+ output_path = message_op_json.getString("output_path");
+ break;
+ default:
+ System.err.println(key);
+ throw new ParsingException("Message operation not valid");
+ }
+ }
+ }
+
+ public void init() {
+ this.what = "";
+ this.to = "";
+ this.save_as = "";
+ this.use = "";
+ this.type = MessageOpType.HTTP;
+ this.template = "";
+ this.output_path = "";
+ }
+
+ public void loader(Operation_API api) {
+ this.imported_api = api;
+ }
+
+ public Operation_API exporter() {
+ if (imported_api instanceof Operation_API) {
+ return (Operation_API) this.imported_api;
+ }
+ return null;
+ }
+
+ /**
+ * Given an operation, and a message, execute the Message operations contained in the operation
+ *
+ * @param op the operation containing the message operation
+ * @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 {
+ for (MessageOperation mop : op.getMessageOperations()) {
+ Pattern pattern;
+ Matcher matcher;
+ try {
+ if (mop.type == MessageOperation.MessageOpType.GENERATE_POC) {
+ if (!op.api.is_request) {
+ throw new ParsingException("Invalid POC generation, message should be a request");
+ }
+
+ if (!mop.template.equals("csrf")) {
+ continue; // other templates not supported yet
+ }
+
+ String poc = Tools.generate_CSRF_POC(op.api.message);
+
+ try {
+ File myObj = new File(mop.output_path);
+ myObj.createNewFile();
+ } catch (IOException e) {
+ throw new ParsingException("Invalid POC generation output path: "
+ + mop.output_path + " " + e.getMessage());
+ }
+ try {
+ FileWriter myWriter = new FileWriter(mop.output_path);
+ myWriter.write(poc);
+ myWriter.close();
+ } catch (IOException e) {
+ throw new ParsingException("Something went wrong while writing output file for POC generator: "
+ + mop.output_path + " " + e.getMessage());
+ }
+ } else {
+ if (mop.action != null) {
+ switch (mop.action) {
+ case REMOVE_PARAMETER:
+ switch (mop.from) {
+ case URL:
+ // Works
+ if (!op.api.is_request) {
+ throw new ParsingException("Searching URL in response");
+ }
+ String url_header = op.api.message.getUrlHeader();
+ pattern = Pattern.compile("&?" + mop.what + "=[^& ]*((?=&)|(?= ))");
+ matcher = pattern.matcher(url_header);
+ String new_url = matcher.replaceFirst("");
+ op.api.message.setUrlHeader(new_url);
+ op.processed_message = op.api.message.getMessage(op.api.is_request, helpers);
+ break;
+
+ case HEAD:
+ op.api.message.removeHeadParameter(op.api.is_request, mop.what);
+ op.processed_message = op.api.message.getMessage(op.api.is_request, helpers);
+ break;
+
+ case BODY:
+ String body = new String(op.api.message.getBody(op.api.is_request));
+ pattern = Pattern.compile(mop.what);
+ matcher = pattern.matcher(body);
+ op.api.message.setBody(op.api.is_request, matcher.replaceAll(""));
+ //Automatically update content-lenght
+ op.processed_message = op.api.message.getMessage(op.api.is_request, helpers);
+ break;
+ }
+ break;
+
+ case ADD:
+ if (getAdding(mop, op.api.vars) == null | getAdding(mop, op.api.vars).equals("")) {
+ // TODO: should raise exception or set operation not applicable?
+ break;
+ }
+ switch (mop.from) {
+ case HEAD: {
+ op.api.message.addHeadParameter(op.api.is_request, mop.what, getAdding(mop, op.api.vars));
+ op.processed_message = op.api.message.getMessage(op.api.is_request, helpers);
+ break;
+ }
+ case BODY: {
+ String tmp = new String(op.api.message.getBody(op.api.is_request));
+ tmp = tmp + getAdding(mop, op.api.vars);
+ op.api.message.setBody(op.api.is_request, tmp);
+ //Automatically update content-lenght
+ op.processed_message = op.api.message.getMessage(op.api.is_request, helpers);
+ break;
+ }
+ case URL:
+ if (!op.api.is_request) {
+ throw new ParsingException("Searching URL in response");
+ }
+ String header_0 = op.api.message.getUrlHeader();
+
+ pattern = Pattern.compile("&?" + mop.what + "=[^& ]*((?=&)|(?= ))");
+ matcher = pattern.matcher(header_0);
+
+ String newHeader_0 = "";
+ boolean found = false;
+ while (matcher.find() & !found) {
+ String before = header_0.substring(0, matcher.end());
+ String after = header_0.substring(matcher.end());
+ newHeader_0 = before + getAdding(mop, op.api.vars) + after;
+ found = true;
+ }
+ op.api.message.setUrlHeader(newHeader_0);
+ op.processed_message = op.api.message.getMessage(op.api.is_request, helpers);
+ break;
+ }
+ break;
+
+ case EDIT:
+ op.processed_message = Tools.editMessageParam(
+ helpers,
+ mop.what,
+ mop.from,
+ op.api.message,
+ op.api.is_request,
+ getAdding(mop, op.api.vars),
+ true);
+ break;
+
+ case EDIT_REGEX:
+ op.processed_message = Tools.editMessage(
+ helpers,
+ mop.what,
+ mop,
+ op.api.message,
+ op.api.is_request,
+ getAdding(mop, op.api.vars));
+ break;
+
+ case REMOVE_MATCH_WORD:
+ switch (mop.from) {
+ case HEAD: {
+ List headers = op.api.message.getHeaders(op.api.is_request);
+ pattern = Pattern.compile(mop.what);
+ List new_headers = new ArrayList<>();
+
+ for (String header : headers) {
+ matcher = pattern.matcher(header);
+ new_headers.add(matcher.replaceAll(""));
+ }
+
+ op.api.message.setHeaders(op.api.is_request, new_headers);
+ op.processed_message = op.api.message.getMessage(op.api.is_request, helpers);
+ break;
+ }
+ case BODY: {
+ pattern = Pattern.compile(mop.what);
+ matcher = pattern.matcher(new String(op.api.message.getBody(op.api.is_request)));
+ op.api.message.setBody(op.api.is_request, matcher.replaceAll(""));
+
+ //Automatically update content-lenght
+ op.processed_message = op.api.message.getMessage(op.api.is_request, helpers);
+ break;
+ }
+ case URL:
+ // Works
+ if (!op.api.is_request) {
+ throw new ParsingException("Searching URL in response");
+ }
+ String header_0 = op.api.message.getUrlHeader();
+
+ pattern = Pattern.compile(mop.what);
+ matcher = pattern.matcher(header_0);
+ String newHeader_0 = matcher.replaceFirst("");
+
+ op.api.message.setUrlHeader(newHeader_0);
+ op.processed_message = op.api.message.getMessage(op.api.is_request, helpers);
+ break;
+ }
+ break;
+
+ case SAVE:
+ case SAVE_MATCH:
+ switch (mop.from) {
+ case HEAD: {
+ String value = "";
+ if (mop.action == MessageOperation.MessageOperationActions.SAVE) {
+ value = op.api.message.getHeadParam(op.api.is_request, mop.what).trim();
+ } else {
+ List headers = op.api.message.getHeaders(op.api.is_request);
+ pattern = Pattern.compile(mop.what);
+ for (String h : headers) {
+ matcher = pattern.matcher(h);
+ value = "";
+ while (matcher.find()) {
+ value = matcher.group();
+ break;
+ }
+ }
+ }
+
+ Var v = new Var();
+ v.name = mop.save_as;
+ v.isMessage = false;
+ v.value = value;
+ op.api.vars.add(v);
+ break;
+ }
+ case BODY: {
+ String tmp = new String(op.api.message.getBody(op.api.is_request), StandardCharsets.UTF_8);
+ pattern = Pattern.compile(mop.what);
+ matcher = pattern.matcher(tmp);
+ Var v = new Var();
+
+ while (matcher.find()) {
+ v.name = mop.save_as;
+ v.isMessage = false;
+ v.value = matcher.group();
+ break;
+ }
+ op.api.vars.add(v);
+ break;
+ }
+ case URL: {
+ // works
+ if (!op.api.is_request) {
+ throw new ParsingException("Searching URL in response");
+ }
+ String header_0 = op.api.message.getUrlHeader();
+
+ pattern = mop.action == MessageOperation.MessageOperationActions.SAVE ?
+ Pattern.compile(mop.what + "=[^& ]*(?=(&| ))") :
+ Pattern.compile(mop.what);
+
+ matcher = pattern.matcher(header_0);
+ String value = "";
+
+ if (matcher.find()) {
+ String matched = matcher.group();
+ value = mop.action == MessageOperation.MessageOperationActions.SAVE ?
+ matched.split("=")[1] :
+ matched;
+
+ Var v = new Var();
+ v.name = mop.save_as;
+ v.isMessage = false;
+ v.value = value;
+ op.api.vars.add(v);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ applicable = true;
+
+ if (op.processed_message != null) {
+ if (op.api.is_request) {
+ op.api.message.setRequest(op.processed_message);
+ } 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();
+ }
+ }
+ return op;
+ }
+
+ /**
+ * All the possible actions of a MessageOperation
+ */
+ public enum MessageOperationActions {
+ REMOVE_PARAMETER,
+ REMOVE_MATCH_WORD,
+ EDIT,
+ EDIT_REGEX,
+ ADD,
+ SAVE,
+ SAVE_MATCH;
+
+ /**
+ * From a string get the corresponding enum value
+ *
+ * @param input the string
+ * @return the enum value
+ * @throws ParsingException if the input is malformed
+ */
+ public static MessageOperationActions fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "remove parameter":
+ return REMOVE_PARAMETER;
+ case "remove match word":
+ return REMOVE_MATCH_WORD;
+ case "edit":
+ return EDIT;
+ case "edit regex":
+ return EDIT_REGEX;
+ case "add":
+ return ADD;
+ case "save":
+ return SAVE;
+ case "save match":
+ return SAVE_MATCH;
+ default:
+ throw new ParsingException("invalid check operation");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ /**
+ * The possible types of messageOps
+ */
+ public enum MessageOpType {
+ HTTP,
+ GENERATE_POC;
+
+ /**
+ * From a string get the corresponding enum value
+ *
+ * @param input the string
+ * @return the enum value
+ * @throws ParsingException if the input is malformed
+ */
+ public static MessageOpType fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "http":
+ return HTTP;
+ case "generate_poc":
+ return GENERATE_POC;
+ default:
+ throw new ParsingException("invalid message Op Type");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+}
diff --git a/tool/src/main/java/migt/MessageType.java b/tool/src/main/java/migt/MessageType.java
new file mode 100644
index 0000000..b07d331
--- /dev/null
+++ b/tool/src/main/java/migt/MessageType.java
@@ -0,0 +1,71 @@
+package migt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class storing a MessageType
+ *
+ * @author Matteo Bitussi
+ */
+public class MessageType implements Cloneable {
+ String name;
+ Boolean isRequest; // this tells if the message where the checks are executed is a request or a response
+ List checks;
+ String responseName;
+ String requestName;
+
+ Boolean getByResponse; // this tells if you are getting a response by checking a request
+ Boolean getByRequest; // this tells if you are getting a request by checking a response
+
+ boolean msg_to_process_is_request; // this tells whether the message to be intercepted is a request or a response
+
+ /**
+ * Instantiate a MessageType
+ *
+ * @param name the name of that message
+ * @param isRequest if the message is a request
+ */
+ public MessageType(String name, Boolean isRequest) {
+ this.name = name;
+ this.isRequest = isRequest;
+ this.checks = new ArrayList<>();
+ this.responseName = "";
+ this.requestName = "";
+ this.getByResponse = false;
+ this.getByRequest = false;
+ this.msg_to_process_is_request = isRequest; // init with this, if getByResponse or getByRequest, then change
+ // accordingly
+ }
+
+ /**
+ * From a list of message types, get the corresponding MessageType from the name
+ *
+ * @param list the list of message types
+ * @param name the name of the message type
+ * @return the corresponding MessageType (if found)
+ * @throws Exception if the MessageType can not be found
+ */
+ public static MessageType getFromList(List list, String name) throws ParsingException {
+ for (MessageType act : list) {
+ try {
+ if (act.name.equals(name)) {
+ return act;
+ } else if (act.responseName.equals(name)) {
+ MessageType tmp = (MessageType) act.clone();
+ tmp.getByResponse = true;
+ tmp.msg_to_process_is_request = false;
+ return tmp;
+ } else if (act.requestName.equals(name)) {
+ MessageType tmp = (MessageType) act.clone();
+ tmp.getByRequest = true;
+ tmp.msg_to_process_is_request = true;
+ return tmp;
+ }
+ } catch (CloneNotSupportedException e) {
+ throw new ParsingException(e.getMessage());
+ }
+ }
+ throw new ParsingException("cannot find the specified message type \" " + name + "\"");
+ }
+}
diff --git a/tool/src/main/java/migt/Module.java b/tool/src/main/java/migt/Module.java
new file mode 100644
index 0000000..a4ccc74
--- /dev/null
+++ b/tool/src/main/java/migt/Module.java
@@ -0,0 +1,104 @@
+package migt;
+
+import burp.IExtensionHelpers;
+import org.json.JSONObject;
+
+/**
+ * This class is the Parent class inherited by all modules. It provides some methods and parameters to be
+ * used by other classes
+ */
+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
+
+ public Module() {
+
+ }
+
+ /**
+ * Instantiate the module by parsing a JSONObject
+ *
+ * @param json_module
+ */
+ 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.
+ *
+ * @return
+ */
+ public void validate() throws ParsingException {
+
+ }
+
+ /**
+ * Method used to get the API object of this module to be used in other modules.
+ */
+ public T getAPI() {
+ return null;
+ }
+
+ /**
+ * Method used to set the API object of this module, when it is edited or simply to initiate it.
+ */
+ public void setAPI(API api) {
+
+ }
+
+ /**
+ * Placeholder of a loader method for the API. This method should load all the things needed by the module
+ * from the previous module where the API is imported from.
+ *
+ * @param api the imported API
+ */
+ public void loader(API api) {
+
+ }
+
+ /**
+ * Placeholder of a exporter function. This function should return the API object to where it has been loaded after
+ * it has been edited. There is no need to call the exporter if the API is not edited.
+ *
+ * @return the edited API
+ * @throws ParsingException
+ */
+ public API exporter() throws ParsingException {
+ return null;
+ }
+
+ /**
+ * Update the result of this module from a child module. For convenience, returns the result of the module
+ *
+ * @param module the module to save the result from
+ * @param The module class
+ */
+ public boolean setResult(T module) {
+ if (!module.applicable) {
+ this.applicable = false;
+ this.result = false;
+ } else if (!module.result) {
+ this.result = false;
+ }
+ return module.result;
+ }
+
+ /**
+ * Get the result of this module
+ *
+ * @return the result of the module
+ */
+ public boolean getResult() {
+ return this.result;
+ }
+}
diff --git a/tool/src/main/java/migt/Operation.java b/tool/src/main/java/migt/Operation.java
new file mode 100644
index 0000000..3919e38
--- /dev/null
+++ b/tool/src/main/java/migt/Operation.java
@@ -0,0 +1,513 @@
+package migt;
+
+import burp.IHttpService;
+import burp.IInterceptedProxyMessage;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.PatternSyntaxException;
+
+import static migt.Tools.*;
+
+/**
+ * Class storing an Operation in a Test
+ *
+ * @author Matteo Bitussi
+ */
+public class Operation extends Module {
+ public List messageOperations;
+ public String from_session;
+ public String to_session;
+ public Then then;
+ public String save_name;
+ public String session_port;
+ public List preconditions;
+ public String replace_request_name;
+ public String replace_response_name;
+ 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
+ public List decodeOperations;
+ // Session operation
+ // API
+ Operation_API api;
+ private List checks;
+ private String messageType;
+ private Action action;
+ private String session;
+ private SessionOperation.SessionAction sessionAction;
+
+ /**
+ * Instantiate an operation
+ */
+ public Operation() {
+ init();
+ }
+
+ /**
+ * Instantiate an Operation parsing a JSON object
+ *
+ * @param operation_json the operation defined in MIG-L as JSONObject
+ * @param isActive if the operation is used inside an active or passive test
+ * @param messageTypes All the message types imported
+ * @throws Exception
+ */
+ public Operation(JSONObject operation_json,
+ boolean isActive,
+ List messageTypes) throws Exception {
+ init();
+
+ if (isActive) {
+ // If the test is active parse also these
+ if (operation_json.has("session")) {
+ // If is a Session Operation
+ String session = operation_json.getString("session");
+ String action = operation_json.getString("action");
+
+ List lsop = SessionOperation.parseFromJson(operation_json);
+ if (lsop != null) {
+ for (SessionOperation sop : lsop) {
+ session_operations.add(sop);
+ }
+ }
+
+ setSession(session);
+ setSessionAction(action);
+ isSessionOp = true;
+ return;
+ }
+
+ // If is a standard operation
+ String action = operation_json.getString("action");
+ setAction(action);
+
+ if (operation_json.has("from session")) {
+ from_session = operation_json.getString("from session");
+ }
+ if (operation_json.has("to session")) {
+ to_session = operation_json.getString("to session");
+ }
+ if (operation_json.has("then")) {
+ then = Then.fromString(operation_json.getString("then"));
+ }
+ if (operation_json.has("save")) {
+ save_name = operation_json.getString("save");
+ }
+ if (operation_json.has("replace request")) {
+ replace_request_name = operation_json.getString("replace request");
+ } else if (operation_json.has("replace response")) {
+ replace_response_name = operation_json.getString("replace response");
+ }
+
+ // Preconditions
+ if (operation_json.has("preconditions")) {
+ JSONArray checks = operation_json.getJSONArray("preconditions");
+ preconditions = Tools.parseChecksFromJSON(checks);
+ }
+ }
+
+ setMessageType(operation_json.getString("message type"), messageTypes);
+
+ // Message Operations
+ if (operation_json.has("message operations")) {
+ JSONArray message_ops = operation_json.getJSONArray("message operations");
+ for (int k = 0; k < message_ops.length(); k++) {
+ JSONObject act_message_op = message_ops.getJSONObject(k);
+ MessageOperation message_op = new MessageOperation(act_message_op);
+ messageOperations.add(message_op);
+ }
+ }
+
+ // checks
+ if (operation_json.has("checks")) {
+ JSONArray checks = operation_json.getJSONArray("checks");
+ setChecks(Tools.parseChecksFromJSON(checks));
+ }
+
+ // Decode Operations
+ if (operation_json.has("decode operations")) {
+ JSONArray decode_ops = operation_json.getJSONArray("decode operations");
+ for (int k = 0; k < decode_ops.length(); k++) {
+ JSONObject act_decode_op = decode_ops.getJSONObject(k);
+ // recursion managed inside
+ DecodeOperation decode_op = new DecodeOperation(act_decode_op);
+ decodeOperations.add(decode_op);
+ }
+ }
+ }
+
+ private void init() {
+ this.messageOperations = new ArrayList<>();
+ this.preconditions = new ArrayList<>();
+ this.checks = new ArrayList<>();
+ this.setChecks(new ArrayList<>());
+ this.matchedMessages = new ArrayList<>();
+ this.session_operations = new ArrayList<>();
+ this.log_messages = new ArrayList<>();
+ this.decodeOperations = new ArrayList<>();
+ this.from_session = "";
+ this.to_session = "";
+ this.save_name = "";
+ this.session_port = "";
+ this.replace_response_name = "";
+ this.replace_request_name = "";
+ this.messageType = "";
+ this.session = "";
+ this.processed_message_service = null;
+ this.processed_message = null;
+ }
+
+ public String getMessageType() {
+ return messageType;
+ }
+
+ /**
+ * Set the message type of the message the operation needs to deal with
+ *
+ * @param messageType the name of the message type
+ * @param msg_types the list of message types
+ * @throws Exception Thrown if the message type is not found
+ */
+ public void setMessageType(String messageType, List msg_types) throws Exception {
+ if (MessageType.getFromList(msg_types, messageType) != null) {
+ this.messageType = messageType;
+ } else {
+ throw new ParsingException("Message type not found");
+ }
+ this.messageType = messageType;
+ }
+
+ public List getMessageOperations() {
+ return messageOperations;
+ }
+
+ public List getChecks() {
+ return checks;
+ }
+
+ public void setChecks(List checks) {
+ this.checks = checks;
+ }
+
+ public Action getAction() {
+ return action;
+ }
+
+ public void setAction(String action) throws ParsingException {
+ this.setAction(Action.fromString(action));
+ }
+
+ public void setAction(Action action) {
+ this.action = action;
+ }
+
+ public String getSession() {
+ return session;
+ }
+
+ public void setSession(String sessionName) {
+ this.session = sessionName;
+ }
+
+ public SessionOperation.SessionAction getSessionAction() {
+ return sessionAction;
+ }
+
+ public void setSessionAction(String sessionAction) throws ParsingException {
+ this.setSessionAction(SessionOperation.SessionAction.fromString(sessionAction));
+ }
+
+ public void setSessionAction(SessionOperation.SessionAction sessionAction) {
+ this.sessionAction = sessionAction;
+ }
+
+ public List getDecodeOperations() {
+ return decodeOperations;
+ }
+
+ public void setDecodeOperations(List decodeOperations) {
+ this.decodeOperations = decodeOperations;
+ }
+
+ /**
+ * Used to process session operations of a given operation
+ *
+ * @return An array of Object elements, the first is the edited operation, the second is the updated variables
+ */
+ public List executeSessionOps(Test t,
+ List vars) throws ParsingException {
+ List updated_vars = vars;
+ for (SessionOperation sop : this.session_operations) {
+/*
+ List vars_new = eal.onBeforeExSessionOps();
+
+ for (Var v : vars_new) {
+ if (!updated_vars.contains(v)) {
+ updated_vars.inse
+ }
+ }
+ */
+ Session session = t.getSession(sop.from_session);
+ Track track = session.track;
+
+ switch (sop.action) {
+ case SAVE:
+ Var v = new Var();
+ v.name = sop.as;
+ v.isMessage = false;
+ v.value = "";
+ switch (sop.target) {
+ case TRACK:
+ for (SessionTrackAction sa : t.getSession(sop.from_session).track
+ .getStasFromMarkers(sop.at, sop.to, sop.is_from_included, sop.is_to_included)) {
+ v.value += sa.toString() + "\n";
+ }
+ break;
+ case LAST_ACTION:
+ v.value = session.last_action.toString();
+ break;
+ case LAST_ACTION_ELEM:
+ v.value = session.last_action.elem;
+ break;
+ case LAST_ACTION_ELEM_PARENT:
+ v.value = findParentDiv(session.last_action.elem);
+ break;
+ case LAST_CLICK:
+ v.value = session.last_click.toString();
+ break;
+ case LAST_CLICK_ELEM:
+ v.value = session.last_click.elem;
+ break;
+ case LAST_CLICK_ELEM_PARENT:
+ v.value = findParentDiv(session.last_click.elem);
+ break;
+ case LAST_OPEN:
+ v.value = session.last_open.toString();
+ break;
+ case LAST_OPEN_ELEM:
+ v.value = session.last_open.elem;
+ break;
+ case LAST_URL:
+ v.value = session.last_url;
+ break;
+ case ALL_ASSERT:
+ for (SessionTrackAction sa : t.getSession(sop.from_session).track.getTrack()) {
+ if (sa.isAssert) {
+ v.value += sa + "\n";
+ }
+ }
+ break;
+ }
+ updated_vars.add(v);
+ break;
+
+ case INSERT:
+ String to_be_added = buildStringWithVars(updated_vars, sop.what);
+ track.insert(new Marker(sop.at), to_be_added);
+ break;
+
+ case MARKER:
+ switch (sop.target) {
+ case LAST_ACTION:
+ case LAST_ACTION_ELEM:
+ track.mark(session.last_action, sop.mark_name);
+ break;
+ case LAST_CLICK:
+ case LAST_CLICK_ELEM:
+ track.mark(session.last_click, sop.mark_name);
+ break;
+ case LAST_OPEN:
+ case LAST_OPEN_ELEM:
+ track.mark(session.last_open, sop.mark_name);
+ break;
+ case ALL_ASSERT:
+ for (SessionTrackAction sa : t.getSession(sop.from_session).track.getTrack()) {
+ if (sa.isAssert) {
+ track.mark(sa, sop.mark_name);
+ }
+ }
+ break;
+ case TRACK:
+ case LAST_URL:
+ throw new ParsingException("Invalid session operation target: " + sop.target);
+ default:
+ throw new ParsingException("Invalid session operation target");
+ }
+ break;
+ case REMOVE:
+ if (sop.to != null && !sop.to.equals("")) {
+ // TODO: remove interval of indices instead of using the remove construct of lists, because it
+ // removes duplicated things
+
+ int[] range = t.getSession(sop.from_session).track.
+ getStasIndexFromRange(sop.at, sop.to, sop.is_from_included, sop.is_to_included);
+
+
+ t.getSession(sop.from_session).track.getTrack().subList(range[0], range[1] + 1).clear();
+ } else {
+ track.remove(new Marker(sop.at));
+ }
+ break;
+ }
+ }
+
+ return updated_vars;
+ }
+
+ public Operation_API getAPI() {
+ return this.api;
+ // TODO: check if the api should be updated with the processed message before returning it
+ // TODO: the api should be updated i.e. if the message is edited before making it available
+ }
+
+ public void setAPI(Operation_API api) {
+ this.api = api;
+
+ // add the intercepted message to the matched messages to be displayed
+ if (!matchedMessages.contains(api.message)) {
+ // it could be added multiple times because this method is called by other Modules that returns this api
+ // edited
+ matchedMessages.add(api.message);
+ }
+
+ // updates the processed message from the api
+ this.processed_message = api.message.build_message(api.is_request);
+ }
+
+ public void execute() {
+ if (!preconditions.isEmpty()) {
+ try {
+ applicable = Tools.executeChecks(
+ preconditions,
+ api.message,
+ api.is_request,
+ api.vars
+ );
+ if (!applicable) return;
+ } catch (ParsingException e) {
+ applicable = false;
+ e.printStackTrace();
+ return;
+ }
+ }
+
+ // Replace the message with the saved one if asked
+ if (api.is_request) {
+ if (!replace_request_name.equals("")) {
+ 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();
+ applicable = false;
+ return;
+ }
+ }
+ } else {
+ if (!replace_response_name.equals("")) {
+ 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();
+ applicable = false;
+ return;
+ }
+ }
+ }
+
+ // execute the message operations and the decode ops
+ try {
+ applicable = true;
+ executeMessageOperations(this, helpers); // TOOD: change to edits
+ if (!applicable | !result)
+ return;
+ executeDecodeOps(this, helpers, api.vars);
+ if (!applicable | !result)
+ return;
+ executeChecks(this, api.vars);
+ if (!applicable | !result)
+ return;
+
+ } catch (ParsingException | PatternSyntaxException e) {
+ applicable = false;
+ e.printStackTrace();
+ return;
+ }
+
+ if (!save_name.equals("")) {
+ Var v = new Var();
+ 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);
+ }
+ }
+
+ /**
+ * Enum containing all the possible Active operation actions
+ */
+ public enum Action {
+ INTERCEPT;
+
+ /**
+ * From a string get the corresponding enum value
+ *
+ * @param input the string
+ * @return the enum value
+ * @throws ParsingException if the input is malformed
+ */
+ public static Action fromString(String input) throws ParsingException {
+ if (input != null) {
+ if (input.equals("intercept")) {
+ return INTERCEPT;
+ }
+ throw new ParsingException("invalid check operation");
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ /**
+ * Enum that contains all the possible action to do after a message is received
+ */
+ public enum Then {
+ FORWARD,
+ DROP;
+
+ /**
+ * From a string get the corresponding enum value
+ *
+ * @param input the string
+ * @return the enum value
+ * @throws ParsingException if the input is malformed
+ */
+ public static Then fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "forward":
+ return FORWARD;
+ case "drop":
+ return DROP;
+ default:
+ throw new ParsingException("invalid check operation");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+}
+
diff --git a/tool/src/main/java/migt/Operation_API.java b/tool/src/main/java/migt/Operation_API.java
new file mode 100644
index 0000000..bb8a955
--- /dev/null
+++ b/tool/src/main/java/migt/Operation_API.java
@@ -0,0 +1,23 @@
+package migt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides an API for an Operation module, to be used by other modules.
+ */
+public class Operation_API extends API {
+ public HTTPReqRes message;
+ public List vars;
+ boolean is_request;
+
+ public Operation_API(HTTPReqRes message, boolean is_request) {
+ this.message = message;
+ this.is_request = is_request;
+ this.vars = new ArrayList<>();
+ }
+
+ public Operation_API(List vars) {
+ this.vars = vars;
+ }
+}
diff --git a/tool/src/main/java/burp/ParsingException.java b/tool/src/main/java/migt/ParsingException.java
similarity index 96%
rename from tool/src/main/java/burp/ParsingException.java
rename to tool/src/main/java/migt/ParsingException.java
index 2b45fe2..9afda57 100644
--- a/tool/src/main/java/burp/ParsingException.java
+++ b/tool/src/main/java/migt/ParsingException.java
@@ -1,4 +1,4 @@
-package burp;
+package migt;
/**
* Exception raised when the parsing of the language fails
diff --git a/tool/src/main/java/burp/Session.java b/tool/src/main/java/migt/Session.java
similarity index 97%
rename from tool/src/main/java/burp/Session.java
rename to tool/src/main/java/migt/Session.java
index 970519a..1f173e7 100644
--- a/tool/src/main/java/burp/Session.java
+++ b/tool/src/main/java/migt/Session.java
@@ -1,4 +1,7 @@
-package burp;
+package migt;
+
+import burp.IExtensionHelpers;
+import burp.IHttpRequestResponsePersisted;
import java.util.ArrayList;
import java.util.List;
@@ -18,11 +21,6 @@ public class Session {
public String last_url;
public String name = "";
String port;
-
- public Track getTrack() {
- return track;
- }
-
Track track;
int index = 0;
List messages;
@@ -76,6 +74,10 @@ public Session(String name, String port) {
}
}
+ public Track getTrack() {
+ return track;
+ }
+
/**
* Tells if the session's track has an element
*
@@ -129,6 +131,7 @@ public HTTPReqRes addMessage(IHttpRequestResponsePersisted message, IExtensionHe
/**
* Set the track of this session by parsing a string track
+ *
* @param raw_track the track in string format
* @return the parsed track as a Track object
* @throws ParsingException if the track is malformed
diff --git a/tool/src/main/java/migt/SessionOperation.java b/tool/src/main/java/migt/SessionOperation.java
new file mode 100644
index 0000000..ead878d
--- /dev/null
+++ b/tool/src/main/java/migt/SessionOperation.java
@@ -0,0 +1,344 @@
+package migt;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Class containing a session Operation
+ *
+ * @author Matteo Bitussi
+ */
+public class SessionOperation {
+ public String from_session;
+ public SessOperationAction action;
+ public String what;
+ public String as;
+ public String at;
+ public String to; // until to which ession action save
+ public boolean is_from_included = false;
+ public boolean is_to_included = false;
+ public SessOperationTarget target;
+ public String mark_name;
+
+ /**
+ * Parses a list of session operations from json
+ *
+ * @param act_operation the session operations as JSON object
+ * @return the list of tession operations
+ * @throws ParsingException if the parsing goes wrong
+ */
+ public static List parseFromJson(JSONObject act_operation) throws ParsingException {
+ List lsop = new ArrayList<>();
+ if (act_operation.has("session operations")) {
+ JSONArray session_ops = act_operation.getJSONArray("session operations");
+ for (int l = 0; l < session_ops.length(); l++) {
+ JSONObject act_session_op = session_ops.getJSONObject(l);
+ SessionOperation sop = new SessionOperation();
+ Iterator keys = act_session_op.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+
+ switch (key) {
+ case "session":
+ sop.from_session = act_session_op.getString("session");
+ break;
+ case "save":
+ sop.action = SessOperationAction.SAVE;
+ sop.target = SessOperationTarget
+ .getFromString(act_session_op.getString("save"));
+ break;
+ case "as":
+ sop.as = act_session_op.getString("as");
+ break;
+ case "insert":
+ sop.action = SessOperationAction.INSERT;
+ sop.what = act_session_op.getString("insert");
+ break;
+ case "at":
+ sop.at = act_session_op.getString("at");
+ break;
+ case "mark":
+ sop.action = SessOperationAction.MARKER;
+ sop.target = SessOperationTarget
+ .getFromString(act_session_op.getString("mark"));
+ sop.mark_name = act_session_op.getString("name");
+ break;
+ case "name":
+ //Already processed in mark
+ break;
+ case "remove":
+ sop.action = SessOperationAction.REMOVE;
+ if (sop.at == null || sop.at.length() == 0) {
+ sop.at = act_session_op.getString("remove");
+ }
+ break;
+
+ case "range":
+ List res = parseRange(act_session_op.getString("range"));
+ sop.is_from_included = (boolean) res.get(0);
+ sop.is_to_included = (boolean) res.get(1);
+ sop.at = (String) res.get(2);
+ sop.to = (String) res.get(3);
+ break;
+
+ default:
+ throw new ParsingException("Unexpected value: " + key);
+ }
+ }
+ lsop.add(sop);
+ }
+ return lsop;
+ }
+ return null;
+ }
+
+ /**
+ * Parse a string containing a range in the form of [number, number] or (number,number], based on the type of
+ * parenthesis you can say that the rance is included [ or excluded (
+ *
+ * @param range The string containing the range to parse
+ * @return Position 0, true if from included.
+ * Position 1: true if to included.
+ * Position 2: from,
+ * Position 3: to
+ */
+ public static List parseRange(String range) throws ParsingException {
+ Pattern p = Pattern.compile("^(\\(|\\[)\\s*([^\\[\\],]*)\\s*,\\s*([^\\[\\],]*)\\s*(\\)|\\])$");
+ Matcher m = p.matcher(range);
+
+ List l = new ArrayList<>();
+
+ List tmp = new ArrayList<>();
+
+ int count = m.groupCount();
+
+ if (count != 4)
+ throw new ParsingException("invalid range in session operation: \"" + range + "\"");
+
+ while (m.find()) {
+ tmp.add(m.group(1));
+ tmp.add(m.group(2));
+ tmp.add(m.group(3));
+ tmp.add(m.group(4));
+ }
+
+ if (tmp.size() != 4)
+ throw new ParsingException("invalid range in session operation: \"" + range + "\"");
+
+ l.add(tmp.get(0).equals("["));
+ l.add(tmp.get(3).equals("]"));
+ l.add(tmp.get(1));
+ l.add(tmp.get(2));
+
+ return l;
+ }
+
+ /**
+ * Enum containing all the possible session operation actions
+ */
+ public enum SessionAction {
+ START,
+ PAUSE,
+ RESUME,
+ STOP,
+ CLEAR_COOKIES;
+
+ /**
+ * From a string get the corresponding enum value
+ *
+ * @param input the string
+ * @return the enum value
+ * @throws ParsingException if the input is malformed
+ */
+ public static SessionAction fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "start":
+ return START;
+ case "pause":
+ return PAUSE;
+ case "resume":
+ return RESUME;
+ case "stop":
+ return STOP;
+ case "clear cookies":
+ return CLEAR_COOKIES;
+ default:
+ throw new ParsingException("invalid Session action");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
+
+ /**
+ * Defines the action of a session action
+ */
+ public enum SessAction {
+ CLICK,
+ OPEN,
+ TYPE,
+ SNAPSHOT,
+ DIFF,
+ EQUALS,
+ WAIT,
+ SET_VAR,
+ CLEAR_COOKIES,
+ ASSERT_CLICKABLE,
+ ASSERT_NOT_CLICKABLE,
+ ASSERT_VISIBLE,
+ ASSERT_NOT_VISIBLE,
+ ASSERT_ELEM_CONTENT_IS,
+ ASSERT_ELEM_CONTENT_HAS,
+ ASSERT_ELEM_CLASS_IS,
+ ASSERT_ELEM_CLASS_HAS,
+ ASSERT_ELEM_HAS_ATTRIBUTE,
+ ASSERT_ELEM_NOT_HAS_ATTRIBUTE,
+ ALERT;
+
+ /**
+ * Get a session action enum value from a string
+ *
+ * @param s the string
+ * @return the enum value
+ * @throws ParsingException if the string is invalid
+ */
+ public static SessAction getFromString(String s) throws ParsingException {
+ switch (s) {
+ case "assert click":
+ case "click":
+ return CLICK;
+ case "open":
+ case "assert open": // just an alias of open
+ return OPEN;
+ case "type":
+ return TYPE;
+ case "snapshot":
+ return SNAPSHOT;
+ case "diff":
+ return DIFF;
+ case "equals":
+ return EQUALS;
+ case "wait":
+ return WAIT;
+ case "set var":
+ return SET_VAR;
+ case "clear cookies":
+ return CLEAR_COOKIES;
+ case "assert clickable":
+ return ASSERT_CLICKABLE;
+ case "assert not clickable":
+ return ASSERT_NOT_CLICKABLE;
+ case "assert visible":
+ return ASSERT_VISIBLE;
+ case "assert not visible":
+ return ASSERT_NOT_VISIBLE;
+ case "assert element content is":
+ return ASSERT_ELEM_CONTENT_IS;
+ case "assert element content has":
+ return ASSERT_ELEM_CONTENT_HAS;
+ case "assert element class is":
+ return ASSERT_ELEM_CLASS_IS;
+ case "assert element class has":
+ return ASSERT_ELEM_CLASS_HAS;
+ case "assert element has attribute":
+ return ASSERT_ELEM_HAS_ATTRIBUTE;
+ case "assert element not has attribute":
+ return ASSERT_ELEM_NOT_HAS_ATTRIBUTE;
+ case "alert":
+ return ALERT;
+ default:
+ throw new ParsingException("Invalid session action \"" + s + "\"");
+ }
+ }
+ }
+
+ /**
+ * Defines the action of a session operation
+ */
+ public enum SessOperationAction {
+ SAVE,
+ INSERT,
+ MARKER,
+ REMOVE
+ }
+
+ /**
+ * Defines the target of a session operation.
+ * Is it better to use js or just build a form? if a form is used, body has to be interpreted
+ */
+ public enum SessOperationTarget {
+ LAST_ACTION,
+ LAST_ACTION_ELEM,
+ LAST_ACTION_ELEM_PARENT,
+ LAST_CLICK,
+ LAST_CLICK_ELEM,
+ LAST_CLICK_ELEM_PARENT,
+ LAST_OPEN,
+ LAST_OPEN_ELEM,
+ LAST_URL,
+ ALL_ASSERT,
+ TRACK;
+
+ /**
+ * Parse a string containing a session operation target
+ *
+ * @param s the string to parse
+ * @throws ParsingException if the string is malformed, or no session operation target is found
+ */
+ public static SessOperationTarget getFromString(String s) throws ParsingException {
+
+ if (s.contains(".")) {
+ String[] splitted;
+ splitted = s.split("\\.");
+ boolean parent = false;
+ if (splitted.length == 3) {
+ if (splitted[2].equals("parent")) {
+ parent = true;
+ }
+ }
+
+ switch (s) {
+ case "last_action.elem":
+ case "last_action.elem.parent":
+ return parent ? LAST_ACTION_ELEM_PARENT : LAST_ACTION_ELEM;
+ case "last_click.elem":
+ case "last_click.elem.parent":
+ return parent ? LAST_CLICK_ELEM_PARENT : LAST_CLICK_ELEM;
+ case "last_open.elem":
+ return LAST_OPEN_ELEM;
+ case "last_url":
+ return LAST_URL;
+ case "all_assert":
+ return ALL_ASSERT;
+ default:
+ throw new ParsingException("invalid target in session operation");
+ }
+ } else {
+ switch (s) {
+ case "track":
+ return TRACK;
+ case "last_action":
+ return LAST_ACTION;
+ case "last_click":
+ return LAST_CLICK;
+ case "last_open":
+ return LAST_OPEN;
+ case "last_url":
+ return LAST_URL;
+ case "all_assert":
+ return ALL_ASSERT;
+ default:
+ throw new ParsingException("invalid target in session operation");
+ }
+ }
+ }
+ }
+}
diff --git a/tool/src/main/java/burp/SessionTrackAction.java b/tool/src/main/java/migt/SessionTrackAction.java
similarity index 83%
rename from tool/src/main/java/burp/SessionTrackAction.java
rename to tool/src/main/java/migt/SessionTrackAction.java
index 4488e2e..92b6a09 100644
--- a/tool/src/main/java/burp/SessionTrackAction.java
+++ b/tool/src/main/java/migt/SessionTrackAction.java
@@ -1,7 +1,8 @@
-package burp;
+package migt;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* This class represents an user action in a session
@@ -9,7 +10,7 @@
* @author Matteo Bitussi
*/
public class SessionTrackAction {
- public Utils.SessAction action;
+ public SessionOperation.SessAction action;
public String elem_type;
public String elem_source;
public String elem;
@@ -33,6 +34,7 @@ public SessionTrackAction() {
/**
* Constructor parsing a raw action in string format
+ *
* @param raw_action The user action in string format
* @throws ParsingException If the user action is written wrongly
*/
@@ -43,6 +45,7 @@ public SessionTrackAction(String raw_action) throws ParsingException {
/**
* Function used to parse a string containing a raw user action in string format
+ *
* @param raw_action the user action in string format
* @throws ParsingException if user action is not written properly
*/
@@ -53,18 +56,18 @@ public void parse_raw_action(String raw_action) throws ParsingException {
throw new ParsingException("invalid session action \"" + raw_action + "\"");
}
- action = Utils.SessAction.getFromString(splitted[0].trim());
+ action = SessionOperation.SessAction.getFromString(splitted[0].trim());
if (splitted[0].trim().contains("assert")) {
isAssert = true;
}
- if (action == Utils.SessAction.CLEAR_COOKIES) return;
+ if (action == SessionOperation.SessAction.CLEAR_COOKIES) return;
elem = splitted[1].trim();
- if (!(action == Utils.SessAction.OPEN) &&
- action != Utils.SessAction.WAIT &&
- action != Utils.SessAction.ALERT &&
- action != Utils.SessAction.SET_VAR) {
+ if (!(action == SessionOperation.SessAction.OPEN) &&
+ action != SessionOperation.SessAction.WAIT &&
+ action != SessionOperation.SessAction.ALERT &&
+ action != SessionOperation.SessAction.SET_VAR) {
String[] tmp = elem.split("=");
elem_type = tmp[0].trim();
elem_source = tmp[1].trim();
@@ -79,6 +82,7 @@ public void parse_raw_action(String raw_action) throws ParsingException {
/**
* Prints the user action as it was in string format. Compliant with the language.
+ *
* @return
*/
@Override
@@ -151,7 +155,7 @@ public String toString() {
case CLEAR_COOKIES:
break;
}
- if (action == Utils.SessAction.TYPE) {
+ if (action == SessionOperation.SessAction.TYPE) {
res += " " + content;
}
@@ -167,10 +171,10 @@ public boolean equals(Object o) {
if (isAssert != that.isAssert) return false;
if (action != that.action) return false;
- if (elem_type != null ? !elem_type.equals(that.elem_type) : that.elem_type != null) return false;
- if (elem_source != null ? !elem_source.equals(that.elem_source) : that.elem_source != null) return false;
- if (elem != null ? !elem.equals(that.elem) : that.elem != null) return false;
- if (content != null ? !content.equals(that.content) : that.content != null) return false;
- return markers != null ? markers.equals(that.markers) : that.markers == null;
+ if (!Objects.equals(elem_type, that.elem_type)) return false;
+ if (!Objects.equals(elem_source, that.elem_source)) return false;
+ if (!Objects.equals(elem, that.elem)) return false;
+ if (!Objects.equals(content, that.content)) return false;
+ return Objects.equals(markers, that.markers);
}
}
\ No newline at end of file
diff --git a/tool/src/main/java/burp/Test.java b/tool/src/main/java/migt/Test.java
similarity index 66%
rename from tool/src/main/java/burp/Test.java
rename to tool/src/main/java/migt/Test.java
index 13eb0d3..66506a7 100644
--- a/tool/src/main/java/burp/Test.java
+++ b/tool/src/main/java/migt/Test.java
@@ -1,4 +1,8 @@
-package burp;
+package migt;
+
+import burp.IInterceptedProxyMessage;
+import org.json.JSONArray;
+import org.json.JSONObject;
import java.io.BufferedWriter;
import java.io.File;
@@ -8,6 +12,7 @@
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
/**
@@ -16,33 +21,122 @@
* @author Matteo Bitussi
*/
public class Test {
- public Utils.ResultType result;
+ public ResultType result;
public String resultSession;
public List sessions;
public String references;
public String violated_properties;
public String affected_entity;
public String mitigations;
+ public List vars;
+ public String error_str;
+ public Boolean error;
Boolean isActive;
List operations;
boolean success = false;
boolean applicable = true;
- boolean error = false;
- String error_srt;
// Infos
String name;
String description;
/**
- * Instantiate a test
+ * Empty constructor for tests
*/
public Test() {
+ init();
+ }
+
+ /**
+ * Instantiate a test
+ */
+ public Test(JSONObject test_json,
+ List messageTypes) throws Exception {
+ init();
+
+ description = test_json.getString("description");
+ name = test_json.getString("name");
+ setType(test_json.getString("type"));
+
+ Iterator keys = test_json.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+
+ switch (key) {
+ case "name":
+ case "type":
+ case "description":
+ case "result":
+ case "operations":
+ case "sessions":
+ break;
+ case "references":
+ references = test_json.getString("references");
+ break;
+ case "violated_properties":
+ violated_properties = test_json.getString("violated_properties");
+ break;
+ case "mitigations":
+ mitigations = test_json.getString("mitigations");
+ break;
+ case "affected_entity":
+ affected_entity = test_json.getString("affected_entity");
+ break;
+ default:
+ throw new ParsingException("Invalid key \"" + key + "\"");
+ }
+ }
+
+ // set result
+ if (isActive) {
+ if (test_json.has("result")) {
+ String tmp = test_json.getString("result");
+ if (tmp.contains("assert_only")) {
+ result = ResultType.fromString(tmp);
+ } else {
+ tmp = tmp.trim();
+ String[] splitted = tmp.split("flow");
+
+ if (splitted.length > 1) {
+ resultSession = splitted[1].trim();
+ }
+ result = ResultType.fromString(splitted[0].trim());
+ }
+ }
+ }
+
+ if (test_json.has("sessions")) {
+ JSONArray arrSess = test_json.getJSONArray("sessions");
+ Iterator it = arrSess.iterator();
+
+ while (it.hasNext()) {
+ String act_sess_name = (String) it.next();
+ sessions.add(new Session(act_sess_name));
+ }
+ } else {
+ throw new ParsingException("session tag is missing");
+ }
+
+ //Array of Operations
+ JSONArray arrOps = test_json.getJSONArray("operations");
+
+ //Reads all the operations
+ for (int j = 0; j < arrOps.length(); j++) {
+ JSONObject act_operation = arrOps.getJSONObject(j);
+
+ Operation op = new Operation(act_operation, isActive, messageTypes);
+ operations.add(op);
+ }
+ }
+
+ public void init() {
+ vars = new ArrayList<>();
this.resultSession = "";
this.name = "";
this.description = "";
- this.error_srt = "";
this.operations = new ArrayList<>();
this.sessions = new ArrayList<>();
+ this.error_str = "";
+ this.error = false;
this.success = false;
this.isActive = false;
@@ -83,14 +177,14 @@ public List getRows() {
int count = 0;
for (Operation op : operations) {
- for (Operation.MatchedMessage msg : op.matchedMessages) {
+ for (HTTPReqRes msg : op.matchedMessages) {
String[] tmp = new String[]{
String.valueOf(count),
String.valueOf(op.getMessageType()),
- String.valueOf(op.getMessageSection()),
- op.isRegex ? op.getRegex() : op.getChecks().toString(),
+ "",
+ op.getChecks().toString(),
msg.index.toString(),
- msg.isFail ? "failed" : "passed"};
+ "-"}; // TODO: somehow put if the message made the test fail
res.add(tmp);
}
count++;
@@ -136,6 +230,7 @@ public Session getSession(String session_name) throws ParsingException {
/**
* Function used to log the test informations, such as matched messages, all the messages intercepted, and sessions
+ *
* @param log_folder The folder where to log the test
*/
public void logTest(String log_folder) {
@@ -190,28 +285,28 @@ public void logTest(String log_folder) {
"/operation_" +
op_count +
"_" + o.getMessageType();
- for (Operation.MatchedMessage m : o.matchedMessages) {
- if (m.message != null) {
- if (m.message.getRequest() != null) {
+ for (HTTPReqRes m : o.matchedMessages) {
+ if (m != null) {
+ if (m.getRequest() != null) {
File log_message = new File(base_path + "_request.raw");
try {
FileWriter fw = new FileWriter(log_message.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(header);
- bw.write(new String(m.message.getRequest(), StandardCharsets.UTF_8));
+ bw.write(new String(m.getRequest(), StandardCharsets.UTF_8));
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
message_count++;
}
- if (m.message.getResponse() != null) {
+ if (m.getResponse() != null) {
File log_message = new File(base_path + "_response.raw");
try {
FileWriter fw = new FileWriter(log_message.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(header);
- bw.write(new String(m.message.getResponse(), StandardCharsets.UTF_8));
+ bw.write(new String(m.getResponse(), StandardCharsets.UTF_8));
bw.close();
} catch (IOException e) {
e.printStackTrace();
@@ -279,4 +374,37 @@ public void logTest(String log_folder) {
op_count++;
}
}
+
+ /**
+ * The result type of (also the oracle) of an Active test
+ */
+ public enum ResultType {
+ CORRECT_FLOW,
+ INCORRECT_FLOW,
+ ASSERT_ONLY;
+
+ /**
+ * From a string get the corresponding enum value
+ *
+ * @param input the string
+ * @return the enum value
+ * @throws ParsingException if the input is malformed
+ */
+ public static ResultType fromString(String input) throws ParsingException {
+ if (input != null) {
+ switch (input) {
+ case "correct":
+ return CORRECT_FLOW;
+ case "incorrect":
+ return INCORRECT_FLOW;
+ case "assert_only":
+ return ASSERT_ONLY;
+ default:
+ throw new ParsingException("invalid result");
+ }
+ } else {
+ throw new NullPointerException();
+ }
+ }
+ }
}
diff --git a/tool/src/main/java/burp/TestSuite.java b/tool/src/main/java/migt/TestSuite.java
similarity index 86%
rename from tool/src/main/java/burp/TestSuite.java
rename to tool/src/main/java/migt/TestSuite.java
index b6afbd0..5740d2e 100644
--- a/tool/src/main/java/burp/TestSuite.java
+++ b/tool/src/main/java/migt/TestSuite.java
@@ -1,4 +1,4 @@
-package burp;
+package migt;
import java.util.ArrayList;
import java.util.List;
@@ -12,8 +12,6 @@ public class TestSuite {
String name;
String description;
List tests;
- Test currentTest;
- boolean metadata = false;
/**
* Instantiate the TestSuite
@@ -22,8 +20,6 @@ public TestSuite() {
this.name = "";
this.description = "";
this.tests = new ArrayList<>();
-
- this.currentTest = null;
}
/**
@@ -37,8 +33,6 @@ public TestSuite(String name, String description, List tests) {
this.name = name;
this.description = description;
this.tests = tests;
-
- this.currentTest = null;
}
public List getTests() {
diff --git a/tool/src/main/java/migt/Tools.java b/tool/src/main/java/migt/Tools.java
new file mode 100644
index 0000000..8e8be5d
--- /dev/null
+++ b/tool/src/main/java/migt/Tools.java
@@ -0,0 +1,869 @@
+package migt;
+
+import burp.IExtensionHelpers;
+import com.jayway.jsonpath.Configuration;
+import com.jayway.jsonpath.JsonPath;
+import com.jayway.jsonpath.PathNotFoundException;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Class with methods to process messages and execute tests
+ *
+ * @author Matteo Bitussi
+ */
+public class Tools {
+ /**
+ * Function that execute the given passive test.
+ *
+ * @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
+ * @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;
+
+ for (i = 0; i < messageList.size(); i++) {
+ j = 0;
+ while (j < test.operations.size() && res) {
+ Operation currentOP = test.operations.get(j);
+ MessageType msg_type = MessageType.getFromList(msg_types, currentOP.getMessageType());
+
+ if (currentOP.api == null) {
+ currentOP.api = new Operation_API(test.vars);
+ } else {
+ currentOP.api.vars = test.vars;
+ }
+
+ 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();
+ }
+
+ test.vars = currentOP.api.vars;
+ j++;
+ }
+ }
+
+ for (Operation op : test.operations) {
+ if (!op.applicable) {
+ res = false;
+ test.applicable = false;
+ break;
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * Function that given a list of headers, concatenates them in a single string
+ *
+ * @param headers the list of headers
+ * @return the string
+ */
+ public static String getAllHeaders(List headers) {
+ StringBuilder out = new StringBuilder();
+ for (Object o : headers) {
+ out.append(o.toString());
+ out.append("\n");
+ }
+ return out.toString();
+ }
+
+ /**
+ * This function execute a list of checks over a message, returning true if all the checks are successful
+ *
+ * @param checks a List of checks
+ * @param message the message to be checked
+ * @param isRequest set true if the request has to be checked, false for the response
+ * @return returns the result of the checks (true if all the tests are successful)
+ */
+ public static boolean executeChecks(List checks,
+ HTTPReqRes message,
+ boolean isRequest,
+ List vars) throws ParsingException {
+ for (Check c : checks) {
+ if (!c.execute(message, isRequest, vars)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Execute a list of checks in an operation. Uses API.
+ *
+ * @param op the operation to execute checks from
+ * @return the result of the checks
+ * @throws ParsingException if something goes wrong related to the definition of the test
+ */
+ public static Operation executeChecks(Operation op, List vars) throws ParsingException {
+ for (Check c : op.getChecks()) {
+ c.loader(op.api);
+ c.execute(vars);
+ if (!op.setResult(c))
+ break;
+ }
+ return op;
+ }
+
+ /**
+ * 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
+ * @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.execute(vars);
+ if (!op.setResult(dop))
+ break;
+ op.setAPI(dop.exporter());
+ }
+
+ return 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
+ * @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.execute(vars);
+ if (!op.setResult(dop))
+ break;
+ op.setAPI(dop.exporter());
+ }
+
+ return op;
+ }
+
+ /**
+ * Executes the edit operations inside a decode operation
+ *
+ * @param op the decode operation to run the edit operations from
+ * @return the Decode operation (edited)
+ * @throws ParsingException if something goes wrong
+ */
+ public static DecodeOperation executeEditOps(DecodeOperation op,
+ List vars) throws ParsingException {
+ DecodeOperation_API api = op.getAPI();
+ for (EditOperation eop : op.editOperations) {
+ eop.loader(api);
+ eop.execute(vars);
+ if (!op.setResult(eop))
+ break;
+ op.setAPI(eop.exporter());
+ }
+
+ return op;
+ }
+
+ public static Operation executeMessageOperations(Operation op, IExtensionHelpers helpers) throws ParsingException {
+ for (MessageOperation mop : op.messageOperations) {
+ mop.loader(op.api);
+ mop.execute(op, helpers);
+ op.setAPI(mop.exporter());
+ if (op.setResult(op))
+ break;
+ }
+ return op;
+ }
+
+ /**
+ * Function that parses checks from a JSON array
+ *
+ * @param checks_array the JSONarray that should contain checks
+ * @return a List of Check elements
+ * @throws ParsingException if the input is malformed
+ */
+ public static List parseChecksFromJSON(JSONArray checks_array) throws ParsingException {
+ List res = new ArrayList<>();
+ for (int k = 0; k < checks_array.length(); k++) {
+ JSONObject act_check = checks_array.getJSONObject(k);
+ Check check = new Check(act_check);
+
+ if (check.in == null) {
+ throw new ParsingException("In tag cannot be empty");
+ }
+ res.add(check);
+ }
+ return res;
+ }
+
+ /**
+ * Parses a list of Edit operations from a JSON array
+ *
+ * @param edits_array the input JSON array containing the edit operations
+ * @return the parsed list of Edit operations
+ * @throws ParsingException if there are problems parsing the JSON array
+ */
+ public static List parseEditsFromJSON(JSONArray edits_array) throws ParsingException {
+ List res = new ArrayList<>();
+ for (int i = 0; i < edits_array.length(); i++) {
+ JSONObject act_edit = edits_array.getJSONObject(i);
+ EditOperation edit = new EditOperation(act_edit);
+ res.add(edit);
+ }
+ return res;
+ }
+
+ /**
+ * Function used to parse the message types from a string
+ *
+ * @param input a string containing the msg types in JSON
+ * @return a List of messagetype objects
+ * @throws ParsingException if the input is malformed
+ */
+ public static List readMsgTypeFromJson(String input) throws ParsingException {
+ List msg_types = new ArrayList<>();
+
+ JSONObject obj = new JSONObject(input);
+ JSONArray message_types = obj.getJSONArray("message_types");
+
+ for (int i = 0; i < message_types.length(); i++) {
+ JSONObject act_msg_type = message_types.getJSONObject(i);
+
+ String name = act_msg_type.getString("name");
+ Boolean isRequest = act_msg_type.getBoolean("is request");
+
+ MessageType msg_obj = new MessageType(name, isRequest);
+
+ if (act_msg_type.has("response name")) {
+ msg_obj.responseName = act_msg_type.getString("response name");
+ }
+ if (act_msg_type.has("request name")) {
+ msg_obj.requestName = act_msg_type.getString("request name");
+ }
+
+ if (act_msg_type.has("checks")) {
+ msg_obj.checks = parseChecksFromJSON(act_msg_type.getJSONArray("checks"));
+ } else {
+ throw new ParsingException("message type definition is invalid, no checks or regex found");
+ }
+ msg_types.add(msg_obj);
+ }
+
+ return msg_types;
+ }
+
+ /**
+ * Returns the adding of a message operation, decides if the value to be inserted/edited should be a variable or
+ * a typed value and return it
+ *
+ * @param m the message operation which has to be examined
+ * @return the adding to be used in add/edit
+ * @throws ParsingException if the variable name is not valid or the variable has not been initiated
+ */
+ public static String getAdding(MessageOperation m, List vars) throws ParsingException {
+ if (!m.use.isEmpty()) {
+ return getVariableByName(m.use, vars).value;
+ } else {
+
+ return m.to;
+ }
+ }
+
+ /**
+ * Returns the default string that contains the default message types that fill a msg_def.json file
+ *
+ * @return the string
+ */
+ public static String getDefaultJSONMsgType() {
+ return "{\n" +
+ " \"message_types\": [\n" +
+ " {\n" +
+ " \"name\": \"authorization request\",\n" +
+ " \"is request\": true,\n" +
+ " \"response name\": \"authorization response\",\n" +
+ " \"checks\": [\n" +
+ " {\n" +
+ " \"in\": \"url\",\n" +
+ " \"check param\": \"response_type\",\n" +
+ " \"is present\": \"true\"\n" +
+ " }\n" +
+ " ]\n" +
+ " },\n" +
+ " {\n" +
+ " \"name\": \"token request\",\n" +
+ " \"is request\": true,\n" +
+ " \"response name\": \"token response\",\n" +
+ " \"checks\": [\n" +
+ " {\n" +
+ " \"in\": \"url\",\n" +
+ " \"check param\": \"code\",\n" +
+ " \"is present\": \"true\"\n" +
+ " }\n" +
+ " ]\n" +
+ " },\n" +
+ " {\n" +
+ " \"name\": \"coda landing request\",\n" +
+ " \"is request\": true,\n" +
+ " \"response name\": \"coda landing response\",\n" +
+ " \"checks\": [\n" +
+ " {\n" +
+ " \"in\": \"url\",\n" +
+ " \"check\": \"/welcome\",\n" +
+ " \"is present\": \"true\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"in\": \"head\",\n" +
+ " \"check\": \"Host\",\n" +
+ " \"is\": \"coda.io\"\n" +
+ " }\n" +
+ " ]\n" +
+ " },\n" +
+ " {\n" +
+ " \"name\": \"saml request\",\n" +
+ " \"is request\": true,\n" +
+ " \"checks\": [\n" +
+ " {\n" +
+ " \"in\": \"url\",\n" +
+ " \"check param\": \"SAMLRequest\",\n" +
+ " \"is present\": true\n" +
+ " }\n" +
+ " ]\n" +
+ " },\n" +
+ " {\n" +
+ " \"name\": \"saml response\",\n" +
+ " \"is request\": true,\n" +
+ " \"checks\": [\n" +
+ " {\n" +
+ " \"in\": \"body\",\n" +
+ " \"check param\": \"SAMLResponse\",\n" +
+ " \"is present\": true\n" +
+ " }\n" +
+ " ]\n" +
+ " }\n" +
+ " ]\n" +
+ "}";
+ }
+
+ /**
+ * Returns the default string that contains the default config for the config.json file
+ *
+ * @return the string
+ */
+ public static String getDefaultJSONConfig() {
+ return "{\n" +
+ " \"last_driver_path\":\"\",\n" +
+ " \"last_browser_used\": \"\"\n" +
+ "}";
+ }
+
+ /**
+ * Removes all the newlines from a string
+ *
+ * @return the edited message
+ */
+ public static String removeNewline(String input) {
+ Pattern p = Pattern.compile("\n");
+ Matcher m = p.matcher(input);
+
+ String out = m.replaceAll("");
+ return out;
+ }
+
+ /**
+ * Builds a string, substituting variables names with values
+ *
+ * @param vars the list of variables to use
+ * @param s the string
+ * @return the builded string
+ * @throws ParsingException if a variable is not found
+ */
+ public static String buildStringWithVars(List vars, String s) throws ParsingException {
+ Pattern p = Pattern.compile("\\$[^\\$]*\\$");
+ Matcher m = p.matcher(s);
+
+ String res = s;
+
+ HashMap req_var = new HashMap<>();
+
+ while (m.find()) {
+ String act_match = m.group();
+ act_match = act_match.replaceAll("\\$", "");
+ req_var.put(act_match, getVariableByName(act_match, vars).value);
+ }
+
+ if (req_var.size() == 0) {
+ return s;
+ }
+
+ for (String key : req_var.keySet()) {
+ res = res.replaceAll("\\$" + key + "\\$", Matcher.quoteReplacement(req_var.get(key)));
+ }
+ return res;
+ }
+
+ /**
+ * Given a name, returns the corresponding variable
+ *
+ * @param name the name of the variable
+ * @return the Var object
+ * @throws ParsingException if the variable cannot be found
+ */
+ public static Var getVariableByName(String name, List vars) throws ParsingException {
+ for (Var act : vars) {
+ if (act.name.equals(name)) {
+ return act;
+ }
+ }
+ throw new ParsingException("variable \"" + name + "\" not defined");
+ }
+
+ /**
+ * Generates a CSRF POC from an HTTP request message
+ *
+ * @param message the message to generate the POC from
+ * @return the html poc as a string
+ */
+ public static String generate_CSRF_POC(HTTPReqRes message) {
+
+ String CSFR_TEMPLATE = "\n" +
+ "\n" +
+ " \n" +
+ " Attack Page \n" +
+ " Service Provider (SP) is your service.
\n" +
+ " Identity Provider (IdP) is the provider with which the SP allows to associate the account.
\n" +
+ " These are the steps to reproduce the attack:
\n" +
+ " 1. The victim clicks on button to initiate force-login to IdP and victim logs in as the attacker because IdP suffering of Pre-Authentication Login CSRF. To simulate this step, the victim logs in with the attacker's IdP credentials.
\n" +
+ " 2. The victim logins at SP with victim credentials.
\n" +
+ " 3. The victim clicks on following link which suffers of CSRF, to associate attacker IdP account with the Victim SP account.
\n" +
+ " $INSERT_HERE$\n" +
+ " \n" +
+ " 4. If the IdP attacker account has been associated with the victim SP account then the vulnerability has been properly exploited.
\n" +
+ " \n" +
+ "";
+
+ String POST_TEMPLATE =
+ " \n";
+
+ String TEMPLATE_BODY_PARAMS = " \n" +
+ " $PARAM_NAME$ \n" +
+ " \n" +
+ " \n" +
+ " \n" +
+ " ";
+
+ String encoding = message.getHeadParam(true, "Content-Type").strip();
+ String body = new String(message.getBody(true), StandardCharsets.UTF_8); // splitMessage(message, helpers, true).get(2);
+ String url = message.getUrl();
+ String method = message.getUrlHeader().split(" ")[0];
+
+ Pattern p = Pattern.compile("");
+ Matcher m = p.matcher(body);
+
+ String res = "";
+
+ res = POST_TEMPLATE;
+ p = Pattern.compile("\\$ENCODING_TYPE\\$");
+ m = p.matcher(res);
+ res = m.replaceAll(encoding);
+
+ p = Pattern.compile("\\$METHOD\\$");
+ m = p.matcher(res);
+ res = m.replaceAll(method);
+
+ if (method.equals("POST")) {
+ p = Pattern.compile("([^=]*)=([^&\\n$]*)(&|\\n|$)");
+ m = p.matcher(body);
+ String out_body_params = "";
+
+ if (body.length() != 0) {
+ Map body_params = new HashMap<>();
+ while (m.find()) {
+ String name = m.group(1);
+ String value = m.group(2);
+ if (name.length() != 0) {
+ body_params.put(name,
+ value.length() != 0 ? value : "");
+ }
+ }
+ for (String key : body_params.keySet()) {
+ String tmp = TEMPLATE_BODY_PARAMS;
+ p = Pattern.compile("\\$PARAM_NAME\\$");
+ m = p.matcher(tmp);
+
+ tmp = m.replaceAll(key);
+
+ p = Pattern.compile("\\$PARAM_VALUE\\$");
+ m = p.matcher(tmp);
+
+ tmp = m.replaceAll(body_params.get(key));
+
+ out_body_params += tmp;
+ }
+ }
+
+ p = Pattern.compile("\\$URL\\$");
+ m = p.matcher(res);
+ res = m.replaceAll(url);
+
+ p = Pattern.compile("\\$BODY_PARAMETERS\\$");
+ m = p.matcher(res);
+ res = m.replaceAll(out_body_params);
+ } else {
+ boolean has_query_params = url.split("\\?").length > 1;
+ String out_query_params = "";
+
+ if (has_query_params) {
+ String raw_query_params = url.split("\\?")[1];
+
+ p = Pattern.compile("([^=\\n&]*)=([^=\\n&]*)");
+ m = p.matcher(raw_query_params);
+
+ Map query_params = new HashMap<>();
+ while (m.find()) {
+ String name = m.group(1);
+ String value = m.group(2);
+ if (name.length() != 0) {
+ query_params.put(name, value.length() != 0 ? value : "");
+ }
+ }
+ for (String key : query_params.keySet()) {
+ String tmp = TEMPLATE_BODY_PARAMS;
+ p = Pattern.compile("\\$PARAM_NAME\\$");
+ m = p.matcher(tmp);
+
+ tmp = m.replaceAll(key);
+
+ p = Pattern.compile("\\$PARAM_VALUE\\$");
+ m = p.matcher(tmp);
+
+ tmp = m.replaceAll(query_params.get(key));
+
+ out_query_params += tmp;
+ }
+ }
+
+ p = Pattern.compile("\\$URL\\$");
+ m = p.matcher(res);
+ res = m.replaceAll(has_query_params ? url.split("\\?")[0] : url);
+
+ p = Pattern.compile("\\$BODY_PARAMETERS\\$");
+ m = p.matcher(res);
+ res = m.replaceAll(out_query_params);
+ }
+
+ String tmp = CSFR_TEMPLATE;
+ p = Pattern.compile("\\$INSERT_HERE\\$");
+ m = p.matcher(tmp);
+ tmp = m.replaceAll(res);
+
+ return tmp;
+ }
+
+ /**
+ * Create batches of passive tests, grouping them by the session they need to execute.
+ *
+ * @return An HashMap object having as keys, Strings representing the sessions names, and as value a list of tests
+ * that need to execute that session
+ */
+ public static HashMap> batchPassivesFromSession(List testList) throws ParsingException {
+ HashMap> batch = new HashMap<>();
+ for (Test t : testList) {
+ if (t.sessions.size() == 0) {
+ throw new ParsingException("Undefined session in test " + t.name);
+ }
+
+ if (!batch.containsKey(t.sessions.get(0).name)) {
+ List n = new ArrayList<>();
+ n.add(t);
+ batch.put(t.sessions.get(0).name, n);
+ } else {
+ List tmp = batch.get(t.sessions.get(0).name);
+ tmp.add(t);
+ batch.put(t.sessions.get(0).name, tmp);
+ }
+ }
+ return batch;
+ }
+
+ /**
+ * From a batch of tests grouped by sessions, return a list containing all the tests
+ *
+ * @param batch the batch of tests in the form of a MAP>
+ * @return a list containing all the tests
+ */
+ public static List