Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2c2aab6

Browse files
committed
Sensitive broadcast
1 parent 4ce4185 commit 2c2aab6

8 files changed

Lines changed: 330 additions & 0 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
public void sendBroadcast1(Context context, String token, String refreshToken)
2+
{
3+
{
4+
// BAD: broadcast sensitive information without permission
5+
Intent intent = new Intent();
6+
intent.setAction("com.example.custom_action");
7+
intent.putExtra("token", token);
8+
intent.putExtra("refreshToken", refreshToken);
9+
context.sendBroadcast(intent);
10+
}
11+
12+
{
13+
// GOOD: broadcast sensitive information with permission
14+
Intent intent = new Intent();
15+
intent.setAction("com.example.custom_action");
16+
intent.putExtra("token", token);
17+
intent.putExtra("refreshToken", refreshToken);
18+
context.sendBroadcast(intent, "com.example.user_permission");
19+
}
20+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>Broadcasted intents in an Android application are visible to all applications installed on the same mobile device, exposing all sensitive information they contain.</p>
8+
<p>Broadcasts are vulnerable to passive eavesdropping or active denial of service attacks when an intent is broadcasted without specifying any receiver permission or receiver application.</p>
9+
</overview>
10+
11+
<recommendation>
12+
<p>Specify receiver permission or specify receiver application in broadcasted intents, or switch to <code>LocalBroadcastManager</code> or the latest <code>LiveData</code> library.</p>
13+
</recommendation>
14+
15+
<example>
16+
<p>The following example shows two ways of broadcasting intents. In the 'BAD' case, no "receiver permission" is specified. In the 'GOOD' case, "receiver permission" is specified.</p>
17+
<sample src="SensitiveBroadcast.java" />
18+
</example>
19+
20+
<references>
21+
<li>
22+
<a href="https://cwe.mitre.org/data/definitions/927.html">CWE-927: Use of Implicit Intent for Sensitive Communication</a>
23+
</li>
24+
</references>
25+
</qhelp>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/**
2+
* @name Use of Implicit Intent for Sensitive Communication
3+
* @id java/sensitive-broadcast
4+
* @description An Android application uses implicit intents to broadcast sensitive data to all applications without specifying any receiver permission.
5+
* @kind path-problem
6+
* @tags security
7+
* external/cwe-927
8+
*/
9+
10+
import java
11+
import semmle.code.java.frameworks.android.Intent
12+
import semmle.code.java.dataflow.TaintTracking
13+
import DataFlow
14+
import PathGraph
15+
16+
/**
17+
* Gets a regular expression for matching names of variables that indicate the value being held contains sensitive information.
18+
*/
19+
private string getSensitiveInfoRegex() {
20+
result = "(?i).*challenge|pass(wd|word|code|phrase)(?!.*question).*" or
21+
result = "(?i).*(token|email|phone|username|userid|ticket).*"
22+
}
23+
24+
/**
25+
* Method call to pass information to the `Intent` object either directly through intent extra or indirectly through intent extra bundle.
26+
*/
27+
class PutExtraMethodAccess extends MethodAccess {
28+
PutExtraMethodAccess() {
29+
getMethod().getName().regexpMatch("put\\w*Extra(s*)") and
30+
getMethod().getDeclaringType() instanceof TypeIntent and
31+
not exists(
32+
MethodAccess setPackageVa // Intent without specifying receiving package name of the 3rd party app
33+
|
34+
setPackageVa.getMethod().hasName(["setPackage", "setClass", "setClassName", "setComponent"]) and
35+
setPackageVa.getQualifier().(VarAccess).getVariable().getAnAccess() = getQualifier()
36+
)
37+
or
38+
getMethod().getName().regexpMatch("put\\w+") and
39+
getMethod().getDeclaringType().hasQualifiedName("android.os", "Bundle")
40+
}
41+
}
42+
43+
/** Finds variables that hold sensitive information judging by their names. */
44+
class SensitiveInfoExpr extends Expr {
45+
SensitiveInfoExpr() {
46+
exists(Variable v | this = v.getAnAccess() |
47+
v.getName().toLowerCase().regexpMatch(getSensitiveInfoRegex())
48+
)
49+
}
50+
}
51+
52+
/**
53+
* The method access of `context.sendBroadcast`.
54+
*/
55+
class SendBroadcastMethodAccess extends MethodAccess {
56+
SendBroadcastMethodAccess() {
57+
this.getMethod().getDeclaringType() instanceof TypeContext and
58+
this.getMethod().getName().matches("send%Broadcast%")
59+
}
60+
}
61+
62+
/**
63+
* Holds if a `sendBroadcast` call doesn't specify receiver permission.
64+
*/
65+
predicate isSensitiveBroadcastSink(DataFlow::Node sink) {
66+
exists(SendBroadcastMethodAccess ma |
67+
sink.asExpr() = ma.getAnArgument() and
68+
(
69+
ma.getMethod().hasName("sendBroadcast") and
70+
(
71+
ma.getNumArgument() = 1 or // sendBroadcast(Intent intent)
72+
ma.getArgument(1) instanceof NullLiteral // sendBroadcast(Intent intent, String receiverPermission)
73+
)
74+
or
75+
ma.getMethod().hasName("sendBroadcastAsUser") and
76+
(
77+
ma.getNumArgument() = 2 or // sendBroadcastAsUser(Intent intent, UserHandle user)
78+
ma.getArgument(2) instanceof NullLiteral // sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission)
79+
)
80+
or
81+
ma.getMethod().hasName("sendBroadcastWithMultiplePermissions") and
82+
ma.getArgument(1) instanceof NullLiteral // sendBroadcastWithMultiplePermissions(Intent intent, String[] receiverPermissions)
83+
or
84+
//Method calls of `sendOrderedBroadcast` whose second argument is always `receiverPermission`
85+
ma.getMethod().hasName("sendOrderedBroadcast") and
86+
ma.getArgument(1) instanceof NullLiteral
87+
or
88+
//Method call of `sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)`
89+
ma.getMethod().hasName("sendOrderedBroadcastAsUser") and
90+
ma.getArgument(2) instanceof NullLiteral
91+
)
92+
)
93+
}
94+
95+
/**
96+
* Taint configuration tracking flow from variables containing sensitive information to broadcasted intents.
97+
*/
98+
class SensitiveBroadcastConfig extends DataFlow::Configuration {
99+
SensitiveBroadcastConfig() { this = "Sensitive Broadcast Configuration" }
100+
101+
override predicate isSource(DataFlow::Node source) {
102+
source.asExpr() instanceof SensitiveInfoExpr
103+
}
104+
105+
override predicate isSink(DataFlow::Node sink) { isSensitiveBroadcastSink(sink) }
106+
107+
/**
108+
* Holds if there is an additional flow step from `PutExtraMethodAccess` to a broadcasted intent.
109+
*/
110+
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
111+
exists(PutExtraMethodAccess pa |
112+
node1.asExpr() = pa.getAnArgument() and node2.asExpr() = pa.getQualifier()
113+
)
114+
}
115+
}
116+
117+
from SensitiveBroadcastConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink
118+
where cfg.hasFlowPath(source, sink)
119+
select sink.getNode(), source, sink, "Sending $@ to broadcast.", source.getNode(),
120+
"sensitive information"
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
edges
2+
| SensitiveBroadcast.java:11:34:11:38 | token : String | SensitiveBroadcast.java:13:31:13:36 | intent |
3+
| SensitiveBroadcast.java:12:41:12:52 | refreshToken : String | SensitiveBroadcast.java:13:31:13:36 | intent |
4+
| SensitiveBroadcast.java:23:33:23:40 | username : String | SensitiveBroadcast.java:25:31:25:36 | intent |
5+
| SensitiveBroadcast.java:24:32:24:39 | password : String | SensitiveBroadcast.java:25:31:25:36 | intent |
6+
| SensitiveBroadcast.java:36:40:36:47 | username : String | SensitiveBroadcast.java:39:31:39:36 | intent |
7+
| SensitiveBroadcast.java:37:39:37:46 | password : String | SensitiveBroadcast.java:39:31:39:36 | intent |
8+
nodes
9+
| SensitiveBroadcast.java:11:34:11:38 | token : String | semmle.label | token : String |
10+
| SensitiveBroadcast.java:12:41:12:52 | refreshToken : String | semmle.label | refreshToken : String |
11+
| SensitiveBroadcast.java:13:31:13:36 | intent | semmle.label | intent |
12+
| SensitiveBroadcast.java:23:33:23:40 | username : String | semmle.label | username : String |
13+
| SensitiveBroadcast.java:24:32:24:39 | password : String | semmle.label | password : String |
14+
| SensitiveBroadcast.java:25:31:25:36 | intent | semmle.label | intent |
15+
| SensitiveBroadcast.java:36:40:36:47 | username : String | semmle.label | username : String |
16+
| SensitiveBroadcast.java:37:39:37:46 | password : String | semmle.label | password : String |
17+
| SensitiveBroadcast.java:39:31:39:36 | intent | semmle.label | intent |
18+
#select
19+
| SensitiveBroadcast.java:13:31:13:36 | intent | SensitiveBroadcast.java:11:34:11:38 | token : String | SensitiveBroadcast.java:13:31:13:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:11:34:11:38 | token | sensitive information |
20+
| SensitiveBroadcast.java:13:31:13:36 | intent | SensitiveBroadcast.java:12:41:12:52 | refreshToken : String | SensitiveBroadcast.java:13:31:13:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:12:41:12:52 | refreshToken | sensitive information |
21+
| SensitiveBroadcast.java:25:31:25:36 | intent | SensitiveBroadcast.java:23:33:23:40 | username : String | SensitiveBroadcast.java:25:31:25:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:23:33:23:40 | username | sensitive information |
22+
| SensitiveBroadcast.java:25:31:25:36 | intent | SensitiveBroadcast.java:24:32:24:39 | password : String | SensitiveBroadcast.java:25:31:25:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:24:32:24:39 | password | sensitive information |
23+
| SensitiveBroadcast.java:39:31:39:36 | intent | SensitiveBroadcast.java:36:40:36:47 | username : String | SensitiveBroadcast.java:39:31:39:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:36:40:36:47 | username | sensitive information |
24+
| SensitiveBroadcast.java:39:31:39:36 | intent | SensitiveBroadcast.java:37:39:37:46 | password : String | SensitiveBroadcast.java:39:31:39:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:37:39:37:46 | password | sensitive information |
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import android.content.Context;
2+
import android.content.Intent;
3+
import android.os.Bundle;
4+
5+
class SensitiveBroadcast {
6+
7+
//Tests broadcast of access token with intent extra.
8+
public void sendBroadcast1(Context context, String token, String refreshToken) {
9+
Intent intent = new Intent();
10+
intent.setAction("com.example.custom_action");
11+
intent.putExtra("token", token);
12+
intent.putExtra("refreshToken", refreshToken);
13+
context.sendBroadcast(intent);
14+
}
15+
16+
//Tests broadcast of sensitive user information with intent extra.
17+
public void sendBroadcast2(Context context) {
18+
String username = "test123";
19+
String password = "abc12345";
20+
21+
Intent intent = new Intent();
22+
intent.setAction("com.example.custom_action");
23+
intent.putExtra("name", username);
24+
intent.putExtra("pwd", password);
25+
context.sendBroadcast(intent);
26+
}
27+
28+
//Tests broadcast of sensitive user information with extra bundle.
29+
public void sendBroadcast3(Context context) {
30+
String username = "test123";
31+
String password = "abc12345";
32+
33+
Intent intent = new Intent();
34+
intent.setAction("com.example.custom_action");
35+
Bundle bundle = new Bundle();
36+
bundle.putCharSequence("name", username);
37+
bundle.putCharSequence("pwd", password);
38+
intent.putExtras(bundle);
39+
context.sendBroadcast(intent);
40+
}
41+
42+
//Tests broadcast of sensitive user information with permission.
43+
public void sendBroadcast4(Context context) {
44+
String username = "test123";
45+
String password = "abc12345";
46+
47+
Intent intent = new Intent();
48+
intent.setAction("com.example.custom_action");
49+
intent.putExtra("name", username);
50+
intent.putExtra("pwd", password);
51+
context.sendBroadcast(intent, "com.example.user_permission");
52+
}
53+
54+
//Tests broadcast of sensitive user information to a specific application.
55+
public void sendBroadcast5(Context context) {
56+
String username = "test123";
57+
String password = "abc12345";
58+
59+
Intent intent = new Intent();
60+
intent.setAction("com.example.custom_action");
61+
intent.setClassName("com.example2", "com.example2.UserInfoHandler");
62+
intent.putExtra("name", username);
63+
intent.putExtra("pwd", password);
64+
context.sendBroadcast(intent);
65+
}
66+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security/CWE/CWE-927/SensitiveBroadcast.ql
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0

java/ql/test/stubs/google-android-9.0.0/android/content/Intent.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1996,4 +1996,77 @@ public void writeToParcel(Parcel out, int flags) {
19961996

19971997
public void readFromParcel(Parcel in) {
19981998
}
1999+
2000+
/**
2001+
* Retrieve the application package name this Intent is limited to. When
2002+
* resolving an Intent, if non-null this limits the resolution to only
2003+
* components in the given application package.
2004+
*
2005+
* @return The name of the application package for the Intent.
2006+
*
2007+
* @see #resolveActivity
2008+
* @see #setPackage
2009+
*/
2010+
public String getPackage() {
2011+
return null;
2012+
}
2013+
2014+
/**
2015+
* (Usually optional) Set an explicit application package name that limits
2016+
* the components this Intent will resolve to. If left to the default
2017+
* value of null, all components in all applications will considered.
2018+
* If non-null, the Intent can only match the components in the given
2019+
* application package.
2020+
*
2021+
* @param packageName The name of the application package to handle the
2022+
* intent, or null to allow any application package.
2023+
*
2024+
* @return Returns the same Intent object, for chaining multiple calls
2025+
* into a single statement.
2026+
*
2027+
* @see #getPackage
2028+
* @see #resolveActivity
2029+
*/
2030+
public Intent setPackage(String packageName) {
2031+
return null;
2032+
}
2033+
2034+
/**
2035+
* Convenience for calling {@link #setComponent} with an
2036+
* explicit class name.
2037+
*
2038+
* @param packageContext A Context of the application package implementing
2039+
* this class.
2040+
* @param className The name of a class inside of the application package
2041+
* that will be used as the component for this Intent.
2042+
*
2043+
* @return Returns the same Intent object, for chaining multiple calls
2044+
* into a single statement.
2045+
*
2046+
* @see #setComponent
2047+
* @see #setClass
2048+
*/
2049+
public Intent setClassName(Context packageContext, String className) {
2050+
return null;
2051+
}
2052+
2053+
/**
2054+
* Convenience for calling {@link #setComponent} with an
2055+
* explicit application package name and class name.
2056+
*
2057+
* @param packageName The name of the package implementing the desired
2058+
* component.
2059+
* @param className The name of a class inside of the application package
2060+
* that will be used as the component for this Intent.
2061+
*
2062+
* @return Returns the same Intent object, for chaining multiple calls
2063+
* into a single statement.
2064+
*
2065+
* @see #setComponent
2066+
* @see #setClass
2067+
*/
2068+
public Intent setClassName(String packageName, String className) {
2069+
return null;
2070+
}
2071+
19992072
}

0 commit comments

Comments
 (0)