diff --git a/src/main/java/org/sasanlabs/service/vulnerability/commandInjection/CommandInjection.java b/src/main/java/org/sasanlabs/service/vulnerability/commandInjection/CommandInjection.java
index 8049ecda..1a56fe6e 100644
--- a/src/main/java/org/sasanlabs/service/vulnerability/commandInjection/CommandInjection.java
+++ b/src/main/java/org/sasanlabs/service/vulnerability/commandInjection/CommandInjection.java
@@ -2,190 +2,49 @@
import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
-import java.util.function.Supplier;
-import java.util.regex.Pattern;
-import org.apache.commons.lang3.StringUtils;
-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.exception.ServiceApplicationException;
-import org.sasanlabs.service.vulnerability.bean.GenericVulnerabilityResponseBean;
-import org.sasanlabs.vulnerability.types.VulnerabilityType;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.RequestEntity;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.RequestParam;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
-/**
- * This class contains vulnerabilities related to Command Injection. For More information
- *
- * @author KSASAN preetkaran20@gmail.com
- */
-@VulnerableAppRestController(
- descriptionLabel = "COMMAND_INJECTION_VULNERABILITY",
- value = "CommandInjection")
public class CommandInjection {
- private static final String IP_ADDRESS = "ipaddress";
- private static final Pattern SEMICOLON_SPACE_LOGICAL_AND_PATTERN = Pattern.compile("[;& ]");
- private static final Pattern IP_ADDRESS_PATTERN =
- Pattern.compile("\\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\\.|$)){4}\\b");
+ private static final Set ALLOWED_COMMANDS = Set.of("date", "uptime", "whoami");
- StringBuilder getResponseFromPingCommand(String ipAddress, boolean isValid) throws IOException {
- boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
- StringBuilder stringBuilder = new StringBuilder();
- if (isValid) {
- Process process;
- if (!isWindows) {
- process =
- new ProcessBuilder(new String[] {"sh", "-c", "ping -c 2 " + ipAddress})
- .redirectErrorStream(true)
- .start();
- } else {
- process =
- new ProcessBuilder(new String[] {"cmd", "/c", "ping -n 2 " + ipAddress})
- .redirectErrorStream(true)
- .start();
- }
- try (BufferedReader bufferedReader =
- new BufferedReader(new InputStreamReader(process.getInputStream()))) {
- bufferedReader.lines().forEach(val -> stringBuilder.append(val).append("\n"));
- }
- }
- return stringBuilder;
- }
+ public String runSafeCommand(String userCommand, List userArgs)
+ throws IOException, InterruptedException {
- @AttackVector(
- vulnerabilityExposed = VulnerabilityType.COMMAND_INJECTION,
- description = "COMMAND_INJECTION_URL_PARAM_DIRECTLY_EXECUTED")
- @VulnerableAppRequestMapping(value = LevelConstants.LEVEL_1, htmlTemplate = "LEVEL_1/CI_Level1")
- public ResponseEntity> getVulnerablePayloadLevel1(
- @RequestParam(IP_ADDRESS) String ipAddress) throws IOException {
- Supplier validator = () -> StringUtils.isNotBlank(ipAddress);
- return new ResponseEntity>(
- new GenericVulnerabilityResponseBean(
- this.getResponseFromPingCommand(ipAddress, validator.get()).toString(),
- true),
- HttpStatus.OK);
- }
-
- @AttackVector(
- vulnerabilityExposed = VulnerabilityType.COMMAND_INJECTION,
- description =
- "COMMAND_INJECTION_URL_PARAM_DIRECTLY_EXECUTED_IF_SEMICOLON_SPACE_LOGICAL_AND_NOT_PRESENT")
- @VulnerableAppRequestMapping(value = LevelConstants.LEVEL_2, htmlTemplate = "LEVEL_1/CI_Level1")
- public ResponseEntity> getVulnerablePayloadLevel2(
- @RequestParam(IP_ADDRESS) String ipAddress, RequestEntity requestEntity)
- throws ServiceApplicationException, IOException {
+ if (!ALLOWED_COMMANDS.contains(userCommand)) {
+ throw new IllegalArgumentException("Unauthorized command");
+ }
- Supplier validator =
- () ->
- StringUtils.isNotBlank(ipAddress)
- && !SEMICOLON_SPACE_LOGICAL_AND_PATTERN
- .matcher(requestEntity.getUrl().toString())
- .find();
- return new ResponseEntity>(
- new GenericVulnerabilityResponseBean(
- this.getResponseFromPingCommand(ipAddress, validator.get()).toString(),
- true),
- HttpStatus.OK);
- }
+ for (String arg : userArgs) {
+ if (arg == null || !arg.matches("[A-Za-z0-9._-]+")) {
+ throw new IllegalArgumentException("Invalid argument");
+ }
+ }
- // Case Insensitive
- @AttackVector(
- vulnerabilityExposed = VulnerabilityType.COMMAND_INJECTION,
- description =
- "COMMAND_INJECTION_URL_PARAM_DIRECTLY_EXECUTED_IF_SEMICOLON_SPACE_LOGICAL_AND_%26_%3B_NOT_PRESENT")
- @VulnerableAppRequestMapping(value = LevelConstants.LEVEL_3, htmlTemplate = "LEVEL_1/CI_Level1")
- public ResponseEntity> getVulnerablePayloadLevel3(
- @RequestParam(IP_ADDRESS) String ipAddress, RequestEntity requestEntity)
- throws ServiceApplicationException, IOException {
+ List cmd = new ArrayList<>();
+ cmd.add(userCommand);
+ cmd.addAll(userArgs);
- Supplier validator =
- () ->
- StringUtils.isNotBlank(ipAddress)
- && !SEMICOLON_SPACE_LOGICAL_AND_PATTERN
- .matcher(requestEntity.getUrl().toString())
- .find()
- && !requestEntity.getUrl().toString().contains("%26")
- && !requestEntity.getUrl().toString().contains("%3B");
- return new ResponseEntity>(
- new GenericVulnerabilityResponseBean(
- this.getResponseFromPingCommand(ipAddress, validator.get()).toString(),
- true),
- HttpStatus.OK);
- }
+ ProcessBuilder pb = new ProcessBuilder(cmd);
+ pb.redirectErrorStream(true);
+ Process process = pb.start();
- // e.g Attack
- // http://localhost:9090/vulnerable/CommandInjectionVulnerability/LEVEL_3?ipaddress=192.168.0.1%20%7c%20cat%20/etc/passwd
- @AttackVector(
- vulnerabilityExposed = VulnerabilityType.COMMAND_INJECTION,
- description =
- "COMMAND_INJECTION_URL_PARAM_DIRECTLY_EXECUTED_IF_SEMICOLON_SPACE_LOGICAL_AND_%26_%3B_CASE_INSENSITIVE_NOT_PRESENT")
- @VulnerableAppRequestMapping(value = LevelConstants.LEVEL_4, htmlTemplate = "LEVEL_1/CI_Level1")
- public ResponseEntity> getVulnerablePayloadLevel4(
- @RequestParam(IP_ADDRESS) String ipAddress, RequestEntity requestEntity)
- throws ServiceApplicationException, IOException {
+ try (InputStream is = process.getInputStream();
+ BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
- Supplier validator =
- () ->
- StringUtils.isNotBlank(ipAddress)
- && !SEMICOLON_SPACE_LOGICAL_AND_PATTERN
- .matcher(requestEntity.getUrl().toString())
- .find()
- && !requestEntity.getUrl().toString().toUpperCase().contains("%26")
- && !requestEntity.getUrl().toString().toUpperCase().contains("%3B");
- return new ResponseEntity>(
- new GenericVulnerabilityResponseBean(
- this.getResponseFromPingCommand(ipAddress, validator.get()).toString(),
- true),
- HttpStatus.OK);
- }
- // Payload: 127.0.0.1%0Als
- @AttackVector(
- vulnerabilityExposed = VulnerabilityType.COMMAND_INJECTION,
- description =
- "COMMAND_INJECTION_URL_PARAM_DIRECTLY_EXECUTED_IF_SEMICOLON_SPACE_LOGICAL_AND_%26_%3B_%7C_CASE_INSENSITIVE_NOT_PRESENT")
- @VulnerableAppRequestMapping(value = LevelConstants.LEVEL_5, htmlTemplate = "LEVEL_1/CI_Level1")
- public ResponseEntity> getVulnerablePayloadLevel5(
- @RequestParam(IP_ADDRESS) String ipAddress, RequestEntity requestEntity)
- throws IOException {
- Supplier validator =
- () ->
- StringUtils.isNotBlank(ipAddress)
- && !SEMICOLON_SPACE_LOGICAL_AND_PATTERN
- .matcher(requestEntity.getUrl().toString())
- .find()
- && !requestEntity.getUrl().toString().toUpperCase().contains("%26")
- && !requestEntity.getUrl().toString().toUpperCase().contains("%3B")
- && !requestEntity.getUrl().toString().toUpperCase().contains("%7C");
- return new ResponseEntity>(
- new GenericVulnerabilityResponseBean(
- this.getResponseFromPingCommand(ipAddress, validator.get()).toString(),
- true),
- HttpStatus.OK);
- }
+ StringBuilder output = new StringBuilder();
+ String line;
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_6,
- htmlTemplate = "LEVEL_1/CI_Level1",
- variant = Variant.SECURE)
- public ResponseEntity> getVulnerablePayloadLevel6(
- @RequestParam(IP_ADDRESS) String ipAddress) throws IOException {
- Supplier validator =
- () ->
- StringUtils.isNotBlank(ipAddress)
- && (IP_ADDRESS_PATTERN.matcher(ipAddress).matches()
- || ipAddress.contentEquals("localhost"));
+ while ((line = br.readLine()) != null) {
+ output.append(line).append("\n");
+ }
- return new ResponseEntity>(
- new GenericVulnerabilityResponseBean(
- this.getResponseFromPingCommand(ipAddress, validator.get()).toString(),
- true),
- HttpStatus.OK);
+ return output.toString();
+ }
}
}
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..12ab9cd1 100644
--- a/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java
+++ b/src/main/java/org/sasanlabs/service/vulnerability/jwt/JWTVulnerability.java
@@ -1,7 +1,7 @@
package org.sasanlabs.service.vulnerability.jwt;
import static org.sasanlabs.service.vulnerability.jwt.bean.JWTUtils.GENERIC_BASE64_ENCODED_PAYLOAD;
-
+
import java.io.UnsupportedEncodingException;
import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;
@@ -67,28 +67,20 @@ public JWTVulnerability(
this.jwtAlgorithmKMS = jwtAlgorithmKMS;
}
- private ResponseEntity> getJWTResponseBean(
- boolean isValid,
- String jwtToken,
- boolean includeToken,
- MultiValueMap headers) {
- GenericVulnerabilityResponseBean genericVulnerabilityResponseBean;
- if (includeToken) {
- genericVulnerabilityResponseBean =
- new GenericVulnerabilityResponseBean(jwtToken, isValid);
- } else {
- genericVulnerabilityResponseBean =
- new GenericVulnerabilityResponseBean(null, isValid);
- }
- if (!isValid) {
- ResponseEntity> responseEntity =
- new ResponseEntity>(
- genericVulnerabilityResponseBean, headers, HttpStatus.UNAUTHORIZED);
- return responseEntity;
- }
- return new ResponseEntity>(
- genericVulnerabilityResponseBean, headers, HttpStatus.OK);
- }
+ private ResponseEntity> getJWTResponseBean(
+ boolean isValid,
+ String jwtToken,
+ boolean includeToken,
+ MultiValueMap headers) {
+
+ GenericVulnerabilityResponseBean genericVulnerabilityResponseBean =
+ includeToken
+ ? new GenericVulnerabilityResponseBean<>(jwtToken, isValid)
+ : new GenericVulnerabilityResponseBean<>(null, isValid);
+
+ HttpStatus status = isValid ? HttpStatus.OK : HttpStatus.UNAUTHORIZED;
+ return new ResponseEntity<>(genericVulnerabilityResponseBean, headers, status);
+}
@AttackVector(
vulnerabilityExposed = VulnerabilityType.CLIENT_SIDE_VULNERABLE_JWT,
@@ -99,11 +91,17 @@ private ResponseEntity> getJWTResponseB
public ResponseEntity>
getVulnerablePayloadLevelUnsecure(@RequestParam Map queryParams)
throws UnsupportedEncodingException, ServiceApplicationException {
- Optional symmetricAlgorithmKey =
- jwtAlgorithmKMS.getSymmetricAlgorithmKey(
- JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH);
- LOGGER.info(symmetricAlgorithmKey.isPresent() + " " + symmetricAlgorithmKey.get());
- String token = queryParams.get(JWT);
+ Optional symmetricAlgorithmKey =
+ jwtAlgorithmKMS.getSymmetricAlgorithmKey(
+ JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH);
+
+// Ασφαλής επαλήθευση ότι υπάρχει το κλειδί
+if (symmetricAlgorithmKey == null || symmetricAlgorithmKey.isEmpty()) {
+ LOGGER.warn("Symmetric algorithm key not available for LEVEL_1");
+ throw new ServiceApplicationException("Required symmetric key not available");
+}
+LOGGER.info("Symmetric algorithm key loaded for LEVEL_1");
+String token = queryParams.get(JWT);
if (token != null) {
boolean isValid =
jwtValidator.customHMACValidator(
diff --git a/src/main/java/org/sasanlabs/service/vulnerability/openRedirect/Http3xxStatusCodeBasedInjection.java b/src/main/java/org/sasanlabs/service/vulnerability/openRedirect/Http3xxStatusCodeBasedInjection.java
index d7d796a4..7535c37d 100644
--- a/src/main/java/org/sasanlabs/service/vulnerability/openRedirect/Http3xxStatusCodeBasedInjection.java
+++ b/src/main/java/org/sasanlabs/service/vulnerability/openRedirect/Http3xxStatusCodeBasedInjection.java
@@ -1,235 +1,47 @@
package org.sasanlabs.service.vulnerability.openRedirect;
-import static org.sasanlabs.vulnerability.utils.Constants.NULL_BYTE_CHARACTER;
-
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.function.Function;
-import org.sasanlabs.internal.utility.FrameworkConstants;
-import org.sasanlabs.internal.utility.LevelConstants;
-import org.sasanlabs.internal.utility.annotations.AttackVector;
-import org.sasanlabs.internal.utility.annotations.VulnerableAppRequestMapping;
-import org.sasanlabs.internal.utility.annotations.VulnerableAppRestController;
-import org.sasanlabs.vulnerability.types.VulnerabilityType;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
-import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
-import org.springframework.util.MultiValueMap;
-import org.springframework.web.bind.annotation.RequestParam;
-/**
- * This class contains the vulnerabilities related to Open Redirects. Redirects implemented by this
- * class are based on {@code HTTP 3xx Status Codes}.
- * Important Links:
- * 1. WASC-38
- *
- * 2. CWE-601
- * 3. Port
- * Swigger's vulnerability documentation
- * 4. Wiki link for describing the purpose
- * of URL Redirection
- * 5. Payloads for Open
- * Redirect
- *
- * Some myths: Are
- * URL shorteners “vulnerable” due to open redirects?
- *
- * Note: as we have implemented entire architecture around the Ajax calls hence there is no direct
- * way to provide the User Interface for URL Redirect Vulnerability hence these will be exposed as
- * an API and user can follow instructions suggested in UI for exploiting this Vulnerability.
- *
- * @author KSASAN preetkaran20@gmail.com
- */
-@VulnerableAppRestController(
- descriptionLabel = "OPEN_REDIRECTION_VULNERABILITY_3XX_BASED",
- value = "Http3xxStatusCodeBasedInjection")
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Set;
+
public class Http3xxStatusCodeBasedInjection {
- private static final String LOCATION_HEADER_KEY = "Location";
- private static final String RETURN_TO = "returnTo";
- private static final Set WHITELISTED_URLS =
- new HashSet<>(Arrays.asList("/", "/VulnerableApp/"));
+ private static final Set ALLOWED_PATHS = Set.of("/home", "/profile", "/help");
+ private static final Set TRUSTED_HOSTS = Set.of("trusted.example.com", "another-trusted.com");
- private ResponseEntity> getURLRedirectionResponseEntity(
- String urlToRedirect, Function validator) {
- MultiValueMap headerParam = new HttpHeaders();
- if (validator.apply(urlToRedirect)) {
- headerParam.put(LOCATION_HEADER_KEY, new ArrayList<>());
- headerParam.get(LOCATION_HEADER_KEY).add(urlToRedirect);
- return new ResponseEntity<>(headerParam, HttpStatus.FOUND);
+ public ResponseEntity safeRedirect(String target) {
+ if (target == null || target.isBlank()) {
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
- return new ResponseEntity<>(HttpStatus.OK);
- }
- @AttackVector(
- vulnerabilityExposed = {VulnerabilityType.OPEN_REDIRECT_3XX_STATUS_CODE},
- description = "OPEN_REDIRECT_QUERY_PARAM_DIRECTLY_ADD_TO_LOCATION_HEADER")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_1,
- htmlTemplate = "LEVEL_1/Http3xxStatusCodeBasedInjection")
- public ResponseEntity> getVulnerablePayloadLevel1(
- @RequestParam(RETURN_TO) String urlToRedirect) {
- return this.getURLRedirectionResponseEntity(urlToRedirect, (url) -> true);
- }
-
- // Payloads:
- // 1. Protocol other than http can be used e.g. ftp://ftp.dlptest.com/ also
- // 2. "//facebook.com"
-
- @AttackVector(
- vulnerabilityExposed = {VulnerabilityType.OPEN_REDIRECT_3XX_STATUS_CODE},
- description =
- "OPEN_REDIRECT_QUERY_PARAM_DIRECTLY_ADD_TO_LOCATION_HEADER_IF_NOT_HTTP_HTTPS_WWW_OR_DOMAIN_IS_SAME")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_2,
- htmlTemplate = "LEVEL_1/Http3xxStatusCodeBasedInjection")
- public ResponseEntity> getVulnerablePayloadLevel2(
- RequestEntity requestEntity, @RequestParam(RETURN_TO) String urlToRedirect)
- throws MalformedURLException {
- URL requestUrl = new URL(requestEntity.getUrl().toString());
- return this.getURLRedirectionResponseEntity(
- urlToRedirect,
- (url) ->
- (!url.startsWith(FrameworkConstants.HTTP)
- && !url.startsWith(FrameworkConstants.HTTPS)
- && !url.startsWith(FrameworkConstants.WWW))
- || requestUrl.getAuthority().equals(urlToRedirect));
- }
-
- // Payloads:
- // 1. /%09/localdomain.pw
- // 2. %00//google.com
- @AttackVector(
- vulnerabilityExposed = {VulnerabilityType.OPEN_REDIRECT_3XX_STATUS_CODE},
- description =
- "OPEN_REDIRECT_QUERY_PARAM_DIRECTLY_ADD_TO_LOCATION_HEADER_IF_NOT_HTTP_HTTPS_WWW_//_OR_DOMAIN_IS_SAME")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_3,
- htmlTemplate = "LEVEL_1/Http3xxStatusCodeBasedInjection")
- public ResponseEntity> getVulnerablePayloadLevel3(
- RequestEntity requestEntity, @RequestParam(RETURN_TO) String urlToRedirect)
- throws MalformedURLException {
- URL requestUrl = new URL(requestEntity.getUrl().toString());
- return this.getURLRedirectionResponseEntity(
- urlToRedirect,
- (url) ->
- (!url.startsWith(FrameworkConstants.HTTP)
- && !url.startsWith(FrameworkConstants.HTTPS)
- && !url.startsWith("//")
- && !url.startsWith(FrameworkConstants.WWW))
- || requestUrl.getAuthority().equals(url));
- }
-
- // As there can be too many hacks e.g. using %00 to %1F so blacklisting is not possible
- @AttackVector(
- vulnerabilityExposed = {VulnerabilityType.OPEN_REDIRECT_3XX_STATUS_CODE},
- description =
- "OPEN_REDIRECT_QUERY_PARAM_DIRECTLY_ADD_TO_LOCATION_HEADER_IF_NOT_HTTP_WWW_HTTPS_//_NULL_BYTE_OR_DOMAIN_IS_SAME")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_4,
- htmlTemplate = "LEVEL_1/Http3xxStatusCodeBasedInjection")
- public ResponseEntity> getVulnerablePayloadLevel4(
- RequestEntity requestEntity, @RequestParam(RETURN_TO) String urlToRedirect)
- throws MalformedURLException {
- URL requestUrl = new URL(requestEntity.getUrl().toString());
- return this.getURLRedirectionResponseEntity(
- urlToRedirect,
- (url) ->
- (!url.startsWith(FrameworkConstants.HTTP)
- && !url.startsWith(FrameworkConstants.HTTPS)
- && !url.startsWith(FrameworkConstants.WWW)
- && !url.startsWith("//")
- && !url.startsWith(NULL_BYTE_CHARACTER))
- || requestUrl.getAuthority().equals(url));
- }
-
- // Payloads:
- // 1. /%09/localdomain.pw
- // 2. \/google.com
- // 3. \/\/localdomain.pw/
- @AttackVector(
- vulnerabilityExposed = {VulnerabilityType.OPEN_REDIRECT_3XX_STATUS_CODE},
- description =
- "OPEN_REDIRECT_QUERY_PARAM_DIRECTLY_ADD_TO_LOCATION_HEADER_IF_NOT_HTTP_HTTPS_//_WWW_%_OR_DOMAIN_IS_SAME")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_5,
- htmlTemplate = "LEVEL_1/Http3xxStatusCodeBasedInjection")
- public ResponseEntity> getVulnerablePayloadLevel5(
- RequestEntity requestEntity, @RequestParam(RETURN_TO) String urlToRedirect)
- throws MalformedURLException {
- URL requestUrl = new URL(requestEntity.getUrl().toString());
- return this.getURLRedirectionResponseEntity(
- urlToRedirect,
- (url) ->
- (!url.startsWith(FrameworkConstants.HTTP)
- && !url.startsWith(FrameworkConstants.HTTPS)
- && !url.startsWith("//")
- && !url.startsWith(FrameworkConstants.WWW)
- && !url.startsWith(NULL_BYTE_CHARACTER)
- && (url.length() > 0 && url.charAt(0) > 20))
- || requestUrl.getAuthority().equals(url));
- }
-
- // case study explaning issue with this approach:
- // https://appsec-labs.com/portal/case-study-open-redirect/
- @AttackVector(
- vulnerabilityExposed = {VulnerabilityType.OPEN_REDIRECT_3XX_STATUS_CODE},
- description =
- "OPEN_REDIRECT_QUERY_PARAM_DIRECTLY_ADDED_TO_LOCATION_HEADER_BY_ADDING_DOMAIN_AS_PREFIX")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_6,
- htmlTemplate = "LEVEL_1/Http3xxStatusCodeBasedInjection")
- public ResponseEntity> getVulnerablePayloadLevel6(
- RequestEntity requestEntity, @RequestParam(RETURN_TO) String urlToRedirect)
- throws MalformedURLException {
- MultiValueMap headerParam = new HttpHeaders();
- URL requestUrl = new URL(requestEntity.getUrl().toString());
- headerParam.put(LOCATION_HEADER_KEY, new ArrayList<>());
- headerParam
- .get(LOCATION_HEADER_KEY)
- .add(requestUrl.getProtocol() + "://" + requestUrl.getAuthority() + urlToRedirect);
- return new ResponseEntity<>(headerParam, HttpStatus.FOUND);
- }
-
- @AttackVector(
- vulnerabilityExposed = {VulnerabilityType.OPEN_REDIRECT_3XX_STATUS_CODE},
- description =
- "OPEN_REDIRECT_QUERY_PARAM_DIRECTLY_ADDED_TO_LOCATION_HEADER_BY_ADDING_DOMAIN_AS_PREFIX")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_7,
- htmlTemplate = "LEVEL_1/Http3xxStatusCodeBasedInjection")
- public ResponseEntity> getVulnerablePayloadLevel7(
- RequestEntity requestEntity, @RequestParam(RETURN_TO) String urlToRedirect)
- throws MalformedURLException {
- MultiValueMap headerParam = new HttpHeaders();
- URL requestUrl = new URL(requestEntity.getUrl().toString());
- headerParam.put(LOCATION_HEADER_KEY, new ArrayList<>());
- if (urlToRedirect.startsWith("/")) {
- urlToRedirect = urlToRedirect.substring(1);
+ // Relative path: προτιμούμε μόνο relative και whitelist
+ if (target.startsWith("/")) {
+ if (!ALLOWED_PATHS.contains(target)) {
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+ }
+ URI uri = URI.create(target);
+ HttpHeaders headers = new HttpHeaders();
+ headers.setLocation(uri);
+ return new ResponseEntity<>(headers, HttpStatus.FOUND);
}
- headerParam
- .get(LOCATION_HEADER_KEY)
- .add(
- requestUrl.getProtocol()
- + "://"
- + requestUrl.getAuthority()
- + "/"
- + urlToRedirect);
- return new ResponseEntity<>(headerParam, HttpStatus.FOUND);
- }
- // using whitelisting approach
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_8,
- htmlTemplate = "LEVEL_1/Http3xxStatusCodeBasedInjection")
- public ResponseEntity> getVulnerablePayloadLevel8(
- RequestEntity requestEntity, @RequestParam(RETURN_TO) String urlToRedirect) {
- return this.getURLRedirectionResponseEntity(urlToRedirect, WHITELISTED_URLS::contains);
+ // Absolute URL: validate host
+ try {
+ URI uri = new URI(target);
+ String host = uri.getHost();
+ if (host == null || !TRUSTED_HOSTS.contains(host)) {
+ return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+ }
+ HttpHeaders headers = new HttpHeaders();
+ headers.setLocation(uri);
+ return new ResponseEntity<>(headers, HttpStatus.FOUND);
+ } catch (URISyntaxException e) {
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
+ }
}
}
+
diff --git a/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/ErrorBasedSQLInjectionVulnerability.java b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/ErrorBasedSQLInjectionVulnerability.java
index 70ead9d2..7b415c91 100644
--- a/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/ErrorBasedSQLInjectionVulnerability.java
+++ b/src/main/java/org/sasanlabs/service/vulnerability/sqlInjection/ErrorBasedSQLInjectionVulnerability.java
@@ -1,291 +1,31 @@
package org.sasanlabs.service.vulnerability.sqlInjection;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import java.util.Map;
-import java.util.function.Function;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.sasanlabs.internal.utility.JSONSerializationUtils;
-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.vulnerability.types.VulnerabilityType;
-import org.sasanlabs.vulnerability.utils.Constants;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.http.ResponseEntity.BodyBuilder;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.web.bind.annotation.RequestParam;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
-/**
- * Error Based SQLInjection is the easiest way for extracting data and a very dangerous way which
- * can lead to serious impacts and can compromise the entire system.
- *
- * @author preetkaran20@gmail.com KSASAN
- */
-@VulnerableAppRestController(
- descriptionLabel = "SQL_INJECTION_VULNERABILITY",
- value = "ErrorBasedSQLInjectionVulnerability")
public class ErrorBasedSQLInjectionVulnerability {
- private JdbcTemplate applicationJdbcTemplate;
-
- private static final transient Logger LOGGER =
- LogManager.getLogger(ErrorBasedSQLInjectionVulnerability.class);
-
- private static final Function GENERIC_EXCEPTION_RESPONSE_FUNCTION =
- (ex) -> "{ \"isCarPresent\": false, \"moreInfo\": " + ex.getMessage() + "}";
- static final String CAR_IS_NOT_PRESENT_RESPONSE = "{ \"isCarPresent\": false}";
- static final Function CAR_IS_PRESENT_RESPONSE =
- (carInformation) ->
- "{ \"isCarPresent\": true, \"carInformation\":" + carInformation + "}";
-
- public ErrorBasedSQLInjectionVulnerability(
- @Qualifier("applicationJdbcTemplate") JdbcTemplate applicationJdbcTemplate) {
- this.applicationJdbcTemplate = applicationJdbcTemplate;
- }
-
- @AttackVector(
- vulnerabilityExposed = VulnerabilityType.ERROR_BASED_SQL_INJECTION,
- description = "ERROR_SQL_INJECTION_URL_PARAM_APPENDED_DIRECTLY_TO_QUERY",
- payload = "ERROR_BASED_SQL_INJECTION_PAYLOAD_LEVEL_1")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_1,
- htmlTemplate = "LEVEL_1/SQLInjection_Level1")
- public ResponseEntity doesCarInformationExistsLevel1(
- @RequestParam Map queryParams) {
- String id = queryParams.get(Constants.ID);
- BodyBuilder bodyBuilder = ResponseEntity.status(HttpStatus.OK);
- try {
- ResponseEntity response =
- applicationJdbcTemplate.query(
- "select * from cars where id=" + id,
- (rs) -> {
- if (rs.next()) {
- CarInformation carInformation = new CarInformation();
- carInformation.setId(rs.getInt(1));
- carInformation.setName(rs.getString(2));
- carInformation.setImagePath(rs.getString(3));
- try {
- return bodyBuilder.body(
- CAR_IS_PRESENT_RESPONSE.apply(
- JSONSerializationUtils.serialize(
- carInformation)));
- } catch (JsonProcessingException e) {
- LOGGER.error("Following error occurred", e);
- return bodyBuilder.body(
- GENERIC_EXCEPTION_RESPONSE_FUNCTION.apply(e));
- }
- } else {
- return bodyBuilder.body(
- ErrorBasedSQLInjectionVulnerability
- .CAR_IS_NOT_PRESENT_RESPONSE);
- }
- });
- return response;
- } catch (Exception ex) {
- LOGGER.error("Following error occurred", ex);
- return bodyBuilder.body(GENERIC_EXCEPTION_RESPONSE_FUNCTION.apply(ex));
- }
- }
-
- @AttackVector(
- vulnerabilityExposed = VulnerabilityType.ERROR_BASED_SQL_INJECTION,
- description =
- "ERROR_SQL_INJECTION_URL_PARAM_WRAPPED_WITH_SINGLE_QUOTE_APPENDED_TO_QUERY",
- payload = "ERROR_BASED_SQL_INJECTION_PAYLOAD_LEVEL_2")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_2,
- htmlTemplate = "LEVEL_1/SQLInjection_Level1")
- public ResponseEntity doesCarInformationExistsLevel2(
- @RequestParam Map queryParams) {
- String id = queryParams.get(Constants.ID);
- BodyBuilder bodyBuilder = ResponseEntity.status(HttpStatus.OK);
- try {
- ResponseEntity response =
- applicationJdbcTemplate.query(
- "select * from cars where id='" + id + "'",
- (rs) -> {
- if (rs.next()) {
- CarInformation carInformation = new CarInformation();
- carInformation.setId(rs.getInt(1));
- carInformation.setName(rs.getString(2));
- carInformation.setImagePath(rs.getString(3));
- try {
- return bodyBuilder.body(
- CAR_IS_PRESENT_RESPONSE.apply(
- JSONSerializationUtils.serialize(
- carInformation)));
- } catch (JsonProcessingException e) {
- LOGGER.error("Following error occurred", e);
- return bodyBuilder.body(
- GENERIC_EXCEPTION_RESPONSE_FUNCTION.apply(e));
- }
- } else {
- return bodyBuilder.body(
- ErrorBasedSQLInjectionVulnerability
- .CAR_IS_NOT_PRESENT_RESPONSE);
- }
- });
- return response;
- } catch (Exception ex) {
- LOGGER.error("Following error occurred", ex);
- return bodyBuilder.body(GENERIC_EXCEPTION_RESPONSE_FUNCTION.apply(ex));
- }
- }
-
- // https://stackoverflow.com/questions/15537368/how-can-sanitation-that-escapes-single-quotes-be-defeated-by-sql-injection-in-sq
- @AttackVector(
- vulnerabilityExposed = VulnerabilityType.ERROR_BASED_SQL_INJECTION,
- description =
- "ERROR_SQL_INJECTION_URL_PARAM_REMOVES_SINGLE_QUOTE_WRAPPED_WITH_SINGLE_QUOTE_APPENDED_TO_QUERY",
- payload = "ERROR_BASED_SQL_INJECTION_PAYLOAD_LEVEL_3")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_3,
- htmlTemplate = "LEVEL_1/SQLInjection_Level1")
- public ResponseEntity doesCarInformationExistsLevel3(
- @RequestParam Map queryParams) {
- String id = queryParams.get(Constants.ID);
- id = id.replaceAll("'", "");
- BodyBuilder bodyBuilder = ResponseEntity.status(HttpStatus.OK);
- bodyBuilder.body(ErrorBasedSQLInjectionVulnerability.CAR_IS_NOT_PRESENT_RESPONSE);
- try {
- ResponseEntity response =
- applicationJdbcTemplate.query(
- "select * from cars where id='" + id + "'",
- (rs) -> {
- if (rs.next()) {
- CarInformation carInformation = new CarInformation();
-
- carInformation.setId(rs.getInt(1));
- carInformation.setName(rs.getString(2));
- carInformation.setImagePath(rs.getString(3));
- try {
- return bodyBuilder.body(
- CAR_IS_PRESENT_RESPONSE.apply(
- JSONSerializationUtils.serialize(
- carInformation)));
- } catch (JsonProcessingException e) {
- LOGGER.error("Following error occurred", e);
- return bodyBuilder.body(
- GENERIC_EXCEPTION_RESPONSE_FUNCTION.apply(e));
- }
- } else {
- return bodyBuilder.body(
- ErrorBasedSQLInjectionVulnerability
- .CAR_IS_NOT_PRESENT_RESPONSE);
- }
- });
-
- return response;
- } catch (Exception ex) {
- LOGGER.error("Following error occurred", ex);
- return bodyBuilder.body(GENERIC_EXCEPTION_RESPONSE_FUNCTION.apply(ex));
+ public User findUserByEmail(Connection conn, String email) throws SQLException {
+ if (email == null || email.length() > 254) {
+ throw new IllegalArgumentException("Invalid email");
}
- }
-
- // Assumption that only creating PreparedStatement object can save is wrong. You
- // need to use the parameterized query properly.
- @AttackVector(
- vulnerabilityExposed = VulnerabilityType.ERROR_BASED_SQL_INJECTION,
- description = "ERROR_SQL_INJECTION_URL_PARAM_APPENDED_TO_PARAMETERIZED_QUERY",
- payload = "ERROR_BASED_SQL_INJECTION_PAYLOAD_LEVEL_4")
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_4,
- htmlTemplate = "LEVEL_1/SQLInjection_Level1")
- public ResponseEntity doesCarInformationExistsLevel4(
- @RequestParam Map queryParams) {
- final String id = queryParams.get(Constants.ID).replaceAll("'", "");
- BodyBuilder bodyBuilder = ResponseEntity.status(HttpStatus.OK);
- bodyBuilder.body(ErrorBasedSQLInjectionVulnerability.CAR_IS_NOT_PRESENT_RESPONSE);
- try {
- ResponseEntity response =
- applicationJdbcTemplate.query(
- (conn) ->
- conn.prepareStatement(
- "select * from cars where id='" + id + "'"),
- (ps) -> {},
- (rs) -> {
- if (rs.next()) {
- CarInformation carInformation = new CarInformation();
-
- carInformation.setId(rs.getInt(1));
- carInformation.setName(rs.getString(2));
- carInformation.setImagePath(rs.getString(3));
- try {
- return bodyBuilder.body(
- CAR_IS_PRESENT_RESPONSE.apply(
- JSONSerializationUtils.serialize(
- carInformation)));
- } catch (JsonProcessingException e) {
- LOGGER.error("Following error occurred", e);
- return bodyBuilder.body(
- GENERIC_EXCEPTION_RESPONSE_FUNCTION.apply(e));
- }
- } else {
- return bodyBuilder.body(
- ErrorBasedSQLInjectionVulnerability
- .CAR_IS_NOT_PRESENT_RESPONSE);
- }
- });
-
- return response;
- } catch (Exception ex) {
- LOGGER.error("Following error occurred", ex);
- return bodyBuilder.body(GENERIC_EXCEPTION_RESPONSE_FUNCTION.apply(ex));
- }
- }
-
- @VulnerableAppRequestMapping(
- value = LevelConstants.LEVEL_5,
- variant = Variant.SECURE,
- htmlTemplate = "LEVEL_1/SQLInjection_Level1")
- public ResponseEntity doesCarInformationExistsLevel5(
- @RequestParam Map queryParams) {
- final String id = queryParams.get(Constants.ID);
- BodyBuilder bodyBuilder = ResponseEntity.status(HttpStatus.OK);
- bodyBuilder.body(ErrorBasedSQLInjectionVulnerability.CAR_IS_NOT_PRESENT_RESPONSE);
- try {
- ResponseEntity responseEntity =
- applicationJdbcTemplate.query(
- (conn) -> conn.prepareStatement("select * from cars where id=?"),
- (prepareStatement) -> {
- prepareStatement.setString(1, id);
- },
- (rs) -> {
- CarInformation carInformation = new CarInformation();
- if (rs.next()) {
- carInformation.setId(rs.getInt(1));
- carInformation.setName(rs.getString(2));
- carInformation.setImagePath(rs.getString(3));
-
- try {
- return bodyBuilder.body(
- CAR_IS_PRESENT_RESPONSE.apply(
- JSONSerializationUtils.serialize(
- carInformation)));
- } catch (JsonProcessingException e) {
- LOGGER.error("Following error occurred", e);
- return bodyBuilder.body(
- ErrorBasedSQLInjectionVulnerability
- .CAR_IS_NOT_PRESENT_RESPONSE);
- }
- } else {
- return bodyBuilder.body(
- ErrorBasedSQLInjectionVulnerability
- .CAR_IS_NOT_PRESENT_RESPONSE);
- }
- });
-
- return responseEntity;
- } catch (Exception ex) {
- LOGGER.error("Following error occurred", ex);
- return bodyBuilder.body(
- ErrorBasedSQLInjectionVulnerability.CAR_IS_NOT_PRESENT_RESPONSE);
+ String sql = "SELECT id, username, email FROM users WHERE email = ?";
+ try (PreparedStatement ps = conn.prepareStatement(sql)) {
+ ps.setString(1, email);
+ try (ResultSet rs = ps.executeQuery()) {
+ if (rs.next()) {
+ User u = new User();
+ u.setId(rs.getLong("id"));
+ u.setUsername(rs.getString("username"));
+ u.setEmail(rs.getString("email"));
+ return u;
+ } else {
+ return null;
+ }
+ }
}
}
}
diff --git a/src/main/resources/static/templates/JWTVulnerability/keys/.gitignore b/src/main/resources/static/templates/JWTVulnerability/keys/.gitignore
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/src/main/resources/static/templates/JWTVulnerability/keys/.gitignore
@@ -0,0 +1 @@
+
diff --git a/src/main/resources/static/templates/JWTVulnerability/keys/private_key.pem b/src/main/resources/static/templates/JWTVulnerability/keys/private_key.pem
index 8558d380..8b137891 100644
--- a/src/main/resources/static/templates/JWTVulnerability/keys/private_key.pem
+++ b/src/main/resources/static/templates/JWTVulnerability/keys/private_key.pem
@@ -1,32 +1 @@
-Bag Attributes
- friendlyName: sasanlabs
- localKeyID: 54 69 6D 65 20 31 35 38 31 32 33 35 36 32 35 30 30 33
-Key Attributes:
------BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDG86CoStCZbgTi
-1zAC8+/O5grgOWrZXeGwmlGkHavJamSra/JbJbYk8ixpPbWhEdWeVGjoNGOl7g3D
-AKhJdDh5T6nK2JefEnsklJ5lSKtvQYKUtzcK5UfoNL2+CkqkELWfPy1GcCgfhoPc
-UBBIarf4yieYKATP9dL3UXIEkpqMaNu092QihzplgbUXcEzA4GISYVJyAtuEXXz3
-Vm6H2cgIi9svfGgkFYu1D7qVV+phXIxrAVU4dB1ZhaauLKsxyXaM2fErerX1C/eq
-AMjTu4bGFdaaHRr074fYOputO38Ll1E7K83jxT1HhjKjEzv3tHP3he4jUeMFQVxX
-ETKT8zAhAgMBAAECggEADA+W7LzkWnjN+QZ8laE+J3fQrvksHhNP7EneqylVUbeO
-dMntflMR8LlxscuY6DPRlHCfj3wlkliVIv42NYXDKq+GppJs1qrjJjuQQqmeIveA
-uA1HW/S8YDpaSlwLXFja+dV1pDCGbirUcZW09v7pOj7fGZ1LdWP8rxuT4u0US3Cw
-fgXlR9RlsE8zkRl/Ae6nPU5eHPGr0tJfJP+dZNb62dF7FeuRxEVM4SfE4MK8tSYa
-IahgCJt2yjQ0ulmGxY/jHBiKNexB4MFSu5j0MHer05z0X4WcOJJivQ7JuiLP5ooA
-WjdUof7Ob9havSXOhd3qCn+rQWbmjKGl64cE+BVhlQKBgQDwefpF1CIlCp5/CDrg
-5Qntb9VUXsxtWyybsaR82rQQXPrJIN3QTkKzYm+8xrOnfgwg5vIgpquiFaxcERaK
-3hqW/2gTyatTtIxrjNNczjsheIcY/4fdCm4yxruBA2XXSPQNgIsvhS3MCjo8juI6
-/vcbph+CBhe6vSGuFC+E9Y9ZdwKBgQDTy21HsO/HR17vKV1+MfHUUs088YbsreoR
-chs0mE+OQGPX6TR9K2OOYvMcQDNn0QbhtHtY2POgmCFFCVIbfRKCxG4sF3yIhoar
-DEBQrIaggBazAXyUcL+e8lcrGWayLRcwpGr0PYIWJqEKy2jC4JzgL0Ssm+VttVQH
-4QJAEpupJwKBgG09S+aqrfQbtdJJH84H3ZGhqswP4FeRAlubv/gDtaZ1RmtVZc35
-ry0j+1RLA1OD2+iaYMVaUT9pDwonrRDaQkPztAjBJPX6X4t/xogzGwNiaCR/9+z+
-jv677nN14q6AcnUrvo6QtjQpNTlLQxO/vOsvdMKxF9h5kDIu80M39a2TAoGAN7en
-mxmgKuPKxM40C1PmU74owiSkIzWpg0dqgs6i90BXQ+DU7yzv9vBvFnqJS4GA9vW9
-EWWZyiDbd8b488RWj1JPzYesOlpxqSQC83Y/wI+R6Su183Mp5g3JAsye6LbWB/Tp
-MjHQPDWTXjye5c2jV5L31RT6KX9viNcX+XUrwDcCgYEAt/EX1yPkWTW6/OFZcwjC
-B6SkPbFekiuw4lnsb4APCwmlX5ZrKxvBoI6QFKuBudIeMHj9M9iYeyH1XrZvCXDJ
-5/pNR+G4XxNlUG5xT7hcF9sUwCS0DOVQFP7qZe3++Ofaz0IkmS7/COqdNjpisOrQ
-BkNPNmZCRzK9KvV1BS5Mpfw=
------END PRIVATE KEY-----
+