VNX-360: Android receiver for a system broadcast trusts the intent without checking its action
Reports BroadcastReceiver.onReceive methods (Java, Kotlin) that act on a system broadcast such as BOOT_COMPLETED or SMS_RECEIVED without checking intent.getAction(), where the receiver is reachable by other apps.
| Field | Value |
|---|---|
| Rule ID | VNX-360 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | Android, Java, Kotlin |
| CWE | CWE-360 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | trust-system-event android broadcast-receiver cwe-360 |
| Source | internal/sast/rules/vnx-360.rego |
Overview
VNX-360 maps to CWE-360: Trust of System Event Data. Only the system can broadcast BOOT_COMPLETED, SMS_RECEIVED or POWER_CONNECTED, but any installed app can deliver an intent straight to an exported receiver by naming its component. A receiver that acts without checking the intent’s action therefore treats a forged “system event”, and its extras, as genuine.
The rule reports, in Java and Kotlin, an onReceive(Context, Intent) method whose body (up to 40 lines) never reads the intent’s action (getAction(), intent.action, intent?.action), when the receiver is:
- registered in the same file with an
IntentFilter(...)/addAction(...)naming a system action (Intent.ACTION_*,"android.intent.action.*","android.provider.Telephony.*",Telephony.Sms.Intents.*,*_ACTIONconstants), or - declared in an
AndroidManifest.xml<receiver>whose intent filter names anandroid.*system action and which is neitherandroid:exported="false"nor protected byandroid:permission.
Not reported: receivers that check the action, receivers for the app’s own custom actions only (VNX-ANDROID-003 judges exported components), receivers registered with RECEIVER_NOT_EXPORTED, non-exported or permission-protected manifest receivers, and test files.
What Gets Flagged
<receiver android:name=".sync.BootReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
// FLAGGED: any app can send this receiver an explicit intent with any extras
public void onReceive(Context context, Intent intent) {
String account = intent.getStringExtra("account");
SyncService.wipeAndResync(context, account);
}
// NOT FLAGGED
public void onReceive(Context context, Intent intent) {
if (!Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
return;
}
SyncService.schedule(context);
}
Remediation
- Check
intent.getAction()against the expected system action before doing anything. - Do not trust extras on a system-event intent; system broadcasts such as
BOOT_COMPLETEDcarry none. - Register dynamic receivers with
RECEIVER_NOT_EXPORTED, and protect SMS receivers withandroid:permission="android.permission.BROADCAST_SMS".
References
- CWE-360: Trust of System Event Data
- CWE-925: Improper Verification of Intent by Broadcast Receiver
- Android Developers: Broadcasts security considerations
Suppressing this rule
Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):
# nosec vnx-360
// nosec vnx-360
Or record an ignore rule: vulnetix ignore add --rule vnx-360 --reason "...". See Suppressing findings.