mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Added query ImplicitPendingIntents
This commit is contained in:
@@ -116,6 +116,7 @@ private module Frameworks {
|
||||
private import semmle.code.java.security.ResponseSplitting
|
||||
private import semmle.code.java.security.InformationLeak
|
||||
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.JndiInjection
|
||||
private import semmle.code.java.security.LdapInjection
|
||||
|
||||
@@ -87,6 +87,11 @@ class AndroidBundle extends Class {
|
||||
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. */
|
||||
class ExplicitIntent extends Expr {
|
||||
ExplicitIntent() {
|
||||
|
||||
171
java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll
Normal file
171
java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll
Normal 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"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
43
java/ql/src/Security/CWE/CWE-927/ImplicitPendingIntents.java
Normal file
43
java/ql/src/Security/CWE/CWE-927/ImplicitPendingIntents.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
22
java/ql/src/Security/CWE/CWE-927/ImplicitPendingIntents.ql
Normal file
22
java/ql/src/Security/CWE/CWE-927/ImplicitPendingIntents.ql
Normal 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"
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user