Skip to content

Java: Add query for Improper Verification of Intent by Broadcast Receiver (CWE-925) #8669

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
merged 11 commits into from
Jun 29, 2022
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/** Definitions for the improper intent verification query. */

import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.xml.AndroidManifest
import semmle.code.java.frameworks.android.Intent

/** An `onReceive` method of a `BroadcastReceiver` */
private class OnReceiveMethod extends Method {
OnReceiveMethod() { this.getASourceOverriddenMethod*() instanceof AndroidReceiveIntentMethod }

/** Gets the parameter of this method that holds the received `Intent`. */
Parameter getIntentParameter() { result = this.getParameter(1) }
}

/** A configuration to detect whether the `action` of an `Intent` is checked. */
private class VerifiedIntentConfig extends DataFlow::Configuration {
VerifiedIntentConfig() { this = "VerifiedIntentConfig" }

override predicate isSource(DataFlow::Node src) {
src.asParameter() = any(OnReceiveMethod orm).getIntentParameter()
}

override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
ma.getMethod().hasQualifiedName("android.content", "Intent", "getAction") and
sink.asExpr() = ma.getQualifier()
)
}
}

/** An `onReceive` method that doesn't verify the action of the intent it receives. */
private class UnverifiedOnReceiveMethod extends OnReceiveMethod {
UnverifiedOnReceiveMethod() {
not any(VerifiedIntentConfig c).hasFlow(DataFlow::parameterNode(this.getIntentParameter()), _)
}
}

/** Gets the name of an intent action that can only be sent by the system. */
string getASystemActionName() {
result =
[
"AIRPLANE_MODE", "AIRPLANE_MODE_CHANGED", "APPLICATION_LOCALE_CHANGED",
"APPLICATION_RESTRICTIONS_CHANGED", "BATTERY_CHANGED", "BATTERY_LOW", "BATTERY_OKAY",
"BOOT_COMPLETED", "CONFIGURATION_CHANGED", "DEVICE_STORAGE_LOW", "DEVICE_STORAGE_OK",
"DREAMING_STARTED", "DREAMING_STOPPED", "EXTERNAL_APPLICATIONS_AVAILABLE",
"EXTERNAL_APPLICATIONS_UNAVAILABLE", "LOCALE_CHANGED", "LOCKED_BOOT_COMPLETED",
"MY_PACKAGE_REPLACED", "MY_PACKAGE_SUSPENDED", "MY_PACKAGE_UNSUSPENDED", "NEW_OUTGOING_CALL",
"PACKAGES_SUSPENDED", "PACKAGES_UNSUSPENDED", "PACKAGE_ADDED", "PACKAGE_CHANGED",
"PACKAGE_DATA_CLEARED", "PACKAGE_FIRST_LAUNCH", "PACKAGE_FULLY_REMOVED", "PACKAGE_INSTALL",
"PACKAGE_NEEDS_VERIFICATION", "PACKAGE_REMOVED", "PACKAGE_REPLACED", "PACKAGE_RESTARTED",
"PACKAGE_VERIFIED", "POWER_CONNECTED", "POWER_DISCONNECTED", "REBOOT", "SCREEN_OFF",
"SCREEN_ON", "SHUTDOWN", "TIMEZONE_CHANGED", "TIME_TICK", "UID_REMOVED", "USER_PRESENT"
]
}

/** An expression or XML attribute that contains the name of a system intent action. */
class SystemActionName extends AndroidActionXmlElement {
string name;

SystemActionName() {
name = getASystemActionName() and
this.getActionName() = "android.intent.action." + name
}

/** Gets the name of the system intent that this expression or attribute represents. */
string getSystemActionName() { result = name }
}

