Split ArbitraryApkInstallation file into 3 files

This commit is contained in:
Ed Minnix
2023-03-07 18:11:25 -05:00
parent 2d1088e923
commit 3ea167cadf
3 changed files with 191 additions and 184 deletions

View File

@@ -0,0 +1,87 @@
/** Provide classes to reason about Android Intents that can install APKs. */
import java
import semmle.code.java.frameworks.android.Intent
import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.FlowSources
/** A string literal that represents the MIME type for Android APKs. */
class PackageArchiveMimeTypeLiteral extends StringLiteral {
PackageArchiveMimeTypeLiteral() { this.getValue() = "application/vnd.android.package-archive" }
}
/** The `android.content.Intent.ACTION_INSTALL_PACKAGE` constant. */
class InstallPackageAction extends Expr {
InstallPackageAction() {
this.(StringLiteral).getValue() = "android.intent.action.INSTALL_PACKAGE"
or
exists(VarAccess va |
va.getVariable().hasName("ACTION_INSTALL_PACKAGE") and
va.getQualifier().getType() instanceof TypeIntent
)
}
}
/** A method that sets the MIME type of an intent. */
class SetTypeMethod extends Method {
SetTypeMethod() {
this.hasName(["setType", "setTypeAndNormalize"]) and
this.getDeclaringType() instanceof TypeIntent
}
}
/** A method that sets the data URI and the MIME type of an intent. */
class SetDataAndTypeMethod extends Method {
SetDataAndTypeMethod() {
this.hasName(["setDataAndType", "setDataAndTypeAndNormalize"]) and
this.getDeclaringType() instanceof TypeIntent
}
}
/** A method that sets the data URI of an intent. */
class SetDataMethod extends Method {
SetDataMethod() {
this.hasName(["setData", "setDataAndNormalize", "setDataAndType", "setDataAndTypeAndNormalize"]) and
this.getDeclaringType() instanceof TypeIntent
}
}
/** A dataflow sink for the URI of an intent. */
class SetDataSink extends DataFlow::ExprNode {
SetDataSink() {
exists(MethodAccess ma |
this.getExpr() = ma.getQualifier() and
ma.getMethod() instanceof SetDataMethod
)
}
}
/** A method that generates a URI. */
class UriConstructorMethod extends Method {
UriConstructorMethod() {
this.hasQualifiedName("android.net", "Uri", ["fromFile", "fromParts"]) or
this.hasQualifiedName("androidx.core.content", "FileProvider", "getUriForFile")
}
}
/**
* A dataflow source representing the URIs which an APK not controlled by the
* application may come from. Including external storage and web URLs.
*/
class ExternalApkSource extends DataFlow::Node {
ExternalApkSource() {
sourceNode(this, "android-external-storage-dir") or
this.asExpr().(MethodAccess).getMethod() instanceof UriConstructorMethod or
this.asExpr().(StringLiteral).getValue().matches("file://%") or
this instanceof RemoteFlowSource
}
}
/** The `setAction` method of the `android.content.Intent` class. */
class SetActionMethod extends Method {
SetActionMethod() {
this.hasName("setAction") and
this.getDeclaringType() instanceof TypeIntent
}
}

View File

