Skip to content

Commit 1959f49

Browse files
Add Improper Intent Verification query
1 parent ff55eff commit 1959f49

File tree

2 files changed

+172
-0
lines changed

2 files changed

+172
-0
lines changed
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/** Definitions for the improper intent verification query. */
2+
3+
import java
4+
import semmle.code.java.dataflow.DataFlow
5+
6+
/** An `onRecieve` method of a `BroadcastReciever` */
7+
private class OnReceiveMethod extends Method {
8+
OnReceiveMethod() {
9+
this.getASourceOverriddenMethod*()
10+
.hasQualifiedName("android.content", "BroadcastReciever", "onReceeve")
11+
}
12+
13+
/** Gets the paramter of this method that holds the received `Intent`. */
14+
Parameter getIntentParameter() { result = this.getParameter(1) }
15+
}
16+
17+
/** A configuration to detect whether the `action` of an `Intent` is checked. */
18+
private class VerifiedIntentConfig extends DataFlow::Configuration {
19+
VerifiedIntentConfig() { this = "VerifiedIntentConfig" }
20+
21+
override predicate isSource(DataFlow::Node src) {
22+
src.asParameter() = any(OnReceiveMethod orm).getIntentParameter()
23+
}
24+
25+
override predicate isSink(DataFlow::Node sink) {
26+
exists(MethodAccess ma |
27+
ma.getMethod().hasQualifiedName("android.content", "Intent", "getAction") and
28+
sink.asExpr() = ma.getQualifier()
29+
)
30+
}
31+
}
32+
33+
/** An `onRecieve` method that doesn't verify the action of the intent it recieves. */
34+
class UnverifiedOnReceiveMethod extends OnReceiveMethod {
35+
UnverifiedOnReceiveMethod() {
36+
not any(VerifiedIntentConfig c).hasFlow(DataFlow::parameterNode(this.getIntentParameter()), _)
37+
}
38+
}
39+
40+
/** Gets the name of an intent action that can only be sent by the system. */
41+
string getASystemActionName() {
42+
result =
43+
[
44+
"AIRPLANE_MODE", "AIRPLANE_MODE_CHANGED", "APPLICATION_LOCALE_CHANGED",
45+
"APPLICATION_RESTRICTIONS_CHANGED", "BATTERY_CHANGED", "BATTERY_LOW", "BATTERY_OKAY",
46+
"BOOT_COMPLETED", "CONFIGURATION_CHANGED", "DEVICE_STORAGE_LOW", "DEVICE_STORAGE_OK",
47+
"DREAMING_STARTED", "DREAMING_STOPPED", "EXTERNAL_APPLICATIONS_AVAILABLE",
48+
"EXTERNAL_APPLICATIONS_UNAVAILABLE", "LOCALE_CHANGED", "LOCKED_BOOT_COMPLETED",
49+
"MY_PACKAGE_REPLACED", "MY_PACKAGE_SUSPENDED", "MY_PACKAGE_UNSUSPENDED", "NEW_OUTGOING_CALL",
50+
"PACKAGES_SUSPENDED", "PACKAGES_UNSUSPENDED", "PACKAGE_ADDED", "PACKAGE_CHANGED",
51+
"PACKAGE_DATA_CLEARED", "PACKAGE_FIRST_LAUNCH", "PACKAGE_FULLY_REMOVED", "PACKAGE_INSTALL",
52+
"PACKAGE_NEEDS_VERIFICATION", "PACKAGE_REMOVED", "PACKAGE_REPLACED", "PACKAGE_RESTARTED",
53+
"PACKAGE_VERIFIED", "POWER_CONNECTED", "POWER_DISCONNECTED", "REBOOT", "SCREEN_OFF",
54+
"SCREEN_ON", "SHUTDOWN", "TIMEZONE_CHANGED", "TIME_TICK", "UID_REMOVED", "USER_PRESENT"
55+
]
56+
}
57+
58+
/** An expression or XML attribute that contains the name of a system intent action. */
59+
class SystemActionName extends Top {
60+
string name;
61+
62+
SystemActionName() {
63+
name = getASystemActionName() and
64+
(
65+
this.(StringLiteral).getValue() = "android.intent.action." + name
66+
or
67+
this.(FieldRead).getField().hasQualifiedName("android.content", "Intent", "ACTION_" + name)
68+
or
69+
this.(XMLAttribute).getValue() = "android.intent.action." + name
70+
)
71+
}
72+
73+
/** Gets the name of the system intent that this expression or attriute represents. */
74+
string getName() { result = name }
75+
}
76+
77+
/** A call to `Context.registerReciever` */
78+
private class RegisterReceiverCall extends MethodAccess {
79+
RegisterReceiverCall() {
80+
this.getMethod()
81+
.getASourceOverriddenMethod*()
82+
.hasQualifiedName("android.content", "Context", "registerReceiver")
83+
}
84+
85+
/** Gets the `BroadcastReceiver` argument to this call. */
86+
Expr getReceiverArgument() { result = this.getArgument(0) }
87+
88+
/** Gets the `IntentFilter` argument to this call. */
89+
Expr getFilterArgument() { result = this.getArgument(1) }
90+
}
91+
92+
/** A configuration to detect uses of `registerReciever` with system intent actions. */
93+
private class RegisterSystemActionConfig extends DataFlow::Configuration {
94+
RegisterSystemActionConfig() { this = "RegisterSystemActionConfig" }
95+
96+
override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SystemActionName }
97+
98+
override predicate isSink(DataFlow::Node node) {
99+
exists(RegisterReceiverCall ma | node.asExpr() = ma.getFilterArgument())
100+
}
101+
102+
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
103+
exists(ConstructorCall cc |
104+
cc.getConstructedType().hasQualifiedName("android.content", "IntentFilter") and
105+
node1.asExpr() = cc.getArgument(0) and
106+
node2.asExpr() = cc
107+
)
108+
or
109+
exists(MethodAccess ma |
110+
ma.getMethod().hasQualifiedName("android.content", "IntentFilter", "create") and
111+
node1.asExpr() = ma.getArgument(0) and
112+
node2.asExpr() = ma
113+
)
114+
or
115+
exists(MethodAccess ma |
116+
ma.getMethod().hasQualifiedName("android.content", "IntentFilter", "addAction") and
117+
node1.asExpr() = ma.getArgument(0) and
118+
node2.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = ma.getQualifier()
119+
)
120+
}
121+
}
122+
123+
/** Holds if `rrc` registers a reciever `orm` to recieve the system action `sa` that doesn't verifiy intents it recieves. */
124+
predicate registeredUnverifiedSystemReciever(
125+
RegisterReceiverCall rrc, UnverifiedOnReceiveMethod orm, SystemActionName sa
126+
) {
127+
exists(RegisterSystemActionConfig conf, ConstructorCall cc |
128+
conf.hasFlow(DataFlow::exprNode(sa), DataFlow::exprNode(rrc.getFilterArgument())) and
129+
cc.getConstructedType() = orm.getDeclaringType() and
130+
DataFlow::localExprFlow(cc, rrc.getReceiverArgument())
131+
)
132+
}
133+
134+
/** Holds if the XML element `rec` declares a reciever `orm` to recieve the system action named `sa` that doesn't verifiy intents it recieves. */
135+
predicate xmlUnverifiedSystemReciever(
136+
XMLElement rec, UnverifiedOnReceiveMethod orm, SystemActionName sa
137+
) {
138+
exists(XMLElement filter, XMLElement action, Class ormty |
139+
rec.hasName("receiver") and
140+
filter.hasName("intent-filter") and
141+
action.hasName("action") and
142+
filter = rec.getAChild() and
143+
action = rec.getAChild() and
144+
ormty = orm.getDeclaringType() and
145+
rec.getAttribute("android:name").getValue() = ["." + ormty.getName(), ormty.getQualifiedName()] and
146+
action.getAttribute("android:name") = sa
147+
)
148+
}
149+
150+
/** Holds if `reg` registers (either explicitly or through XML) a reciever `orm` to recieve the system action named `sa` that doesn't verify intents it recieves. */
151+
predicate unverifiedSystemReciever(Top reg, Method orm, SystemActionName sa) {
152+
registeredUnverifiedSystemReciever(reg, orm, sa) or
153+
xmlUnverifiedSystemReciever(reg, orm, sa)
154+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @name Improper Verification of Intent by Broadcast Reciever
3+
* @description The Android application uses a Broadcast Receiver that receives an Intent but does not properly verify that the Intent came from an authorized source.
4+
* @kind problem
5+
* @problem.severity warning
6+
* @precision high
7+
* @id java/improper-intent-verification
8+
* @tags security
9+
* external/cwe/cwe-925
10+
*/
11+
12+
import java
13+
import semmle.code.java.security.ImproperIntentVerificationQuery
14+
15+
from Top reg, Method orm, SystemActionName sa
16+
where unverifiedSystemReciever(reg, orm, sa)
17+
select orm, "This reciever doesn't verify intents it recieves, and is registered $@ to recieve $@.",
18+
reg, "here", sa, "the system action " + sa.getName()

0 commit comments

Comments
 (0)