/** Holds if the XML element `rec` declares a receiver `orm` to receive the system action named `sa` that doesn't verify intents it receives. */
predicate unverifiedSystemReceiver(
AndroidReceiverXmlElement rec, UnverifiedOnReceiveMethod orm, SystemActionName sa
) {
exists(Class ormty |
ormty = orm.getDeclaringType() and
rec.getComponentName() = ["." + ormty.getName(), ormty.getQualifiedName()] and
rec.getAnIntentFilterElement().getAnActionElement() = sa
)
}
9 changes: 9 additions & 0 deletions java/ql/src/Security/CWE/CWE-925/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="test">
<application>
<receiver android:name=".BootReceiverXml">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
7 changes: 7 additions & 0 deletions java/ql/src/Security/CWE/CWE-925/Bad.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
public class ShutdownReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
mainActivity.saveLocalData();
mainActivity.stopActivity();
}
}
10 changes: 10 additions & 0 deletions java/ql/src/Security/CWE/CWE-925/Good.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
public class ShutdownReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
if (!intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
return;
}
mainActivity.saveLocalData();
mainActivity.stopActivity();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">

<qhelp>

<overview>
<p>
When an Android application uses a <code>BroadcastReceiver</code> to receive intents,
it is also able to receive explicit intents that are sent directly to it, regardless of its filter.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the its pronoun refer to here, the app or the BroadcastReceiver (sorry, not a developer)?

Copy link
Contributor Author

@joefarebrother joefarebrother Jun 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It refers to the BroadcastReceiver


Certain intent actions are only able to be sent by the operating system, not third-party applications.
However, a <code>BroadcastReceiver</code> that is registered to receive system intents is still able to receive
intents from a third-party application, so it should check that the intent received has the expected action.
Otherwise, a third-party application could impersonate the system this way to cause unintended behavior, such as a denial of service.
</p>
</overview>

<example>
<p>In the following code, the <code>ShutdownReceiver</code> initiates a shutdown procedure upon receiving an intent,
without checking that the received action is indeed <code>ACTION_SHUTDOWN</code>. This allows third-party applications to
send explicit intents to this receiver to cause a denial of service.</p>
<sample src="Bad.java" />
<sample src="AndroidManifest.xml" />
</example>

<recommendation>
<p>
In the <code>onReceive</code> method of a <code>BroadcastReciever</code>, the action of the received Intent should be checked. The following code demonstrates this.
</p>
<sample src="Good.java" />
</recommendation>



<references>

</references>

</qhelp>
19 changes: 19 additions & 0 deletions java/ql/src/Security/CWE/CWE-925/ImproperIntentVerification.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* @name Improper verification of intent by broadcast receiver
* @description A broadcast receiver that does not verify intents it receives may be susceptible to unintended behavior by third party applications sending it explicit intents.
* @kind problem
* @problem.severity warning
* @security-severity 8.2
* @precision high
* @id java/improper-intent-verification
* @tags security
* external/cwe/cwe-925
*/

import java
import semmle.code.java.security.ImproperIntentVerificationQuery

from AndroidReceiverXmlElement reg, Method orm, SystemActionName sa
where unverifiedSystemReceiver(reg, orm, sa)
select orm, "This reciever doesn't verify intents it receives, and is registered $@ to receive $@.",
reg, "here", sa, "the system action " + sa.getName()
6 changes: 6 additions & 0 deletions java/ql/src/change-notes/2022-04-27-intent-verification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: newQuery
---
* A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added.
This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered
to receive system intents.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="test">
<application>
<receiver android:name=".BootReceiverXml">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
13 changes: 13 additions & 0 deletions java/ql/test/query-tests/security/CWE-925/BootReceiverXml.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package test;
import android.content.Intent;
import android.content.Context;
import android.content.BroadcastReceiver;

class BootReceiverXml extends BroadcastReceiver {
void doStuff(Intent intent) {}

@Override
public void onReceive(Context ctx, Intent intent) { // $hasResult
doStuff(intent);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import java
import semmle.code.java.security.ImproperIntentVerificationQuery
import TestUtilities.InlineExpectationsTest

class HasFlowTest extends InlineExpectationsTest {
HasFlowTest() { this = "HasFlowTest" }

override string getARelevantTag() { result = "hasResult" }

override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasResult" and
exists(Method orm | unverifiedSystemReceiver(_, orm, _) |
orm.getLocation() = location and
element = orm.toString() and
value = ""
)
}
}
1 change: 1 addition & 0 deletions java/ql/test/query-tests/security/CWE-925/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0