@@ -0,0 +1,103 @@
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.dataflow.TaintTracking3
private import semmle.code.java.security.ArbitraryApkInstallation
/**
* A dataflow configuration for flow from an external source of an APK to the
* `setData[AndType][AndNormalize]` method of an intent.
*/
class ApkConfiguration extends DataFlow::Configuration {
ApkConfiguration() { this = "ApkConfiguration" }
override predicate isSource(DataFlow::Node node) { node instanceof ExternalApkSource }
override predicate isSink(DataFlow::Node node) {
exists(MethodAccess ma |
ma.getMethod() instanceof SetDataMethod and
ma.getArgument(0) = node.asExpr() and
(
any(PackageArchiveMimeTypeConfiguration c).hasFlowToExpr(ma.getQualifier())
or
any(InstallPackageActionConfiguration c).hasFlowToExpr(ma.getQualifier())
)
)
}
}
/**
* A dataflow configuration tracking the flow from the `android.content.Intent.ACTION_INSTALL_PACKAGE`
* constant to either the constructor of an intent or the `setAction` method of an intent.
*
* This is used to track if an intent is used to install an APK.
*/
private class InstallPackageActionConfiguration extends TaintTracking3::Configuration {
InstallPackageActionConfiguration() { this = "InstallPackageActionConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof InstallPackageAction
}
override predicate isAdditionalTaintStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
state1 instanceof DataFlow::FlowStateEmpty and
state2 = "hasPackageInstallAction" and
(
exists(ConstructorCall cc |
cc.getConstructedType() instanceof TypeIntent and
node1.asExpr() = cc.getArgument(0) and
node1.asExpr().getType() instanceof TypeString and
node2.asExpr() = cc
)
or
exists(MethodAccess ma |
ma.getMethod() instanceof SetActionMethod and
node1.asExpr() = ma.getArgument(0) and
node2.asExpr() = ma.getQualifier()
)
)
}
override predicate isSink(DataFlow::Node node, DataFlow::FlowState state) {
state = "hasPackageInstallAction" and node.asExpr().getType() instanceof TypeIntent
}
}
/**
* A dataflow configuration tracking the flow of the Android APK MIME type to
* the `setType` or `setTypeAndNormalize` method of an intent, followed by a call
* to `setData[AndType][AndNormalize]`.
*/
private class PackageArchiveMimeTypeConfiguration extends TaintTracking2::Configuration {
PackageArchiveMimeTypeConfiguration() { this = "PackageArchiveMimeTypeConfiguration" }
override predicate isSource(DataFlow::Node node) {
node.asExpr() instanceof PackageArchiveMimeTypeLiteral
}
override predicate isAdditionalTaintStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
state1 instanceof DataFlow::FlowStateEmpty and
state2 = "typeSet" and
exists(MethodAccess ma |
ma.getQualifier() = node2.asExpr() and
(
ma.getMethod() instanceof SetTypeMethod and
ma.getArgument(0) = node1.asExpr()
or
ma.getMethod() instanceof SetDataAndTypeMethod and
ma.getArgument(1) = node1.asExpr()
)
)
}
override predicate isSink(DataFlow::Node node, DataFlow::FlowState state) {
state = "typeSet" and
node instanceof SetDataSink
}
}

View File

