Renamed to AndroidIntentRedirection

Added qhelp
This commit is contained in:
Tony Torralba
2021-08-02 12:29:52 +02:00
parent 09d96e65b8
commit fd8a128693
10 changed files with 113 additions and 78 deletions

View File

@@ -1,18 +0,0 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p></p>
</overview>
<recommendation>
<p></p>
</recommendation>
<example>
<p></p>
<sample src="" />
</example>
<references>
<li>
<a href=""></a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,33 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>An exported Android component that obtains a user-provided Intent and uses it to launch another component
can be exploited to obtain access to private, unexported components of the same app or to launch other apps' components
in behalf of the victim app.</p>
</overview>
<recommendation>
<p>Do not export compontents that start other components from a user-provided Intent.
They can be made private by setting the `android:exported` property to `false` in the app's Android Manifest.</p>
<p>If this is not possible, restrict either which apps can send Intents to the affected component, or which components can be started from it.</p>
</recommendation>
<example>
<p>The following snippet contains two examples.
In the first example, an arbitrary component can be started from the externally provided `forward_intent` Intent.
In the second example, the destination component of the Intent is first checked to make sure it is safe.</p>
<sample src="AndroidIntentRedirectionSample.java" />
</example>
<references>
<li>
Google:
<a href="https://support.google.com/faqs/answer/9267555?hl=en">Remediation for Intent Redirection Vulnerability</a>.
</li>
<li>
OWASP Mobile Security Testing Guide:
<a href="https://mobile-security.gitbook.io/mobile-security-testing-guide/android-testing-guide/0x05a-platform-overview#intents">Intents</a>.
</li>
<li>
Android Developers:
<a href="https://developer.android.com/guide/topics/manifest/activity-element#exported">The `android:exported` attribute</a>.
</li>
</references>
</qhelp>

View File

