mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Merge pull request #6599 from joefarebrother/android-sensitive-communication
Java: Promote android sensitive broadcast query
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
lgtm,codescanning
|
||||||
|
* The query "Leaking sensitive information through an implicit Intent" (`java/android/sensitive-communication`) has been promoted from experimental to the main query pack. Its results will now appear by default. The query was originally [submitted as an experimental query by @luchua-bc.](https://github.com/github/codeql/pull/4512)
|
||||||
@@ -79,6 +79,7 @@ private module Frameworks {
|
|||||||
private import internal.ContainerFlow
|
private import internal.ContainerFlow
|
||||||
private import semmle.code.java.frameworks.android.Android
|
private import semmle.code.java.frameworks.android.Android
|
||||||
private import semmle.code.java.frameworks.android.Intent
|
private import semmle.code.java.frameworks.android.Intent
|
||||||
|
private import semmle.code.java.frameworks.android.SQLite
|
||||||
private import semmle.code.java.frameworks.android.XssSinks
|
private import semmle.code.java.frameworks.android.XssSinks
|
||||||
private import semmle.code.java.frameworks.ApacheHttp
|
private import semmle.code.java.frameworks.ApacheHttp
|
||||||
private import semmle.code.java.frameworks.apache.Collections
|
private import semmle.code.java.frameworks.apache.Collections
|
||||||
@@ -114,8 +115,6 @@ private module Frameworks {
|
|||||||
private import semmle.code.java.security.OgnlInjection
|
private import semmle.code.java.security.OgnlInjection
|
||||||
private import semmle.code.java.security.XPath
|
private import semmle.code.java.security.XPath
|
||||||
private import semmle.code.java.security.XsltInjection
|
private import semmle.code.java.security.XsltInjection
|
||||||
private import semmle.code.java.frameworks.android.Android
|
|
||||||
private import semmle.code.java.frameworks.android.SQLite
|
|
||||||
private import semmle.code.java.frameworks.Jdbc
|
private import semmle.code.java.frameworks.Jdbc
|
||||||
private import semmle.code.java.frameworks.SpringJdbc
|
private import semmle.code.java.frameworks.SpringJdbc
|
||||||
private import semmle.code.java.frameworks.MyBatis
|
private import semmle.code.java.frameworks.MyBatis
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
/** Provides definitions to reason about Android Sensitive Communication queries */
|
||||||
|
|
||||||
|
import java
|
||||||
|
import semmle.code.java.dataflow.TaintTracking
|
||||||
|
import semmle.code.java.frameworks.android.Intent
|
||||||
|
import semmle.code.java.security.SensitiveActions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets regular expression for matching names of Android variables that indicate the value being held contains sensitive information.
|
||||||
|
*/
|
||||||
|
private string getAndroidSensitiveInfoRegex() { result = "(?i).*(email|phone|ticket).*" }
|
||||||
|
|
||||||
|
/** Finds variables that hold sensitive information judging by their names. */
|
||||||
|
private class SensitiveInfoExpr extends Expr {
|
||||||
|
SensitiveInfoExpr() {
|
||||||
|
exists(Variable v | this = v.getAnAccess() |
|
||||||
|
v.getName().regexpMatch([getCommonSensitiveInfoRegex(), getAndroidSensitiveInfoRegex()])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate maybeNullArg(Expr ex) {
|
||||||
|
exists(DataFlow::Node src, DataFlow::Node sink, MethodAccess ma |
|
||||||
|
ex = ma.getAnArgument() and
|
||||||
|
sink.asExpr() = ex and
|
||||||
|
src.asExpr() instanceof NullLiteral
|
||||||
|
|
|
||||||
|
DataFlow::localFlow(src, sink)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate maybeEmptyArrayArg(Expr ex) {
|
||||||
|
exists(DataFlow::Node src, DataFlow::Node sink, MethodAccess ma |
|
||||||
|
ex = ma.getAnArgument() and
|
||||||
|
sink.asExpr() = ex and
|
||||||
|
src.asExpr().(ArrayCreationExpr).getFirstDimensionSize() = 0
|
||||||
|
|
|
||||||
|
DataFlow::localFlow(src, sink)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if a `sendBroadcast` call doesn't specify receiver permission.
|
||||||
|
*/
|
||||||
|
private predicate isSensitiveBroadcastSink(DataFlow::Node sendBroadcastCallArg) {
|
||||||
|
exists(MethodAccess ma, string name | ma.getMethod().hasName(name) |
|
||||||
|
ma.getMethod().getDeclaringType().getASourceSupertype*() instanceof TypeContext and
|
||||||
|
sendBroadcastCallArg.asExpr() = ma.getAnArgument() and
|
||||||
|
(
|
||||||
|
name = "sendBroadcast" and
|
||||||
|
(
|
||||||
|
// sendBroadcast(Intent intent)
|
||||||
|
ma.getNumArgument() = 1
|
||||||
|
or
|
||||||
|
// sendBroadcast(Intent intent, String receiverPermission)
|
||||||
|
maybeNullArg(ma.getArgument(1))
|
||||||
|
)
|
||||||
|
or
|
||||||
|
name = "sendBroadcastAsUser" and
|
||||||
|
(
|
||||||
|
// sendBroadcastAsUser(Intent intent, UserHandle user)
|
||||||
|
ma.getNumArgument() = 2
|
||||||
|
or
|
||||||
|
// sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission)
|
||||||
|
maybeNullArg(ma.getArgument(2))
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// sendBroadcastWithMultiplePermissions(Intent intent, String[] receiverPermissions)
|
||||||
|
name = "sendBroadcastWithMultiplePermissions" and
|
||||||
|
maybeEmptyArrayArg(ma.getArgument(1))
|
||||||
|
or
|
||||||
|
// Method calls of `sendOrderedBroadcast` whose second argument is always `receiverPermission`
|
||||||
|
name = "sendOrderedBroadcast" and
|
||||||
|
(
|
||||||
|
// sendOrderedBroadcast(Intent intent, String receiverPermission)
|
||||||
|
// sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
|
||||||
|
maybeNullArg(ma.getArgument(1)) and
|
||||||
|
ma.getNumArgument() = [2, 7]
|
||||||
|
or
|
||||||
|
// sendOrderedBroadcast(Intent intent, String receiverPermission, String receiverAppOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
|
||||||
|
maybeNullArg(ma.getArgument(1)) and
|
||||||
|
maybeNullArg(ma.getArgument(2)) and
|
||||||
|
ma.getNumArgument() = 8
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
|
||||||
|
name = "sendOrderedBroadcastAsUser" and
|
||||||
|
maybeNullArg(ma.getArgument(2))
|
||||||
|
or
|
||||||
|
// sendStickyBroadcast(Intent intent)
|
||||||
|
// sendStickyBroadcast(Intent intent, Bundle options)
|
||||||
|
// sendStickyBroadcastAsUser(Intent intent, UserHandle user)
|
||||||
|
// sendStickyOrderedBroadcast(Intent intent, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
|
||||||
|
// sendStickyOrderedBroadcastAsUser(Intent intent, UserHandle user, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
|
||||||
|
name =
|
||||||
|
[
|
||||||
|
"sendStickyBroadcast", "sendStickyBroadcastAsUser", "sendStickyOrderedBroadcast",
|
||||||
|
"sendStickyOrderedBroadcastAsUser"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `arg` is an argument in a use of a `startActivity` or `startService` method that sends an Intent to another application.
|
||||||
|
*/
|
||||||
|
private predicate isStartActivityOrServiceSink(DataFlow::Node arg) {
|
||||||
|
exists(MethodAccess ma, string name | ma.getMethod().hasName(name) |
|
||||||
|
arg.asExpr() = ma.getArgument(0) and
|
||||||
|
ma.getMethod().getDeclaringType().getASourceSupertype*() instanceof TypeContext and
|
||||||
|
// startActivity(Intent intent)
|
||||||
|
// startActivity(Intent intent, Bundle options)
|
||||||
|
// startActivities(Intent[] intents)
|
||||||
|
// startActivities(Intent[] intents, Bundle options)
|
||||||
|
// startService(Intent service)
|
||||||
|
// startForegroundService(Intent service)
|
||||||
|
// bindService (Intent service, int flags, Executor executor, ServiceConnection conn)
|
||||||
|
// bindService (Intent service, Executor executor, ServiceConnection conn)
|
||||||
|
name =
|
||||||
|
["startActivity", "startActivities", "startService", "startForegroundService", "bindService"]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate isCleanIntent(Expr intent) {
|
||||||
|
intent.getType() instanceof TypeIntent and
|
||||||
|
(
|
||||||
|
exists(MethodAccess setRecieverMa |
|
||||||
|
setRecieverMa.getQualifier() = intent and
|
||||||
|
setRecieverMa.getMethod().hasName(["setPackage", "setClass", "setClassName", "setComponent"])
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// Handle the cases where the PackageContext and Class are set at construction time
|
||||||
|
// Intent(Context packageContext, Class<?> cls)
|
||||||
|
// Intent(String action, Uri uri, Context packageContext, Class<?> cls)
|
||||||
|
exists(ConstructorCall cc | cc = intent |
|
||||||
|
cc.getConstructedType() instanceof TypeIntent and
|
||||||
|
cc.getNumArgument() > 1 and
|
||||||
|
(
|
||||||
|
cc.getArgument(0).getType() instanceof TypeContext and
|
||||||
|
not maybeNullArg(cc.getArgument(1))
|
||||||
|
or
|
||||||
|
cc.getArgument(2).getType() instanceof TypeContext and
|
||||||
|
not maybeNullArg(cc.getArgument(3))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taint configuration tracking flow from variables containing sensitive information to broadcast Intents.
|
||||||
|
*/
|
||||||
|
class SensitiveCommunicationConfig extends TaintTracking::Configuration {
|
||||||
|
SensitiveCommunicationConfig() { this = "Sensitive Communication Configuration" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node source) {
|
||||||
|
source.asExpr() instanceof SensitiveInfoExpr
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node sink) {
|
||||||
|
isSensitiveBroadcastSink(sink)
|
||||||
|
or
|
||||||
|
isStartActivityOrServiceSink(sink)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if broadcast doesn't specify receiving package name of the 3rd party app
|
||||||
|
*/
|
||||||
|
override predicate isSanitizer(DataFlow::Node node) {
|
||||||
|
exists(DataFlow::Node intent | isCleanIntent(intent.asExpr()) |
|
||||||
|
DataFlow::localFlow(intent, node)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
|
||||||
|
super.allowImplicitRead(node, c)
|
||||||
|
or
|
||||||
|
this.isSink(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,30 +2,24 @@
|
|||||||
<qhelp>
|
<qhelp>
|
||||||
|
|
||||||
<overview>
|
<overview>
|
||||||
<p>Broadcast intents in an Android application are visible to all applications installed on the same mobile device, exposing all sensitive information they contain.</p>
|
<p>When an implicit Intent is used with a method such as <code>startActivity</code>, <code>startService</code>, or <code>sendBroadcast</code>, it may be read by other applications on the device.</p>
|
||||||
<p>Broadcasts are vulnerable to passive eavesdropping or active denial of service attacks when an intent is broadcast without specifying any receiver permission or receiver application.</p>
|
<p>This means that sensitive data in these Intents may be leaked.</p>
|
||||||
</overview>
|
</overview>
|
||||||
|
|
||||||
<recommendation>
|
<recommendation>
|
||||||
<p>
|
<p>
|
||||||
Specify a receiver permission or application when broadcasting intents, or switch to
|
For <code>sendBroadcast</code> methods, a receiver permission may be specified so that only applications with a certain permission may receive the Intent;
|
||||||
<code>LocalBroadcastManager</code>
|
or a <code>LocalBroadcastManager</code> may be used.
|
||||||
or the latest
|
Otherwise, ensure that Intents containing sensitive data have an explicit receiver class set.
|
||||||
<code>LiveData</code>
|
|
||||||
library.
|
|
||||||
</p>
|
</p>
|
||||||
</recommendation>
|
</recommendation>
|
||||||
|
|
||||||
<example>
|
<example>
|
||||||
<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" or "receiver application" is specified.</p>
|
<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" or "receiver application" is specified.</p>
|
||||||
<sample src="SensitiveBroadcast.java" />
|
<sample src="SensitiveCommunication.java" />
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
<references>
|
<references>
|
||||||
<li>
|
|
||||||
CWE:
|
|
||||||
<a href="https://cwe.mitre.org/data/definitions/927.html">CWE-927: Use of Implicit Intent for Sensitive Communication</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
Android Developers:
|
Android Developers:
|
||||||
<a href="https://developer.android.com/guide/components/broadcasts">Security considerations and best practices for sending and receiving broadcasts</a>
|
<a href="https://developer.android.com/guide/components/broadcasts">Security considerations and best practices for sending and receiving broadcasts</a>
|
||||||
@@ -46,5 +40,9 @@
|
|||||||
Android Developers:
|
Android Developers:
|
||||||
<a href="https://developer.android.com/topic/libraries/architecture/livedata">Android LiveData Overview</a>
|
<a href="https://developer.android.com/topic/libraries/architecture/livedata">Android LiveData Overview</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
Oversecured:
|
||||||
|
<a href="https://blog.oversecured.com/Interception-of-Android-implicit-intents/">Interception of Android implicit intents</a>
|
||||||
|
</li>
|
||||||
</references>
|
</references>
|
||||||
</qhelp>
|
</qhelp>
|
||||||
21
java/ql/src/Security/CWE/CWE-927/SensitiveCommunication.ql
Normal file
21
java/ql/src/Security/CWE/CWE-927/SensitiveCommunication.ql
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @name Leaking sensitive information through an implicit Intent
|
||||||
|
* @description An Android application uses implicit Intents containing sensitive data
|
||||||
|
* in a way that exposes it to arbitrary applications on the device.
|
||||||
|
* @kind path-problem
|
||||||
|
* @problem.severity warning
|
||||||
|
* @security-severity 8.2
|
||||||
|
* @precision medium
|
||||||
|
* @id java/android/sensitive-communication
|
||||||
|
* @tags security
|
||||||
|
* external/cwe/cwe-927
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java
|
||||||
|
import semmle.code.java.security.AndroidSensitiveCommunicationQuery
|
||||||
|
import DataFlow::PathGraph
|
||||||
|
|
||||||
|
from SensitiveCommunicationConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||||
|
where cfg.hasFlowPath(source, sink)
|
||||||
|
select sink.getNode(), source, sink, "This call may leak sensitive information from $@.",
|
||||||
|
source.getNode(), "here"
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
/**
|
|
||||||
* @name Broadcasting sensitive data to all Android applications
|
|
||||||
* @description An Android application uses implicit intents to broadcast
|
|
||||||
* sensitive data to all applications without specifying any
|
|
||||||
* receiver permission.
|
|
||||||
* @kind path-problem
|
|
||||||
* @problem.severity warning
|
|
||||||
* @precision medium
|
|
||||||
* @id java/sensitive-broadcast
|
|
||||||
* @tags security
|
|
||||||
* external/cwe/cwe-927
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java
|
|
||||||
import semmle.code.java.dataflow.DataFlow3
|
|
||||||
import semmle.code.java.dataflow.TaintTracking
|
|
||||||
import semmle.code.java.frameworks.android.Intent
|
|
||||||
import semmle.code.java.security.SensitiveActions
|
|
||||||
import DataFlow::PathGraph
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets regular expression for matching names of Android variables that indicate the value being held contains sensitive information.
|
|
||||||
*/
|
|
||||||
private string getAndroidSensitiveInfoRegex() { result = "(?i).*(email|phone|ticket).*" }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method call to pass information to the `Intent` object.
|
|
||||||
*/
|
|
||||||
class PutIntentExtraMethodAccess extends MethodAccess {
|
|
||||||
PutIntentExtraMethodAccess() {
|
|
||||||
(
|
|
||||||
getMethod().getName().matches("put%Extra") or
|
|
||||||
getMethod().hasName("putExtras")
|
|
||||||
) and
|
|
||||||
getMethod().getDeclaringType() instanceof TypeIntent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method call to pass information to the intent extra bundle object.
|
|
||||||
*/
|
|
||||||
class PutBundleExtraMethodAccess extends MethodAccess {
|
|
||||||
PutBundleExtraMethodAccess() {
|
|
||||||
getMethod().getName().regexpMatch("put\\w+") and
|
|
||||||
getMethod().getDeclaringType().getASupertype*().hasQualifiedName("android.os", "BaseBundle")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Finds variables that hold sensitive information judging by their names. */
|
|
||||||
class SensitiveInfoExpr extends Expr {
|
|
||||||
SensitiveInfoExpr() {
|
|
||||||
exists(Variable v | this = v.getAnAccess() |
|
|
||||||
v.getName().regexpMatch([getCommonSensitiveInfoRegex(), getAndroidSensitiveInfoRegex()])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A method access of the `Context.sendBroadcast` family.
|
|
||||||
*/
|
|
||||||
class SendBroadcastMethodAccess extends MethodAccess {
|
|
||||||
SendBroadcastMethodAccess() {
|
|
||||||
this.getMethod().getDeclaringType() instanceof TypeContext and
|
|
||||||
this.getMethod().getName().matches("send%Broadcast%")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NullArgFlowConfig extends DataFlow2::Configuration {
|
|
||||||
NullArgFlowConfig() { this = "Flow configuration with a null argument" }
|
|
||||||
|
|
||||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof NullLiteral }
|
|
||||||
|
|
||||||
override predicate isSink(DataFlow::Node sink) {
|
|
||||||
exists(SendBroadcastMethodAccess ma | sink.asExpr() = ma.getAnArgument())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class EmptyArrayArgFlowConfig extends DataFlow3::Configuration {
|
|
||||||
EmptyArrayArgFlowConfig() { this = "Flow configuration with an empty array argument" }
|
|
||||||
|
|
||||||
override predicate isSource(DataFlow::Node src) {
|
|
||||||
src.asExpr().(ArrayCreationExpr).getFirstDimensionSize() = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isSink(DataFlow::Node sink) {
|
|
||||||
exists(SendBroadcastMethodAccess ma | sink.asExpr() = ma.getAnArgument())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if a `sendBroadcast` call doesn't specify receiver permission.
|
|
||||||
*/
|
|
||||||
predicate isSensitiveBroadcastSink(DataFlow::Node sink) {
|
|
||||||
exists(SendBroadcastMethodAccess ma |
|
|
||||||
sink.asExpr() = ma.getAnArgument() and
|
|
||||||
(
|
|
||||||
ma.getMethod().hasName("sendBroadcast") and
|
|
||||||
(
|
|
||||||
ma.getNumArgument() = 1 // sendBroadcast(Intent intent)
|
|
||||||
or
|
|
||||||
// sendBroadcast(Intent intent, String receiverPermission)
|
|
||||||
exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(1))))
|
|
||||||
)
|
|
||||||
or
|
|
||||||
ma.getMethod().hasName("sendBroadcastAsUser") and
|
|
||||||
(
|
|
||||||
ma.getNumArgument() = 2 or // sendBroadcastAsUser(Intent intent, UserHandle user)
|
|
||||||
exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(2)))) // sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
ma.getMethod().hasName("sendBroadcastWithMultiplePermissions") and
|
|
||||||
exists(EmptyArrayArgFlowConfig config |
|
|
||||||
config.hasFlow(_, DataFlow::exprNode(ma.getArgument(1))) // sendBroadcastWithMultiplePermissions(Intent intent, String[] receiverPermissions)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Method calls of `sendOrderedBroadcast` whose second argument is always `receiverPermission`
|
|
||||||
ma.getMethod().hasName("sendOrderedBroadcast") and
|
|
||||||
(
|
|
||||||
// sendOrderedBroadcast(Intent intent, String receiverPermission) or sendOrderedBroadcast(Intent intent, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
|
|
||||||
exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(1)))) and
|
|
||||||
ma.getNumArgument() <= 7
|
|
||||||
or
|
|
||||||
// sendOrderedBroadcast(Intent intent, String receiverPermission, String receiverAppOp, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)
|
|
||||||
exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(1)))) and
|
|
||||||
exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(2)))) and
|
|
||||||
ma.getNumArgument() = 8
|
|
||||||
)
|
|
||||||
or
|
|
||||||
// Method call of `sendOrderedBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData, Bundle initialExtras)`
|
|
||||||
ma.getMethod().hasName("sendOrderedBroadcastAsUser") and
|
|
||||||
exists(NullArgFlowConfig conf | conf.hasFlow(_, DataFlow::exprNode(ma.getArgument(2))))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Taint configuration tracking flow from variables containing sensitive information to broadcast intents.
|
|
||||||
*/
|
|
||||||
class SensitiveBroadcastConfig extends TaintTracking::Configuration {
|
|
||||||
SensitiveBroadcastConfig() { this = "Sensitive Broadcast Configuration" }
|
|
||||||
|
|
||||||
override predicate isSource(DataFlow::Node source) {
|
|
||||||
source.asExpr() instanceof SensitiveInfoExpr
|
|
||||||
}
|
|
||||||
|
|
||||||
override predicate isSink(DataFlow::Node sink) { isSensitiveBroadcastSink(sink) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if there is an additional flow step from `PutIntentExtraMethodAccess` or `PutBundleExtraMethodAccess` that taints the `Intent` or its extras `Bundle`.
|
|
||||||
*/
|
|
||||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
|
||||||
exists(PutIntentExtraMethodAccess pia |
|
|
||||||
node1.asExpr() = pia.getAnArgument() and node2.asExpr() = pia.getQualifier()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(PutBundleExtraMethodAccess pba |
|
|
||||||
node1.asExpr() = pba.getAnArgument() and node2.asExpr() = pba.getQualifier()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if broadcast doesn't specify receiving package name of the 3rd party app
|
|
||||||
*/
|
|
||||||
override predicate isSanitizer(DataFlow::Node node) {
|
|
||||||
exists(MethodAccess setReceiverMa |
|
|
||||||
setReceiverMa.getMethod().hasName(["setPackage", "setClass", "setClassName", "setComponent"]) and
|
|
||||||
setReceiverMa.getQualifier().(VarAccess).getVariable().getAnAccess() = node.asExpr()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
from SensitiveBroadcastConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
|
||||||
where cfg.hasFlowPath(source, sink)
|
|
||||||
select sink.getNode(), source, sink, "Sending $@ to broadcast.", source.getNode(),
|
|
||||||
"sensitive information"
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
edges
|
|
||||||
| SensitiveBroadcast.java:12:34:12:38 | token : String | SensitiveBroadcast.java:14:31:14:36 | intent |
|
|
||||||
| SensitiveBroadcast.java:13:41:13:52 | refreshToken : String | SensitiveBroadcast.java:14:31:14:36 | intent |
|
|
||||||
| SensitiveBroadcast.java:25:32:25:39 | password : String | SensitiveBroadcast.java:26:31:26:36 | intent |
|
|
||||||
| SensitiveBroadcast.java:36:35:36:39 | email : String | SensitiveBroadcast.java:38:31:38:36 | intent |
|
|
||||||
| SensitiveBroadcast.java:50:9:50:16 | userinfo [post update] [<element>] : String | SensitiveBroadcast.java:52:31:52:36 | intent |
|
|
||||||
| SensitiveBroadcast.java:50:22:50:29 | password : String | SensitiveBroadcast.java:50:9:50:16 | userinfo [post update] [<element>] : String |
|
|
||||||
| SensitiveBroadcast.java:97:35:97:40 | ticket : String | SensitiveBroadcast.java:98:54:98:59 | intent |
|
|
||||||
| SensitiveBroadcast.java:109:32:109:39 | passcode : String | SensitiveBroadcast.java:111:54:111:59 | intent |
|
|
||||||
| SensitiveBroadcast.java:136:33:136:38 | passwd : String | SensitiveBroadcast.java:140:54:140:59 | intent |
|
|
||||||
nodes
|
|
||||||
| SensitiveBroadcast.java:12:34:12:38 | token : String | semmle.label | token : String |
|
|
||||||
| SensitiveBroadcast.java:13:41:13:52 | refreshToken : String | semmle.label | refreshToken : String |
|
|
||||||
| SensitiveBroadcast.java:14:31:14:36 | intent | semmle.label | intent |
|
|
||||||
| SensitiveBroadcast.java:25:32:25:39 | password : String | semmle.label | password : String |
|
|
||||||
| SensitiveBroadcast.java:26:31:26:36 | intent | semmle.label | intent |
|
|
||||||
| SensitiveBroadcast.java:36:35:36:39 | email : String | semmle.label | email : String |
|
|
||||||
| SensitiveBroadcast.java:38:31:38:36 | intent | semmle.label | intent |
|
|
||||||
| SensitiveBroadcast.java:50:9:50:16 | userinfo [post update] [<element>] : String | semmle.label | userinfo [post update] [<element>] : String |
|
|
||||||
| SensitiveBroadcast.java:50:22:50:29 | password : String | semmle.label | password : String |
|
|
||||||
| SensitiveBroadcast.java:52:31:52:36 | intent | semmle.label | intent |
|
|
||||||
| SensitiveBroadcast.java:97:35:97:40 | ticket : String | semmle.label | ticket : String |
|
|
||||||
| SensitiveBroadcast.java:98:54:98:59 | intent | semmle.label | intent |
|
|
||||||
| SensitiveBroadcast.java:109:32:109:39 | passcode : String | semmle.label | passcode : String |
|
|
||||||
| SensitiveBroadcast.java:111:54:111:59 | intent | semmle.label | intent |
|
|
||||||
| SensitiveBroadcast.java:136:33:136:38 | passwd : String | semmle.label | passwd : String |
|
|
||||||
| SensitiveBroadcast.java:140:54:140:59 | intent | semmle.label | intent |
|
|
||||||
subpaths
|
|
||||||
#select
|
|
||||||
| SensitiveBroadcast.java:14:31:14:36 | intent | SensitiveBroadcast.java:12:34:12:38 | token : String | SensitiveBroadcast.java:14:31:14:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:12:34:12:38 | token | sensitive information |
|
|
||||||
| SensitiveBroadcast.java:14:31:14:36 | intent | SensitiveBroadcast.java:13:41:13:52 | refreshToken : String | SensitiveBroadcast.java:14:31:14:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:13:41:13:52 | refreshToken | sensitive information |
|
|
||||||
| SensitiveBroadcast.java:26:31:26:36 | intent | SensitiveBroadcast.java:25:32:25:39 | password : String | SensitiveBroadcast.java:26:31:26:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:25:32:25:39 | password | sensitive information |
|
|
||||||
| SensitiveBroadcast.java:38:31:38:36 | intent | SensitiveBroadcast.java:36:35:36:39 | email : String | SensitiveBroadcast.java:38:31:38:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:36:35:36:39 | email | sensitive information |
|
|
||||||
| SensitiveBroadcast.java:52:31:52:36 | intent | SensitiveBroadcast.java:50:22:50:29 | password : String | SensitiveBroadcast.java:52:31:52:36 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:50:22:50:29 | password | sensitive information |
|
|
||||||
| SensitiveBroadcast.java:98:54:98:59 | intent | SensitiveBroadcast.java:97:35:97:40 | ticket : String | SensitiveBroadcast.java:98:54:98:59 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:97:35:97:40 | ticket | sensitive information |
|
|
||||||
| SensitiveBroadcast.java:111:54:111:59 | intent | SensitiveBroadcast.java:109:32:109:39 | passcode : String | SensitiveBroadcast.java:111:54:111:59 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:109:32:109:39 | passcode | sensitive information |
|
|
||||||
| SensitiveBroadcast.java:140:54:140:59 | intent | SensitiveBroadcast.java:136:33:136:38 | passwd : String | SensitiveBroadcast.java:140:54:140:59 | intent | Sending $@ to broadcast. | SensitiveBroadcast.java:136:33:136:38 | passwd | sensitive information |
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
experimental/Security/CWE/CWE-927/SensitiveBroadcast.ql
|
|
||||||
@@ -11,7 +11,7 @@ class SensitiveBroadcast {
|
|||||||
intent.setAction("com.example.custom_action");
|
intent.setAction("com.example.custom_action");
|
||||||
intent.putExtra("token", token);
|
intent.putExtra("token", token);
|
||||||
intent.putExtra("refreshToken", refreshToken);
|
intent.putExtra("refreshToken", refreshToken);
|
||||||
context.sendBroadcast(intent);
|
context.sendBroadcast(intent); // $ hasTaintFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
// BAD - Tests broadcast of sensitive user information with intent extra.
|
// BAD - Tests broadcast of sensitive user information with intent extra.
|
||||||
@@ -23,7 +23,7 @@ class SensitiveBroadcast {
|
|||||||
intent.setAction("com.example.custom_action");
|
intent.setAction("com.example.custom_action");
|
||||||
intent.putExtra("name", userName);
|
intent.putExtra("name", userName);
|
||||||
intent.putExtra("pwd", password);
|
intent.putExtra("pwd", password);
|
||||||
context.sendBroadcast(intent);
|
context.sendBroadcast(intent); // $ hasTaintFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
// BAD - Tests broadcast of email information with extra bundle.
|
// BAD - Tests broadcast of email information with extra bundle.
|
||||||
@@ -35,7 +35,7 @@ class SensitiveBroadcast {
|
|||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString("email", email);
|
bundle.putString("email", email);
|
||||||
intent.putExtras(bundle);
|
intent.putExtras(bundle);
|
||||||
context.sendBroadcast(intent);
|
context.sendBroadcast(intent); // $ hasTaintFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
// BAD - Tests broadcast of sensitive user information with null permission.
|
// BAD - Tests broadcast of sensitive user information with null permission.
|
||||||
@@ -49,7 +49,7 @@ class SensitiveBroadcast {
|
|||||||
userinfo.add(username);
|
userinfo.add(username);
|
||||||
userinfo.add(password);
|
userinfo.add(password);
|
||||||
intent.putStringArrayListExtra("userinfo", userinfo);
|
intent.putStringArrayListExtra("userinfo", userinfo);
|
||||||
context.sendBroadcast(intent, null);
|
context.sendBroadcast(intent, null); // $ hasTaintFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
// GOOD - Tests broadcast of sensitive user information with permission using string literal.
|
// GOOD - Tests broadcast of sensitive user information with permission using string literal.
|
||||||
@@ -95,7 +95,7 @@ class SensitiveBroadcast {
|
|||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
intent.setAction("com.example.custom_action");
|
intent.setAction("com.example.custom_action");
|
||||||
intent.putExtra("ticket", ticket);
|
intent.putExtra("ticket", ticket);
|
||||||
context.sendBroadcastWithMultiplePermissions(intent, new String[]{});
|
context.sendBroadcastWithMultiplePermissions(intent, new String[]{}); // $ hasTaintFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
// BAD - Tests broadcast of sensitive user information with multiple permissions using empty array initialization through a variable.
|
// BAD - Tests broadcast of sensitive user information with multiple permissions using empty array initialization through a variable.
|
||||||
@@ -108,7 +108,7 @@ class SensitiveBroadcast {
|
|||||||
intent.putExtra("name", username);
|
intent.putExtra("name", username);
|
||||||
intent.putExtra("pwd", passcode);
|
intent.putExtra("pwd", passcode);
|
||||||
String[] perms = new String[0];
|
String[] perms = new String[0];
|
||||||
context.sendBroadcastWithMultiplePermissions(intent, perms);
|
context.sendBroadcastWithMultiplePermissions(intent, perms); // $ hasTaintFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
// GOOD - Tests broadcast of sensitive user information with multiple permissions.
|
// GOOD - Tests broadcast of sensitive user information with multiple permissions.
|
||||||
@@ -137,12 +137,11 @@ class SensitiveBroadcast {
|
|||||||
intent.putExtras(bundle);
|
intent.putExtras(bundle);
|
||||||
String[] perms = new String[0];
|
String[] perms = new String[0];
|
||||||
String[] perms2 = perms;
|
String[] perms2 = perms;
|
||||||
context.sendBroadcastWithMultiplePermissions(intent, perms2);
|
context.sendBroadcastWithMultiplePermissions(intent, perms2); // $ hasTaintFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BAD - Tests broadcast of sensitive user information with multiple permissions using empty array initialization through two variables and `intent.getExtras().putString()`.
|
* BAD - Tests broadcast of sensitive user information with multiple permissions using empty array initialization through two variables and `intent.getExtras().putString()`.
|
||||||
* Note this case of `getExtras().putString(...)` is not yet detected thus is beyond what the query is capable of.
|
|
||||||
*/
|
*/
|
||||||
public void sendBroadcast12(Context context) {
|
public void sendBroadcast12(Context context) {
|
||||||
String username = "test123";
|
String username = "test123";
|
||||||
@@ -156,7 +155,7 @@ class SensitiveBroadcast {
|
|||||||
intent.getExtras().putString("pwd", password);
|
intent.getExtras().putString("pwd", password);
|
||||||
String[] perms = new String[0];
|
String[] perms = new String[0];
|
||||||
String[] perms2 = perms;
|
String[] perms2 = perms;
|
||||||
context.sendBroadcastWithMultiplePermissions(intent, perms2);
|
context.sendBroadcastWithMultiplePermissions(intent, perms2); // $ hasTaintFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
// GOOD - Tests broadcast of sensitive user information with ordered broadcast.
|
// GOOD - Tests broadcast of sensitive user information with ordered broadcast.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import java
|
||||||
|
import semmle.code.java.security.AndroidSensitiveCommunicationQuery
|
||||||
|
import TestUtilities.InlineExpectationsTest
|
||||||
|
import TestUtilities.InlineFlowTest
|
||||||
|
|
||||||
|
class HasFlowTest extends InlineFlowTest {
|
||||||
|
override DataFlow::Configuration getTaintFlowConfig() {
|
||||||
|
result = any(SensitiveCommunicationConfig c)
|
||||||
|
}
|
||||||
|
|
||||||
|
override DataFlow::Configuration getValueFlowConfig() { none() }
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0
|
// semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0
|
||||||
Reference in New Issue
Block a user