Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 25 additions & 17 deletions demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/MASTG-DEMO-0058.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
---
platform: android
title: Use of Insecure ECB Block Mode in KeyGenParameterSpec
title: Using KeyGenParameterSpec with a Broken ECB Block Mode
id: MASTG-DEMO-0058
code: [kotlin]
test: MASTG-TEST-0232
---

### Sample

The code below generates symmetric encryption keys meant to be stored in the Android KeyStore, but it does so using the ECB block mode, which is considered broken due to practical known-plaintext attacks and is disallowed by NIST for data encryption. The method used to set the block modes is [`KeyGenParameterSpec.Builder#setBlockModes(...)`](https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setBlockModes(java.lang.String[])):
This code demonstrates the risks of using AES in ECB mode (which is a broken mode of operation) using three scenarios:

```kotlin
public KeyGenParameterSpec.Builder setBlockModes (String... blockModes)
```
1. Importing a raw AES key into AndroidKeyStore with the purpose "decrypt" and mode "ECB"
2. Importing a raw AES key into AndroidKeyStore with the purpose "encrypt" and mode "ECB"
3. Generating an AES key in AndroidKeyStore with the purpose "encrypt" or "decrypt" and mode "ECB"

Current versions of Android prohibit the usage of keys with for ECB in some cases. For example, it is not possible to use the key to encrypt data by the default. Nevertheless, there are some case, where ECB can still be used:

- Decrypt data
- Encrypt data with a key given `setRandomizedEncryptionRequired` is set to `false`
Current versions of Android prohibit the use of keys with ECB in some cases. For example, it is possible to use such a key for decryption but not to encrypt data by default, unless randomized encryption is explicitly disabled (bad practice).

{{ MastgTest.kt }}

When executing the code, you will see the following results for each of the three scenarios:

1. Decryption succeeds because that's always allowed.
2. Encryption succeeds. The import succeeds in this case because we explicitly disable randomized encryption (bad practice). Otherwise, `KeyStore.setEntry` would fail with an error similar to the one for scenario 3.
3. Encryption cannot even happen because the generation fails (`KeyGenerator.init` specifically) due to randomized encryption not being disabled. The error says `"Randomized encryption (IND-CPA) required but may be violated by block mode: ECB. See android.security.keystore.KeyGenParameterSpec documentation"`.

### Steps

1. Install the app on a device (@MASTG-TECH-0005)
Expand All @@ -29,22 +32,27 @@ Current versions of Android prohibit the usage of keys with for ECB in some case
4. Click the **Start** button
5. Stop the script by pressing `Ctrl+C` and/or `q` to quit the Frida CLI

These are the relevant methods we are hooking to detect the use of ECB and whether randomized encryption is disabled:

