mirror of
https://github.com/github/codeql.git
synced 2025-12-21 03:06: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.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
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
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