-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
PoC frida.re Base Script #3359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+1,189
−0
Merged
PoC frida.re Base Script #3359
Changes from all commits
Commits
Show all changes
49 commits
Select commit
Hold shift + click to select a range
4d1d2b0
added frida.re base script and DEMO-0058 which uses this script
bernhste 246fdf9
clean up after frida.re run, clearer description in DEMO code
bernhste 2acc017
added previously removed output.txt
bernhste 4120adc
Added DEMO-0059
bernhste 1946708
changed demo to be compatible with the MAS Android base app from the …
bernhste 09c89d4
refactor: improve formatting and add missing semicolons in registerHook
cpholguera ca6251a
feat: add maxFrames parameter to registerHook for stack trace control
cpholguera d9b74ae
fix: change output file extension from .txt to .json in run.sh
cpholguera cbd5040
update demo-0058 output to be json
cpholguera 8a78b1c
update demo-0059 output to be json
cpholguera 4703049
fix demo-0059 kotlin file to be the original
cpholguera 4974b99
Merge branch 'OWASP:master' into DEMO-KeyGenParamSpec
bernhste c0785a2
removed reversed code from demos which only use frida.re
bernhste 8818782
removed reference to reversed files
bernhste b0675ea
Merge branch 'OWASP:master' into DEMO-KeyGenParamSpec
bernhste 6308bd2
moved frida.re decoder to /utils
bernhste 4218303
Merge branch 'OWASP:master' into DEMO-KeyGenParamSpec
bernhste a495baf
refactored the frida files for more clarity, and less folders
bernhste 210cbfa
Merge branch 'OWASP:master' into DEMO-KeyGenParamSpec
bernhste 5022d75
JSON evaluation for frida.re script done in evaluat.sh, fixed wrong f…
bernhste de052a0
Update demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/MASTG-DEMO-0058.md
bernhste 762b3ad
Update demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/MASTG-DEMO-0058.md
bernhste 4c29d82
changed the order of the files shown in the steps (hooks.js bevore ru…
bernhste 34fe78b
Update demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/MASTG-DEMO-0058.md
bernhste 8bb145c
Update demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/MASTG-DEMO-0058.md
bernhste 7784bdc
Update demos/android/MASVS-STORAGE/MASTG-DEMO-0059/MASTG-DEMO-0059.md
bernhste b696d05
updated evaluation text in demo 0058/0059 for better clarity.
bernhste 5491380
Update demos/android/MASVS-STORAGE/MASTG-DEMO-0059/MastgTest.kt
bernhste 3dc936d
updated how to use SharedPreferences in secure and insecure ways.
bernhste 436b463
Update demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/hooks.js
bernhste f4c7aae
updated DEMO-0058 with additional use cases of ECB keys on current An…
bernhste b033f52
updated output.json
bernhste 872caa4
updated output.json
bernhste d653de6
Merge branch 'master' into DEMO-KeyGenParamSpec
bernhste 1100fc4
fixed a MD file linting issue
bernhste 044ceda
updated Check Website Build" to also work with PR form forks
bernhste 31f7ea8
revert changes to Check Website Build due to pipeline error
bernhste 03dac59
Merge branch 'master' into DEMO-KeyGenParamSpec
cpholguera f6505ca
Merge branch 'master' into DEMO-KeyGenParamSpec
cpholguera f4a1f54
Merge branch 'master' into DEMO-KeyGenParamSpec
bernhste 8e1c052
Merge branch 'OWASP:master' into DEMO-KeyGenParamSpec
bernhste 8638823
Update demos/android/MASVS-STORAGE/MASTG-DEMO-0059/hooks.js
bernhste c12806b
added the suggested tests for the sandbox analysis to the shared pref…
bernhste 980ee97
fix md lint
bernhste 664f608
Merge branch 'master' into DEMO-KeyGenParamSpec
bernhste 1eb6617
Update utils/frida/android/android_decoder.js
bernhste 0fac32a
Update utils/frida/android/base_script.js
bernhste f14a3ab
Update demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/hooks.js
bernhste 5a0c8e5
Update demos/android/MASVS-STORAGE/MASTG-DEMO-0059/hooks.js
bernhste File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/MASTG-DEMO-0058.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| --- | ||
| platform: android | ||
| title: Use of Insecure ECB Block Mode in KeyGenParameterSpec | ||
| 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[])): | ||
|
|
||
| ```kotlin | ||
| public KeyGenParameterSpec.Builder setBlockModes (String... blockModes) | ||
| ``` | ||
|
|
||
| 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` | ||
|
|
||
| {{ MastgTest.kt }} | ||
|
|
||
| ### Steps | ||
|
|
||
| 1. Install the app on a device (@MASTG-TECH-0005) | ||
| 2. Make sure you have @MASTG-TOOL-0001 installed on your machine and the frida-server running on the device | ||
| 3. Run `run.sh` to spawn the app with Frida | ||
| 4. Click the **Start** button | ||
| 5. Stop the script by pressing `Ctrl+C` and/or `q` to quit the Frida CLI | ||
|
|
||
| {{ 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. | ||
|
|
||
| {{ 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`. | ||
|
|
||
| {{ evaluate.sh }} | ||
|
|
||
| See @MASTG-TEST-0232 for more information. | ||
114 changes: 114 additions & 0 deletions
114
demos/android/MASVS-CRYPTO/MASTG-DEMO-0058/MastgTest.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| package org.owasp.mastestapp | ||
bernhste marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| import android.content.Context | ||
| import android.security.keystore.KeyGenParameterSpec | ||
| import android.security.keystore.KeyProperties | ||
| import android.security.keystore.KeyProtection | ||
| import java.security.KeyStore | ||
| import javax.crypto.Cipher | ||
| import javax.crypto.KeyGenerator | ||
| import javax.crypto.SecretKey | ||
| import android.util.Base64 | ||
| import javax.crypto.spec.SecretKeySpec | ||
|
|
||
| class MastgTest(private val context: Context) { | ||
|
|
||
| fun mastgTest(): String { | ||
|
|
||
| val results = mutableListOf<String>() | ||
| var rawKey: SecretKey? = null | ||
| 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" | ||
| val keyBytes = rawKeyString.chunked(2).map { it.toInt(16).toByte() }.toByteArray() | ||
| rawKey = SecretKeySpec(keyBytes, KeyProperties.KEY_ALGORITHM_AES) | ||
|
|
||
| // The cipher text is 'Hello from OWASP MASTG!' AES/ECB encrypted using CyberChef: | ||
| // https://gchq.github.io/CyberChef/#recipe=AES_Encrypt(%7B'option':'Hex','string':'43ede5660e82123ee091d6b4c8f7d150'%7D,%7B'option':'Hex','string':''%7D,'ECB','Raw','Hex',%7B'option':'Hex','string':''%7D)&input=SGVsbG8gZnJvbSBPV0FTUCBNQVNURyE | ||
| val encryptedDataString = "20b0eef4e5ad3d8984a4fb94f6001885f0ce25104cb8251f600624b46dcefb92" | ||
| encryptedData = encryptedDataString.chunked(2).map { it.toInt(16).toByte() }.toByteArray() | ||
|
|
||
| val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } | ||
| val alias = "importedAesKey" | ||
| val entry = KeyStore.SecretKeyEntry(rawKey) | ||
| val protection = KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) | ||
| .setBlockModes(KeyProperties.BLOCK_MODE_ECB) | ||
| .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) | ||
| .build() | ||
| keyStore.setEntry(alias, entry, protection) | ||
| val importedKey = keyStore.getKey(alias, null) as SecretKey | ||
| val cipher2 = Cipher.getInstance("AES/ECB/PKCS7Padding").apply { | ||
| init(Cipher.DECRYPT_MODE, importedKey) | ||
| } | ||
| decryptedData = cipher2.doFinal(encryptedData) | ||
| val decryptedString = String(decryptedData) | ||
| results.add("\n[*] Keystore-imported AES ECB key decryption (plaintext):\n\n$decryptedString") | ||
| } catch (e: Exception) { | ||
| 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) | ||
| try { | ||
| if (rawKey == null || encryptedData == null) { | ||
| throw IllegalStateException("Key or data missing for encryption") | ||
| } | ||
| val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } | ||
| val alias = "importedAesKey2" | ||
| val entry = KeyStore.SecretKeyEntry(rawKey) | ||
| val protection = KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT) | ||
| .setBlockModes(KeyProperties.BLOCK_MODE_ECB) | ||
| .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) | ||
| .setRandomizedEncryptionRequired(false) // For demonstration purposes, we disable randomized encryption | ||
| .build() | ||
| keyStore.setEntry(alias, entry, protection) | ||
| val importedKey = keyStore.getKey(alias, null) as SecretKey | ||
| val cipher3 = Cipher.getInstance("AES/ECB/PKCS7Padding").apply { | ||
| init(Cipher.ENCRYPT_MODE, importedKey) | ||
| } | ||
| val encryptedBytes = cipher3.doFinal(decryptedData) | ||
| val encrypted = Base64.encodeToString(encryptedBytes, Base64.DEFAULT) | ||
|
|
||
| results.add("\n\n[*] Keystore-imported AES ECB key encryption (ciphertext):\n\n$encrypted") | ||
| } catch (e: Exception) { | ||
| results.add("\n\n[!] Keystore-imported AES ECB key encryption error:\n\n${e.message}") | ||
| } | ||
|
|
||
| // keystore key generation and encryption | ||
| try { | ||
| val keyAlias = "testKeyGenParameter" | ||
| val keyStore = KeyStore.getInstance("AndroidKeyStore").apply { load(null) } | ||
| val spec = KeyGenParameterSpec.Builder( | ||
| keyAlias, | ||
| KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT | ||
| ) | ||
| .setBlockModes(KeyProperties.BLOCK_MODE_ECB) | ||
| .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) | ||
| // .setRandomizedEncryptionRequired(false) // Disabling randomized encryption would allow the key to be used in ECB mode. | ||
| .build() | ||
| KeyGenerator.getInstance( | ||
| KeyProperties.KEY_ALGORITHM_AES, | ||
| "AndroidKeyStore" | ||
| ).apply { | ||
| init(spec) | ||
| generateKey() | ||
| } | ||
|
|
||
| val secretKey = keyStore.getKey(keyAlias, null) as SecretKey | ||
| val cipher = Cipher.getInstance("AES/ECB/PKCS7Padding").apply { | ||
| init(Cipher.ENCRYPT_MODE, secretKey) | ||
| } | ||
| val encrypted = Base64.encodeToString(cipher.doFinal(decryptedData), Base64.DEFAULT) | ||
| results.add("\n[*] Keystore-generated AES ECB key encryption (ciphertext):\n\n$encrypted") | ||
| } catch (e: Exception) { | ||
| results.add("\n[!] Keystore-generated AES ECB error:\n\n${e.message}") | ||
| } | ||
|
|
||
| return results.joinToString("\n") | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| #!/bin/bash | ||
|
|
||
| jq ' | ||
| select( | ||
| .class=="android.security.keystore.KeyGenParameterSpec$Builder" | ||
| and .method=="setBlockModes" | ||
| and (.inputParameters[0].value | contains(["ECB"])) | ||
| ) | ||
| ' output.json |
cpholguera marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| var target = { | ||
| category: "CRYPTO", | ||
| demo: "0058", | ||
| hooks: [ | ||
| { | ||
cpholguera marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| class: "android.security.keystore.KeyGenParameterSpec$Builder", | ||
| methods: [ | ||
| "setBlockModes", | ||
| "setRandomizedEncryptionRequired" | ||
| ] | ||
| }, | ||
| { | ||
| class: "android.security.keystore.KeyProtection$Builder", | ||
| methods: [ | ||
| "setBlockModes", | ||
| "setRandomizedEncryptionRequired" | ||
| ] | ||
| } | ||
| ] | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| { | ||
| "id": "523e8eb7-e155-4792-bdae-6c2a728c87ac", | ||
| "category": "CRYPTO", | ||
| "time": "2025-08-01T09:00:07.277Z", | ||
| "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)" | ||
| ], | ||
| "inputParameters": [ | ||
| { | ||
| "type": "[Ljava.lang.String;", | ||
| "value": [ | ||
| "ECB" | ||
| ] | ||
| } | ||
| ], | ||
| "returnValue": [ | ||
| { | ||
| "type": "android.security.keystore.KeyProtection$Builder", | ||
| "value": "<instance: android.security.keystore.KeyProtection$Builder>" | ||
| } | ||
| ] | ||
| } | ||
| { | ||
| "id": "a162bca9-454e-4d48-a737-0ac6e73983c7", | ||
| "category": "CRYPTO", | ||
| "time": "2025-08-01T09:00:07.288Z", | ||
| "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)" | ||
| ], | ||
| "inputParameters": [ | ||
| { | ||
| "type": "[Ljava.lang.String;", | ||
| "value": [ | ||
| "ECB" | ||
| ] | ||
| } | ||
| ], | ||
| "returnValue": [ | ||
| { | ||
| "type": "android.security.keystore.KeyProtection$Builder", | ||
| "value": "<instance: android.security.keystore.KeyProtection$Builder>" | ||
| } | ||
| ] | ||
| } | ||
| { | ||
| "id": "8dd8050c-dbc0-4662-804a-8bfb2151ca34", | ||
| "category": "CRYPTO", | ||
| "time": "2025-08-01T09:00:07.291Z", | ||
| "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)" | ||
| ], | ||
| "inputParameters": [ | ||
| { | ||
| "type": "boolean", | ||
| "value": false | ||
| } | ||
| ], | ||
| "returnValue": [ | ||
| { | ||
| "type": "android.security.keystore.KeyProtection$Builder", | ||
| "value": "<instance: android.security.keystore.KeyProtection$Builder>" | ||
| } | ||
| ] | ||
| } | ||
| { | ||
| "id": "394de339-b6c6-485e-babc-672ff5df315f", | ||
| "category": "CRYPTO", | ||
| "time": "2025-08-01T09:00:07.300Z", | ||
| "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)" | ||
| ], | ||
| "inputParameters": [ | ||
| { | ||
| "type": "[Ljava.lang.String;", | ||
| "value": [ | ||
| "ECB" | ||
| ] | ||
| } | ||
| ], | ||
| "returnValue": [ | ||
| { | ||
| "type": "android.security.keystore.KeyGenParameterSpec$Builder", | ||
| "value": "<instance: android.security.keystore.KeyGenParameterSpec$Builder>" | ||
| } | ||
| ] | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| #!/bin/bash | ||
| ../../../../utils/frida/android/run.sh ./hooks.js |
70 changes: 70 additions & 0 deletions
70
demos/android/MASVS-STORAGE/MASTG-DEMO-0059/MASTG-DEMO-0059.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| --- | ||
| platform: android | ||
| title: App Writing Sensitive Data to Sandbox using SharedPreferences | ||
| id: MASTG-DEMO-0059 | ||
| code: [kotlin] | ||
| test: MASTG-TEST-0207 | ||
bernhste marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| --- | ||
|
|
||
| ### Sample | ||
|
|
||
| The code snippet below shows sample code which stores sensitive data using `SharedPreferences`. It stores sensitive data using `String` and `StringSet`. | ||
|
|
||
| {{ MastgTest.kt }} | ||
|
|
||
| ### Steps | ||
|
|
||
| 1. Install the app on a device (@MASTG-TECH-0005) | ||
| 2. Make sure you have @MASTG-TOOL-0001 installed on your machine and the frida-server running on the device | ||
| 3. Run `run.sh` to spawn the app with Frida | ||
| 4. Click the **Start** button | ||
| 5. Stop the script by pressing `Ctrl+C` and/or `q` to quit the Frida CLI | ||
|
|
||
| {{ hooks.js # run.sh }} | ||
|
|
||
| ### Observation | ||
|
|
||
| The output shows all instances of strings written via `SharedPreferences` that were found at runtime. A backtrace is also provided to help identify the location in the code. | ||
|
|
||
| {{ output.json }} | ||
|
|
||
| ### Evaluation | ||
|
|
||
| In output.json we can identify several entries that use the `SharedPreferences` API write strings to the app's local sandbox. In this case to `/data/data/org.owasp.mastestapp/shared_prefs/MasSharedPref_Sensitive_Data.xml`: | ||
|
|
||
| - `putString` is used to write an unencrypted `UnencryptedGitHubToken` of value `ghp_1234567890a...` | ||
| - `putString` is used to write an encrypted `EncryptedAwsKey` of value `V1QyXhGV88RQLmMjoTLLl...` | ||
| - `putStringSet` is used to write an unencrypted `UnencryptedPreSharedKeys` set with values `MIIEvAIBADAN...` and `gJXS9EwpuzK8...` | ||
|
|
||
| We can use the values and try to trace them back to crypto method calls and check if they are encrypted. For example, let's analyze the `EncryptedAwsKey` of value `V1QyXhGV88RQLmMjoTLLl...`: | ||
|
|
||
| - `V1QyXhGV88RQLmMjoTLLl...` is the return value of `Base64.encodeToString` for the input `0x5754325e1195f3c45...` | ||
| - `0xa132cb95022985be` is the return value of `Cipher.doFinal` for the input `AKIAIOSFODNN7EXAMPLE` | ||
|
|
||
| However, we cannot find any calls to `Base64.encodeToString` or `Cipher.***` for the `preSharedKeys` values written by `putStringSet` (`MIIEvAIBADAN...` and `gJXS9EwpuzK8...`). | ||
|
|
||
| You can confirm this by reverse engineering the app and inspecting the code. Inspect the `stackTrace` of the `putString` and `putStringSet` entries, then go to the corresponding locations in the code. For example, go to the `org.owasp.mastestapp.MastgTest.mastgTest` method and try to trace back the input parameters to determine whether they are encrypted. | ||
|
|
||
| The test **fails** due because we found some entries that aren't encrypted. | ||
|
|
||
| Any data in the app sandbox can be extracted using backups or root access on a compromised phone. For example, run the following command: | ||
|
|
||
| ```sh | ||
| adb shell cat /data/data/org.owasp.mastestapp/shared_prefs/MasSharedPref_Sensitive_Data.xml | ||
| ``` | ||
|
|
||
| Which returns: | ||
|
|
||
| ```xml | ||
| <?xml version='1.0' encoding='utf-8' standalone='yes' ?> | ||
| <map> | ||
| <string name="EncryptedAwsKey">V1QyXhGV88RQLmMjoTLLlQIphb6SKf4CBqx+PqhH/TTPFtCh9RPTAYezWW5RPhPP </string> | ||
| <set name="UnencryptedPreSharedKeys"> | ||
| <string>gJXS9EwpuzK8U1TOgfplwfKEVngCE2D5FNBQWvNmuHHbigmTCabsA=</string> | ||
| <string>MIIEvAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBALfX7kbfFv3pc3JjOHQ=</string> | ||
| </set> | ||
| <string name="UnencryptedGitHubToken">ghp_1234567890abcdefghijklmnOPQRSTUV</string> | ||
| </map> | ||
| ``` | ||
|
|
||
| All entries that aren't encrypted can be leveraged by an attacker. | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.