Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
0cdcd45
Deprecate old test
Diolor Sep 24, 2025
536aacb
Add mitmproxy in TECH-0108
Diolor Sep 26, 2025
525a75e
Create the v2 TEST
Diolor Sep 26, 2025
ea8e1ed
Create a demo based on the test
Diolor Sep 26, 2025
31d6209
Fix lint
Diolor Sep 26, 2025
52d0619
Fix lint
Diolor Sep 26, 2025
99e15f8
Fix lint
Diolor Sep 26, 2025
a12122a
Fix lint
Diolor Sep 26, 2025
4618723
Remove prerequisite
Diolor Sep 29, 2025
13118f7
Rewrite test to include frida
Diolor Oct 1, 2025
ff3bf25
Move to Privacy
Diolor Oct 2, 2025
a45c42f
Move Firebase Analytics dependency
Diolor Oct 6, 2025
ccb8c1c
Move Firebase Analytics dependency
Diolor Oct 6, 2025
2037585
Update title to clarify sensitive information exposure in Firebase An…
Diolor Oct 6, 2025
7e4ecf2
Allocate IDs
Diolor Oct 20, 2025
9078204
Merge remote-tracking branch 'origin/master' into port-MASTG-TEST-0004
Diolor Oct 20, 2025
fab5dea
Allocate IDs
Diolor Oct 20, 2025
4351d41
Update weakness reference in MASTG-TEST-0297.md
Diolor Nov 17, 2025
625d6b3
Merge branch 'master' into port-MASTG-TEST-0004
cpholguera Dec 3, 2025
dc4e504
Merge branch 'master' into port-MASTG-TEST-0004
cpholguera Dec 3, 2025
2e7adb8
Split test into 4 smaller tests.
Diolor Dec 4, 2025
1d02dae
Fix id
Diolor Dec 4, 2025
4dea6df
Add demo for dynamic evaluation of sensitive data in runtime
Diolor Dec 4, 2025
0bc53db
Update demos/android/MASVS-PRIVACY/MASTG-DEMO-00de3/hooks.js
Diolor Dec 5, 2025
1183269
Apply suggestions from code review
Diolor Dec 5, 2025
64fcf89
Update references in MASTG-TEST-02te4.md and MASTG-TEST-0004.md
Diolor Dec 5, 2025
af21b06
Apply suggestions from code review
Diolor Dec 9, 2025
f91db2e
add UI automation
cpholguera Dec 9, 2025
5aae326
Merge branch 'master' into port-MASTG-TEST-0004
Diolor Dec 16, 2025
b088cdd
Remove MASTG-DEMO-00de1
Diolor Dec 17, 2025
b3f7659
Refine documentation for SDK method observations and data handling tests
Diolor Dec 17, 2025
2c5e4bd
Moved to #3595
Diolor Dec 17, 2025
57a91ab
correct output
cpholguera Dec 18, 2025
f638528
Last set of minor tweaks including title updates, links to data safet…
cpholguera Dec 18, 2025
3d7ee2e
fix IDs
cpholguera Dec 18, 2025
8b5aca8
Merge branch 'master' into port-MASTG-TEST-0004
cpholguera Dec 18, 2025
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
39 changes: 39 additions & 0 deletions demos/android/MASVS-PRIVACY/MASTG-DEMO-0081/MASTG-DEMO-0081.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
platform: android
title: Sensitive User Data Sent to Firebase Analytics with Frida
id: MASTG-DEMO-0081
code: [kotlin]
test: MASTG-TEST-0319
---

## Sample

