From 18414653fdf01f6b80c298be54866a587b3c38ba Mon Sep 17 00:00:00 2001 From: idawda Date: Mon, 11 Mar 2024 18:30:44 +0530 Subject: [PATCH 1/4] Decryption Utility for NR-234886 Co-authored-by: Lovesh Baya --- .../instrumentator/utils/HashGenerator.java | 4 + .../intcodeagent/utils/EncryptorUtils.java | 81 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/utils/HashGenerator.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/utils/HashGenerator.java index 128ec2381..dc6f5914c 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/utils/HashGenerator.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/instrumentator/utils/HashGenerator.java @@ -147,6 +147,10 @@ public static String getSHA256HexDigest(List data) { String input = StringUtils.join(data); return getChecksum(input); } + public static String getSHA256HexDigest(String data) { + String input = StringUtils.join(data); + return getChecksum(input); + } /** * Gets the xxHash64 hex digest. diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java new file mode 100644 index 000000000..0e13c2c7a --- /dev/null +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java @@ -0,0 +1,81 @@ +package com.newrelic.agent.security.intcodeagent.utils; + +import com.newrelic.agent.security.instrumentator.utils.HashGenerator; +import com.newrelic.api.agent.security.NewRelicSecurity; +import com.newrelic.api.agent.security.utils.logging.LogLevel; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.lang3.StringUtils; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidAlgorithmParameterException; +import java.security.spec.KeySpec; + +public class EncryptorUtils { + public static final String PBKDF_2_WITH_HMAC_SHA_1 = "PBKDF2WithHmacSHA1"; + public static final String AES_CBC_PKCS_5_PADDING = "AES/CBC/PKCS5Padding"; + public static final String AES = "AES"; + private static final int ITERATION = 1024; + private static final int KEY_LEN = 256; + private static final int OFFSET = 16; + private static final String ERROR_WHILE_GENERATING_REQUIRED_SALT_FROM_S_S = "Error while generating required salt from %s : %s"; + private static final String ERROR_WHILE_DECRYPTION = "Error while decryption %s : %s "; + private static final String ENCRYPTED_DATA_S_DECRYPTED_DATA_S = "Encrypted Data : %s , Decrypted data %s "; + public static final String INCORRECT_SECRET_PROVIDED_S_S = "Incorrect Password / salt provided : %s"; + public static final String EMPTY_PASSWORD_PROVIDED_S = "Empty Password provided %s"; + public static final String DATA_TO_BE_DECRYPTED_IS_EMPTY_S = "Data to be decrypted is Empty %s"; + + public static String decrypt(String password, String encryptedData) { + String decryptedData; + if (StringUtils.isBlank(password)){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(EMPTY_PASSWORD_PROVIDED_S, password), EncryptorUtils.class.getName()); + return null; + } + if (StringUtils.isBlank(encryptedData)){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(DATA_TO_BE_DECRYPTED_IS_EMPTY_S, encryptedData), EncryptorUtils.class.getName()); + return null; + } + try { + SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF_2_WITH_HMAC_SHA_1); + KeySpec spec = new PBEKeySpec(password.toCharArray(), generateSalt(password), ITERATION, KEY_LEN); + SecretKey tmp = factory.generateSecret(spec); + SecretKey secret = new SecretKeySpec(tmp.getEncoded(), AES); + + Cipher cipher = Cipher.getInstance(AES_CBC_PKCS_5_PADDING); + cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(new byte[OFFSET])); + + // Decrypt the content + byte[] decryptedBytes = cipher.doFinal(Hex.decodeHex(encryptedData)); + + decryptedData = new String(decryptedBytes, OFFSET, decryptedBytes.length - OFFSET); + NewRelicSecurity.getAgent().log(LogLevel.FINEST, String.format(ENCRYPTED_DATA_S_DECRYPTED_DATA_S, encryptedData, decryptedData), EncryptorUtils.class.getName()); + return decryptedData; + } catch (DecoderException ignored) { + + } catch (InvalidAlgorithmParameterException e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(INCORRECT_SECRET_PROVIDED_S_S, e.getMessage()), EncryptorUtils.class.getName()); + } catch (Exception e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(ERROR_WHILE_DECRYPTION, encryptedData, e.getMessage()), EncryptorUtils.class.getName()); + } + return null; + } + + private static boolean verifyHashData(String knownDecryptedDataHash, String decryptedData) { + return StringUtils.equals(HashGenerator.getSHA256HexDigest(decryptedData), knownDecryptedDataHash); + } + + private static byte[] generateSalt(String salt) throws DecoderException { + try { + return Hex.decodeHex(String.valueOf(Hex.encodeHex(StringUtils.left(salt, OFFSET).getBytes()))); + } catch (DecoderException e) { + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(ERROR_WHILE_GENERATING_REQUIRED_SALT_FROM_S_S, salt, e.getMessage()), EncryptorUtils.class.getName()); + NewRelicSecurity.getAgent().reportIncident(LogLevel.WARNING, String.format(ERROR_WHILE_GENERATING_REQUIRED_SALT_FROM_S_S, salt, e.getMessage()), e, EncryptorUtils.class.getName()); + throw e; + } + } +} From 76d60b7a8cc250b58af236def7eb554aad1667fa Mon Sep 17 00:00:00 2001 From: lovesh-ap Date: Fri, 15 Mar 2024 10:14:27 +0530 Subject: [PATCH 2/4] NR-234886 : Add logics to decrypt header and use the decrypted values. Add required API --- gradle.properties | 2 +- .../intcodeagent/utils/EncryptorUtils.java | 3 ++- .../com/newrelic/api/agent/security/Agent.java | 12 ++++++++++++ .../com/newrelic/api/agent/security/Agent.java | 5 +++++ .../newrelic/api/agent/security/NoOpAgent.java | 5 +++++ .../api/agent/security/SecurityAgent.java | 2 ++ .../instrumentation/helpers/ServletHelper.java | 16 +++++++++++++--- .../api/agent/security/schema/StringUtils.java | 1 + 8 files changed, 41 insertions(+), 5 deletions(-) diff --git a/gradle.properties b/gradle.properties index 765ec9338..8ee271ff2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ # The agent version. agentVersion=1.1.2 -jsonVersion=1.1.1 +jsonVersion=1.2.0 # Updated exposed NR APM API version. nrAPIVersion=8.4.0 diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java index 0e13c2c7a..afa1e3f44 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java @@ -1,5 +1,6 @@ package com.newrelic.agent.security.intcodeagent.utils; +import com.newrelic.agent.security.AgentInfo; import com.newrelic.agent.security.instrumentator.utils.HashGenerator; import com.newrelic.api.agent.security.NewRelicSecurity; import com.newrelic.api.agent.security.utils.logging.LogLevel; @@ -65,7 +66,7 @@ public static String decrypt(String password, String encryptedData) { return null; } - private static boolean verifyHashData(String knownDecryptedDataHash, String decryptedData) { + public static boolean verifyHashData(String knownDecryptedDataHash, String decryptedData) { return StringUtils.equals(HashGenerator.getSHA256HexDigest(decryptedData), knownDecryptedDataHash); } diff --git a/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java b/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java index 3e4fac1cf..3b298e080 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/api/agent/security/Agent.java @@ -9,6 +9,7 @@ import com.newrelic.agent.security.intcodeagent.constants.AgentServices; import com.newrelic.agent.security.intcodeagent.filelogging.FileLoggerThreadPool; import com.newrelic.agent.security.intcodeagent.filelogging.LogFileHelper; +import com.newrelic.agent.security.intcodeagent.utils.EncryptorUtils; import com.newrelic.api.agent.security.utils.logging.LogLevel; import com.newrelic.agent.security.intcodeagent.logging.HealthCheckScheduleThread; import com.newrelic.agent.security.intcodeagent.logging.IAgentConstants; @@ -657,4 +658,15 @@ public void retransformUninstrumentedClass(Class classToRetransform) { NewRelic.getAgent().getLogger().log(Level.FINER, "Class ", classToRetransform, " already instrumented."); } } + + @Override + public String decryptAndVerify(String encryptedData, String hashVerifier) { + String decryptedData = EncryptorUtils.decrypt(AgentInfo.getInstance().getLinkingMetadata().get(INRSettingsKey.NR_ENTITY_GUID), encryptedData); + if(EncryptorUtils.verifyHashData(hashVerifier, decryptedData)) { + return decryptedData; + } else { + NewRelic.getAgent().getLogger().log(Level.WARNING, String.format("Agent data decryption verifier fails on data : %s hash : %s", encryptedData, hashVerifier), Agent.class.getName()); + return null; + } + } } \ No newline at end of file diff --git a/newrelic-security-api-test-impl/src/main/java/com/newrelic/api/agent/security/Agent.java b/newrelic-security-api-test-impl/src/main/java/com/newrelic/api/agent/security/Agent.java index 4eeb1c4c1..ac62898c7 100644 --- a/newrelic-security-api-test-impl/src/main/java/com/newrelic/api/agent/security/Agent.java +++ b/newrelic-security-api-test-impl/src/main/java/com/newrelic/api/agent/security/Agent.java @@ -190,4 +190,9 @@ public void reportIncident(LogLevel logLevel, String event, Throwable exception, public void retransformUninstrumentedClass(Class classToRetransform) { } + + @Override + public String decryptAndVerify(String encryptedData, String hashVerifier) { + return null; + } } \ No newline at end of file diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/NoOpAgent.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/NoOpAgent.java index c30f48430..b110083e5 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/NoOpAgent.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/NoOpAgent.java @@ -121,5 +121,10 @@ public void reportIncident(LogLevel logLevel, String event, Throwable exception, @Override public void retransformUninstrumentedClass(Class classToRetransform) {} + @Override + public String decryptAndVerify(String encryptedData, String hashVerifier) { + return null; + } + } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/SecurityAgent.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/SecurityAgent.java index 49b9c14ab..81a4d65eb 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/SecurityAgent.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/SecurityAgent.java @@ -65,4 +65,6 @@ public interface SecurityAgent { void reportIncident(LogLevel logLevel, String event, Throwable exception, String caller); void retransformUninstrumentedClass(Class classToRetransform); + + String decryptAndVerify(String encryptedData, String hashVerifier); } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java index df4fe1ee2..501197bae 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java @@ -92,8 +92,18 @@ public static K2RequestIdentifier parseFuzzRequestIdentifierHeader(String reques if (data.length >= 6 && StringUtils.isNotBlank(data[5])) { k2RequestIdentifierInstance.setRefKey(data[5].trim()); } - if (data.length >= 7) { - for (int i = 6; i < data.length; i++) { + if (data.length >= 8) { + String encryptedData = data[6].trim(); + String hashVerifier = data[7].trim(); + String filesToCreate = NewRelicSecurity.getAgent().decryptAndVerify(encryptedData, hashVerifier); + if(StringUtils.isBlank(filesToCreate)){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format("Request Identifier decryption of files failed : %s hash : %s", encryptedData, hashVerifier), ServletHelper.class.getName()); + return k2RequestIdentifierInstance; + } + + String[] allFiles = StringUtils.splitByWholeSeparatorWorker(filesToCreate, StringUtils.COMMA_DELIMETER, -1, false); + + for (int i = 0; i < allFiles.length; i++) { String tmpFile = data[i].trim(); if(StringUtils.contains(tmpFile, NR_CSEC_VALIDATOR_HOME_TMP_URL_ENCODED)) { tmpFile = urlDecode(tmpFile); @@ -116,7 +126,7 @@ public static K2RequestIdentifier parseFuzzRequestIdentifierHeader(String reques Files.createFile(fileToCreate.toPath()); } catch (Throwable e) { String message = "Error while parsing fuzz request : %s"; - NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format(message, e.getMessage()), e, ServletHelper.class.getName()); + NewRelicSecurity.getAgent().log(LogLevel.INFO, String.format(message, e.getMessage()), e, ServletHelper.class.getName()); } } } diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/StringUtils.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/StringUtils.java index 08cfc96a5..ade0ee424 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/StringUtils.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/schema/StringUtils.java @@ -11,6 +11,7 @@ public class StringUtils { public static final String LF = "\n"; public static final int INDEX_NOT_FOUND = -1; + public static final String COMMA_DELIMETER = ","; /** *

Checks if a CharSequence is not empty (""), not null and not whitespace only.

From bed154a09e05ffa0bde3b094f7f0c78fde5d2348 Mon Sep 17 00:00:00 2001 From: idawda Date: Fri, 15 Mar 2024 15:56:38 +0530 Subject: [PATCH 3/4] fix file creation in IAST --- .../agent/security/instrumentation/helpers/ServletHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java index 501197bae..8217daf9a 100644 --- a/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java +++ b/newrelic-security-api/src/main/java/com/newrelic/api/agent/security/instrumentation/helpers/ServletHelper.java @@ -104,7 +104,7 @@ public static K2RequestIdentifier parseFuzzRequestIdentifierHeader(String reques String[] allFiles = StringUtils.splitByWholeSeparatorWorker(filesToCreate, StringUtils.COMMA_DELIMETER, -1, false); for (int i = 0; i < allFiles.length; i++) { - String tmpFile = data[i].trim(); + String tmpFile = allFiles[i].trim(); if(StringUtils.contains(tmpFile, NR_CSEC_VALIDATOR_HOME_TMP_URL_ENCODED)) { tmpFile = urlDecode(tmpFile); } From b2c0f283004a50eb7e5ac778523355e482f6d6a1 Mon Sep 17 00:00:00 2001 From: idawda Date: Tue, 19 Mar 2024 11:13:50 +0530 Subject: [PATCH 4/4] add null checks to hash verification --- .../agent/security/intcodeagent/utils/EncryptorUtils.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java index afa1e3f44..1d0e52e0d 100644 --- a/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java +++ b/newrelic-security-agent/src/main/java/com/newrelic/agent/security/intcodeagent/utils/EncryptorUtils.java @@ -67,6 +67,14 @@ public static String decrypt(String password, String encryptedData) { } public static boolean verifyHashData(String knownDecryptedDataHash, String decryptedData) { + if (StringUtils.isBlank(decryptedData)){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format("Decrypted Data is empty %s", decryptedData), EncryptorUtils.class.getName()); + return false; + } + if (StringUtils.isBlank(knownDecryptedDataHash)){ + NewRelicSecurity.getAgent().log(LogLevel.WARNING, String.format("Known-Decrypted Data Hash is empty %s", knownDecryptedDataHash), EncryptorUtils.class.getName()); + return false; + } return StringUtils.equals(HashGenerator.getSHA256HexDigest(decryptedData), knownDecryptedDataHash); }