From 0c23ff9a2c459c667e54425801ada545eab05e68 Mon Sep 17 00:00:00 2001 From: leiber bertel Date: Sun, 18 Aug 2024 22:34:05 -0500 Subject: [PATCH 1/9] feat: implement header param injection handling for JWT vulnerabilities --- .../internal/utility/LevelConstants.java | 1 + .../vulnerability/jwt/JWTVulnerability.java | 26 ++++++++++++++ .../types/VulnerabilityType.java | 2 +- .../resources/i18n/messages_en_US.properties | 5 ++- .../resources/i18n/messages_es.properties | 2 ++ .../LEVEL_13/HeaderInjection_Level13.css | 36 +++++++++++++++++++ .../LEVEL_13/HeaderInjection_Level13.html | 22 ++++++++++++ .../LEVEL_13/HeaderInjection_Level13.js | 18 ++++++++++ src/main/resources/static/vulnerableApp.js | 7 +++- 9 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.css create mode 100644 src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.html create mode 100644 src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js diff --git a/src/main/java/org/sasanlabs/internal/utility/LevelConstants.java b/src/main/java/org/sasanlabs/internal/utility/LevelConstants.java index bff253e9..76687664 100755 --- a/src/main/java/org/sasanlabs/internal/utility/LevelConstants.java +++ b/src/main/java/org/sasanlabs/internal/utility/LevelConstants.java @@ -18,6 +18,7 @@ public interface LevelConstants { String LEVEL_10 = "LEVEL_10"; String LEVEL_11 = "LEVEL_11"; String LEVEL_12 = "LEVEL_12"; + String LEVEL_13 = "LEVEL_13"; static int getOrdinal(String level) { if (level.indexOf("_") > 0) { diff --git a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java index 56e7e3c3..575f6e0e 100644 --- a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java @@ -32,6 +32,8 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestParam; +import javax.servlet.http.HttpServletRequest; + /** * JWT client and server side implementation issues and remediations. Server side issues like: 1. * Weak HMAC key 2. none algorithm attack 3. Weak Hash algorithm 4. tweak Algorithm and Key. @@ -662,4 +664,28 @@ private ResponseEntity> getJWTResponseB true, token, true, CollectionUtils.toMultiValueMap(headers)); return responseEntity; } + + @AttackVector( + vulnerabilityExposed = VulnerabilityType.HEADER_INJECTION, + description = "HEADER_INJECTION_VULNERABILITY_EXAMPLE" + ) + @VulnerableAppRequestMapping( + value = LevelConstants.LEVEL_13, + htmlTemplate = "LEVEL_13/HeaderInjection_Level13" + ) + public ResponseEntity> getHeaderInjectionVulnerability( + HttpServletRequest request) { + String headerValue = request.getHeader("User-Defined-Header"); + if (headerValue != null && headerValue.contains("malicious")) { + return new ResponseEntity<>( + new GenericVulnerabilityResponseBean<>("Vulnerability exploited!", false), + HttpStatus.OK + ); + } + return new ResponseEntity<>( + new GenericVulnerabilityResponseBean<>("Safe header", true), + HttpStatus.OK + ); + } + } diff --git a/src/main/java/org/sasanlabs/vulnerability/types/VulnerabilityType.java b/src/main/java/org/sasanlabs/vulnerability/types/VulnerabilityType.java index 06c34af7..a5d4cc17 100644 --- a/src/main/java/org/sasanlabs/vulnerability/types/VulnerabilityType.java +++ b/src/main/java/org/sasanlabs/vulnerability/types/VulnerabilityType.java @@ -21,7 +21,7 @@ public enum VulnerabilityType { CLIENT_SIDE_VULNERABLE_JWT(null, null), SERVER_SIDE_VULNERABLE_JWT(null, null), INSECURE_CONFIGURATION_JWT(null, null), - + HEADER_INJECTION(20, 20), PATH_TRAVERSAL(22, 33), COMMAND_INJECTION(77, 31), diff --git a/src/main/resources/i18n/messages_en_US.properties b/src/main/resources/i18n/messages_en_US.properties index d73d7e89..f6353b0f 100755 --- a/src/main/resources/i18n/messages_en_US.properties +++ b/src/main/resources/i18n/messages_en_US.properties @@ -285,4 +285,7 @@ SSRF_VULNERABILITY_URL_WITHOUT_CHECK=No validation on the provided URL. SSRF_VULNERABILITY_URL_IF_NOT_FILE_PROTOCOL=file:// protocol is not allowed for the provided URL. SSRF_VULNERABILITY_URL_IF_NOT_FILE_PROTOCOL_AND_169.254.169.254=file:// protocol as well as access to internal metadata service IP 169.254.169.254 is not allowed. SSRF_VULNERABILITY_URL_IF_NOT_FILE_PROTOCOL_AND_INTERNAL_METADATA_URL=file:// protocol as well as access to internal metadata service is not allowed. -SSRF_VULNERABILITY_URL_ONLY_IF_IN_THE_WHITELIST=Only Whitelisted URL is allowed. \ No newline at end of file +SSRF_VULNERABILITY_URL_ONLY_IF_IN_THE_WHITELIST=Only Whitelisted URL is allowed. + +# JWT Injection Header +HEADER_INJECTION_VULNERABILITY_EXAMPLE=Header Injection Vulnerability Example \ No newline at end of file diff --git a/src/main/resources/i18n/messages_es.properties b/src/main/resources/i18n/messages_es.properties index df0319a8..57bf7f35 100644 --- a/src/main/resources/i18n/messages_es.properties +++ b/src/main/resources/i18n/messages_es.properties @@ -228,6 +228,8 @@ COOKIE_BASED_KEY_CONFUSION_JWT_VULNERABILITY=Validador de token JWT basado en co COOKIE_BASED_FOR_JWK_HEADER_BASED_JWT_VULNERABILITY=Validador de token JWT basado en cookies, vulnerable por confianza en el campo JWK sin chequear antes si la clave pública provista está presente en TrustStore o no. COOKIE_BASED_EMPTY_TOKEN_JWT_VULNERABILITY=Token JWT basado en cookies, vulnerable por el ataque de token vacío. +# JWT Injection Header +HEADER_INJECTION_VULNERABILITY_EXAMPLE=Ejemplo de vulnerabilidad de inyección de encabezado # SQL Injection Vulnerability diff --git a/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.css b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.css new file mode 100644 index 00000000..6b383918 --- /dev/null +++ b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.css @@ -0,0 +1,36 @@ +#header_injection_level_13 { + color: black; + text-align: justify; +} + +#enterHeader { + font-size: 15px; + display: flex; + margin: 10px; + flex-direction: column; +} + +#headerName, #headerValue { + flex: 1; + word-wrap: break-word; + margin-top: 10px; +} + +#headerResponse { + font-size: 15px; + word-wrap: break-word; + text-align: center; + margin: 10px; +} + +#sendHeader { + background: blueviolet; + display: inline-block; + padding: 4px 4px; + margin: 10px; + border: 1px solid transparent; + border-radius: 2px; + transition: 0.2s opacity; + color: #FFF; + font-size: 12px; +} diff --git a/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.html b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.html new file mode 100644 index 00000000..776251f7 --- /dev/null +++ b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.html @@ -0,0 +1,22 @@ + + + + + Header Injection + + +
+
+
+
Header Name:
+ +
Header Value:
+ +
+ +
+
+
+ + + \ No newline at end of file diff --git a/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js new file mode 100644 index 00000000..1baeddeb --- /dev/null +++ b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js @@ -0,0 +1,18 @@ +function addEventListenerToSendHeaderButton() { + document.getElementById("sendHeader").addEventListener("click", function() { + const headerName = document.getElementById("headerName").value; + const headerValue = document.getElementById("headerValue").value; + + let url = getUrlForVulnerabilityLevel(); + + doGetAjaxCall(function(data) { + document.getElementById("headerResponse").innerHTML = data.isValid ? + "Header Injection was successful!" : + "Header Injection failed. Please try again."; + }, url, true, { + [headerName]: headerValue + }); + }); +} + +addEventListenerToSendHeaderButton(); diff --git a/src/main/resources/static/vulnerableApp.js b/src/main/resources/static/vulnerableApp.js index c633de55..759f6d24 100644 --- a/src/main/resources/static/vulnerableApp.js +++ b/src/main/resources/static/vulnerableApp.js @@ -235,7 +235,7 @@ function genericResponseHandler(xmlHttpRequest, callBack, isJson) { } } -function doGetAjaxCall(callBack, url, isJson) { +function doGetAjaxCall(callBack, url, isJson, headers = {}) { let xmlHttpRequest = new XMLHttpRequest(); xmlHttpRequest.onreadystatechange = function () { genericResponseHandler(xmlHttpRequest, callBack, isJson); @@ -245,6 +245,11 @@ function doGetAjaxCall(callBack, url, isJson) { "Content-Type", isJson ? "application/json" : "text/html" ); + + for (const header in headers) { + xmlHttpRequest.setRequestHeader(header, headers[header]); + } + xmlHttpRequest.send(); } From 4812d61125159a2b03c6ef17ef7869c7b34c76f7 Mon Sep 17 00:00:00 2001 From: leiber bertel Date: Sun, 18 Aug 2024 22:57:26 -0500 Subject: [PATCH 2/9] apply spotless --- .../sasanlabs/service/vulnerability/jwt/JWTVulnerability.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java index 575f6e0e..82000799 100644 --- a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java @@ -673,8 +673,7 @@ private ResponseEntity> getJWTResponseB value = LevelConstants.LEVEL_13, htmlTemplate = "LEVEL_13/HeaderInjection_Level13" ) - public ResponseEntity> getHeaderInjectionVulnerability( - HttpServletRequest request) { + public ResponseEntity> getHeaderInjectionVulnerability(HttpServletRequest request) { String headerValue = request.getHeader("User-Defined-Header"); if (headerValue != null && headerValue.contains("malicious")) { return new ResponseEntity<>( @@ -688,4 +687,5 @@ public ResponseEntity> getHeaderInjecti ); } + } From 262afac06878e476da7525fd1190ab5fc65e80d7 Mon Sep 17 00:00:00 2001 From: leiber bertel Date: Sun, 18 Aug 2024 23:17:50 -0500 Subject: [PATCH 3/9] apply spotless again --- .../vulnerability/jwt/JWTVulnerability.java | 21 +++++--------- .../LEVEL_13/HeaderInjection_Level13.js | 29 +++++++++++-------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java index 82000799..b70fff1b 100644 --- a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import javax.servlet.http.HttpServletRequest; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.sasanlabs.internal.utility.LevelConstants; @@ -32,8 +33,6 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.bind.annotation.RequestParam; -import javax.servlet.http.HttpServletRequest; - /** * JWT client and server side implementation issues and remediations. Server side issues like: 1. * Weak HMAC key 2. none algorithm attack 3. Weak Hash algorithm 4. tweak Algorithm and Key. @@ -667,25 +666,19 @@ private ResponseEntity> getJWTResponseB @AttackVector( vulnerabilityExposed = VulnerabilityType.HEADER_INJECTION, - description = "HEADER_INJECTION_VULNERABILITY_EXAMPLE" - ) + description = "HEADER_INJECTION_VULNERABILITY_EXAMPLE") @VulnerableAppRequestMapping( value = LevelConstants.LEVEL_13, - htmlTemplate = "LEVEL_13/HeaderInjection_Level13" - ) - public ResponseEntity> getHeaderInjectionVulnerability(HttpServletRequest request) { + htmlTemplate = "LEVEL_13/HeaderInjection_Level13") + public ResponseEntity> getHeaderInjectionVulnerability( + HttpServletRequest request) { String headerValue = request.getHeader("User-Defined-Header"); if (headerValue != null && headerValue.contains("malicious")) { return new ResponseEntity<>( new GenericVulnerabilityResponseBean<>("Vulnerability exploited!", false), - HttpStatus.OK - ); + HttpStatus.OK); } return new ResponseEntity<>( - new GenericVulnerabilityResponseBean<>("Safe header", true), - HttpStatus.OK - ); + new GenericVulnerabilityResponseBean<>("Safe header", true), HttpStatus.OK); } - - } diff --git a/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js index 1baeddeb..d8a0d401 100644 --- a/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js +++ b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js @@ -1,18 +1,23 @@ function addEventListenerToSendHeaderButton() { - document.getElementById("sendHeader").addEventListener("click", function() { - const headerName = document.getElementById("headerName").value; - const headerValue = document.getElementById("headerValue").value; + document.getElementById("sendHeader").addEventListener("click", function () { + const headerName = document.getElementById("headerName").value; + const headerValue = document.getElementById("headerValue").value; - let url = getUrlForVulnerabilityLevel(); + let url = getUrlForVulnerabilityLevel(); - doGetAjaxCall(function(data) { - document.getElementById("headerResponse").innerHTML = data.isValid ? - "Header Injection was successful!" : - "Header Injection failed. Please try again."; - }, url, true, { - [headerName]: headerValue - }); - }); + doGetAjaxCall( + function (data) { + document.getElementById("headerResponse").innerHTML = data.isValid + ? "Header Injection was successful!" + : "Header Injection failed. Please try again."; + }, + url, + true, + { + [headerName]: headerValue, + } + ); + }); } addEventListenerToSendHeaderButton(); From ebcae7431dcc15c65c6921489b9464d0276e2703 Mon Sep 17 00:00:00 2001 From: leiber bertel Date: Sat, 28 Sep 2024 17:51:56 -0500 Subject: [PATCH 4/9] fix: modified to meet requirements --- build.gradle | 7 +- .../vulnerability/jwt/JWTVulnerability.java | 43 ++++++- .../SampleVulnerability.java | 109 ++++++++++++++++++ .../LEVEL_13/HeaderInjection_Level13.js | 23 ++-- .../LEVEL_1/SampleVulnerability.css | 16 +++ .../LEVEL_1/SampleVulnerability.html | 9 ++ .../LEVEL_1/SampleVulnerability.js | 23 ++++ 7 files changed, 215 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java create mode 100644 src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.css create mode 100644 src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.html create mode 100644 src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.js diff --git a/build.gradle b/build.gradle index 25fe4e2c..a126b805 100644 --- a/build.gradle +++ b/build.gradle @@ -150,7 +150,7 @@ dependencies { implementation group: 'org.json', name: 'json', version: '20190722' //https://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt - implementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '8.3' + implementation group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '9.31' // https://mvnrepository.com/artifact/commons-io/commons-io implementation group: 'commons-io', name: 'commons-io', version: '2.7' @@ -158,6 +158,11 @@ dependencies { implementation group: 'io.github.sasanlabs', name: 'facade-schema', version: '1.0.1' implementation group: 'commons-fileupload', name: 'commons-fileupload', version: '1.5' + + // https://mvnrepository.com/artifact/com.auth0/java-jwt + implementation group: 'com.auth0', name: 'java-jwt', version: '4.2.1' + + } test { diff --git a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java index b70fff1b..194933f3 100644 --- a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java @@ -2,6 +2,12 @@ import static org.sasanlabs.service.vulnerability.jwt.bean.JWTUtils.GENERIC_BASE64_ENCODED_PAYLOAD; +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.auth0.jwt.interfaces.JWTVerifier; +import com.nimbusds.jose.jwk.JWK; +import com.nimbusds.jose.jwk.RSAKey; import java.io.UnsupportedEncodingException; import java.security.KeyPair; import java.security.interfaces.RSAPrivateKey; @@ -672,12 +678,41 @@ private ResponseEntity> getJWTResponseB htmlTemplate = "LEVEL_13/HeaderInjection_Level13") public ResponseEntity> getHeaderInjectionVulnerability( HttpServletRequest request) { - String headerValue = request.getHeader("User-Defined-Header"); - if (headerValue != null && headerValue.contains("malicious")) { + String jwtToken = request.getHeader("Authorization"); + if (jwtToken == null || !jwtToken.startsWith("Bearer ")) { return new ResponseEntity<>( - new GenericVulnerabilityResponseBean<>("Vulnerability exploited!", false), - HttpStatus.OK); + new GenericVulnerabilityResponseBean<>("No JWT token provided", true), + HttpStatus.BAD_REQUEST); } + + jwtToken = jwtToken.substring(7); // Remove "Bearer " prefix + + try { + DecodedJWT decodedJWT = com.auth0.jwt.JWT.decode(jwtToken); + String jwkHeader = decodedJWT.getHeaderClaim("jwk").asString(); + + if (jwkHeader != null) { + JWK jwk = JWK.parse(jwkHeader); + + RSAKey rsaKey = (RSAKey) jwk; + RSAPublicKey publicKey = rsaKey.toRSAPublicKey(); + + Algorithm algorithm = Algorithm.RSA256(publicKey, null); + JWTVerifier verifier = com.auth0.jwt.JWT.require(algorithm).build(); + verifier.verify(jwtToken); + + return new ResponseEntity<>( + new GenericVulnerabilityResponseBean<>( + "JWK Header Injection Exploited!", false), + HttpStatus.OK); + } + + } catch (Exception e) { + return new ResponseEntity<>( + new GenericVulnerabilityResponseBean<>("Invalid JWT", true), + HttpStatus.BAD_REQUEST); + } + return new ResponseEntity<>( new GenericVulnerabilityResponseBean<>("Safe header", true), HttpStatus.OK); } diff --git a/src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java new file mode 100644 index 00000000..93727ab3 --- /dev/null +++ b/src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java @@ -0,0 +1,109 @@ +package org.sasanlabs.service.vulnerability.sampleVulnerability; + +import org.sasanlabs.internal.utility.LevelConstants; +import org.sasanlabs.internal.utility.Variant; +import org.sasanlabs.internal.utility.annotations.AttackVector; +import org.sasanlabs.internal.utility.annotations.VulnerableAppRequestMapping; +import org.sasanlabs.internal.utility.annotations.VulnerableAppRestController; +import org.sasanlabs.service.vulnerability.bean.GenericVulnerabilityResponseBean; +import org.sasanlabs.vulnerability.types.VulnerabilityType; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * This is a sample vulnerability for helping developers in adding a new Vulnerability for + * VulnerableApp + * + * @author KSASAN preetkaran20@gmail.com + */ +/** + * {@code VulnerableAppRestController} annotation is similar to {@link + * org.springframework.stereotype.Controller} Annotation + */ +@VulnerableAppRestController( + /** + * "descriptionLabel" parameter of annotation is i18n label stored in {@link + * /VulnerableApp/src/main/resources/i18n/}. This descriptionLabel will be shown in the UI + * as the description of the Vulnerability. It helps students to learn about the + * vulnerability and can also include some of the useful references etc. + */ + descriptionLabel = "SAMPLE_VULNERABILITY", + /** + * "value" parameter of annotation is used to create the request mapping. e.g. for the below + * parameter value, /VulnerableApp/SampleVulnerability will be created as URI Path. + */ + value = "SampleVulnerability") +public class SampleVulnerability { + + /** + * {@code AttackVector} annotation is used to create the Hints section in the User Interface. + * This annotation can be mentioned multiple times in case the same vulnerability level + */ + @AttackVector( + /** + * "vulnerabilityExposed" parameter is used to depict the Vulnerability exposed by the + * level. For example say a level is exposing SQL_INJECTION. + */ + vulnerabilityExposed = VulnerabilityType.SAMPLE_VULNERABILITY, + /** + * "description" parameter of annotation is i18n label stored in {@link + * /VulnerableApp/src/main/resources/i18n/}. This description will be shown in the UI as + * hint to give some indication on how the level is handling input to help user to crack + * the level. + */ + description = "SAMPLE_VULNERABILITY_USER_INPUT_HANDLING_INJECTION", + + /** + * "payload" parameter of annotation is i18n label stored in {@link + * /VulnerableApp/src/main/resources/attackvectors/*.properties}. This payload will be + * shown in UI to help users find/exploit the vulnerability + */ + payload = "NOT_APPLICABLE") + /** + * This annotation is similar to {@link RequestMapping} SpringBoot annotation. It will map the + * endpoint to /VulnerableApp/SampleVulnerability/LEVEL_1 where LEVEL_1 is coming from the value + * parameter. + */ + @VulnerableAppRequestMapping( + /** + * "value" parameter is used to map the level to URI path + * /VulnerableApp/SampleVulnerability/${value}. + */ + value = LevelConstants.LEVEL_1, + + /** + * "htmlTemplate" is used to load the UI for the level for taking input from the user. + * It points to files in directory + * src/main/resource/static/templates/${VulnerabilityName} e.g. + * src/main/resource/static/templates/SampleVulnerability as ${htmlTemplate}.js, + * ${htmlTemplate}.css, ${htmlTemplate}.html. e.g. in this case it will be: + * src/main/resource/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability_Level1.js + * etc + * + *

CSS, JS and HTML are all loaded to render the UI. + */ + htmlTemplate = "LEVEL_1/SampleVulnerability") + public GenericVulnerabilityResponseBean sampleUnsecuredLevel( + @RequestParam("name") String key) { + /** Add Business logic here */ + return new GenericVulnerabilityResponseBean<>("Not Implemented", true); + } + + /** For secured level there is no need for {@link AttackVector} annotation. */ + @VulnerableAppRequestMapping( + value = LevelConstants.LEVEL_2, + + // Can reuse the same UI template in case it doesn't change between levels + htmlTemplate = "LEVEL_1/SampleVulnerability", + /** + * "variant" parameter defines whether the level is secure or not and same is depicted + * in the UI as a closed lock and open lock icon. Default value of the variant is + * UNSECURE so in case a secure level is added, please add the variant as {@link + * Variant#SECURE} + */ + variant = Variant.SECURE) + public GenericVulnerabilityResponseBean sampleSecuredLevel( + @RequestParam("name") String key) { + /** Add Business logic here */ + return new GenericVulnerabilityResponseBean<>("Not Implemented", true); + } +} diff --git a/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js index d8a0d401..ea8f0cec 100644 --- a/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js +++ b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js @@ -5,17 +5,20 @@ function addEventListenerToSendHeaderButton() { let url = getUrlForVulnerabilityLevel(); + const manipulatedJwt = "eyJhbGciOiJSUzI1NiIsImtpZCI6Im1hbGljaW91cy1rZXktaWQifQ.eyJzdWIiOiJleGFtcGxldXNlciIsIm5hbWUiOiJKV1QgVXNlciIsImlhdCI6MTYwOTAxMjAwMH0.c7qHUq1HbHj8AWjKbcIYH2NZnE6PtNyXTnJTWZELvFbfbFhc5BQ_w8e24fXL2OzhhOT5qHVzFvHgOeEYFLZNGEDlJhF4o76yHsMJdWQFL4I5uZjG0o8XV0HjDdM7GqEmx2j0JHi6vJ8Q3pIqGzUBmb7bgzD4kENnP-UqfkbNl2ykYZ9Nybw_E7CAV4OxuqE4QyIpZV2VttWjefK3c6TIj9hNWvYYgipKwHFLXbOV-rOZ6K-_H_4D-kbr0LKPPX-s4b11o0wtS3y1FiHDXEvsmEjhRApEc_jk5uZY-AGPUc9Nl9t6iT_Nh1Q8Usz-jZifg03NwumJjDNtz-nS7gzg"; + doGetAjaxCall( - function (data) { - document.getElementById("headerResponse").innerHTML = data.isValid - ? "Header Injection was successful!" - : "Header Injection failed. Please try again."; - }, - url, - true, - { - [headerName]: headerValue, - } + function (data) { + document.getElementById("headerResponse").innerHTML = data.isValid + ? "Header Injection was successful!" + : "Header Injection failed. Please try again."; + }, + url, + true, + { + [headerName]: headerValue, + "Authorization": `Bearer ${manipulatedJwt}` + } ); }); } diff --git a/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.css b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.css new file mode 100644 index 00000000..e5d57b25 --- /dev/null +++ b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.css @@ -0,0 +1,16 @@ +#SampleVulnerability { + color: black; + text-align: center; +} + +#fetchDetails { + background: blueviolet; + display: inline-block; + padding: 8px 8px; + margin: 10px; + border: 2px solid transparent; + border-radius: 3px; + transition: 0.2s opacity; + color: #FFF; + font-size: 12px; +} \ No newline at end of file diff --git a/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.html b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.html new file mode 100644 index 00000000..dddeea7d --- /dev/null +++ b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.html @@ -0,0 +1,9 @@ +

+
+
+ This is a Sample Vulnerability. please add the UI components here. +
+ +
+
+
\ No newline at end of file diff --git a/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.js b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.js new file mode 100644 index 00000000..78641721 --- /dev/null +++ b/src/main/resources/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability.js @@ -0,0 +1,23 @@ +function addingEventListenerToFetchData() { + document + .getElementById("fetchDetails") + .addEventListener("click", function () { + /** + * getUrlForVulnerabilityLevel() method provides url to call the Vulnerability Level + * of Sample Vulnerability. + * e.g. /VulnerableApp/SampleVulnerability/LEVEL_1 for LEVEL_1 + */ + let url = getUrlForVulnerabilityLevel(); + /** + * doGetAjaxCall() method is used to do the ajax get call to the Vulnerability Level + */ + doGetAjaxCall(fetchDataCallback, url + "?name=dummyInput", true); + }); +} +// Used to register event on the button or any other component +addingEventListenerToFetchData(); + +//Callback function to handle the response and render in the UI +function fetchDataCallback(data) { + document.getElementById("response").innerHTML = data.content; +} From be4751a2b26e58bb52d5cb1e1339873191e5c9c3 Mon Sep 17 00:00:00 2001 From: leiber bertel Date: Sat, 28 Sep 2024 18:00:47 -0500 Subject: [PATCH 5/9] style: apply spotless --- .../LEVEL_13/HeaderInjection_Level13.js | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js index ea8f0cec..2911023e 100644 --- a/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js +++ b/src/main/resources/static/templates/JWTVulnerability/LEVEL_13/HeaderInjection_Level13.js @@ -5,20 +5,21 @@ function addEventListenerToSendHeaderButton() { let url = getUrlForVulnerabilityLevel(); - const manipulatedJwt = "eyJhbGciOiJSUzI1NiIsImtpZCI6Im1hbGljaW91cy1rZXktaWQifQ.eyJzdWIiOiJleGFtcGxldXNlciIsIm5hbWUiOiJKV1QgVXNlciIsImlhdCI6MTYwOTAxMjAwMH0.c7qHUq1HbHj8AWjKbcIYH2NZnE6PtNyXTnJTWZELvFbfbFhc5BQ_w8e24fXL2OzhhOT5qHVzFvHgOeEYFLZNGEDlJhF4o76yHsMJdWQFL4I5uZjG0o8XV0HjDdM7GqEmx2j0JHi6vJ8Q3pIqGzUBmb7bgzD4kENnP-UqfkbNl2ykYZ9Nybw_E7CAV4OxuqE4QyIpZV2VttWjefK3c6TIj9hNWvYYgipKwHFLXbOV-rOZ6K-_H_4D-kbr0LKPPX-s4b11o0wtS3y1FiHDXEvsmEjhRApEc_jk5uZY-AGPUc9Nl9t6iT_Nh1Q8Usz-jZifg03NwumJjDNtz-nS7gzg"; + const manipulatedJwt = + "eyJhbGciOiJSUzI1NiIsImtpZCI6Im1hbGljaW91cy1rZXktaWQifQ.eyJzdWIiOiJleGFtcGxldXNlciIsIm5hbWUiOiJKV1QgVXNlciIsImlhdCI6MTYwOTAxMjAwMH0.c7qHUq1HbHj8AWjKbcIYH2NZnE6PtNyXTnJTWZELvFbfbFhc5BQ_w8e24fXL2OzhhOT5qHVzFvHgOeEYFLZNGEDlJhF4o76yHsMJdWQFL4I5uZjG0o8XV0HjDdM7GqEmx2j0JHi6vJ8Q3pIqGzUBmb7bgzD4kENnP-UqfkbNl2ykYZ9Nybw_E7CAV4OxuqE4QyIpZV2VttWjefK3c6TIj9hNWvYYgipKwHFLXbOV-rOZ6K-_H_4D-kbr0LKPPX-s4b11o0wtS3y1FiHDXEvsmEjhRApEc_jk5uZY-AGPUc9Nl9t6iT_Nh1Q8Usz-jZifg03NwumJjDNtz-nS7gzg"; doGetAjaxCall( - function (data) { - document.getElementById("headerResponse").innerHTML = data.isValid - ? "Header Injection was successful!" - : "Header Injection failed. Please try again."; - }, - url, - true, - { - [headerName]: headerValue, - "Authorization": `Bearer ${manipulatedJwt}` - } + function (data) { + document.getElementById("headerResponse").innerHTML = data.isValid + ? "Header Injection was successful!" + : "Header Injection failed. Please try again."; + }, + url, + true, + { + [headerName]: headerValue, + Authorization: `Bearer ${manipulatedJwt}`, + } ); }); } From b7cea1951d84ad475dd9c76b25150cd8c9579c29 Mon Sep 17 00:00:00 2001 From: leiber bertel Date: Wed, 2 Oct 2024 20:16:08 -0500 Subject: [PATCH 6/9] fix: Maintain implementation so as not to be dependent on external dependency and update messages --- .../vulnerability/jwt/JWTVulnerability.java | 33 +++++++++---------- .../vulnerability/jwt/bean/JWTUtils.java | 1 + .../resources/i18n/messages_en_US.properties | 2 +- .../resources/i18n/messages_es.properties | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java index 194933f3..821768b2 100644 --- a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java @@ -3,11 +3,11 @@ import static org.sasanlabs.service.vulnerability.jwt.bean.JWTUtils.GENERIC_BASE64_ENCODED_PAYLOAD; import com.auth0.jwt.JWT; -import com.auth0.jwt.algorithms.Algorithm; -import com.auth0.jwt.interfaces.DecodedJWT; -import com.auth0.jwt.interfaces.JWTVerifier; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jwt.SignedJWT; import java.io.UnsupportedEncodingException; import java.security.KeyPair; import java.security.interfaces.RSAPrivateKey; @@ -672,39 +672,38 @@ private ResponseEntity> getJWTResponseB @AttackVector( vulnerabilityExposed = VulnerabilityType.HEADER_INJECTION, - description = "HEADER_INJECTION_VULNERABILITY_EXAMPLE") + description = "HEADER_INJECTION_VULNERABILITY") @VulnerableAppRequestMapping( value = LevelConstants.LEVEL_13, htmlTemplate = "LEVEL_13/HeaderInjection_Level13") public ResponseEntity> getHeaderInjectionVulnerability( HttpServletRequest request) { String jwtToken = request.getHeader("Authorization"); - if (jwtToken == null || !jwtToken.startsWith("Bearer ")) { + if (jwtToken == null || !jwtToken.startsWith(JWTUtils.BEARER_PREFIX)) { return new ResponseEntity<>( new GenericVulnerabilityResponseBean<>("No JWT token provided", true), HttpStatus.BAD_REQUEST); } - jwtToken = jwtToken.substring(7); // Remove "Bearer " prefix + jwtToken = jwtToken.replaceFirst("^" + JWTUtils.BEARER_PREFIX, "").trim(); try { - DecodedJWT decodedJWT = com.auth0.jwt.JWT.decode(jwtToken); - String jwkHeader = decodedJWT.getHeaderClaim("jwk").asString(); + SignedJWT signedJWT = SignedJWT.parse(jwtToken); + + String jwkHeader = (String) signedJWT.getHeader().toJSONObject().get("jwk"); if (jwkHeader != null) { JWK jwk = JWK.parse(jwkHeader); - RSAKey rsaKey = (RSAKey) jwk; RSAPublicKey publicKey = rsaKey.toRSAPublicKey(); - Algorithm algorithm = Algorithm.RSA256(publicKey, null); - JWTVerifier verifier = com.auth0.jwt.JWT.require(algorithm).build(); - verifier.verify(jwtToken); - - return new ResponseEntity<>( - new GenericVulnerabilityResponseBean<>( - "JWK Header Injection Exploited!", false), - HttpStatus.OK); + JWSVerifier verifier = new RSASSAVerifier(publicKey); + if (signedJWT.verify(verifier)) { + return new ResponseEntity<>( + new GenericVulnerabilityResponseBean<>( + "JWK Header Injection Exploited!", false), + HttpStatus.OK); + } } } catch (Exception e) { diff --git a/src/main/java/org/sasanlabs/service/vulnerability/jwt/bean/JWTUtils.java b/src/main/java/org/sasanlabs/service/vulnerability/jwt/bean/JWTUtils.java index cee663c6..10386e40 100755 --- a/src/main/java/org/sasanlabs/service/vulnerability/jwt/bean/JWTUtils.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/jwt/bean/JWTUtils.java @@ -37,6 +37,7 @@ public class JWTUtils { public static final String JWT_EC_ALGORITHM_IDENTIFIER = "EC"; public static final String JWT_OCTET_ALGORITHM_IDENTIFIER = "ED"; public static final String JWT_HMAC_SHA_256_ALGORITHM = "HS256"; + public static final String BEARER_PREFIX = "Bearer "; // TODO need to make it better. public static final String HS256_TOKEN_TO_BE_SIGNED = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." diff --git a/src/main/resources/i18n/messages_en_US.properties b/src/main/resources/i18n/messages_en_US.properties index f6353b0f..c0ebb66b 100755 --- a/src/main/resources/i18n/messages_en_US.properties +++ b/src/main/resources/i18n/messages_en_US.properties @@ -288,4 +288,4 @@ SSRF_VULNERABILITY_URL_IF_NOT_FILE_PROTOCOL_AND_INTERNAL_METADATA_URL=file:// pr SSRF_VULNERABILITY_URL_ONLY_IF_IN_THE_WHITELIST=Only Whitelisted URL is allowed. # JWT Injection Header -HEADER_INJECTION_VULNERABILITY_EXAMPLE=Header Injection Vulnerability Example \ No newline at end of file +HEADER_INJECTION_VULNERABILITY=It tests how a JWT header can be manipulated to alter the signature verification. \ No newline at end of file diff --git a/src/main/resources/i18n/messages_es.properties b/src/main/resources/i18n/messages_es.properties index 57bf7f35..c8f1fe35 100644 --- a/src/main/resources/i18n/messages_es.properties +++ b/src/main/resources/i18n/messages_es.properties @@ -229,7 +229,7 @@ COOKIE_BASED_FOR_JWK_HEADER_BASED_JWT_VULNERABILITY=Validador de token JWT basad COOKIE_BASED_EMPTY_TOKEN_JWT_VULNERABILITY=Token JWT basado en cookies, vulnerable por el ataque de token vacío. # JWT Injection Header -HEADER_INJECTION_VULNERABILITY_EXAMPLE=Ejemplo de vulnerabilidad de inyección de encabezado +HEADER_INJECTION_VULNERABILITY=Prueba cómo un encabezado JWT puede ser manipulado para alterar la verificación de la firma. # SQL Injection Vulnerability From 672b4eabd8ba04ff79c313a9ba7d21f991c40b33 Mon Sep 17 00:00:00 2001 From: leiber bertel Date: Sun, 10 Nov 2024 00:27:54 -0500 Subject: [PATCH 7/9] chore: removes unnecessary dependence --- build.gradle | 5 ----- 1 file changed, 5 deletions(-) diff --git a/build.gradle b/build.gradle index a126b805..3573ed8c 100644 --- a/build.gradle +++ b/build.gradle @@ -158,11 +158,6 @@ dependencies { implementation group: 'io.github.sasanlabs', name: 'facade-schema', version: '1.0.1' implementation group: 'commons-fileupload', name: 'commons-fileupload', version: '1.5' - - // https://mvnrepository.com/artifact/com.auth0/java-jwt - implementation group: 'com.auth0', name: 'java-jwt', version: '4.2.1' - - } test { From 324fa33655ad40ff00063f15eb55983fb731fb78 Mon Sep 17 00:00:00 2001 From: leiber bertel Date: Sun, 10 Nov 2024 00:28:32 -0500 Subject: [PATCH 8/9] refactor: removes unnecessary import --- .../sasanlabs/service/vulnerability/jwt/JWTVulnerability.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java index 821768b2..4cd88d68 100644 --- a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java +++ b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java @@ -2,7 +2,6 @@ import static org.sasanlabs.service.vulnerability.jwt.bean.JWTUtils.GENERIC_BASE64_ENCODED_PAYLOAD; -import com.auth0.jwt.JWT; import com.nimbusds.jose.JWSVerifier; import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.jwk.JWK; From 261ea3931c073a5d1dca655201404790ebe67f2f Mon Sep 17 00:00:00 2001 From: leiber bertel Date: Sun, 10 Nov 2024 00:36:31 -0500 Subject: [PATCH 9/9] refactor: remove boilerplate --- .../SampleVulnerability.java | 109 ------------------ 1 file changed, 109 deletions(-) delete mode 100644 src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java diff --git a/src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java deleted file mode 100644 index 93727ab3..00000000 --- a/src/main/java/org/sasanlabs/service/vulnerability/sampleVulnerability/SampleVulnerability.java +++ /dev/null @@ -1,109 +0,0 @@ -package org.sasanlabs.service.vulnerability.sampleVulnerability; - -import org.sasanlabs.internal.utility.LevelConstants; -import org.sasanlabs.internal.utility.Variant; -import org.sasanlabs.internal.utility.annotations.AttackVector; -import org.sasanlabs.internal.utility.annotations.VulnerableAppRequestMapping; -import org.sasanlabs.internal.utility.annotations.VulnerableAppRestController; -import org.sasanlabs.service.vulnerability.bean.GenericVulnerabilityResponseBean; -import org.sasanlabs.vulnerability.types.VulnerabilityType; -import org.springframework.web.bind.annotation.RequestParam; - -/** - * This is a sample vulnerability for helping developers in adding a new Vulnerability for - * VulnerableApp - * - * @author KSASAN preetkaran20@gmail.com - */ -/** - * {@code VulnerableAppRestController} annotation is similar to {@link - * org.springframework.stereotype.Controller} Annotation - */ -@VulnerableAppRestController( - /** - * "descriptionLabel" parameter of annotation is i18n label stored in {@link - * /VulnerableApp/src/main/resources/i18n/}. This descriptionLabel will be shown in the UI - * as the description of the Vulnerability. It helps students to learn about the - * vulnerability and can also include some of the useful references etc. - */ - descriptionLabel = "SAMPLE_VULNERABILITY", - /** - * "value" parameter of annotation is used to create the request mapping. e.g. for the below - * parameter value, /VulnerableApp/SampleVulnerability will be created as URI Path. - */ - value = "SampleVulnerability") -public class SampleVulnerability { - - /** - * {@code AttackVector} annotation is used to create the Hints section in the User Interface. - * This annotation can be mentioned multiple times in case the same vulnerability level - */ - @AttackVector( - /** - * "vulnerabilityExposed" parameter is used to depict the Vulnerability exposed by the - * level. For example say a level is exposing SQL_INJECTION. - */ - vulnerabilityExposed = VulnerabilityType.SAMPLE_VULNERABILITY, - /** - * "description" parameter of annotation is i18n label stored in {@link - * /VulnerableApp/src/main/resources/i18n/}. This description will be shown in the UI as - * hint to give some indication on how the level is handling input to help user to crack - * the level. - */ - description = "SAMPLE_VULNERABILITY_USER_INPUT_HANDLING_INJECTION", - - /** - * "payload" parameter of annotation is i18n label stored in {@link - * /VulnerableApp/src/main/resources/attackvectors/*.properties}. This payload will be - * shown in UI to help users find/exploit the vulnerability - */ - payload = "NOT_APPLICABLE") - /** - * This annotation is similar to {@link RequestMapping} SpringBoot annotation. It will map the - * endpoint to /VulnerableApp/SampleVulnerability/LEVEL_1 where LEVEL_1 is coming from the value - * parameter. - */ - @VulnerableAppRequestMapping( - /** - * "value" parameter is used to map the level to URI path - * /VulnerableApp/SampleVulnerability/${value}. - */ - value = LevelConstants.LEVEL_1, - - /** - * "htmlTemplate" is used to load the UI for the level for taking input from the user. - * It points to files in directory - * src/main/resource/static/templates/${VulnerabilityName} e.g. - * src/main/resource/static/templates/SampleVulnerability as ${htmlTemplate}.js, - * ${htmlTemplate}.css, ${htmlTemplate}.html. e.g. in this case it will be: - * src/main/resource/static/templates/SampleVulnerability/LEVEL_1/SampleVulnerability_Level1.js - * etc - * - *

CSS, JS and HTML are all loaded to render the UI. - */ - htmlTemplate = "LEVEL_1/SampleVulnerability") - public GenericVulnerabilityResponseBean sampleUnsecuredLevel( - @RequestParam("name") String key) { - /** Add Business logic here */ - return new GenericVulnerabilityResponseBean<>("Not Implemented", true); - } - - /** For secured level there is no need for {@link AttackVector} annotation. */ - @VulnerableAppRequestMapping( - value = LevelConstants.LEVEL_2, - - // Can reuse the same UI template in case it doesn't change between levels - htmlTemplate = "LEVEL_1/SampleVulnerability", - /** - * "variant" parameter defines whether the level is secure or not and same is depicted - * in the UI as a closed lock and open lock icon. Default value of the variant is - * UNSECURE so in case a secure level is added, please add the variant as {@link - * Variant#SECURE} - */ - variant = Variant.SECURE) - public GenericVulnerabilityResponseBean sampleSecuredLevel( - @RequestParam("name") String key) { - /** Add Business logic here */ - return new GenericVulnerabilityResponseBean<>("Not Implemented", true); - } -}