Added query ImplicitPendingIntents

This commit is contained in:
Tony Torralba
2021-09-30 11:28:39 +02:00
parent 68385dfab5
commit d0077b8c12
8 changed files with 405 additions and 0 deletions

View File

@@ -116,6 +116,7 @@ private module Frameworks {
private import semmle.code.java.security.ResponseSplitting private import semmle.code.java.security.ResponseSplitting
private import semmle.code.java.security.InformationLeak private import semmle.code.java.security.InformationLeak
private import semmle.code.java.security.GroovyInjection private import semmle.code.java.security.GroovyInjection
private import semmle.code.java.security.ImplicitPendingIntents
private import semmle.code.java.security.JexlInjectionSinkModels private import semmle.code.java.security.JexlInjectionSinkModels
private import semmle.code.java.security.JndiInjection private import semmle.code.java.security.JndiInjection
private import semmle.code.java.security.LdapInjection private import semmle.code.java.security.LdapInjection

View File

@@ -87,6 +87,11 @@ class AndroidBundle extends Class {
AndroidBundle() { this.getASupertype*().hasQualifiedName("android.os", "BaseBundle") } AndroidBundle() { this.getASupertype*().hasQualifiedName("android.os", "BaseBundle") }
} }
/** The class `android.app.PendingIntent`. */
class PendingIntent extends Class {
PendingIntent() { this.hasQualifiedName("android.app", "PendingIntent") }
}
/** An `Intent` that explicitly sets a destination component. */ /** An `Intent` that explicitly sets a destination component. */
class ExplicitIntent extends Expr { class ExplicitIntent extends Expr {
ExplicitIntent() { ExplicitIntent() {

View File

@@ -0,0 +1,171 @@
/** Provides classes and predicates for working with implicit `PendingIntent`s. */
import java
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.TaintTracking
private import semmle.code.java.frameworks.android.Intent
private class PendingIntentModels extends SinkModelCsv {
override predicate row(string row) {
row =
[
"android.app;PendingIntent;false;getActivity;;;Argument[2];pending-intent",
"android.app;PendingIntent;false;getActivities;;;Argument[2];pending-intent",
"android.app;PendingIntent;false;getBroadcast;;;Argument[2];pending-intent",
"android.app;PendingIntent;false;getService;;;Argument[2];pending-intent"
]
}
}
// TODO: Remove when https://github.com/github/codeql/pull/6397 gets merged
private class DefaultIntentRedirectionSinkModel extends SinkModelCsv {
override predicate row(string row) {
row =
[
"android.app;Activity;true;startActivityAsCaller;;;Argument[0];intent-start",
"android.app;Activity;true;startActivityForResult;(Intent,int);;Argument[0];intent-start",
"android.app;Activity;true;startActivityForResult;(Intent,int,Bundle);;Argument[0];intent-start",
"android.app;Activity;true;startActivityForResult;(String,Intent,int,Bundle);;Argument[1];intent-start",
"android.app;Activity;true;startActivityForResultAsUser;;;Argument[0];intent-start",
"android.content;Context;true;startActivities;;;Argument[0];intent-start",
"android.content;Context;true;startActivity;;;Argument[0];intent-start",
"android.content;Context;true;startActivityAsUser;;;Argument[0];intent-start",
"android.content;Context;true;startActivityFromChild;;;Argument[1];intent-start",
"android.content;Context;true;startActivityFromFragment;;;Argument[1];intent-start",
"android.content;Context;true;startActivityIfNeeded;;;Argument[0];intent-start",
"android.content;Context;true;startService;;;Argument[0];intent-start",
"android.content;Context;true;startServiceAsUser;;;Argument[0];intent-start",
"android.content;Context;true;sendBroadcast;;;Argument[0];intent-start",
"android.content;Context;true;sendBroadcastAsUser;;;Argument[0];intent-start",
"android.content;Context;true;sendBroadcastWithMultiplePermissions;;;Argument[0];intent-start",
"android.content;Context;true;sendStickyBroadcast;;;Argument[0];intent-start",
"android.content;Context;true;sendStickyBroadcastAsUser;;;Argument[0];intent-start",
"android.content;Context;true;sendStickyOrderedBroadcast;;;Argument[0];intent-start",
"android.content;Context;true;sendStickyOrderedBroadcastAsUser;;;Argument[0];intent-start"
]
}
}
// TODO: Remove when https://github.com/github/codeql/pull/6599 gets merged
private class IntentBundleFlowSteps extends SummaryModelCsv {
override predicate row(string row) {
row =
[
//"namespace;type;subtypes;name;signature;ext;input;output;kind"
"android.os;BaseBundle;true;get;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;BaseBundle;true;getString;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;BaseBundle;true;getString;(String,String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;BaseBundle;true;getString;(String,String);;Argument[1];ReturnValue;value",
"android.os;BaseBundle;true;getStringArray;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;BaseBundle;true;keySet;();;MapKey of Argument[-1];Element of ReturnValue;value",
"android.os;BaseBundle;true;putAll;(PersistableBundle);;MapKey of Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putAll;(PersistableBundle);;MapValue of Argument[0];MapValue of Argument[-1];value",
"android.os;BaseBundle;true;putBoolean;;;Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putBooleanArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putDouble;;;Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putDoubleArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putInt;;;Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putIntArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putLong;;;Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putLongArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putString;;;Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putString;;;Argument[1];MapValue of Argument[-1];value",
"android.os;BaseBundle;true;putStringArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;BaseBundle;true;putStringArray;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;getBinder;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getBundle;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getByteArray;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getCharArray;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getCharSequence;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getCharSequence;(String,CharSequence);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getCharSequence;(String,CharSequence);;Argument[1];ReturnValue;value",
"android.os;Bundle;true;getCharSequenceArray;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getCharSequenceArrayList;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getParcelable;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getParcelableArray;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getParcelableArrayList;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getSerializable;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getSparseParcelableArray;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;getStringArrayList;(String);;MapValue of Argument[-1];ReturnValue;value",
"android.os;Bundle;true;putAll;(Bundle);;MapKey of Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putAll;(Bundle);;MapValue of Argument[0];MapValue of Argument[-1];value",
"android.os;Bundle;true;putBinder;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putBinder;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putBundle;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putBundle;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putByte;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putByteArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putByteArray;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putChar;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putCharArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putCharArray;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putCharSequence;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putCharSequence;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putCharSequenceArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putCharSequenceArray;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putCharSequenceArrayList;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putCharSequenceArrayList;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putFloat;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putFloatArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putIntegerArrayList;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putParcelable;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putParcelable;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putParcelableArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putParcelableArray;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putParcelableArrayList;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putParcelableArrayList;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putSerializable;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putSerializable;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putShort;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putShortArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putSize;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putSizeF;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putSparceParcelableArray;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putSparseParcelableArray;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;putStringArrayList;;;Argument[0];MapKey of Argument[-1];value",
"android.os;Bundle;true;putStringArrayList;;;Argument[1];MapValue of Argument[-1];value",
"android.os;Bundle;true;readFromParcel;;;Argument[0];MapKey of Argument[-1];taint",
"android.os;Bundle;true;readFromParcel;;;Argument[0];MapValue of Argument[-1];taint",
"android.content;Intent;true;getExtras;();;SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getBundleExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getByteArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getCharArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getCharSequenceArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getCharSequenceArrayListExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getCharSequenceExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getParcelableArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getParcelableArrayListExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getParcelableExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getSerializableExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getStringArrayExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getStringArrayListExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;getStringExtra;(String);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];ReturnValue;value",
"android.content;Intent;true;putCharSequenceArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putCharSequenceArrayListExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putCharSequenceArrayListExtra;;;Argument[-1];ReturnValue;value",
"android.content;Intent;true;putExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putExtra;;;Argument[-1];ReturnValue;value",
"android.content;Intent;true;putIntegerArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putIntegerArrayListExtra;;;Argument[-1];ReturnValue;value",
"android.content;Intent;true;putParcelableArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putParcelableArrayListExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putParcelableArrayListExtra;;;Argument[-1];ReturnValue;value",
"android.content;Intent;true;putStringArrayListExtra;;;Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putStringArrayListExtra;;;Argument[1];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putStringArrayListExtra;;;Argument[-1];ReturnValue;value",
"android.content;Intent;true;putExtras;(Bundle);;MapKey of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putExtras;(Bundle);;MapValue of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putExtras;(Bundle);;Argument[-1];ReturnValue;value",
"android.content;Intent;true;putExtras;(Intent);;MapKey of SyntheticField[android.content.Intent.extras] of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putExtras;(Intent);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;putExtras;(Intent);;Argument[-1];ReturnValue;value",
"android.content;Intent;true;replaceExtras;(Bundle);;MapKey of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;replaceExtras;(Bundle);;MapValue of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;replaceExtras;(Bundle);;Argument[-1];ReturnValue;value",
"android.content;Intent;true;replaceExtras;(Intent);;MapKey of SyntheticField[android.content.Intent.extras] of Argument[0];MapKey of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;replaceExtras;(Intent);;MapValue of SyntheticField[android.content.Intent.extras] of Argument[0];MapValue of SyntheticField[android.content.Intent.extras] of Argument[-1];value",
"android.content;Intent;true;replaceExtras;(Intent);;Argument[-1];ReturnValue;value"
]
}
}

View File

@@ -0,0 +1,98 @@
/** Provides taint tracking configurations to be used in queries related to implicit `PendingIntent`s. */
import java
import semmle.code.java.dataflow.ExternalFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.frameworks.android.Intent
import semmle.code.java.security.ImplicitPendingIntents
/**
* A taint tracking configuration for implicit `PendingIntent`s
* being wrapped in another implicit `Intent` that gets started.
*/
class ImplicitPendingIntentStartConf extends TaintTracking::Configuration {
ImplicitPendingIntentStartConf() { this = "ImplicitPendingIntentStartConf" }
override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof ImplicitPendingIntentCreation
}
override predicate isSink(DataFlow::Node sink) {
sink instanceof IntentStartSink and
// startService can't actually start implicit intents since API 21
not exists(MethodAccess ma, Method m |
ma.getMethod() = m and
m.getDeclaringType().getASupertype*() instanceof TypeContext and
m.hasName("startService") and
sink.asExpr() = ma.getArgument(0)
)
}
override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof ExplicitIntentSanitizer
}
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
super.allowImplicitRead(node, c)
or
this.isSink(node)
}
}
private class ImplicitPendingIntentCreation extends Expr {
ImplicitPendingIntentCreation() {
exists(Argument arg |
this.getType() instanceof PendingIntent and
exists(ImplicitPendingIntentConf conf | conf.hasFlowTo(DataFlow::exprNode(arg))) and
arg.getCall() = this
)
}
}
private class IntentStartSink extends DataFlow::Node {
IntentStartSink() { sinkNode(this, "intent-start") }
}
private class ImplicitPendingIntentConf extends DataFlow2::Configuration {
ImplicitPendingIntentConf() { this = "PendingIntentConf" }
override predicate isSource(DataFlow::Node source) {
exists(ClassInstanceExpr cc |
cc.getConstructedType() instanceof TypeIntent and source.asExpr() = cc
)
}
override predicate isSink(DataFlow::Node sink) { sink instanceof MutablePendingIntentSink }
override predicate isBarrier(DataFlow::Node barrier) {
barrier instanceof ExplicitIntentSanitizer
}
}
private class PendingIntentSink extends DataFlow::Node {
PendingIntentSink() { sinkNode(this, "pending-intent") }
}
private class MutablePendingIntentSink extends PendingIntentSink {
MutablePendingIntentSink() {
exists(Argument flagArgument |
flagArgument = this.asExpr().(Argument).getCall().getArgument(3)
|
// API < 31, PendingIntents are mutable by default
not TaintTracking::localExprTaint(getPendingIntentFlagAccess("FLAG_IMMUTABLE"), flagArgument)
or
// API >= 31, PendingIntents need to explicitly set mutability
TaintTracking::localExprTaint(getPendingIntentFlagAccess("FLAG_MUTABLE"), flagArgument)
)
}
}
private Expr getPendingIntentFlagAccess(string flagName) {
exists(Field f |
f.getDeclaringType() instanceof PendingIntent and
f.isPublic() and
f.isFinal() and
f.hasName(flagName) and
f.getAnAccess() = result
)
}

View File

@@ -0,0 +1,43 @@
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
public class ImplicitPendingIntents extends Activity {
public void onCreate(Bundle savedInstance) {
{
// BAD: an implicit Intent is used to create a PendingIntent.
// The PendingIntent is then added to another implicit Intent
// and started.
Intent baseIntent = new Intent();
PendingIntent pi =
PendingIntent.getActivity(this, 0, baseIntent, PendingIntent.FLAG_ONE_SHOT);
Intent fwdIntent = new Intent("SOME_ACTION");
fwdIntent.putExtra("fwdIntent", pi);
sendBroadcast(fwdIntent);
}
{
// GOOD: both the PendingIntent and the wrapping Intent are explicit.
Intent safeIntent = new Intent(this, AnotherActivity.class);
PendingIntent pi =
PendingIntent.getActivity(this, 0, safeIntent, PendingIntent.FLAG_ONE_SHOT);
Intent fwdIntent = new Intent();
fwdIntent.setClassName("destination.package", "DestinationClass");
fwdIntent.putExtra("fwdIntent", pi);
startActivity(fwdIntent);
}
{
// GOOD: The PendingIntent is created with FLAG_IMMUTABLE.
Intent baseIntent = new Intent("SOME_ACTION");
PendingIntent pi =
PendingIntent.getActivity(this, 0, baseIntent, PendingIntent.FLAG_IMMUTABLE);
Intent fwdIntent = new Intent();
fwdIntent.setClassName("destination.package", "DestinationClass");
fwdIntent.putExtra("fwdIntent", pi);
startActivity(fwdIntent);
}
}
}

View File

@@ -0,0 +1,58 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>A <code>PendingIntent</code> describes an action in the form of an Intent that is intended to be given and executed
at a later time by another application. The Intent wrapped by a <code>PendingIntent</code> is executed on behalf of
the application that created it, and with its same privileges.</p>
<p>If a <code>PendingIntent</code> is configured to be mutable, the fields of its internal Intent can be changed by the
receiving application if they were not previously set. This means that a mutable <code>PendingIntent</code> that has
not defined a destination component (that is, an implicit <code>PendingIntent</code>) can be directed to any component
by the receiving application, and execute an arbitrary action with the privileges of the application that created it.</p>
<p>If an implicit <code>PendingIntent</code> is wrapped and sent as an extra of an Intent that can be intercepted (that
is, again, an implicit Intent), any malicious application could obtain the <code>PendingIntent</code>, modify the
underlying Intent with an arbitrary destination component, and execute the desired action with elevated privileges.
This could give the malicious application access to private components of the victim application, or the ability to
perform actions without having the necessary permissions.</p>
</overview>
<recommendation>
<p>Avoid creating implicit <code>PendingIntent</code>s. This means that the underlying Intent should always have an
explicit destination component.</p>
<p>Also, when adding the <code>PendingIntent</code> as an extra of another Intent, make sure that said Intent also has
an explicit destination component, so that it is not delivered to untrusted applications.</p>
<p>It is also recommended to create the <code>PendingIntent</code> using the flag <code>FLAG_IMMUTABLE</code> whenever
possible, to prevent the destination component from modifying empty fields of the underlying Intent.</p>
</recommendation>
<example>
<p>In the following examples, a <code>PendingIntent</code> is created and wrapped as an extra of another Intent.
</p>
<p>In the first example, both the <code>PendingIntent</code> and the Intent it is wrapped in are implicit,
reproducing the vulnerability.</p>
<p>In the second example, the issue is avoided by adding explicit destination components to the
<code>PendingIntent</code> and the wrapping Intent.</p>
<p>The third example uses the <code>FLAG_IMMUTABLE</code> flag to prevent the underlying Intent from being modified
by the destination component.</p>
<sample src="ImplicitPendingIntents.java" />
</example>
<references>
<li>
Google Help:
<a href="https://support.google.com/faqs/answer/10437428?hl=en">
Remediation for Implicit PendingIntent Vulnerability
</a>
</li>
<li>
University of Potsdam:
<a href="https://www.cs.uni-potsdam.de/se/papers/esorics18.pdf">
PIAnalyzer: A precise approach for PendingIntent vulnerability analysis
</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,22 @@
/**
* @name Use of implicit Pending Intents
* @description Implicit and mutable PendingIntents being wrapped and sent in another implicit
* Intent may provide access to internal components of the application or cause other
* unintended effects.
* @kind path-problem
* @problem.severity error
* @security-severity 8.2
* @precision high
* @id java/android/pending-intents
* @tags security
* external/cwe/cwe-927
*/
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.security.ImplicitPendingIntentsQuery
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink
where any(ImplicitPendingIntentStartConf conf).hasFlowPath(source, sink)
select sink.getNode(), source, sink, "something"

View File

@@ -0,0 +1,7 @@
---
category: newQuery
---
* A new query "Use of implicit Pending Intents" (`java/android/pending-intents`) has been added.
This query finds implicit and mutable PendingIntents being wrapped and sent in another implicit Intent,
which can provide access to internal components of the application or cause other unintended
effects.