- Setting block modes:
- [`KeyGenParameterSpec.Builder#setBlockModes(...)`](https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setBlockModes(java.lang.String[]))
- [`KeyProtection.Builder#setBlockModes(...)`](https://developer.android.com/reference/android/security/keystore/KeyProtection.Builder#setBlockModes(java.lang.String[])).
- Enabling/disabling randomized encryption:
- [`KeyGenParameterSpec.Builder#setRandomizedEncryptionRequired`](https://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.Builder#setRandomizedEncryptionRequired(boolean))
- [`KeyProtection.Builder#setRandomizedEncryptionRequired`](https://developer.android.com/reference/android/security/keystore/KeyProtection.Builder#setRandomizedEncryptionRequired(boolean))

{{ hooks.js # run.sh }}

### Observation

The output shows all instances of block modes mode that were found at runtime. A backtrace is also provided to help identify the location in the code.
The output shows all instances of block modes that were found at runtime. A backtrace is also provided to help identify the location in the code. If randomized encryption is disabled, that is also indicated in the output.

{{ output.json }}

### Evaluation

The method `setBlockModes` has now been called three times with ECB as one of the block modes.

The test fails, as key used with these `KeyGenParameterSpec` can now be used used to insecurely encrypt data.

You can automatically evaluate the output using tools like `jq` as demonstrated in `evaluation.sh`.
The test fails because the `KeyGenParameterSpec.Builder#setBlockModes(...)` and `KeyProtection.Builder#setBlockModes(...)` methods have been called with ECB.

{{ evaluate.sh }}
{{ evaluation.txt # evaluate.sh }}

See @MASTG-TEST-0232 for more information.
Regardless of whether the encryption succeeds or not, ECB should never be used in security-sensitive apps. Also, being present in the app may indicate issues in other parts of the app ecosystem (e.g., backend services).
9 changes: 4 additions & 5 deletions demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/MastgTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ class MastgTest(private val context: Context) {
var encryptedData: ByteArray? = null
var decryptedData: ByteArray? = null

// Suppose we received a raw key from a secure source and we want to use it for decryption.
// The following commented-out code is an example of generating a raw key and encrypting data with it.
// We obtained the raw key and encrypted data from the logs and added them to the code for demonstration purposes.
try {
// Suppose we received the raw key from a secure source and we want to use it for decryption.
val rawKeyString = "43ede5660e82123ee091d6b4c8f7d150"
Expand Down Expand Up @@ -53,7 +50,8 @@ class MastgTest(private val context: Context) {
results.add("\n[!] Keystore-imported AES ECB key decryption error:\n\n${e.message}")
}

// import the raw key into AndroidKeyStore for encryption which would fail unless randomized encryption is disabled (bad practice)
// Import the raw key into AndroidKeyStore with the purpose "encrypt" and mode "ECB"
// The import succeeds in this case because we explicitly disable randomized encryption (bad practice)
try {
if (rawKey == null || encryptedData == null) {
throw IllegalStateException("Key or data missing for encryption")
Expand All @@ -79,7 +77,8 @@ class MastgTest(private val context: Context) {
results.add("\n\n[!] Keystore-imported AES ECB key encryption error:\n\n${e.message}")
}

// keystore key generation and encryption
// Generate a raw key in the AndroidKeyStore with the purpose "encrypt" or "decrypt" and and mode "ECB"
// The generation fails in this case because we don't disable randomized encryption
try {
val keyAlias = "testKeyGenParameter"
val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) }
Expand Down
16 changes: 9 additions & 7 deletions demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/evaluate.sh
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/bin/bash

jq '
select(
.class=="android.security.keystore.KeyGenParameterSpec$Builder"
and .method=="setBlockModes"
and (.inputParameters[0].value | contains(["ECB"]))
)
' output.json
jq -r -s '
flatten
| .[]
| select(
.method=="setBlockModes"
and any(.inputParameters[]?.value[]?; . == "ECB")
)
| "Class: \(.class), Method: \(.method), Block modes: \([.inputParameters[]?.value[]?] | join(", "))"
' output.json > evaluation.txt
3 changes: 3 additions & 0 deletions demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/evaluation.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Class: android.security.keystore.KeyProtection$Builder, Method: setBlockModes, Block modes: ECB
Class: android.security.keystore.KeyProtection$Builder, Method: setBlockModes, Block modes: ECB
Class: android.security.keystore.KeyGenParameterSpec$Builder, Method: setBlockModes, Block modes: ECB
56 changes: 24 additions & 32 deletions demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/output.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
{
"id": "523e8eb7-e155-4792-bdae-6c2a728c87ac",
"id": "a0a0815c-fa2c-43a5-ad35-10c2a3d097cd",
"category": "CRYPTO",
"time": "2025-08-01T09:00:07.277Z",
"time": "2025-09-12T16:45:55.639Z",
"class": "android.security.keystore.KeyProtection$Builder",
"method": "setBlockModes",
"stackTrace": [
"android.security.keystore.KeyProtection$Builder.setBlockModes(Native Method)",
"org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:41)",
"org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$7(MainActivity.kt:53)",
"org.owasp.mastestapp.MainActivityKt.$r8$lambda$JVJO2MsmWvFAgk27L17N1ocLpI0(Unknown Source:0)",
"org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda0.invoke(D8$$SyntheticClass:0)",
"androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke-k-4lQ0M(Clickable.kt:639)",
"androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke(Clickable.kt:633)",
"androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend(TapGestureDetector.kt:255)"
"org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$6$lambda$5(MainActivity.kt:55)",
"org.owasp.mastestapp.MainActivityKt.$r8$lambda$PtKdgqcXvbS9cMNZVWq3K3GGQKQ(Unknown Source:0)",
"org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)",
"java.lang.Thread.run(Thread.java:1012)"
],
"inputParameters": [
{
Expand All @@ -30,20 +28,18 @@
]
}
{
"id": "a162bca9-454e-4d48-a737-0ac6e73983c7",
"id": "1ade0c02-74c8-4629-bbd4-5f51c9616b68",
"category": "CRYPTO",
"time": "2025-08-01T09:00:07.288Z",
"time": "2025-09-12T16:45:55.671Z",
"class": "android.security.keystore.KeyProtection$Builder",
"method": "setBlockModes",
"stackTrace": [
"android.security.keystore.KeyProtection$Builder.setBlockModes(Native Method)",
"org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:65)",
"org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$7(MainActivity.kt:53)",
"org.owasp.mastestapp.MainActivityKt.$r8$lambda$JVJO2MsmWvFAgk27L17N1ocLpI0(Unknown Source:0)",
"org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda0.invoke(D8$$SyntheticClass:0)",
"androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke-k-4lQ0M(Clickable.kt:639)",
"androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke(Clickable.kt:633)",
"androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend(TapGestureDetector.kt:255)"
"org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$6$lambda$5(MainActivity.kt:55)",
"org.owasp.mastestapp.MainActivityKt.$r8$lambda$PtKdgqcXvbS9cMNZVWq3K3GGQKQ(Unknown Source:0)",
"org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)",
"java.lang.Thread.run(Thread.java:1012)"
],
"inputParameters": [
{
Expand All @@ -61,20 +57,18 @@
]
}
{
"id": "8dd8050c-dbc0-4662-804a-8bfb2151ca34",
"id": "48a8aa46-4ada-470e-bb5e-47ceefb53e1b",
"category": "CRYPTO",
"time": "2025-08-01T09:00:07.291Z",
"time": "2025-09-12T16:45:55.673Z",
"class": "android.security.keystore.KeyProtection$Builder",
"method": "setRandomizedEncryptionRequired",
"stackTrace": [
"android.security.keystore.KeyProtection$Builder.setRandomizedEncryptionRequired(Native Method)",
"org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:67)",
"org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$7(MainActivity.kt:53)",
"org.owasp.mastestapp.MainActivityKt.$r8$lambda$JVJO2MsmWvFAgk27L17N1ocLpI0(Unknown Source:0)",
"org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda0.invoke(D8$$SyntheticClass:0)",
"androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke-k-4lQ0M(Clickable.kt:639)",
"androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke(Clickable.kt:633)",
"androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend(TapGestureDetector.kt:255)"
"org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$6$lambda$5(MainActivity.kt:55)",
"org.owasp.mastestapp.MainActivityKt.$r8$lambda$PtKdgqcXvbS9cMNZVWq3K3GGQKQ(Unknown Source:0)",
"org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)",
"java.lang.Thread.run(Thread.java:1012)"
],
"inputParameters": [
{
Expand All @@ -90,20 +84,18 @@
]
}
{
"id": "394de339-b6c6-485e-babc-672ff5df315f",
"id": "fe6e69b6-2764-473e-80d2-5a242b56947e",
"category": "CRYPTO",
"time": "2025-08-01T09:00:07.300Z",
"time": "2025-09-12T16:45:55.683Z",
"class": "android.security.keystore.KeyGenParameterSpec$Builder",
"method": "setBlockModes",
"stackTrace": [
"android.security.keystore.KeyGenParameterSpec$Builder.setBlockModes(Native Method)",
"org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:90)",
"org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$7(MainActivity.kt:53)",
"org.owasp.mastestapp.MainActivityKt.$r8$lambda$JVJO2MsmWvFAgk27L17N1ocLpI0(Unknown Source:0)",
"org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda0.invoke(D8$$SyntheticClass:0)",
"androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke-k-4lQ0M(Clickable.kt:639)",
"androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke(Clickable.kt:633)",
"androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend(TapGestureDetector.kt:255)"
"org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$6$lambda$5(MainActivity.kt:55)",
"org.owasp.mastestapp.MainActivityKt.$r8$lambda$PtKdgqcXvbS9cMNZVWq3K3GGQKQ(Unknown Source:0)",
"org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3.run(D8$$SyntheticClass:0)",
"java.lang.Thread.run(Thread.java:1012)"
],
"inputParameters": [
{
Expand Down
Loading