@@ -11,192 +11,9 @@
*/
import java
import semmle.code.java.frameworks.android.Intent
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking2
import semmle.code.java.dataflow.TaintTracking3
import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.dataflow.ExternalFlow
import semmle.code.java.security.ArbitraryApkInstallationQuery
import DataFlow::PathGraph
/** A string literal that represents the MIME type for Android APKs. */
class PackageArchiveMimeTypeLiteral extends StringLiteral {
PackageArchiveMimeTypeLiteral() { this.getValue() = "application/vnd.android.package-archive" }
}
/** The `android.content.Intent.ACTION_INSTALL_PACKAGE` constant. */
class InstallPackageAction extends Expr {
InstallPackageAction() {
this.(StringLiteral).getValue() = "android.intent.action.INSTALL_PACKAGE"
or
exists(VarAccess va |
va.getVariable().hasName("ACTION_INSTALL_PACKAGE") and
va.getQualifier().getType() instanceof TypeIntent
)
}
}
/** A method that sets the MIME type of an intent. */
class SetTypeMethod extends Method {
SetTypeMethod() {
this.hasName(["setType", "setTypeAndNormalize"]) and
this.getDeclaringType() instanceof TypeIntent
}
}
/** A method that sets the data URI and the MIME type of an intent. */
class SetDataAndTypeMethod extends Method {
SetDataAndTypeMethod() {
this.hasName(["setDataAndType", "setDataAndTypeAndNormalize"]) and
this.getDeclaringType() instanceof TypeIntent
}
}
/** A method that sets the data URI of an intent. */
class SetDataMethod extends Method {
SetDataMethod() {
this.hasName(["setData", "setDataAndNormalize", "setDataAndType", "setDataAndTypeAndNormalize"]) and
this.getDeclaringType() instanceof TypeIntent
}
}
/** A dataflow sink for the URI of an intent. */
class SetDataSink extends DataFlow::ExprNode {
SetDataSink() {
exists(MethodAccess ma |
this.getExpr() = ma.getQualifier() and
ma.getMethod() instanceof SetDataMethod
)
}
}
/** A method that generates a URI. */
class UriConstructorMethod extends Method {
UriConstructorMethod() {
this.hasQualifiedName("android.net", "Uri", ["fromFile", "fromParts"]) or
this.hasQualifiedName("androidx.core.content", "FileProvider", "getUriForFile")
}
}
/**
* A dataflow source representing the URIs which an APK not controlled by the
* application may come from. Including external storage and web URLs.
*/
class ExternalApkSource extends DataFlow::Node {
ExternalApkSource() {
sourceNode(this, "android-external-storage-dir") or
this.asExpr().(MethodAccess).getMethod() instanceof UriConstructorMethod or
this.asExpr().(StringLiteral).getValue().matches("file://%") or
this instanceof RemoteFlowSource
}
}
/**
* A dataflow configuration for flow from an external source of an APK to the
* `setData[AndType][AndNormalize]` method of an intent.
*/
class ApkConfiguration extends DataFlow::Configuration {
ApkConfiguration() { this = "ApkConfiguration" }
override predicate isSource(DataFlow::Node node) { node instanceof ExternalApkSource }
override predicate isSink(DataFlow::Node node) {
exists(MethodAccess ma |
ma.getMethod() instanceof SetDataMethod and
ma.getArgument(0) = node.asExpr() and
(
any(PackageArchiveMimeTypeConfiguration c).hasFlowToExpr(ma.getQualifier())
or
any(InstallPackageActionConfiguration c).hasFlowToExpr(ma.getQualifier())
)
)
}
}
/** The `setAction` method of the `android.content.Intent` class. */
class SetActionMethod extends Method {
SetActionMethod() {
this.hasName("setAction") and
this.getDeclaringType() instanceof TypeIntent
}
}
/**
* A dataflow configuration tracking the flow from the `android.content.Intent.ACTION_INSTALL_PACKAGE`
* constant to either the constructor of an intent or the `setAction` method of an intent.
*
* This is used to track if an intent is used to install an APK.
*/
private class InstallPackageActionConfiguration extends TaintTracking3::Configuration {
InstallPackageActionConfiguration() { this = "InstallPackageActionConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof InstallPackageAction
}
override predicate isAdditionalTaintStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
state1 instanceof DataFlow::FlowStateEmpty and
state2 = "hasPackageInstallAction" and
(
exists(ConstructorCall cc |
cc.getConstructedType() instanceof TypeIntent and
node1.asExpr() = cc.getArgument(0) and
node1.asExpr().getType() instanceof TypeString and
node2.asExpr() = cc
)
or
exists(MethodAccess ma |
ma.getMethod() instanceof SetActionMethod and
node1.asExpr() = ma.getArgument(0) and
node2.asExpr() = ma.getQualifier()
)
)
}
override predicate isSink(DataFlow::Node node, DataFlow::FlowState state) {
state = "hasPackageInstallAction" and node.asExpr().getType() instanceof TypeIntent
}
}
/**
* A dataflow configuration tracking the flow of the Android APK MIME type to
* the `setType` or `setTypeAndNormalize` method of an intent, followed by a call
* to `setData[AndType][AndNormalize]`.
*/
private class PackageArchiveMimeTypeConfiguration extends TaintTracking2::Configuration {
PackageArchiveMimeTypeConfiguration() { this = "PackageArchiveMimeTypeConfiguration" }
override predicate isSource(DataFlow::Node node) {
node.asExpr() instanceof PackageArchiveMimeTypeLiteral
}
override predicate isAdditionalTaintStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
state1 instanceof DataFlow::FlowStateEmpty and
state2 = "typeSet" and
exists(MethodAccess ma |
ma.getQualifier() = node2.asExpr() and
(
ma.getMethod() instanceof SetTypeMethod and
ma.getArgument(0) = node1.asExpr()
or
ma.getMethod() instanceof SetDataAndTypeMethod and
ma.getArgument(1) = node1.asExpr()
)
)
}
override predicate isSink(DataFlow::Node node, DataFlow::FlowState state) {
state = "typeSet" and
node instanceof SetDataSink
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink, ApkConfiguration config
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "Arbitrary Android APK installation."