From 23d106c21d8344a3813848d06295d36d344321c9 Mon Sep 17 00:00:00 2001 From: pruDhv! <58649792+sk3l10x1ng@users.noreply.github.com> Date: Sat, 1 Feb 2025 15:10:01 +0530 Subject: [PATCH] [MASWE-0020] Weak Encryption (by @appknox) (#2910) * added weaknesss * updated weakness * added MASTG-DEMO, rule, MASTG-TEST * updated weakness.md * fix spelling * Update weaknesses/MASVS-CRYPTO/MASWE-0020.md * Update weaknesses/MASVS-CRYPTO/MASWE-0020.md * Update weaknesses/MASVS-CRYPTO/MASWE-0020.md * Update weaknesses/MASVS-CRYPTO/MASWE-0020.md * Update weaknesses/MASVS-CRYPTO/MASWE-0020.md * Update weaknesses/MASVS-CRYPTO/MASWE-0020.md * removed duplicate * change status to new * updated title MASTG-TEST-0211.md * updated title MASTG-DEMO-0016.md * updated tests-beta * fix lint * updated MASWE-0020.md * updated mitigations * updated changes * removed MASTG-TEST-0211.md * added new Demo * 2 demo added * renamed semgrep files * updated * rename * updated * updated MASWE-0020.md * updated MASWE-0020.md * updated weak-encryption.yaml * Renamed files * updated Demo.md * rename rules filename * updated changes * updated changes * fix --------- Co-authored-by: Sven --- .../MASTG-DEMO-0022/MASTG-DEMO-0022.md | 33 ++++ .../MASVS-CRYPTO/MASTG-DEMO-0022/MastgTest.kt | 65 +++++++ .../MASTG-DEMO-0022/MastgTest_reversed.java | 77 +++++++++ .../MASVS-CRYPTO/MASTG-DEMO-0022/output.txt | 13 ++ .../MASVS-CRYPTO/MASTG-DEMO-0022/run.sh | 1 + .../MASTG_DEMO-0016/MASTG-DEMO-0016.md | 38 +++++ .../MASVS-CRYPTO/MASTG_DEMO-0016/MastgTest.kt | 133 +++++++++++++++ .../MASTG_DEMO-0016/MastgTest_reversed.java | 158 ++++++++++++++++++ .../MASVS-CRYPTO/MASTG_DEMO-0016/output.txt | 21 +++ .../MASVS-CRYPTO/MASTG_DEMO-0016/run.sh | 1 + ...tg-android-weak-encryption-algorithms.yaml | 12 ++ .../mastg-android-weak-encryption-modes.yaml | 16 ++ weaknesses/MASVS-CRYPTO/MASWE-0020.md | 35 +++- 13 files changed, 594 insertions(+), 9 deletions(-) create mode 100644 demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MASTG-DEMO-0022.md create mode 100644 demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MastgTest.kt create mode 100644 demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MastgTest_reversed.java create mode 100644 demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/output.txt create mode 100755 demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/run.sh create mode 100644 demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MASTG-DEMO-0016.md create mode 100644 demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MastgTest.kt create mode 100644 demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MastgTest_reversed.java create mode 100644 demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/output.txt create mode 100755 demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/run.sh create mode 100644 rules/mastg-android-weak-encryption-algorithms.yaml create mode 100644 rules/mastg-android-weak-encryption-modes.yaml diff --git a/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MASTG-DEMO-0022.md b/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MASTG-DEMO-0022.md new file mode 100644 index 0000000000..263f0c8c10 --- /dev/null +++ b/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MASTG-DEMO-0022.md @@ -0,0 +1,33 @@ +--- +platform: android +title: Uses of Insecure Encryption Algorithms in Cipher with semgrep +id: MASTG-DEMO-0022 +code: [kotlin] +--- + +### Sample + +The code snippet below shows sample code contains use of insecure encryption algorithms. + +{{ MastgTest.kt # MastgTest_reversed.java }} + +### Steps + +Let's run our @MASTG-TOOL-0110 rule against the sample code. + +{{ ../../../../rules/mastg-android-weak-encryption-algorithms.yaml }} + +{{ run.sh }} + +### Observation + +The rule has identified two instances in the code file where an insecure encryption is used. The specified line numbers are from the reversed code for further investigation and remediation. + +{{ output.txt }} + +### Evaluation + +The test fails since several instances of weak encryption algorithms were found: + +- Line 36 utilize insecure DES algorithm. +- Line 59 utilize insecure 3DES algorithm. diff --git a/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MastgTest.kt b/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MastgTest.kt new file mode 100644 index 0000000000..90b268a0f0 --- /dev/null +++ b/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MastgTest.kt @@ -0,0 +1,65 @@ +package org.owasp.mastestapp + +import android.content.Context +import java.security.Key +import javax.crypto.Cipher +import javax.crypto.SecretKeyFactory +import javax.crypto.spec.DESKeySpec +import javax.crypto.spec.DESedeKeySpec +import javax.crypto.spec.SecretKeySpec +import android.util.Base64 + +class MastgTest(private val context: Context) { + + // Vulnerable encryption using DES (weak algorithm) + fun vulnerableDesEncryption(data: String): String { + try { + // Weak key for DES + val keySpec = DESKeySpec("12345678".toByteArray()) + val keyFactory = SecretKeyFactory.getInstance("DES") + val secretKey: Key = keyFactory.generateSecret(keySpec) + + // Weak encryption algorithm (DES) and weak mode (ECB) + val cipher = Cipher.getInstance("DES") + cipher.init(Cipher.ENCRYPT_MODE, secretKey) + + val encryptedData = cipher.doFinal(data.toByteArray()) + return Base64.encodeToString(encryptedData, Base64.DEFAULT) + } catch (e: Exception) { + return "Encryption error: ${e.message}" + } + } + + + // Vulnerable encryption using 3DES (Triple DES) + fun vulnerable3DesEncryption(data: String): String { + try { + // Weak key for 3DES (24-byte key) + val keySpec = DESedeKeySpec("123456789012345678901234".toByteArray()) // 24 bytes key + val keyFactory = SecretKeyFactory.getInstance("DESede") + val secretKey: Key = keyFactory.generateSecret(keySpec) + + // Weak encryption algorithm (3DES) + val cipher = Cipher.getInstance("DESede") + cipher.init(Cipher.ENCRYPT_MODE, secretKey) + + val encryptedData = cipher.doFinal(data.toByteArray()) + return Base64.encodeToString(encryptedData, Base64.DEFAULT) + } catch (e: Exception) { + return "Encryption error: ${e.message}" + } + } + + fun mastgTest(): String { + val sensitiveString = "Hello from the OWASP MASTG Test app." + + // Encrypt with weak DES + val desEncryptedString = vulnerableDesEncryption(sensitiveString) + + // Encrypt with weak 3DES + val tripleDesEncryptedString = vulnerable3DesEncryption(sensitiveString) + + // Returning the encrypted results + return "DES Encrypted: $desEncryptedString\n3DES Encrypted: $tripleDesEncryptedString" + } +} \ No newline at end of file diff --git a/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MastgTest_reversed.java b/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MastgTest_reversed.java new file mode 100644 index 0000000000..9a5b9a5e47 --- /dev/null +++ b/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/MastgTest_reversed.java @@ -0,0 +1,77 @@ +package org.owasp.mastestapp; + +import android.content.Context; +import android.util.Base64; +import java.security.Key; +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.DESedeKeySpec; +import kotlin.Metadata; +import kotlin.jvm.internal.Intrinsics; +import kotlin.text.Charsets; + +/* compiled from: MastgTest.kt */ +@Metadata(d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0002\b\u0004\b\u0007\u0018\u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0002\u0010\u0004J\u0006\u0010\u0005\u001a\u00020\u0006J\u000e\u0010\u0007\u001a\u00020\u00062\u0006\u0010\b\u001a\u00020\u0006J\u000e\u0010\t\u001a\u00020\u00062\u0006\u0010\b\u001a\u00020\u0006R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\n"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "(Landroid/content/Context;)V", "mastgTest", "", "vulnerable3DesEncryption", "data", "vulnerableDesEncryption", "app_debug"}, k = 1, mv = {1, 9, 0}, xi = 48) +/* loaded from: classes4.dex */ +public final class MastgTest { + public static final int $stable = 8; + private final Context context; + + public MastgTest(Context context) { + Intrinsics.checkNotNullParameter(context, "context"); + this.context = context; + } + + public final String vulnerableDesEncryption(String data) { + Intrinsics.checkNotNullParameter(data, "data"); + try { + byte[] bytes = "12345678".getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)"); + DESKeySpec keySpec = new DESKeySpec(bytes); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + Key generateSecret = keyFactory.generateSecret(keySpec); + Intrinsics.checkNotNullExpressionValue(generateSecret, "generateSecret(...)"); + Key secretKey = generateSecret; + Cipher cipher = Cipher.getInstance("DES"); + cipher.init(1, secretKey); + byte[] bytes2 = data.getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes2, "this as java.lang.String).getBytes(charset)"); + byte[] encryptedData = cipher.doFinal(bytes2); + String encodeToString = Base64.encodeToString(encryptedData, 0); + Intrinsics.checkNotNullExpressionValue(encodeToString, "encodeToString(...)"); + return encodeToString; + } catch (Exception e) { + return "Encryption error: " + e.getMessage(); + } + } + + public final String vulnerable3DesEncryption(String data) { + Intrinsics.checkNotNullParameter(data, "data"); + try { + byte[] bytes = "123456789012345678901234".getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)"); + DESedeKeySpec keySpec = new DESedeKeySpec(bytes); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede"); + Key generateSecret = keyFactory.generateSecret(keySpec); + Intrinsics.checkNotNullExpressionValue(generateSecret, "generateSecret(...)"); + Key secretKey = generateSecret; + Cipher cipher = Cipher.getInstance("DESede"); + cipher.init(1, secretKey); + byte[] bytes2 = data.getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes2, "this as java.lang.String).getBytes(charset)"); + byte[] encryptedData = cipher.doFinal(bytes2); + String encodeToString = Base64.encodeToString(encryptedData, 0); + Intrinsics.checkNotNullExpressionValue(encodeToString, "encodeToString(...)"); + return encodeToString; + } catch (Exception e) { + return "Encryption error: " + e.getMessage(); + } + } + + public final String mastgTest() { + String desEncryptedString = vulnerableDesEncryption("Hello from the OWASP MASTG Test app."); + String tripleDesEncryptedString = vulnerable3DesEncryption("Hello from the OWASP MASTG Test app."); + return "DES Encrypted: " + desEncryptedString + "\n3DES Encrypted: " + tripleDesEncryptedString; + } +} diff --git a/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/output.txt b/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/output.txt new file mode 100644 index 0000000000..478fe97295 --- /dev/null +++ b/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/output.txt @@ -0,0 +1,13 @@ + + +┌─────────────────┐ +│ 2 Code Findings │ +└─────────────────┘ + + MastgTest_reversed.java + ❯❱rules.weak-encryption-algorithms + [MASVS-CRYPTO-1] Weak encryption algorithms found in use. + + 36┆ Cipher cipher = Cipher.getInstance("DES"); + ⋮┆---------------------------------------- + 59┆ Cipher cipher = Cipher.getInstance("DESede"); diff --git a/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/run.sh b/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/run.sh new file mode 100755 index 0000000000..0dc0f140fa --- /dev/null +++ b/demos/android/MASVS-CRYPTO/MASTG-DEMO-0022/run.sh @@ -0,0 +1 @@ +NO_COLOR=true semgrep -c ../../../../rules/mastg-android-weak-encryption-algorithms.yaml ./MastgTest_reversed.java --text -o output.txt \ No newline at end of file diff --git a/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MASTG-DEMO-0016.md b/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MASTG-DEMO-0016.md new file mode 100644 index 0000000000..62f4b285e4 --- /dev/null +++ b/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MASTG-DEMO-0016.md @@ -0,0 +1,38 @@ +--- +platform: android +title: Uses of Insecure Encryption Modes in Cipher with semgrep +id: MASTG-DEMO-0016 +code: [kotlin] +test: MASTG-TEST-0221 +--- + +### Sample + +The code snippet below shows sample code contains use of insecure encryption modes. + +{{ MastgTest.kt # MastgTest_reversed.java }} + +### Steps + +Let's run our @MASTG-TOOL-0110 rule against the sample code. + +{{ ../../../../rules/mastg-android-weak-encryption-modes }} + +{{ run.sh }} + +### Observation + +The rule has identified six instances in the code file, where insecure encryption modes are used. + +{{ output.txt }} + +### Evaluation + +The test fails since the output contains several instances of the ECB mode of AES: + +- Line 36 using Cipher.getInstance("AES") defaults to ECB. +- Line 55 using Cipher.getInstance("AES/ECB/NoPadding");. +- Line 76 using Cipher.getInstance("AES/ECB/PKCS5Padding");. +- Line 95 using Cipher.getInstance("AES/ECB/ISO10126Padding");. +- Line 118 using Cipher.getInstance("DES/ECB/PKCS5Padding");. +- Line 141 using Cipher.getInstance("DESede/ECB/PKCS5Padding");. diff --git a/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MastgTest.kt b/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MastgTest.kt new file mode 100644 index 0000000000..1c19d18632 --- /dev/null +++ b/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MastgTest.kt @@ -0,0 +1,133 @@ +package org.owasp.mastestapp + +import android.content.Context +import java.security.Key +import javax.crypto.Cipher +import javax.crypto.SecretKeyFactory +import javax.crypto.spec.DESKeySpec +import javax.crypto.spec.DESedeKeySpec +import javax.crypto.spec.SecretKeySpec +import android.util.Base64 + +class MastgTest(private val context: Context) { + + // Vulnerable AES encryption + fun vulnerableAesEncryption(data: String): String { + try { + val key = "1234567890123456".toByteArray() // 16 bytes key for AES + val secretKeySpec = SecretKeySpec(key, "AES") + + // Default mode for AES (ECB) + val cipher = Cipher.getInstance("AES") + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec) + + val encryptedData = cipher.doFinal(data.toByteArray()) + return Base64.encodeToString(encryptedData, Base64.DEFAULT) + } catch (e: Exception) { + return "Encryption error: ${e.message}" + } + } + + // Vulnerable AES with ECB and NoPadding (manual padding applied) + fun vulnerableAesEcbNoPadding(data: String): String { + try { + val key = "1234567890123456".toByteArray() + val secretKeySpec = SecretKeySpec(key, "AES") + + val cipher = Cipher.getInstance("AES/ECB/NoPadding") + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec) + + // Ensure the data is padded to match the block size + val blockSize = 16 + val paddingLength = blockSize - (data.length % blockSize) + val paddedData = data + "\u0000".repeat(paddingLength) // Null padding + + val encryptedData = cipher.doFinal(paddedData.toByteArray()) + return Base64.encodeToString(encryptedData, Base64.DEFAULT).trim() + } catch (e: Exception) { + return "Encryption error: ${e.message}" + } + } + + // Vulnerable AES with ECB and PKCS5Padding + fun vulnerableAesEcbPkcs5Padding(data: String): String { + try { + val key = "1234567890123456".toByteArray() + val secretKeySpec = SecretKeySpec(key, "AES") + + val cipher = Cipher.getInstance("AES/ECB/PKCS5Padding") + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec) + + val encryptedData = cipher.doFinal(data.toByteArray()) + return Base64.encodeToString(encryptedData, Base64.DEFAULT) + } catch (e: Exception) { + return "Encryption error: ${e.message}" + } + } + + // Vulnerable AES with ECB and ISO10126Padding + fun vulnerableAesEcbIso10126Padding(data: String): String { + try { + val key = "1234567890123456".toByteArray() + val secretKeySpec = SecretKeySpec(key, "AES") + + val cipher = Cipher.getInstance("AES/ECB/ISO10126Padding") + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec) + + val encryptedData = cipher.doFinal(data.toByteArray()) + return Base64.encodeToString(encryptedData, Base64.DEFAULT) + } catch (e: Exception) { + return "Encryption error: ${e.message}" + } + } + + // Vulnerable DES with ECB and PKCS5Padding + fun vulnerableDesEcbPkcs5Padding(data: String): String { + try { + val keySpec = DESKeySpec("12345678".toByteArray()) + val keyFactory = SecretKeyFactory.getInstance("DES") + val secretKey: Key = keyFactory.generateSecret(keySpec) + + val cipher = Cipher.getInstance("DES/ECB/PKCS5Padding") + cipher.init(Cipher.ENCRYPT_MODE, secretKey) + + val encryptedData = cipher.doFinal(data.toByteArray()) + return Base64.encodeToString(encryptedData, Base64.DEFAULT) + } catch (e: Exception) { + return "Encryption error: ${e.message}" + } + } + + // Vulnerable 3DES with ECB and PKCS5Padding + fun vulnerable3DesEcbPkcs5Padding(data: String): String { + try { + val keySpec = DESedeKeySpec("123456789012345678901234".toByteArray()) + val keyFactory = SecretKeyFactory.getInstance("DESede") + val secretKey: Key = keyFactory.generateSecret(keySpec) + + val cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding") + cipher.init(Cipher.ENCRYPT_MODE, secretKey) + + val encryptedData = cipher.doFinal(data.toByteArray()) + return Base64.encodeToString(encryptedData, Base64.DEFAULT) + } catch (e: Exception) { + return "Encryption error: ${e.message}" + } + } + + // Test and return results + fun mastgTest(): String { + val sensitiveString = "Hello from OWASP MASTG!" + + val results = listOf( + "AES Default: ${vulnerableAesEncryption(sensitiveString)}", + "AES ECB NoPadding: ${vulnerableAesEcbNoPadding(sensitiveString)}", + "AES ECB PKCS5Padding: ${vulnerableAesEcbPkcs5Padding(sensitiveString)}", + "AES ECB ISO10126Padding: ${vulnerableAesEcbIso10126Padding(sensitiveString)}", + "DES ECB PKCS5Padding: ${vulnerableDesEcbPkcs5Padding(sensitiveString)}", + "3DES ECB PKCS5Padding: ${vulnerable3DesEcbPkcs5Padding(sensitiveString)}" + ) + + return results.joinToString("\n") + } +} diff --git a/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MastgTest_reversed.java b/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MastgTest_reversed.java new file mode 100644 index 0000000000..6da081f9d0 --- /dev/null +++ b/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/MastgTest_reversed.java @@ -0,0 +1,158 @@ +package org.owasp.mastestapp; + +import android.content.Context; +import android.util.Base64; +import java.security.Key; +import java.util.List; +import javax.crypto.Cipher; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.SecretKeySpec; +import kotlin.Metadata; +import kotlin.collections.CollectionsKt; +import kotlin.jvm.internal.Intrinsics; +import kotlin.text.Charsets; +import kotlin.text.StringsKt; + +/* compiled from: MastgTest.kt */ +@Metadata(d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0002\b\b\b\u0007\u0018\u00002\u00020\u0001B\r\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0002\u0010\u0004J\u0006\u0010\u0005\u001a\u00020\u0006J\u000e\u0010\u0007\u001a\u00020\u00062\u0006\u0010\b\u001a\u00020\u0006J\u000e\u0010\t\u001a\u00020\u00062\u0006\u0010\b\u001a\u00020\u0006J\u000e\u0010\n\u001a\u00020\u00062\u0006\u0010\b\u001a\u00020\u0006J\u000e\u0010\u000b\u001a\u00020\u00062\u0006\u0010\b\u001a\u00020\u0006J\u000e\u0010\f\u001a\u00020\u00062\u0006\u0010\b\u001a\u00020\u0006J\u000e\u0010\r\u001a\u00020\u00062\u0006\u0010\b\u001a\u00020\u0006R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u000e"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "(Landroid/content/Context;)V", "mastgTest", "", "vulnerable3DesEcbPkcs5Padding", "data", "vulnerableAesEcbIso10126Padding", "vulnerableAesEcbNoPadding", "vulnerableAesEcbPkcs5Padding", "vulnerableAesEncryption", "vulnerableDesEcbPkcs5Padding", "app_debug"}, k = 1, mv = {1, 9, 0}, xi = 48) +/* loaded from: classes4.dex */ +public final class MastgTest { + public static final int $stable = 8; + private final Context context; + + public MastgTest(Context context) { + Intrinsics.checkNotNullParameter(context, "context"); + this.context = context; + } + + public final String vulnerableAesEncryption(String data) { + Intrinsics.checkNotNullParameter(data, "data"); + try { + byte[] key = "1234567890123456".getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(key, "this as java.lang.String).getBytes(charset)"); + SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(1, secretKeySpec); + byte[] bytes = data.getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)"); + byte[] encryptedData = cipher.doFinal(bytes); + String encodeToString = Base64.encodeToString(encryptedData, 0); + Intrinsics.checkNotNullExpressionValue(encodeToString, "encodeToString(...)"); + return encodeToString; + } catch (Exception e) { + return "Encryption error: " + e.getMessage(); + } + } + + public final String vulnerableAesEcbNoPadding(String data) { + Intrinsics.checkNotNullParameter(data, "data"); + try { + byte[] key = "1234567890123456".getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(key, "this as java.lang.String).getBytes(charset)"); + SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); + cipher.init(1, secretKeySpec); + int paddingLength = 16 - (data.length() % 16); + String paddedData = data + StringsKt.repeat("\u0000", paddingLength); + byte[] bytes = paddedData.getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)"); + byte[] encryptedData = cipher.doFinal(bytes); + String encodeToString = Base64.encodeToString(encryptedData, 0); + Intrinsics.checkNotNullExpressionValue(encodeToString, "encodeToString(...)"); + return StringsKt.trim((CharSequence) encodeToString).toString(); + } catch (Exception e) { + return "Encryption error: " + e.getMessage(); + } + } + + public final String vulnerableAesEcbPkcs5Padding(String data) { + Intrinsics.checkNotNullParameter(data, "data"); + try { + byte[] key = "1234567890123456".getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(key, "this as java.lang.String).getBytes(charset)"); + SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + cipher.init(1, secretKeySpec); + byte[] bytes = data.getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)"); + byte[] encryptedData = cipher.doFinal(bytes); + String encodeToString = Base64.encodeToString(encryptedData, 0); + Intrinsics.checkNotNullExpressionValue(encodeToString, "encodeToString(...)"); + return encodeToString; + } catch (Exception e) { + return "Encryption error: " + e.getMessage(); + } + } + + public final String vulnerableAesEcbIso10126Padding(String data) { + Intrinsics.checkNotNullParameter(data, "data"); + try { + byte[] key = "1234567890123456".getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(key, "this as java.lang.String).getBytes(charset)"); + SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES"); + Cipher cipher = Cipher.getInstance("AES/ECB/ISO10126Padding"); + cipher.init(1, secretKeySpec); + byte[] bytes = data.getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)"); + byte[] encryptedData = cipher.doFinal(bytes); + String encodeToString = Base64.encodeToString(encryptedData, 0); + Intrinsics.checkNotNullExpressionValue(encodeToString, "encodeToString(...)"); + return encodeToString; + } catch (Exception e) { + return "Encryption error: " + e.getMessage(); + } + } + + public final String vulnerableDesEcbPkcs5Padding(String data) { + Intrinsics.checkNotNullParameter(data, "data"); + try { + byte[] bytes = "12345678".getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)"); + DESKeySpec keySpec = new DESKeySpec(bytes); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); + Key generateSecret = keyFactory.generateSecret(keySpec); + Intrinsics.checkNotNullExpressionValue(generateSecret, "generateSecret(...)"); + Key secretKey = generateSecret; + Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); + cipher.init(1, secretKey); + byte[] bytes2 = data.getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes2, "this as java.lang.String).getBytes(charset)"); + byte[] encryptedData = cipher.doFinal(bytes2); + String encodeToString = Base64.encodeToString(encryptedData, 0); + Intrinsics.checkNotNullExpressionValue(encodeToString, "encodeToString(...)"); + return encodeToString; + } catch (Exception e) { + return "Encryption error: " + e.getMessage(); + } + } + + public final String vulnerable3DesEcbPkcs5Padding(String data) { + Intrinsics.checkNotNullParameter(data, "data"); + try { + byte[] bytes = "123456789012345678901234".getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)"); + DESedeKeySpec keySpec = new DESedeKeySpec(bytes); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede"); + Key generateSecret = keyFactory.generateSecret(keySpec); + Intrinsics.checkNotNullExpressionValue(generateSecret, "generateSecret(...)"); + Key secretKey = generateSecret; + Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding"); + cipher.init(1, secretKey); + byte[] bytes2 = data.getBytes(Charsets.UTF_8); + Intrinsics.checkNotNullExpressionValue(bytes2, "this as java.lang.String).getBytes(charset)"); + byte[] encryptedData = cipher.doFinal(bytes2); + String encodeToString = Base64.encodeToString(encryptedData, 0); + Intrinsics.checkNotNullExpressionValue(encodeToString, "encodeToString(...)"); + return encodeToString; + } catch (Exception e) { + return "Encryption error: " + e.getMessage(); + } + } + + public final String mastgTest() { + List results = CollectionsKt.listOf((Object[]) new String[]{"AES Default: " + vulnerableAesEncryption("Hello from OWASP MASTG!"), "AES ECB NoPadding: " + vulnerableAesEcbNoPadding("Hello from OWASP MASTG!"), "AES ECB PKCS5Padding: " + vulnerableAesEcbPkcs5Padding("Hello from OWASP MASTG!"), "AES ECB ISO10126Padding: " + vulnerableAesEcbIso10126Padding("Hello from OWASP MASTG!"), "DES ECB PKCS5Padding: " + vulnerableDesEcbPkcs5Padding("Hello from OWASP MASTG!"), "3DES ECB PKCS5Padding: " + vulnerable3DesEcbPkcs5Padding("Hello from OWASP MASTG!")}); + return CollectionsKt.joinToString$default(results, "\n", null, null, 0, null, null, 62, null); + } +} diff --git a/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/output.txt b/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/output.txt new file mode 100644 index 0000000000..b86db891da --- /dev/null +++ b/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/output.txt @@ -0,0 +1,21 @@ + + +┌─────────────────┐ +│ 6 Code Findings │ +└─────────────────┘ + + MastgTest_reversed.java + ❯❱rules.weak-encryption-modes + [MASVS-CRYPTO-1] Weak encryption modes found in use. + + 36┆ Cipher cipher = Cipher.getInstance("AES"); + ⋮┆---------------------------------------- + 55┆ Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); + ⋮┆---------------------------------------- + 76┆ Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); + ⋮┆---------------------------------------- + 95┆ Cipher cipher = Cipher.getInstance("AES/ECB/ISO10126Padding"); + ⋮┆---------------------------------------- + 118┆ Cipher cipher = Cipher.getInstance("DES/ECB/PKCS5Padding"); + ⋮┆---------------------------------------- + 141┆ Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding"); diff --git a/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/run.sh b/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/run.sh new file mode 100755 index 0000000000..2045982985 --- /dev/null +++ b/demos/android/MASVS-CRYPTO/MASTG_DEMO-0016/run.sh @@ -0,0 +1 @@ +NO_COLOR=true semgrep -c ../../../../rules/mastg-android-weak-encryption-modes.yaml ./MastgTest_reversed.java --text -o output.txt \ No newline at end of file diff --git a/rules/mastg-android-weak-encryption-algorithms.yaml b/rules/mastg-android-weak-encryption-algorithms.yaml new file mode 100644 index 0000000000..56e2a6c57a --- /dev/null +++ b/rules/mastg-android-weak-encryption-algorithms.yaml @@ -0,0 +1,12 @@ +rules: + - id: weak-encryption-algorithms + languages: + - java + severity: WARNING + metadata: + summary: This rule looks for weak encryption algorithms such as DES, 3DES. + message: "[MASVS-CRYPTO-1] Weak encryption algorithms found in use." + pattern-either: + - pattern: Cipher.getInstance("DES") + - pattern: |- + Cipher.getInstance("DESede") diff --git a/rules/mastg-android-weak-encryption-modes.yaml b/rules/mastg-android-weak-encryption-modes.yaml new file mode 100644 index 0000000000..82c8b6b133 --- /dev/null +++ b/rules/mastg-android-weak-encryption-modes.yaml @@ -0,0 +1,16 @@ +rules: + - id: weak-encryption-modes + languages: + - java + severity: WARNING + metadata: + summary: This rule looks for weak encryption modes such as AES-ECB. + message: "[MASVS-CRYPTO-1] Weak encryption modes found in use." + pattern-either: + - pattern: Cipher.getInstance("AES/ECB/NoPadding") + - pattern: Cipher.getInstance("AES") + - pattern: Cipher.getInstance("AES/ECB/PKCS5Padding") + - pattern: Cipher.getInstance("AES/ECB/ISO10126Padding") + - pattern: Cipher.getInstance("DES/ECB/PKCS5Padding") + - pattern: Cipher.getInstance("DESede/ECB/PKCS5Padding") + diff --git a/weaknesses/MASVS-CRYPTO/MASWE-0020.md b/weaknesses/MASVS-CRYPTO/MASWE-0020.md index 07e649feee..007666532f 100644 --- a/weaknesses/MASVS-CRYPTO/MASWE-0020.md +++ b/weaknesses/MASVS-CRYPTO/MASWE-0020.md @@ -11,17 +11,34 @@ mappings: refs: - https://support.google.com/faqs/answer/10046138?hl=en - https://support.google.com/faqs/answer/9450925?hl=en -- https://support.google.com/faqs/answer/9450925?hl=en - https://developer.android.com/privacy-and-security/cryptography#deprecated-functionality - https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf -draft: - description: The use of outdated encryption methods like DES and 3DES may compromise - data confidentiality and integrity. - topics: - - Weak encryption algorithms (e.g. DES, 3DES, etc.) - - Weak encryption modes (e.g. ECB, etc.) - - Cipher.getInstance("AES") defaults to ECB (Android) -status: draft + +status: new --- +## Overview + +Weak encryption refers to cryptographic systems or implementations that are vulnerable to attack, allowing unauthorised individuals to decrypt secured data. This weakness can be due to a number of reasons, including the use of outdated algorithms, deprecated encryption modes such as ECB and improper implementation practices such as the use of a non-random or empty Initialisation Vector (IV). + +## Impact + +- **Loss of Confidentiality**: Weak encryption may enable attackers to decipher and obtain sensitive information, resulting in unauthorized exposure and possible data breaches. + +- **Loss of Integrity**: Weak encryption can compromise the integrity of data, allowing adversaries to alter or manipulate the information without detection. + +## Mode of Introduction + +- **Use of Deprecated Algorithms** : Relying on outdated or weak encryption algorithms can allow threat actors to attack the cipher text, key or exploit known vulnerabilities in the algorithm, for example through brute force attacks. +- **Insecure Modes of Operation**: Using modes that are considered deprecated increase the attack surface of encrypted information. For example the use of AES/ECB is deprecated as it divides the plaintext into blocks and encrypts each block separately using the same key. This makes the cipher text vulnerable to "known plaintext attacks" and leaks information about the structure of the original plaintext. +- **Predictable Initialization Vectors (IVs)**: If IVs are not random or unique, they can be exploited in attacks like ciphertext injection or pattern recognition. This compromises the confidentiality of encrypted data, especially in modes like CBC (Cipher Block Chaining). +- **Weak Keys**: Short or easily guessable keys compromise encryption strength. The use of small key sizes (e.g., 56-bit keys in DES) can make the encryption susceptible to brute-force attacks. Best practices recommend keys of at least 256 bits for strong encryption. +- **Misuse of Non-Cryptographic Operations**: Relying on techniques such as XOR, Base64 encoding, or simple obfuscation methods for security purposes. These methods provide no actual encryption and can be easily reversed or decoded, exposing sensitive data. + +## Mitigations + +- **Use Secure Encryption Modes**: Choose secure modes such as `AES/GCM/NoPadding` and avoid insecure modes such as ECB. +- **Ensure Proper Initialization Vector Management**: Generate IVs using cryptographically secure random number generators and ensure they are unique for every operation. +- **Use Strong Key Sizes**: Enforce key lengths of at least 256 bits for AES and avoid using small or weak keys such as 56-bit DES keys. +- **Rely on Proper Cryptographic Libraries**: Avoid using XOR, Base64 encoding, or obfuscation as substitutes for encryption and rely on well-vetted cryptographic libraries.