This sample collects the following [sensitive user data](https://support.google.com/googleplay/android-developer/answer/10787469?hl=en#types&zippy=%2Cdata-types) and sends it to Firebase Analytics using the `logEvent` method:

- User ID (**Data type:** User IDs, **Category:** Personal info)
- Blood type (**Data type:** Health info, **Category:** Health and fitness)

For the sake of this demo, we pretend that the app is published on Google Play and that the data types collected are not disclosed in the [Data safety section](https://support.google.com/googleplay/android-developer/answer/10787469?hl=en#types&zippy=%2Cdata-types).

{{ MainActivity.kt # MastgTest.kt # build.gradle.kts.libs }}

## 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. Select a blood type from the dropdown
Comment thread
cpholguera marked this conversation as resolved.
5. Click the **Start** button
6. 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 `logEvent` calls to the Firebase Analytics SDK found at runtime, along with the parameters sent. A backtrace is also provided to help identify the location in the code.

{{ output.json }}

## Evaluation

This test **fails** because sensitive data (`blood_type` parameter) is being sent to Firebase Analytics via the `logEvent` method for a particular user (`user_id` parameter) and this data collection is not disclosed in the Data safety section on Google Play (as we indicated in the sample description).
158 changes: 158 additions & 0 deletions demos/android/MASVS-PRIVACY/MASTG-DEMO-0081/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package org.owasp.mastestapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.decodeFromJsonElement

const val MASTG_TEXT_TAG = "mastgTestText"

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
MainScreen()
}
}
}

fun UpdateDisplayString(
defaultMessage: String,
result: String
): AnnotatedString {
return buildAnnotatedString {
append(defaultMessage)
try {
val jsonArrayFromString = Json.parseToJsonElement(result) as JsonArray
val demoResults = jsonArrayFromString.map { Json.decodeFromJsonElement<DemoResult>(it) }

for (demoResult in demoResults) {
when (demoResult.status) {
Status.PASS -> {
withStyle(style = SpanStyle(color = Color.Green)) {
append("MASTG-DEMO-${demoResult.demoId} demonstrated a successful test:\n${demoResult.message}\n\n")
}
}

Status.FAIL -> {
withStyle(style = SpanStyle(color = Color(0xFFFF9800))) {
append("MASTG-DEMO-${demoResult.demoId} demonstrated a failed test:\n${demoResult.message}\n\n")
}
}

Status.ERROR -> {
withStyle(style = SpanStyle(color = Color.Red)) {
append("MASTG-DEMO-${demoResult.demoId} failed:\n${demoResult.message}\n\n")
}
}
}
}
} catch (_: Exception) {
// not a valid set of DemoResult, so print the result without any parsing
append(result)
}
}

}

@Preview
@Composable
fun MainScreen() {
val defaultMessage = "Click \"Start\" to send the data.\n\n"
var displayString by remember { mutableStateOf(buildAnnotatedString { append(defaultMessage) }) }
var selectedBloodType by remember { mutableStateOf("") }
val context = LocalContext.current
val mastgTestClass = MastgTest(context)
// By default run the test in a separate thread, this ensures that network tests such as those using SSLSocket work properly.
// However, some tests which interact with UI elements need to run on the main thread.
// You can set shouldRunInMainThread = true in MastgTest.kt for those tests.
val runInMainThread = MastgTest::class.members
.find { it.name == "shouldRunInMainThread" }
?.call(mastgTestClass) as? Boolean ?: false

BaseScreen(
onStartClick = {
if (runInMainThread) {
val result = mastgTestClass.mastgTest(selectedBloodType)
displayString = UpdateDisplayString(defaultMessage, result)
} else {
Thread {
val result = mastgTestClass.mastgTest(selectedBloodType)
android.os.Handler(android.os.Looper.getMainLooper()).post {
displayString = UpdateDisplayString(defaultMessage, result)
}
}.start()
}
}
) {
Column(modifier = Modifier.padding(16.dp)) {
// Normal visible selection UI: list of radio buttons for blood types
val bloodTypes = listOf("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-")
Comment thread
cpholguera marked this conversation as resolved.

var expanded by remember { mutableStateOf(false) }

Button(onClick = { expanded = !expanded }) {
Text("Select Blood Type")
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
bloodTypes.forEach {
DropdownMenuItem(
text = { Text(it) },
onClick = {
selectedBloodType = it
expanded = false
}
)
}
}

if (selectedBloodType.isNotEmpty()) {
Text(
modifier = Modifier.padding(vertical = 16.dp),
color = Color.White,
text = "Selected Blood Type: $selectedBloodType"
)
}

Text(
modifier = Modifier
.padding(top = 8.dp)
.testTag(MASTG_TEXT_TAG),
text = displayString,
color = Color.White,
fontSize = 16.sp,
fontFamily = FontFamily.Monospace
)
}
}
}
28 changes: 28 additions & 0 deletions demos/android/MASVS-PRIVACY/MASTG-DEMO-0081/MastgTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.owasp.mastestapp

import android.content.Context
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.logEvent
import kotlin.random.Random

class MastgTest(context: Context) {

val analytics = FirebaseAnalytics.getInstance(context)

// Random arbitrary number for the sake of the demo
val userId: String = (1..8).map { Random.nextInt(0, 10) }.joinToString("")

fun mastgTest(bloodType: String): String {
analytics.logEvent("user_blood_type") {
param("user_id", userId)
param("blood_type", bloodType)
}

return """
'user_blood_type' event was sent to Firebase Analytics.

User id: $userId
Blood type: $bloodType
""".trimIndent()
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions demos/android/MASVS-PRIVACY/MASTG-DEMO-0081/auto.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Running on Pixel_4_API_34_Manually_Created
> Flow flow
Take screenshot before... COMPLETED
Tap on "Select Blood Type"... COMPLETED
Tap on "A+"... COMPLETED
Tap on "Start"... COMPLETED
Take screenshot after... COMPLETED
18 changes: 18 additions & 0 deletions demos/android/MASVS-PRIVACY/MASTG-DEMO-0081/auto.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail

FLOW="flow.yaml"

# Start Frida and redirect stdout and stderr to file
./run.sh 2>&1 &

FRIDA_PID=$!

# Run Maestro (https://docs.maestro.dev/getting-started/installing-maestro)
maestro test "$FLOW" > auto.log 2>&1
MAESTRO_EXIT=$?

# Stop Frida when Maestro completes
kill "$FRIDA_PID" 2>/dev/null || true

exit "$MAESTRO_EXIT"
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
implementation("com.google.firebase:firebase-analytics:23.0.0")
8 changes: 8 additions & 0 deletions demos/android/MASVS-PRIVACY/MASTG-DEMO-0081/flow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
appId: org.owasp.mastestapp
---
#- launchApp
- takeScreenshot: before
- tapOn: "Select Blood Type"
- tapOn: "A+"
- tapOn: "Start"
- takeScreenshot: after
11 changes: 11 additions & 0 deletions demos/android/MASVS-PRIVACY/MASTG-DEMO-0081/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
var target = {
category: "PRIVACY",
hooks: [
{
class: "com.google.firebase.analytics.FirebaseAnalytics",
methods: [
"logEvent"
]
}
]
}
56 changes: 56 additions & 0 deletions demos/android/MASVS-PRIVACY/MASTG-DEMO-0081/output.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"type": "summary",
"hooks": [
{
"class": "com.google.firebase.analytics.FirebaseAnalytics",
"method": "logEvent",
"overloads": [
{
"args": [
"java.lang.String",
"android.os.Bundle"
]
}
]
}
],
"totalHooks": 1,
"errors": [],
"totalErrors": 0
}
{
"id": "68f13e77-c2aa-4778-be90-fc7a5c50af60",
"type": "hook",
"category": "PRIVACY",
"time": "2025-12-04T14:03:05.046Z",
"class": "com.google.firebase.analytics.FirebaseAnalytics",
"method": "logEvent",
"instanceId": 33276343,
"stackTrace": [
"com.google.firebase.analytics.FirebaseAnalytics.logEvent(Native Method)",
"org.owasp.mastestapp.MastgTest.mastgTest(MastgTest.kt:35)",
"org.owasp.mastestapp.MainActivityKt.MainScreen$lambda$15$lambda$14(MainActivity.kt:107)",
"org.owasp.mastestapp.MainActivityKt.$r8$lambda$REihytfpbfdEl1cb3dLDlSFXk5M(Unknown Source:0)",
"org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda2.run(D8$$SyntheticClass:0)",
"java.lang.Thread.run(Thread.java:1119)"
],
"inputParameters": [
{
"declaredType": "java.lang.String",
"value": "user_blood_type"
},
{
"declaredType": "android.os.Bundle",
"value": "<instance: android.os.Bundle>",
"runtimeType": "android.os.Bundle",
"instanceId": "130116900",
"instanceToString": "Bundle[{blood_type=A+, user_id=87495975}]"
}
],
"returnValue": [
{
"declaredType": "void",
"value": "void"
}
]
}
2 changes: 2 additions & 0 deletions demos/android/MASVS-PRIVACY/MASTG-DEMO-0081/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
../../../../utils/frida/android/run.sh ./hooks.js
6 changes: 3 additions & 3 deletions techniques/android/MASTG-TECH-0108.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ In taint analysis, data flows from a "source" to a "sink". A source is where sen

In large applications, manual information flow analysis can be very time-consuming and inaccurate. Taint analysis automates this, with two main methods: static and dynamic. The former examines code without running it, offering broad coverage but potentially yielding false positives. In contrast, dynamic analysis observes real-time application execution, providing actual context but possibly overlooking untriggered issues. A thorough comparison of these techniques is beyond this section's scope.

There are multiple tools which perform taint analysis on native code, including [Triton](https://github.com/jonathansalwan/Triton "Triton") and [bincat](https://github.com/airbus-seclab/bincat "bincat"). However, in this section, we'll primarily focus on Android Java code and utilize @MASTG-TOOL-0099 for the taint analysis. Another notable tool supporting taint analysis for Android apps is [GDA](https://github.com/charles2gan/GDA-android-reversing-Tool/wiki/GDA-Static-Taint-Analysis "GDA").
There are multiple tools which perform taint analysis on native code, including [Triton](https://github.com/jonathansalwan/Triton "Triton"), [bincat](https://github.com/airbus-seclab/bincat "bincat"), and @MASTG-TOOL-0110. However, in this section, we'll primarily focus on Android Java code and utilize @MASTG-TOOL-0099 for the taint analysis. Another notable tool supporting taint analysis for Android apps is [GDA](https://github.com/charles2gan/GDA-android-reversing-Tool/wiki/GDA-Static-Taint-Analysis "GDA").

For our demonstration, we'll use the @MASTG-TOOL-0099 command line tool to perform taint analysis on the [InsecureShop v1.0](https://github.com/hax0rgb/InsecureShop/releases/tag/v1.0 "InsecureShop") application.

The InsecureShop app accepts a username and password as input and stores them in the app's shared preferences. In our taint analysis, we're interested in how this stored username and password are used. In this context, the username and password are the sensitive information, and reading from shared preferences is the source. The sink in this analysis could be various operations, such as sending info over the network, transmitting info via an `Intent`, or storing info in an external file.

To use FlowDroid, firstly, we need to provide an input list of potential sources and sinks to evaluate for. In our case, _reading from shared preferences_ will be the source, while _adding parameters to an `Intent`_ will be the sink. The configuration file will look as follows (we'll name it "source_sink.txt"):
To use FlowDroid, firstly, we need to provide an input list of potential sources and sinks to evaluate. In our case, _reading from shared preferences_ will be the source, while _adding parameters to an `Intent`_ will be the sink. The configuration file will look as follows (we'll name it "source_sink.txt"):

```Jimple
<android.content.SharedPreferences: java.lang.String getString(java.lang.String, java.lang.String)> -> _SOURCE_
Expand Down Expand Up @@ -68,4 +68,4 @@ fun onSendData(view: View) {
}
```

Taint analysis is especially beneficial for automating data flow analysis in intricate applications. However, given the complexity of some apps, the accuracy of such tools can vary. Thus, it's essential for reviewers to find a balance between the accuracy of tools and the time spent on manual analysis.
Taint analysis is especially beneficial for automating data flow analysis in intricate applications. However, given the complexity of some apps, the accuracy of such tools can vary. Thus, reviewers need to find a balance between the accuracy of tools and the time spent on manual analysis.
2 changes: 1 addition & 1 deletion tests-beta/android/MASVS-PRIVACY/MASTG-TEST-0206.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ The output should contain a network traffic log that includes the decrypted HTTP

The test case fails if you can find the PII you entered in the app that is not declared in the app's marketplace privacy declarations (e.g., Data Safety section in Google Play) and/or in its privacy policy.

Note that this test does not provide any code locations where the sensitive data is being sent over the network. In order to identify the code locations, you can use static analysis tools like @MASTG-TOOL-0110 or dynamic analysis tools like @MASTG-TOOL-0031.
Note that this test does not provide any code locations where the sensitive data is being sent over the network. In order to identify the code locations, you can use static analysis tools like @MASTG-TOOL-0110 or dynamic analysis tools like @MASTG-TOOL-0031. Consult @MASTG-TEST-0318 and @MASTG-TEST-0319, respectively, for more details.
29 changes: 29 additions & 0 deletions tests-beta/android/MASVS-PRIVACY/MASTG-TEST-0318.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
platform: android
title: References to SDK APIs Known to Handle Sensitive User Data
id: MASTG-TEST-0318
type: [static]
weakness: MASWE-0112
profiles: [P]
---

## Overview

This test verifies whether an app uses SDK (third-party library) APIs known to handle sensitive user data (e.g., as defined in [Google Play's Data safety section](https://support.google.com/googleplay/android-developer/answer/10787469?hl=en#types&zippy=%2Cdata-types) or the relevant privacy regulations).

As a prerequisite, we need to identify the SDK API methods it uses as entry points for data collection by reviewing the library's documentation or codebase. For example, [Google Analytics for Firebase](https://firebase.google.com/docs/analytics) in its class `FirebaseAnalytics` provides methods such as [`setUserId`](https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics#setUserId(java.lang.String)), [`setUserProperty`](https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics#setUserProperty(java.lang.String,%20java.lang.String)), and [`logEvent`](https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics#logEvent(java.lang.String,%20android.os.Bundle)) that can be used to collect user data.

> Note: This test detects only **potential** sensitive user data handling. For **confirming** that actual user data are being shared, please refer to @MASTG-TEST-0319.

## Steps

1. Use @MASTG-TECH-0013 to reverse engineer the app.
2. Use @MASTG-TECH-0014 to look for uses of these methods where sensitive user data may be passed to the SDK.

## Observation

The output should list the locations where SDK methods are called.

## Evaluation

The test case fails if you can find the use of these SDK methods in the app code, indicating that the app is sharing sensitive user data with the third-party SDK. If no such references are found, the test case passes.
Loading
Loading