@@ -1,11 +1,13 @@
/** /**
* @name Android Intent redirect * @name Android Intent redirection
* @description xxx * @description Starting Android components with user-provided Intents
* can provide access to internal components of the application,
* increasing the attack surface and potentially causing unintended effects.
* @kind path-problem * @kind path-problem
* @problem.severity error * @problem.severity error
* @security-severity xx * @security-severity 7.5
* @precision high * @precision high
* @id java/android/unsafe-android-webview-fetch * @id java/android/intent-redirection
* @tags security * @tags security
* external/cwe/cwe-926 * external/cwe/cwe-926
* external/cwe/cwe-940 * external/cwe/cwe-940
@@ -13,10 +15,10 @@
import java import java
import semmle.code.java.dataflow.DataFlow import semmle.code.java.dataflow.DataFlow
import semmle.code.java.security.AndroidIntentRedirectQuery import semmle.code.java.security.AndroidIntentRedirectionQuery
import DataFlow::PathGraph import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, IntentRedirectConfiguration conf from DataFlow::PathNode source, DataFlow::PathNode sink, IntentRedirectionConfiguration conf
where conf.hasFlowPath(source, sink) where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, select sink.getNode(), source, sink,
"Arbitrary Android activities or services can be started from $@.", source.getNode(), "Arbitrary Android activities or services can be started from $@.", source.getNode(),

View File

@@ -0,0 +1,18 @@
// BAD: A user-provided Intent is used to launch an arbitrary component
Intent forwardIntent = (Intent) getIntent().getParcelableExtra("forward_intent");
startActivity(forwardIntent);
// GOOD: The destination component is checked before launching it
Intent forwardIntent = (Intent) getIntent().getParcelableExtra("forward_intent");
ComponentName destinationComponent = forwardIntent.resolveActivity(getPackageManager());
if (destinationComponent.getPackageName().equals("safe.package") &&
destinationComponent.getClassName().equals("SafeClass")) {
startActivity(forwardIntent);
}
// GOOD: The component that sent the Intent is checked before launching the destination component
Intent forwardIntent = (Intent) getIntent().getParcelableExtra("forward_intent");
ComponentName originComponent = getCallingActivity();
if (originComponent.getPackageName().equals("trusted.package") && originComponent.getClassName("TrustedClass")) {
startActivity(forwardIntent);
}

View File

@@ -5,26 +5,26 @@ private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.frameworks.android.Intent private import semmle.code.java.frameworks.android.Intent
/** /**
* A sink for Intent redirect vulnerabilities in Android, * A sink for Intent redirection vulnerabilities in Android,
* that is, method calls that start Android components (like activities or services). * that is, method calls that start Android components (like activities or services).
*/ */
abstract class IntentRedirectSink extends DataFlow::Node { } abstract class IntentRedirectionSink extends DataFlow::Node { }
/** A sanitizer for data used to start an Android component. */ /** A sanitizer for data used to start an Android component. */
abstract class IntentRedirectSanitizer extends DataFlow::Node { } abstract class IntentRedirectionSanitizer extends DataFlow::Node { }
/** /**
* A unit class for adding additional taint steps. * A unit class for adding additional taint steps.
* *
* Extend this class to add additional taint steps that should apply to `IntentRedirectConfiguration`. * Extend this class to add additional taint steps that should apply to `IntentRedirectionConfiguration`.
*/ */
class IntentRedirectAdditionalTaintStep extends Unit { class IntentRedirectionAdditionalTaintStep extends Unit {
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2); abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
} }
/** Default sink for Intent redirect vulnerabilities. */ /** Default sink for Intent redirection vulnerabilities. */
private class DefaultIntentRedirectSink extends IntentRedirectSink { private class DefaultIntentRedirectionSink extends IntentRedirectionSink {
DefaultIntentRedirectSink() { DefaultIntentRedirectionSink() {
exists(MethodAccess ma, Method m | exists(MethodAccess ma, Method m |
ma.getMethod() = m and ma.getMethod() = m and
this.asExpr() = ma.getAnArgument() and this.asExpr() = ma.getAnArgument() and

View File

@@ -1,25 +1,25 @@
/** Provides taint tracking configurations to be used in Android Intent Redirect queries. */ /** Provides taint tracking configurations to be used in Android Intent redirection queries. */
import java import java
import semmle.code.java.dataflow.FlowSources import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.security.AndroidIntentRedirect import semmle.code.java.security.AndroidIntentRedirection
/** /**
* A taint tracking configuration for user-provided Intents being used to start Android components. * A taint tracking configuration for user-provided Intents being used to start Android components.
*/ */
class IntentRedirectConfiguration extends TaintTracking::Configuration { class IntentRedirectionConfiguration extends TaintTracking::Configuration {
IntentRedirectConfiguration() { this = "IntentRedirectConfiguration" } IntentRedirectionConfiguration() { this = "IntentRedirectionConfiguration" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectSink } override predicate isSink(DataFlow::Node sink) { sink instanceof IntentRedirectionSink }
override predicate isSanitizer(DataFlow::Node sanitizer) { override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof IntentRedirectSanitizer sanitizer instanceof IntentRedirectionSanitizer
} }
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
any(IntentRedirectAdditionalTaintStep c).step(node1, node2) any(IntentRedirectionAdditionalTaintStep c).step(node1, node2)
} }
} }

View File

@@ -5,50 +5,50 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
public class AndroidIntentRedirectTest extends Activity { public class AndroidIntentRedirectionTest extends Activity {
AndroidIntentRedirectTest(Context base) { AndroidIntentRedirectionTest(Context base) {
super(base); super(base);
} }
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
{ {
Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent"); Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirect startActivities(new Intent[] {intent}); // $ hasAndroidIntentRedirection
startActivities(new Intent[] {intent}, null); // $ hasAndroidIntentRedirect startActivities(new Intent[] {intent}, null); // $ hasAndroidIntentRedirection
startActivity(intent); // $ hasAndroidIntentRedirect startActivity(intent); // $ hasAndroidIntentRedirection
startActivity(intent, null); // $ hasAndroidIntentRedirect startActivity(intent, null); // $ hasAndroidIntentRedirection
startActivityAsUser(intent, null); // $ hasAndroidIntentRedirect startActivityAsUser(intent, null); // $ hasAndroidIntentRedirection
startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirect startActivityAsUser(intent, null, null); // $ hasAndroidIntentRedirection
startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirect startActivityAsCaller(intent, null, false, 0); // $ hasAndroidIntentRedirection
startActivityAsUserFromFragment(null, intent, 0, null, null); // $ hasAndroidIntentRedirect startActivityAsUserFromFragment(null, intent, 0, null, null); // $ hasAndroidIntentRedirection
startActivityForResult(intent, 0); // $ hasAndroidIntentRedirect startActivityForResult(intent, 0); // $ hasAndroidIntentRedirection
startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirect startActivityForResult(intent, 0, null); // $ hasAndroidIntentRedirection
startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirect startActivityForResult(null, intent, 0, null); // $ hasAndroidIntentRedirection
startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirect startActivityForResultAsUser(intent, null, 0, null, null); // $ hasAndroidIntentRedirection
startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirect startActivityForResultAsUser(intent, 0, null, null); // $ hasAndroidIntentRedirection
startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirect startActivityForResultAsUser(intent, 0, null); // $ hasAndroidIntentRedirection
} }
{ {
Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent"); Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
startService(intent); // $ hasAndroidIntentRedirect startService(intent); // $ hasAndroidIntentRedirection
startServiceAsUser(intent, null); // $ hasAndroidIntentRedirect startServiceAsUser(intent, null); // $ hasAndroidIntentRedirection
} }
{ {
Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent"); Intent intent = (Intent) getIntent().getParcelableExtra("forward_intent");
sendBroadcast(intent); // $ hasAndroidIntentRedirect sendBroadcast(intent); // $ hasAndroidIntentRedirection
sendBroadcast(intent, null); // $ hasAndroidIntentRedirect sendBroadcast(intent, null); // $ hasAndroidIntentRedirection
sendBroadcast(intent, null, null); // $ hasAndroidIntentRedirect sendBroadcast(intent, null, null); // $ hasAndroidIntentRedirection
sendBroadcast(intent, null, 0); // $ hasAndroidIntentRedirect sendBroadcast(intent, null, 0); // $ hasAndroidIntentRedirection
sendBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirect sendBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
sendBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirect sendBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
sendBroadcastAsUser(intent, null, null, null); // $ hasAndroidIntentRedirect sendBroadcastAsUser(intent, null, null, null); // $ hasAndroidIntentRedirection
sendBroadcastAsUser(intent, null, null, 0); // $ hasAndroidIntentRedirect sendBroadcastAsUser(intent, null, null, 0); // $ hasAndroidIntentRedirection
sendBroadcastAsUserMultiplePermissions(intent, null, null); // $ hasAndroidIntentRedirect sendBroadcastAsUserMultiplePermissions(intent, null, null); // $ hasAndroidIntentRedirection
sendStickyBroadcast(intent); // $ hasAndroidIntentRedirect sendStickyBroadcast(intent); // $ hasAndroidIntentRedirection
sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirect sendStickyBroadcastAsUser(intent, null); // $ hasAndroidIntentRedirection
sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirect sendStickyBroadcastAsUser(intent, null, null); // $ hasAndroidIntentRedirection
sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirect sendStickyOrderedBroadcast(intent, null, null, 0, null, null); // $ hasAndroidIntentRedirection
sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirect sendStickyOrderedBroadcastAsUser(intent, null, null, null, 0, null, null); // $ hasAndroidIntentRedirection
} }
} }

View File

@@ -1,15 +1,15 @@
import java import java
import semmle.code.java.security.AndroidIntentRedirectQuery import semmle.code.java.security.AndroidIntentRedirectionQuery
import TestUtilities.InlineExpectationsTest import TestUtilities.InlineExpectationsTest
class HasAndroidIntentRedirectTest extends InlineExpectationsTest { class HasAndroidIntentRedirectionTest extends InlineExpectationsTest {
HasAndroidIntentRedirectTest() { this = "HasAndroidIntentRedirectTest" } HasAndroidIntentRedirectionTest() { this = "HasAndroidIntentRedirectionTest" }
override string getARelevantTag() { result = "hasAndroidIntentRedirect" } override string getARelevantTag() { result = "hasAndroidIntentRedirection" }
override predicate hasActualResult(Location location, string element, string tag, string value) { override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasAndroidIntentRedirect" and tag = "hasAndroidIntentRedirection" and
exists(DataFlow::Node src, DataFlow::Node sink, IntentRedirectConfiguration conf | exists(DataFlow::Node src, DataFlow::Node sink, IntentRedirectionConfiguration conf |
conf.hasFlow(src, sink) conf.hasFlow(src, sink)
| |
sink.getLocation() = location and sink.getLocation() = location and

View File

@@ -9,7 +9,7 @@
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppTheme" > android:theme="@style/AppTheme" >
<activity <activity
android:name=".AndroidIntentRedirectTest" android:name=".AndroidIntentRedirectionTest"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:label="@string/app_name"> android:label="@string/app_name">
<intent-filter> <intent-filter>