Skip to content

Commit 12fa696

Browse files
authored
Merge pull request github#8669 from joefarebrother/intent-verification
Java: Add query for Improper Verification of Intent by Broadcast Receiver (CWE-925)
2 parents 8b13d1f + f46dd8c commit 12fa696

File tree

12 files changed

+211
-0
lines changed

12 files changed

+211
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/** Definitions for the improper intent verification query. */
2+
3+
import java
4+
import semmle.code.java.dataflow.DataFlow
5+
import semmle.code.xml.AndroidManifest
6+
import semmle.code.java.frameworks.android.Intent
7+
8+
/** An `onReceive` method of a `BroadcastReceiver` */
9+
private class OnReceiveMethod extends Method {
10+
OnReceiveMethod() { this.getASourceOverriddenMethod*() instanceof AndroidReceiveIntentMethod }
11+
12+
/** Gets the parameter of this method that holds the received `Intent`. */
13+
Parameter getIntentParameter() { result = this.getParameter(1) }
14+
}
15+
16+
/** A configuration to detect whether the `action` of an `Intent` is checked. */
17+
private class VerifiedIntentConfig extends DataFlow::Configuration {
18+
VerifiedIntentConfig() { this = "VerifiedIntentConfig" }
19+
20+
override predicate isSource(DataFlow::Node src) {
21+
src.asParameter() = any(OnReceiveMethod orm).getIntentParameter()
22+
}
23+
24+
override predicate isSink(DataFlow::Node sink) {
25+
exists(MethodAccess ma |
26+
ma.getMethod().hasQualifiedName("android.content", "Intent", "getAction") and
27+
sink.asExpr() = ma.getQualifier()
28+
)
29+
}
30+
}
31+
32+
/** An `onReceive` method that doesn't verify the action of the intent it receives. */
33+
private class UnverifiedOnReceiveMethod extends OnReceiveMethod {
34+
UnverifiedOnReceiveMethod() {
35+
not any(VerifiedIntentConfig c).hasFlow(DataFlow::parameterNode(this.getIntentParameter()), _)
36+
}
37+
}
38+
39+
/** Gets the name of an intent action that can only be sent by the system. */
40+
string getASystemActionName() {
41+
result =
42+
[
43+
"AIRPLANE_MODE", "AIRPLANE_MODE_CHANGED", "APPLICATION_LOCALE_CHANGED",
44+
"APPLICATION_RESTRICTIONS_CHANGED", "BATTERY_CHANGED", "BATTERY_LOW", "BATTERY_OKAY",
45+
"BOOT_COMPLETED", "CONFIGURATION_CHANGED", "DEVICE_STORAGE_LOW", "DEVICE_STORAGE_OK",
46+
"DREAMING_STARTED", "DREAMING_STOPPED", "EXTERNAL_APPLICATIONS_AVAILABLE",
47+
"EXTERNAL_APPLICATIONS_UNAVAILABLE", "LOCALE_CHANGED", "LOCKED_BOOT_COMPLETED",
48+
"MY_PACKAGE_REPLACED", "MY_PACKAGE_SUSPENDED", "MY_PACKAGE_UNSUSPENDED", "NEW_OUTGOING_CALL",
49+
"PACKAGES_SUSPENDED", "PACKAGES_UNSUSPENDED", "PACKAGE_ADDED", "PACKAGE_CHANGED",
50+
"PACKAGE_DATA_CLEARED", "PACKAGE_FIRST_LAUNCH", "PACKAGE_FULLY_REMOVED", "PACKAGE_INSTALL",
51+
"PACKAGE_NEEDS_VERIFICATION", "PACKAGE_REMOVED", "PACKAGE_REPLACED", "PACKAGE_RESTARTED",
52+
"PACKAGE_VERIFIED", "POWER_CONNECTED", "POWER_DISCONNECTED", "REBOOT", "SCREEN_OFF",
53+
"SCREEN_ON", "SHUTDOWN", "TIMEZONE_CHANGED", "TIME_TICK", "UID_REMOVED", "USER_PRESENT"
54+
]
55+
}
56+
57+
/** An expression or XML attribute that contains the name of a system intent action. */
58+
class SystemActionName extends AndroidActionXmlElement {
59+
string name;
60+
61+
SystemActionName() {
62+
name = getASystemActionName() and
63+
this.getActionName() = "android.intent.action." + name
64+
}
65+
66+
/** Gets the name of the system intent that this expression or attribute represents. */
67+
string getSystemActionName() { result = name }
68+
}
69+
70+
/** Holds if the XML element `rec` declares a receiver `orm` to receive the system action named `sa` that doesn't verify intents it receives. */
71+
predicate unverifiedSystemReceiver(
72+
AndroidReceiverXmlElement rec, UnverifiedOnReceiveMethod orm, SystemActionName sa
73+
) {
74+
exists(Class ormty |
75+
ormty = orm.getDeclaringType() and
76+
rec.getComponentName() = ["." + ormty.getName(), ormty.getQualifiedName()] and
77+
rec.getAnIntentFilterElement().getAnActionElement() = sa
78+
)
79+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="test">
2+
<application>
3+
<receiver android:name=".BootReceiverXml">
4+
<intent-filter>
5+
<action android:name="android.intent.action.BOOT_COMPLETED" />
6+
</intent-filter>
7+
</receiver>
8+
</application>
9+
</manifest>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
public class ShutdownReceiver extends BroadcastReceiver {
2+
@Override
3+
public void onReceive(final Context context, final Intent intent) {
4+
mainActivity.saveLocalData();
5+
mainActivity.stopActivity();
6+
}
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
public class ShutdownReceiver extends BroadcastReceiver {
2+
@Override
3+
public void onReceive(final Context context, final Intent intent) {
4+
if (!intent.getAction().equals(Intent.ACTION_SHUTDOWN)) {
5+
return;
6+
}
7+
mainActivity.saveLocalData();
8+
mainActivity.stopActivity();
9+
}
10+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
5+
<qhelp>
6+
7+
<overview>
8+
<p>
9+
When an Android application uses a <code>BroadcastReceiver</code> to receive intents,
10+
it is also able to receive explicit intents that are sent directly to it, regardless of its filter.
11+
12+
Certain intent actions are only able to be sent by the operating system, not third-party applications.
13+
However, a <code>BroadcastReceiver</code> that is registered to receive system intents is still able to receive
14+
intents from a third-party application, so it should check that the intent received has the expected action.
15+
Otherwise, a third-party application could impersonate the system this way to cause unintended behavior, such as a denial of service.
16+
</p>
17+
</overview>
18+
19+
<example>
20+
<p>In the following code, the <code>ShutdownReceiver</code> initiates a shutdown procedure upon receiving an intent,
21+
without checking that the received action is indeed <code>ACTION_SHUTDOWN</code>. This allows third-party applications to
22+
send explicit intents to this receiver to cause a denial of service.</p>
23+
<sample src="Bad.java" />
24+
<sample src="AndroidManifest.xml" />
25+
</example>
26+
27+
<recommendation>
28+
<p>
29+
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.
30+
</p>
31+
<sample src="Good.java" />
32+
</recommendation>
33+
34+
35+
36+
<references>
37+
38+
</references>
39+
40+
</qhelp>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @name Improper verification of intent by broadcast receiver
3+
* @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.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @security-severity 8.2
7+
* @precision high
8+
* @id java/improper-intent-verification
9+
* @tags security
10+
* external/cwe/cwe-925
11+
*/
12+
13+
import java
14+
import semmle.code.java.security.ImproperIntentVerificationQuery
15+
16+
from AndroidReceiverXmlElement reg, Method orm, SystemActionName sa
17+
where unverifiedSystemReceiver(reg, orm, sa)
18+
select orm, "This reciever doesn't verify intents it receives, and is registered $@ to receive $@.",
19+
reg, "here", sa, "the system action " + sa.getName()
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
category: newQuery
3+
---
4+
* A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added.
5+
This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered
6+
to receive system intents.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="test">
2+
<application>
3+
<receiver android:name=".BootReceiverXml">
4+
<intent-filter>
5+
<action android:name="android.intent.action.BOOT_COMPLETED" />
6+
</intent-filter>
7+
</receiver>
8+
</application>
9+
</manifest>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package test;
2+
import android.content.Intent;
3+
import android.content.Context;
4+
import android.content.BroadcastReceiver;
5+
6+
class BootReceiverXml extends BroadcastReceiver {
7+
void doStuff(Intent intent) {}
8+
9+
@Override
10+
public void onReceive(Context ctx, Intent intent) { // $hasResult
11+
doStuff(intent);
12+
}
13+
}

java/ql/test/query-tests/security/CWE-925/ImproperIntentVerification.expected

Whitespace-only changes.

0 commit comments

Comments
 (0)