diff --git a/doc/language.md b/doc/language.md index 0095c98..161bdcc 100644 --- a/doc/language.md +++ b/doc/language.md @@ -526,7 +526,7 @@ The Checks tag inside an operation has a list of Check elements, which can be de > Note that `check` accepts only the `is present` tag. -> Note that by default, all the values read from a message (only message, not json) are URL-decoded before the checks are executed. You can disable this behaviour by using `url decode` = false +> Note that by default, all the values read from a message that contains url-encoded values (only message, not json) are URL-decoded before the checks are executed. You can disable this behaviour by using `url decode` = false In passive tests the checks's result are intended as the entire test result, so all the checks has to pass to have a successfull test. diff --git a/tool/src/main/java/migt/Check.java b/tool/src/main/java/migt/Check.java index 283620f..e5d378e 100644 --- a/tool/src/main/java/migt/Check.java +++ b/tool/src/main/java/migt/Check.java @@ -253,8 +253,15 @@ private boolean execute_http(HTTPReqRes message, } // URL-decode matched content - //if (url_decode) - // msg_str = URLDecoder.decode(msg_str, StandardCharsets.UTF_8); + // when a string contains a "+" character then, it is replaced with a space. + if (url_decode) { + Pattern p = Pattern.compile("%[0-9a-fA-F]{2}"); + Matcher m = p.matcher(op_val); + if (m.find()) { + // if the content contains url-encoded characters then, url-decode the content + op_val = URLDecoder.decode(op_val, StandardCharsets.UTF_8); + } + } // if a regex is present, execute it if (!regex.equals("")) { diff --git a/tool/src/main/java/migt/JWT.java b/tool/src/main/java/migt/JWT.java index c2083f6..b3c8a15 100644 --- a/tool/src/main/java/migt/JWT.java +++ b/tool/src/main/java/migt/JWT.java @@ -9,6 +9,8 @@ import java.text.ParseException; import java.util.Base64; +import static migt.Tools.check_json_strings_equals; + /** * Class to manage JWT tokens @@ -17,6 +19,7 @@ */ public class JWT { public String header; + public String header_original; public String payload; public String signature; public String raw; @@ -99,6 +102,7 @@ public void parse(String raw_jwt) throws ParsingException { try { header = parsed_jwt.getHeader().toString(); + header_original = parsed_jwt.getParsedParts()[0].toString(); payload = parsed_jwt.getPayload().toString(); signature = parsed_jwt instanceof JWSObject ? ((JWSObject) parsed_jwt).getSignature().toString() : null; @@ -163,7 +167,11 @@ public String build() throws ParsingException { if (this.header.equals("") || this.payload.equals("")) throw new ParsingException("error in building jwt, empty values"); - res += Base64.getEncoder().encodeToString(header.getBytes()); + if (check_json_strings_equals(header, new String(Base64.getDecoder().decode(header_original)))) { + res += header_original; + } else { + res += Base64.getEncoder().encodeToString(header.getBytes()); + } res += "." + Base64.getEncoder().encodeToString(payload.getBytes()); if (sign) { diff --git a/tool/src/main/java/migt/Tools.java b/tool/src/main/java/migt/Tools.java index 91ba4bd..65f7f7a 100644 --- a/tool/src/main/java/migt/Tools.java +++ b/tool/src/main/java/migt/Tools.java @@ -1,12 +1,15 @@ package migt; import burp.IExtensionHelpers; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; 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; @@ -866,4 +869,18 @@ public static String editJson(EditOperation.Jwt_action action, } return Configuration.defaultConfiguration().jsonProvider().toJson(document); //basically converts to string } + + /** + * Checks that the json resulting by parsing the two strings is equals. Equals in this case means that the same keys + * and values are present, but the order is ignored + * @param s1 the string 1 + * @param s2 the string 2 + * @return true or false depending if the json parsing of the two strings is the same or not + */ + public static boolean check_json_strings_equals(String s1, String s2) { + JsonElement o1 = JsonParser.parseString(s1); + JsonElement o2 = JsonParser.parseString(s2); + + return o1.equals(o2); + } } diff --git a/tool/src/test/java/JWT_Test.java b/tool/src/test/java/JWT_Test.java index 126c254..835380b 100644 --- a/tool/src/test/java/JWT_Test.java +++ b/tool/src/test/java/JWT_Test.java @@ -410,4 +410,34 @@ void test_decodeRawJwt() { out); } */ + + @Test + @DisplayName("Testing jwt decode and encode") + void test_jwt_parse_change_order() { + JWT j = new JWT(); + boolean errors = false; + try { + j.parse(raw_jwt); + + j.header = "{\"alg\":\"RS256\", \"typ\":\"JWT\"}"; // invert header keys + + String out = j.build(); + assertEquals(raw_jwt, out); + String[] splitted = out.split("\\."); + + assertEquals(3, splitted.length); + + assertEquals(raw_header, splitted[0]); + assertEquals(raw_payload, splitted[1]); + assertEquals(raw_signature, splitted[2]); + + //assertEquals(raw_header, j.header); + //assertEquals(raw_payload, j.payload); + //assertEquals(raw_signature, j.signature); + } catch (ParsingException e) { + errors = true; + } + assertFalse(errors); + } + } diff --git a/tool/src/test/java/Utils_Test.java b/tool/src/test/java/Utils_Test.java index e896159..a6d73c9 100644 --- a/tool/src/test/java/Utils_Test.java +++ b/tool/src/test/java/Utils_Test.java @@ -1,5 +1,6 @@ import migt.*; import org.json.JSONObject; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -193,4 +194,14 @@ void executeSessioOps_test() throws ParsingException { assertEquals(1, 0); } } + + @Test + void test_check_json_strings_equals() { + boolean res = Tools.check_json_strings_equals( + "{a : {a : 2}, b : 2}", + "{b : 2, a : {a : 2}}" + ); + + assertEquals(true, res); + } }