From 2087ff0c1bae3b43f687851c6d9048e0da64849f Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Tue, 5 Mar 2024 13:00:47 +0530 Subject: [PATCH 01/11] added support for generating uuid --- .../execution/VariableResolver.java | 29 ++++++++++++++++++- .../java/com/akto/testing/ApiExecutor.java | 6 ++-- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/apps/testing/src/main/java/com/akto/test_editor/execution/VariableResolver.java b/apps/testing/src/main/java/com/akto/test_editor/execution/VariableResolver.java index 9234cdc895..dc528f2c84 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/execution/VariableResolver.java +++ b/apps/testing/src/main/java/com/akto/test_editor/execution/VariableResolver.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -104,6 +105,26 @@ public static List resolveExpression(Map varMap, Object } + private static boolean isSpecialInstruction(String key){ + switch (key) { + case "${random_uuid}": + return true; + + default: + return false; + } + } + + private static String getSpecialInstructionValue(String key){ + switch (key) { + case "uuid": + return UUID.randomUUID().toString(); + + default: + return null; + } + } + public static List resolveExpression(Map varMap, String expression) { Pattern pattern = Pattern.compile("\\$\\{[^}]*\\}"); @@ -119,8 +140,14 @@ public static List resolveExpression(Map varMap, String String param = expressionList.get(index).toString(); try { String match = matcher.group(0); + boolean isSpecialInstruction = isSpecialInstruction(match); match = match.substring(2, match.length()); match = match.substring(0, match.length() - 1); + + if(isSpecialInstruction && !varMap.containsKey(match)){ + varMap.put(match, getSpecialInstructionValue("uuid")); + } + Object val = getValue(varMap, match); if (val == null) { continue; @@ -805,4 +832,4 @@ public static Object multiply(String operand1, String operand2) { // } -} +} \ No newline at end of file diff --git a/libs/utils/src/main/java/com/akto/testing/ApiExecutor.java b/libs/utils/src/main/java/com/akto/testing/ApiExecutor.java index 4b0c703fe7..6d78fa2c7b 100644 --- a/libs/utils/src/main/java/com/akto/testing/ApiExecutor.java +++ b/libs/utils/src/main/java/com/akto/testing/ApiExecutor.java @@ -54,9 +54,9 @@ private static OriginalHttpResponse common(Request request, boolean followRedire HTTPClientHandler.instance.getNewDebugClient(isSaasDeployment, followRedirects, testLogs) : HTTPClientHandler.instance.getHTTPClient(followRedirects); - if (!skipSSRFCheck && !HostDNSLookup.isRequestValid(request.url().host())) { - throw new IllegalArgumentException("SSRF attack attempt"); - } + // if (!skipSSRFCheck && !HostDNSLookup.isRequestValid(request.url().host())) { + // throw new IllegalArgumentException("SSRF attack attempt"); + // } Call call = client.newCall(request); Response response = null; From f23b1bf4f16f9e5395704f74e8ab0a1d990e8892 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Tue, 5 Mar 2024 18:41:30 +0530 Subject: [PATCH 02/11] Modified code for ssrf template --- .../akto/test_editor/execution/Executor.java | 82 ++++++++++++++----- .../test_editor/execution/Operations.java | 29 +++++++ .../execution/VariableResolver.java | 25 ------ .../akto/dao/test_editor/TestEditorEnums.java | 3 +- .../main/java/com/akto/util/Constants.java | 1 + 5 files changed, 94 insertions(+), 46 deletions(-) diff --git a/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java b/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java index fc18afa3a0..23b8bc6ddd 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java +++ b/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java @@ -41,6 +41,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.UUID; import org.json.JSONObject; @@ -432,9 +433,65 @@ private ExecutorSingleOperationResp modifyAuthTokenInRawApi(TestRoles testRole, return null; } + private static Map getToken() { + Map resultMap = new HashMap<>(); + int accountId = Context.accountId.get(); + Organization organization = OrganizationsDao.instance.findOne( + Filters.in(Organization.ACCOUNTS, accountId) + ); + if (organization == null) { + + } + + Tokens tokens; + Bson filters = Filters.and( + Filters.eq(Tokens.ORG_ID, organization.getId()), + Filters.eq(Tokens.ACCOUNT_ID, accountId) + ); + String errMessage = ""; + tokens = TokensDao.instance.findOne(filters); + if (tokens == null) { + errMessage = "error extracting ${akto_header}, token is missing"; + } + if (tokens.isOldToken()) { + errMessage = "error extracting ${akto_header}, token is old"; + } + if(errMessage.length() > 0){ + resultMap.put("error", errMessage); + }else{ + resultMap.put("token", tokens.getToken()); + } + return resultMap; + } + + private static String extractValue(String keyValue, String key) { + String result = ""; + if (keyValue.contains(key)) { + result = keyValue.split(key)[1].split("[,}]")[0]; + result = result.replaceAll("\\}$", ""); + result = result.trim(); + } + return result; + } public ExecutorSingleOperationResp runOperation(String operationType, RawApi rawApi, Object key, Object value, Map varMap, AuthMechanism authMechanism, List customAuthTypes) { switch (operationType.toLowerCase()) { + case "send_ssrf_request": + String keyValue = key.toString().replaceAll("\\$\\{random_uuid\\}", ""); + String url = extractValue(keyValue, "url="); + String redirectUrl = extractValue(keyValue, "redirect_url="); + List uuidList = (List) varMap.getOrDefault("random_uuid", new ArrayList<>()); + String generatedUUID = UUID.randomUUID().toString(); + uuidList.add(generatedUUID); + varMap.put("random_uuid", uuidList); + + Map response = getToken(); + if(response.containsKey("token")){ + String tokenVal = response.get("token"); + return Operations.sendRequestToHostedServer(url + generatedUUID, redirectUrl, tokenVal); + }else{ + return new ExecutorSingleOperationResp(false, response.get("error")); + } case "attach_file": return Operations.addHeader(rawApi, Constants.AKTO_ATTACH_FILE , key.toString()); case "add_body_param": @@ -458,27 +515,12 @@ public ExecutorSingleOperationResp runOperation(String operationType, RawApi raw return Operations.replaceBody(rawApi, newPayload); case "add_header": if (value.equals("${akto_header}")) { - int accountId = Context.accountId.get(); - Organization organization = OrganizationsDao.instance.findOne( - Filters.in(Organization.ACCOUNTS, accountId) - ); - if (organization == null) { - return new ExecutorSingleOperationResp(false, "accountId " + accountId + " isn't associated with any organization"); - } - - Tokens tokens; - Bson filters = Filters.and( - Filters.eq(Tokens.ORG_ID, organization.getId()), - Filters.eq(Tokens.ACCOUNT_ID, accountId) - ); - tokens = TokensDao.instance.findOne(filters); - if (tokens == null) { - return new ExecutorSingleOperationResp(false, "error extracting ${akto_header}, token is missing"); - } - if (tokens.isOldToken()) { - return new ExecutorSingleOperationResp(false, "error extracting ${akto_header}, token is old"); + Map tokenResponse = getToken(); + if(tokenResponse.containsKey("token")){ + value = tokenResponse.get("token"); + }else{ + return new ExecutorSingleOperationResp(false, tokenResponse.get("error")); } - value = tokens.getToken(); } return Operations.addHeader(rawApi, key.toString(), value.toString()); diff --git a/apps/testing/src/main/java/com/akto/test_editor/execution/Operations.java b/apps/testing/src/main/java/com/akto/test_editor/execution/Operations.java index 171fa3c915..802c5b398c 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/execution/Operations.java +++ b/apps/testing/src/main/java/com/akto/test_editor/execution/Operations.java @@ -5,12 +5,17 @@ import java.util.List; import java.util.Map; +import org.yaml.snakeyaml.scanner.Constant; + import com.akto.dto.RawApi; import com.akto.dto.test_editor.ExecutorSingleOperationResp; import com.akto.test_editor.Utils; +import com.akto.util.Constants; import com.akto.util.CookieTransformer; import com.mongodb.BasicDBObject; +import okhttp3.*; + public class Operations { public static ExecutorSingleOperationResp addHeader(RawApi rawApi, String key, String value) { @@ -158,4 +163,28 @@ public static ExecutorSingleOperationResp replaceVarMapValue(Map } + public static ExecutorSingleOperationResp sendRequestToHostedServer(String requestUrl, String redirectUrl, String tokenVal){ + RequestBody emptyBody = RequestBody.create(new byte[]{}, null); + + Request request = new Request.Builder() + .url(requestUrl) + .addHeader("x-akto-redirect-url", redirectUrl) + .addHeader(Constants.AKTO_TOKEN_KEY, tokenVal) + .post(emptyBody) + .build(); + + OkHttpClient client = new OkHttpClient(); + Response okResponse = null; + + try { + okResponse = client.newCall(request).execute(); + if (!okResponse.isSuccessful()) { + return new ExecutorSingleOperationResp(false,"Cannot send request to hosted server"); + } + return new ExecutorSingleOperationResp(true, ""); + }catch (Exception e){ + return new ExecutorSingleOperationResp(false, e.getMessage()); + } + } + } diff --git a/apps/testing/src/main/java/com/akto/test_editor/execution/VariableResolver.java b/apps/testing/src/main/java/com/akto/test_editor/execution/VariableResolver.java index dc528f2c84..eab651f89d 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/execution/VariableResolver.java +++ b/apps/testing/src/main/java/com/akto/test_editor/execution/VariableResolver.java @@ -105,26 +105,6 @@ public static List resolveExpression(Map varMap, Object } - private static boolean isSpecialInstruction(String key){ - switch (key) { - case "${random_uuid}": - return true; - - default: - return false; - } - } - - private static String getSpecialInstructionValue(String key){ - switch (key) { - case "uuid": - return UUID.randomUUID().toString(); - - default: - return null; - } - } - public static List resolveExpression(Map varMap, String expression) { Pattern pattern = Pattern.compile("\\$\\{[^}]*\\}"); @@ -140,14 +120,9 @@ public static List resolveExpression(Map varMap, String String param = expressionList.get(index).toString(); try { String match = matcher.group(0); - boolean isSpecialInstruction = isSpecialInstruction(match); match = match.substring(2, match.length()); match = match.substring(0, match.length() - 1); - if(isSpecialInstruction && !varMap.containsKey(match)){ - varMap.put(match, getSpecialInstructionValue("uuid")); - } - Object val = getValue(varMap, match); if (val == null) { continue; diff --git a/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java b/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java index 098aa53af1..9cbf0323e3 100644 --- a/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java +++ b/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java @@ -204,7 +204,8 @@ public enum TerminalExecutorDataOperands { REPLACE_AUTH_HEADER, REPLACE_BODY, JWT_REPLACE_BODY, - ATTACH_FILE + ATTACH_FILE, + SEND_SSRF_REQUEST, } public enum NonTerminalExecutorDataOperands { diff --git a/libs/dao/src/main/java/com/akto/util/Constants.java b/libs/dao/src/main/java/com/akto/util/Constants.java index ae700ea9ab..2649f8352a 100644 --- a/libs/dao/src/main/java/com/akto/util/Constants.java +++ b/libs/dao/src/main/java/com/akto/util/Constants.java @@ -13,5 +13,6 @@ private Constants() {} public static final String AKTO_IGNORE_FLAG = "x-akto-ignore"; public static final String AKTO_ATTACH_FILE = "x-akto-attach-file"; + public static final String AKTO_TOKEN_KEY = "x-akto-key"; } From 2949f21fc44c66642de9afd2435be137a3b6b3e4 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Tue, 5 Mar 2024 18:42:20 +0530 Subject: [PATCH 03/11] removed comment on check --- libs/utils/src/main/java/com/akto/testing/ApiExecutor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/utils/src/main/java/com/akto/testing/ApiExecutor.java b/libs/utils/src/main/java/com/akto/testing/ApiExecutor.java index 6d78fa2c7b..4b0c703fe7 100644 --- a/libs/utils/src/main/java/com/akto/testing/ApiExecutor.java +++ b/libs/utils/src/main/java/com/akto/testing/ApiExecutor.java @@ -54,9 +54,9 @@ private static OriginalHttpResponse common(Request request, boolean followRedire HTTPClientHandler.instance.getNewDebugClient(isSaasDeployment, followRedirects, testLogs) : HTTPClientHandler.instance.getHTTPClient(followRedirects); - // if (!skipSSRFCheck && !HostDNSLookup.isRequestValid(request.url().host())) { - // throw new IllegalArgumentException("SSRF attack attempt"); - // } + if (!skipSSRFCheck && !HostDNSLookup.isRequestValid(request.url().host())) { + throw new IllegalArgumentException("SSRF attack attempt"); + } Call call = client.newCall(request); Response response = null; From c8ac4f02b9e7831d93dafcd3c313a7f8dffab963 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Wed, 6 Mar 2024 01:17:05 +0530 Subject: [PATCH 04/11] Resolved comments --- .../main/java/com/akto/test_editor/Utils.java | 38 +++++++++++++++++ .../akto/test_editor/execution/Executor.java | 42 +++++++------------ .../test_editor/execution/Operations.java | 27 ------------ 3 files changed, 54 insertions(+), 53 deletions(-) diff --git a/apps/testing/src/main/java/com/akto/test_editor/Utils.java b/apps/testing/src/main/java/com/akto/test_editor/Utils.java index 56b82f9795..9ca73a26e2 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/Utils.java +++ b/apps/testing/src/main/java/com/akto/test_editor/Utils.java @@ -15,7 +15,9 @@ import com.akto.dto.OriginalHttpRequest; import com.akto.dto.RawApi; +import com.akto.dto.test_editor.ExecutorSingleOperationResp; import com.akto.dto.testing.UrlModifierPayload; +import com.akto.util.Constants; import com.akto.util.JSONUtils; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; @@ -27,6 +29,8 @@ import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; +import okhttp3.*; + public class Utils { private static final ObjectMapper mapper = new ObjectMapper(); @@ -617,4 +621,38 @@ public static String convertToHarPayload(String message, int akto_account_id, in return mapper.writeValueAsString(result); } + public static String extractValue(String keyValue, String key) { + String result = ""; + if (keyValue.contains(key)) { + result = keyValue.split(key)[1].split("[,}]")[0]; + result = result.replaceAll("\\}$", ""); + result = result.trim(); + } + return result; + } + + public static ExecutorSingleOperationResp sendRequestToHostedServer(String requestUrl, String redirectUrl, String tokenVal){ + RequestBody emptyBody = RequestBody.create(new byte[]{}, null); + + Request request = new Request.Builder() + .url(requestUrl) + .addHeader("x-akto-redirect-url", redirectUrl) + .addHeader(Constants.AKTO_TOKEN_KEY, tokenVal) + .post(emptyBody) + .build(); + + OkHttpClient client = new OkHttpClient(); + Response okResponse = null; + + try { + okResponse = client.newCall(request).execute(); + if (!okResponse.isSuccessful()) { + return new ExecutorSingleOperationResp(false,"Cannot send request to hosted server"); + } + return new ExecutorSingleOperationResp(true, ""); + }catch (Exception e){ + return new ExecutorSingleOperationResp(false, e.getMessage()); + } + } + } diff --git a/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java b/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java index 23b8bc6ddd..232416c570 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java +++ b/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java @@ -433,8 +433,8 @@ private ExecutorSingleOperationResp modifyAuthTokenInRawApi(TestRoles testRole, return null; } - private static Map getToken() { - Map resultMap = new HashMap<>(); + private static BasicDBObject getBillingTokenForAuth() { + BasicDBObject bDObject; int accountId = Context.accountId.get(); Organization organization = OrganizationsDao.instance.findOne( Filters.in(Organization.ACCOUNTS, accountId) @@ -457,40 +457,30 @@ private static Map getToken() { errMessage = "error extracting ${akto_header}, token is old"; } if(errMessage.length() > 0){ - resultMap.put("error", errMessage); + bDObject = new BasicDBObject("error", errMessage); }else{ - resultMap.put("token", tokens.getToken()); + bDObject = new BasicDBObject("token", tokens.getToken()); } - return resultMap; - } - - private static String extractValue(String keyValue, String key) { - String result = ""; - if (keyValue.contains(key)) { - result = keyValue.split(key)[1].split("[,}]")[0]; - result = result.replaceAll("\\}$", ""); - result = result.trim(); - } - return result; + return bDObject; } public ExecutorSingleOperationResp runOperation(String operationType, RawApi rawApi, Object key, Object value, Map varMap, AuthMechanism authMechanism, List customAuthTypes) { switch (operationType.toLowerCase()) { case "send_ssrf_request": String keyValue = key.toString().replaceAll("\\$\\{random_uuid\\}", ""); - String url = extractValue(keyValue, "url="); - String redirectUrl = extractValue(keyValue, "redirect_url="); + String url = Utils.extractValue(keyValue, "url="); + String redirectUrl = Utils.extractValue(keyValue, "redirect_url="); List uuidList = (List) varMap.getOrDefault("random_uuid", new ArrayList<>()); String generatedUUID = UUID.randomUUID().toString(); uuidList.add(generatedUUID); varMap.put("random_uuid", uuidList); - Map response = getToken(); - if(response.containsKey("token")){ - String tokenVal = response.get("token"); - return Operations.sendRequestToHostedServer(url + generatedUUID, redirectUrl, tokenVal); + BasicDBObject response = getBillingTokenForAuth(); + if(response.getString("token") != null){ + String tokenVal = response.getString("token"); + return Utils.sendRequestToHostedServer(url + generatedUUID, redirectUrl, tokenVal); }else{ - return new ExecutorSingleOperationResp(false, response.get("error")); + return new ExecutorSingleOperationResp(false, response.getString("error")); } case "attach_file": return Operations.addHeader(rawApi, Constants.AKTO_ATTACH_FILE , key.toString()); @@ -515,11 +505,11 @@ public ExecutorSingleOperationResp runOperation(String operationType, RawApi raw return Operations.replaceBody(rawApi, newPayload); case "add_header": if (value.equals("${akto_header}")) { - Map tokenResponse = getToken(); - if(tokenResponse.containsKey("token")){ - value = tokenResponse.get("token"); + BasicDBObject tokenResponse = getBillingTokenForAuth(); + if(tokenResponse.getString("token") != null){ + value = tokenResponse.getString("token"); }else{ - return new ExecutorSingleOperationResp(false, tokenResponse.get("error")); + return new ExecutorSingleOperationResp(false, tokenResponse.getString("error")); } } diff --git a/apps/testing/src/main/java/com/akto/test_editor/execution/Operations.java b/apps/testing/src/main/java/com/akto/test_editor/execution/Operations.java index 802c5b398c..fbe321be74 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/execution/Operations.java +++ b/apps/testing/src/main/java/com/akto/test_editor/execution/Operations.java @@ -10,12 +10,9 @@ import com.akto.dto.RawApi; import com.akto.dto.test_editor.ExecutorSingleOperationResp; import com.akto.test_editor.Utils; -import com.akto.util.Constants; import com.akto.util.CookieTransformer; import com.mongodb.BasicDBObject; -import okhttp3.*; - public class Operations { public static ExecutorSingleOperationResp addHeader(RawApi rawApi, String key, String value) { @@ -163,28 +160,4 @@ public static ExecutorSingleOperationResp replaceVarMapValue(Map } - public static ExecutorSingleOperationResp sendRequestToHostedServer(String requestUrl, String redirectUrl, String tokenVal){ - RequestBody emptyBody = RequestBody.create(new byte[]{}, null); - - Request request = new Request.Builder() - .url(requestUrl) - .addHeader("x-akto-redirect-url", redirectUrl) - .addHeader(Constants.AKTO_TOKEN_KEY, tokenVal) - .post(emptyBody) - .build(); - - OkHttpClient client = new OkHttpClient(); - Response okResponse = null; - - try { - okResponse = client.newCall(request).execute(); - if (!okResponse.isSuccessful()) { - return new ExecutorSingleOperationResp(false,"Cannot send request to hosted server"); - } - return new ExecutorSingleOperationResp(true, ""); - }catch (Exception e){ - return new ExecutorSingleOperationResp(false, e.getMessage()); - } - } - } From ef718d95036c6fc89e02ebae36ffbbbce4c4ccff Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Wed, 6 Mar 2024 11:03:59 +0530 Subject: [PATCH 05/11] changed name of a function --- apps/testing/src/main/java/com/akto/test_editor/Utils.java | 4 ++-- .../main/java/com/akto/test_editor/execution/Executor.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/testing/src/main/java/com/akto/test_editor/Utils.java b/apps/testing/src/main/java/com/akto/test_editor/Utils.java index 9ca73a26e2..3bc5141536 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/Utils.java +++ b/apps/testing/src/main/java/com/akto/test_editor/Utils.java @@ -631,7 +631,7 @@ public static String extractValue(String keyValue, String key) { return result; } - public static ExecutorSingleOperationResp sendRequestToHostedServer(String requestUrl, String redirectUrl, String tokenVal){ + public static ExecutorSingleOperationResp sendRequestToSsrfServer(String requestUrl, String redirectUrl, String tokenVal){ RequestBody emptyBody = RequestBody.create(new byte[]{}, null); Request request = new Request.Builder() @@ -647,7 +647,7 @@ public static ExecutorSingleOperationResp sendRequestToHostedServer(String reque try { okResponse = client.newCall(request).execute(); if (!okResponse.isSuccessful()) { - return new ExecutorSingleOperationResp(false,"Cannot send request to hosted server"); + return new ExecutorSingleOperationResp(false,"Could not send request to the ssrf server."); } return new ExecutorSingleOperationResp(true, ""); }catch (Exception e){ diff --git a/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java b/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java index 232416c570..ed08cdec45 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java +++ b/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java @@ -478,7 +478,7 @@ public ExecutorSingleOperationResp runOperation(String operationType, RawApi raw BasicDBObject response = getBillingTokenForAuth(); if(response.getString("token") != null){ String tokenVal = response.getString("token"); - return Utils.sendRequestToHostedServer(url + generatedUUID, redirectUrl, tokenVal); + return Utils.sendRequestToSsrfServer(url + generatedUUID, redirectUrl, tokenVal); }else{ return new ExecutorSingleOperationResp(false, response.getString("error")); } From 21a471fd9dae4733f55b66fb0ab07e57862a6050 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Sat, 16 Mar 2024 14:33:46 +0530 Subject: [PATCH 06/11] added validation --- .../main/java/com/akto/test_editor/Utils.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/apps/testing/src/main/java/com/akto/test_editor/Utils.java b/apps/testing/src/main/java/com/akto/test_editor/Utils.java index 3bc5141536..05097301e6 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/Utils.java +++ b/apps/testing/src/main/java/com/akto/test_editor/Utils.java @@ -13,6 +13,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.bouncycastle.jce.provider.JDKDSASigner.stdDSA; + import com.akto.dto.OriginalHttpRequest; import com.akto.dto.RawApi; import com.akto.dto.test_editor.ExecutorSingleOperationResp; @@ -655,4 +657,25 @@ public static ExecutorSingleOperationResp sendRequestToSsrfServer(String request } } + public static Boolean sendRequestToSsrfServer(String requestUrl){ + Request request = new Request.Builder() + .url(requestUrl) + .get() + .build(); + + OkHttpClient client = new OkHttpClient(); + Response okResponse = null; + + try { + okResponse = client.newCall(request).execute(); + if (!okResponse.isSuccessful()) { + return false; + }else{ + return okResponse.code() == 202; + } + }catch (Exception e){ + return false; + } + } + } From 4d181b35ad733f9b58bca467140fe1385be7f076 Mon Sep 17 00:00:00 2001 From: ayushaga14 Date: Sat, 16 Mar 2024 15:38:28 +0530 Subject: [PATCH 07/11] add ssrf validate filter --- .../com/akto/test_editor/filter/Filter.java | 7 +++++ .../akto/test_editor/filter/FilterAction.java | 1 + .../data_operands_impl/SsrfUrlHitFilter.java | 31 +++++++++++++++++++ .../akto/dao/test_editor/TestEditorEnums.java | 3 +- .../dao/test_editor/filter/ConfigParser.java | 6 +++- 5 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 apps/testing/src/main/java/com/akto/test_editor/filter/data_operands_impl/SsrfUrlHitFilter.java diff --git a/apps/testing/src/main/java/com/akto/test_editor/filter/Filter.java b/apps/testing/src/main/java/com/akto/test_editor/filter/Filter.java index 7c7fd56b5f..5ad2e8a67f 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/filter/Filter.java +++ b/apps/testing/src/main/java/com/akto/test_editor/filter/Filter.java @@ -42,6 +42,13 @@ public DataOperandsFilterResponse isEndpointValid(FilterNode node, RawApi rawApi Boolean res = filterAction.invokeFilter(dataOperandFilterRequest); return new DataOperandsFilterResponse(res, matchingKeySet, contextEntities, null); } + if (node.getOperand().equalsIgnoreCase(TestEditorEnums.PredicateOperator.SSRF_URL_HIT.toString())) { + Object updatedQuerySet = filterAction.resolveQuerySetValues(null, node.fetchNodeValues(), varMap); + List val = (List) updatedQuerySet; + DataOperandFilterRequest dataOperandFilterRequest = new DataOperandFilterRequest(null, val, "ssrf_url_hit"); + Boolean res = filterAction.invokeFilter(dataOperandFilterRequest); + return new DataOperandsFilterResponse(res, matchingKeySet, contextEntities, null); + } if (! (node.getNodeType().toLowerCase().equals(OperandTypes.Data.toString().toLowerCase()) || node.getNodeType().toLowerCase().equals(OperandTypes.Extract.toString().toLowerCase()) || node.getNodeType().toLowerCase().equals(OperandTypes.Context.toString().toLowerCase() ))) { return new DataOperandsFilterResponse(false, null, null, null); } diff --git a/apps/testing/src/main/java/com/akto/test_editor/filter/FilterAction.java b/apps/testing/src/main/java/com/akto/test_editor/filter/FilterAction.java index 02ebd00438..66cba5f2ca 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/filter/FilterAction.java +++ b/apps/testing/src/main/java/com/akto/test_editor/filter/FilterAction.java @@ -61,6 +61,7 @@ public final class FilterAction { put("contains_jwt", new ContainsJwt()); put("cookie_expire_filter", new CookieExpireFilter()); put("datatype", new DatatypeFilter()); + put("ssrf_url_hit", new SsrfUrlHitFilter()); }}; public FilterAction() { } diff --git a/apps/testing/src/main/java/com/akto/test_editor/filter/data_operands_impl/SsrfUrlHitFilter.java b/apps/testing/src/main/java/com/akto/test_editor/filter/data_operands_impl/SsrfUrlHitFilter.java new file mode 100644 index 0000000000..401ad84622 --- /dev/null +++ b/apps/testing/src/main/java/com/akto/test_editor/filter/data_operands_impl/SsrfUrlHitFilter.java @@ -0,0 +1,31 @@ +package com.akto.test_editor.filter.data_operands_impl; + +import java.util.ArrayList; +import java.util.List; + +import com.akto.dto.test_editor.DataOperandFilterRequest; + +public class SsrfUrlHitFilter extends DataOperandsImpl { + + @Override + public Boolean isValid(DataOperandFilterRequest dataOperandFilterRequest) { + + Boolean result = false; + List querySet = new ArrayList<>(); + String data; + try { + querySet = (List) dataOperandFilterRequest.getQueryset(); + data = (String) dataOperandFilterRequest.getData(); + } catch(Exception e) { + return result; + } + + for (String queryString: querySet) { + System.out.println(queryString); + // trigger function here + } + + return result; + } + +} diff --git a/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java b/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java index 098aa53af1..82435b0854 100644 --- a/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java +++ b/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java @@ -40,7 +40,8 @@ public enum TermOperands { public enum PredicateOperator { AND, OR, - COMPARE_GREATER + COMPARE_GREATER, + SSRF_URL_HIT } public enum KeyValOperator { diff --git a/libs/dao/src/main/java/com/akto/dao/test_editor/filter/ConfigParser.java b/libs/dao/src/main/java/com/akto/dao/test_editor/filter/ConfigParser.java index 997c641de2..3ec8774e51 100644 --- a/libs/dao/src/main/java/com/akto/dao/test_editor/filter/ConfigParser.java +++ b/libs/dao/src/main/java/com/akto/dao/test_editor/filter/ConfigParser.java @@ -44,6 +44,10 @@ public ConfigParserResult validateAndTransform(Map filters, Filt Object values = curNode.getValues(); ConfigParserValidationResult configParserValidationResult = validateNodeAgainstRules(curNode, parentNode, termNodeExists, collectionNodeExists, concernedProperty, contextProperty); + if (curNode.getOperand().equalsIgnoreCase(PredicateOperator.SSRF_URL_HIT.toString())) { + return new ConfigParserResult(null, true, ""); + } + if (!configParserValidationResult.getIsValid()) { return new ConfigParserResult(null, false, configParserValidationResult.getErrMsg()); } @@ -189,7 +193,7 @@ public ConfigParserValidationResult validateNodeAgainstRules(FilterNode curNode, } // 5. Last Node should always be a data/extract node - if (! (curNodeType.equals(OperandTypes.Data.toString().toLowerCase()) || curNodeType.equals(OperandTypes.Extract.toString().toLowerCase()) || curNodeType.equals(OperandTypes.Context.toString().toLowerCase()) || curNode.getOperand().equalsIgnoreCase(TestEditorEnums.PredicateOperator.COMPARE_GREATER.toString()))) { + if (! (curNodeType.equals(OperandTypes.Data.toString().toLowerCase()) || curNodeType.equals(OperandTypes.Extract.toString().toLowerCase()) || curNodeType.equals(OperandTypes.Context.toString().toLowerCase()) || curNode.getOperand().equalsIgnoreCase(TestEditorEnums.PredicateOperator.COMPARE_GREATER.toString()) || curNode.getOperand().equalsIgnoreCase(TestEditorEnums.PredicateOperator.COMPARE_GREATER.toString()))) { if (isString(values) || isListOfString(values)) { configParserValidationResult.setIsValid(false); configParserValidationResult.setErrMsg("Last Node should always be a data/extract node"); From 3cc855adf1e9a6fe6efc1a9b957bbe97b8fda102 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Sat, 16 Mar 2024 16:07:28 +0530 Subject: [PATCH 08/11] modified validator for checking ssrf hit --- apps/testing/src/main/java/com/akto/test_editor/Utils.java | 7 ++++++- .../filter/data_operands_impl/SsrfUrlHitFilter.java | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/apps/testing/src/main/java/com/akto/test_editor/Utils.java b/apps/testing/src/main/java/com/akto/test_editor/Utils.java index 05097301e6..738287c171 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/Utils.java +++ b/apps/testing/src/main/java/com/akto/test_editor/Utils.java @@ -657,7 +657,12 @@ public static ExecutorSingleOperationResp sendRequestToSsrfServer(String request } } - public static Boolean sendRequestToSsrfServer(String requestUrl){ + public static Boolean sendRequestToSsrfServer(String url){ + String requestUrl = ""; + if(!(url.startsWith("http"))){ + requestUrl = "http://ssrf.akto.io/validate/" + url; + } + Request request = new Request.Builder() .url(requestUrl) .get() diff --git a/apps/testing/src/main/java/com/akto/test_editor/filter/data_operands_impl/SsrfUrlHitFilter.java b/apps/testing/src/main/java/com/akto/test_editor/filter/data_operands_impl/SsrfUrlHitFilter.java index 401ad84622..5d8ca68a66 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/filter/data_operands_impl/SsrfUrlHitFilter.java +++ b/apps/testing/src/main/java/com/akto/test_editor/filter/data_operands_impl/SsrfUrlHitFilter.java @@ -4,6 +4,7 @@ import java.util.List; import com.akto.dto.test_editor.DataOperandFilterRequest; +import com.akto.test_editor.Utils; public class SsrfUrlHitFilter extends DataOperandsImpl { @@ -21,8 +22,10 @@ public Boolean isValid(DataOperandFilterRequest dataOperandFilterRequest) { } for (String queryString: querySet) { - System.out.println(queryString); - // trigger function here + if(Utils.sendRequestToSsrfServer(queryString)){ + result = true; + break; + } } return result; From c84dd284ecf903648cf79e21babc2af9c95892a8 Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Sat, 16 Mar 2024 17:55:18 +0530 Subject: [PATCH 09/11] resolved comments for validation of ssrf hit --- apps/testing/src/main/java/com/akto/test_editor/Utils.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/testing/src/main/java/com/akto/test_editor/Utils.java b/apps/testing/src/main/java/com/akto/test_editor/Utils.java index 738287c171..96e6ef7421 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/Utils.java +++ b/apps/testing/src/main/java/com/akto/test_editor/Utils.java @@ -662,7 +662,7 @@ public static Boolean sendRequestToSsrfServer(String url){ if(!(url.startsWith("http"))){ requestUrl = "http://ssrf.akto.io/validate/" + url; } - + Request request = new Request.Builder() .url(requestUrl) .get() @@ -676,7 +676,9 @@ public static Boolean sendRequestToSsrfServer(String url){ if (!okResponse.isSuccessful()) { return false; }else{ - return okResponse.code() == 202; + ResponseBody responseBody = okResponse.body(); + BasicDBObject bd = BasicDBObject.parse(responseBody.string()); + return bd.getBoolean("url-hit"); } }catch (Exception e){ return false; From 6d60379aeeeab700cc6506e68bdf1ee8b81b454c Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Mon, 18 Mar 2024 16:13:04 +0530 Subject: [PATCH 10/11] "changes made for testing ssrf" --- apps/testing/src/main/java/com/akto/test_editor/Utils.java | 6 +++++- .../main/java/com/akto/test_editor/execution/Executor.java | 4 ++-- docker.env | 3 ++- .../main/java/com/akto/dao/test_editor/TestEditorEnums.java | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/testing/src/main/java/com/akto/test_editor/Utils.java b/apps/testing/src/main/java/com/akto/test_editor/Utils.java index 96e6ef7421..bb83f70c10 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/Utils.java +++ b/apps/testing/src/main/java/com/akto/test_editor/Utils.java @@ -660,7 +660,11 @@ public static ExecutorSingleOperationResp sendRequestToSsrfServer(String request public static Boolean sendRequestToSsrfServer(String url){ String requestUrl = ""; if(!(url.startsWith("http"))){ - requestUrl = "http://ssrf.akto.io/validate/" + url; + String hostName ="https://test-services.akto.io/"; + if(System.getenv("SSRF_SERVICE_NAME") != null && System.getenv("SSRF_SERVICE_NAME").length() > 0){ + hostName = System.getenv("SSRF_SERVICE_NAME"); + } + requestUrl = hostName + url; } Request request = new Request.Builder() diff --git a/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java b/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java index ed08cdec45..274bac613a 100644 --- a/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java +++ b/apps/testing/src/main/java/com/akto/test_editor/execution/Executor.java @@ -440,7 +440,7 @@ private static BasicDBObject getBillingTokenForAuth() { Filters.in(Organization.ACCOUNTS, accountId) ); if (organization == null) { - + return new BasicDBObject("error", "organization not found"); } Tokens tokens; @@ -466,7 +466,7 @@ private static BasicDBObject getBillingTokenForAuth() { public ExecutorSingleOperationResp runOperation(String operationType, RawApi rawApi, Object key, Object value, Map varMap, AuthMechanism authMechanism, List customAuthTypes) { switch (operationType.toLowerCase()) { - case "send_ssrf_request": + case "send_ssrf_req": String keyValue = key.toString().replaceAll("\\$\\{random_uuid\\}", ""); String url = Utils.extractValue(keyValue, "url="); String redirectUrl = Utils.extractValue(keyValue, "redirect_url="); diff --git a/docker.env b/docker.env index f9e4d9cd4d..4c02a9279e 100644 --- a/docker.env +++ b/docker.env @@ -5,4 +5,5 @@ AKTO_TRAFFIC_BATCH_SIZE=100 AKTO_TRAFFIC_BATCH_TIME_SECS=10 DASHBOARD_MODE=local_deploy USE_HOSTNAME=true -PUPPETEER_REPLAY_SERVICE_URL=http://akto-puppeteer-replay:3000 \ No newline at end of file +PUPPETEER_REPLAY_SERVICE_URL=http://akto-puppeteer-replay:3000 +SSRF_SERVICE_NAME="https://test-services.akto.io/" \ No newline at end of file diff --git a/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java b/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java index c882d20a2a..9b8b732a69 100644 --- a/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java +++ b/libs/dao/src/main/java/com/akto/dao/test_editor/TestEditorEnums.java @@ -206,7 +206,7 @@ public enum TerminalExecutorDataOperands { REPLACE_BODY, JWT_REPLACE_BODY, ATTACH_FILE, - SEND_SSRF_REQUEST, + SEND_SSRF_REQ, } public enum NonTerminalExecutorDataOperands { From 25098cf50af1e4e2afb4dce96a8231adfee19a2c Mon Sep 17 00:00:00 2001 From: Ark2307 Date: Thu, 21 Mar 2024 09:50:12 +0530 Subject: [PATCH 11/11] added interceptors for jira --- apps/dashboard/src/main/resources/struts.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/dashboard/src/main/resources/struts.xml b/apps/dashboard/src/main/resources/struts.xml index f7021c5034..cfe776f844 100644 --- a/apps/dashboard/src/main/resources/struts.xml +++ b/apps/dashboard/src/main/resources/struts.xml @@ -2864,23 +2864,39 @@ + + JIRA_INTEGRATION + 422 false ^actionErrors.*, ^responses.* + + 403 + false + ^actionErrors.* + + + JIRA_INTEGRATION + 422 false ^actionErrors.*, ^responses.* + + 403 + false + ^actionErrors.* +