mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Arbitrary APK Installation MVP
This commit is contained in:
105
java/ql/src/Security/CWE/CWE-094/ArbitraryAPKInstallation.ql
Normal file
105
java/ql/src/Security/CWE/CWE-094/ArbitraryAPKInstallation.ql
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @name Android APK installation
|
||||
* @description Installing an APK from an untrusted source.
|
||||
* @kind path-problem
|
||||
* @tags security
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.frameworks.android.Intent
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
class PackageArchiveMimeTypeLiteral extends StringLiteral {
|
||||
PackageArchiveMimeTypeLiteral() { this.getValue() = "application/vnd.android.package-archive" }
|
||||
}
|
||||
|
||||
class SetTypeMethod extends Method {
|
||||
SetTypeMethod() {
|
||||
this.hasName(["setType", "setTypeAndNormalize"]) and
|
||||
this.getDeclaringType().getASupertype*() instanceof TypeIntent
|
||||
}
|
||||
}
|
||||
|
||||
class SetDataAndTypeMethod extends Method {
|
||||
SetDataAndTypeMethod() {
|
||||
this.hasName(["setDataAndType", "setDataAndTypeAndNormalize"]) and
|
||||
this.getDeclaringType().getASupertype*() instanceof TypeIntent
|
||||
}
|
||||
}
|
||||
|
||||
class SetDataMethod extends Method {
|
||||
SetDataMethod() {
|
||||
this.hasName(["setData", "setDataAndNormalize", "setDataAndType", "setDataAndTypeAndNormalize"]) and
|
||||
this.getDeclaringType().getASupertype*() instanceof TypeIntent
|
||||
}
|
||||
}
|
||||
|
||||
class SetDataSink extends DataFlow::ExprNode {
|
||||
SetDataSink() { this.getExpr().(MethodAccess).getMethod() instanceof SetDataMethod }
|
||||
|
||||
DataFlow::ExprNode getUri() { result.asExpr() = this.getExpr().(MethodAccess).getArgument(0) }
|
||||
}
|
||||
|
||||
class UriParseMethod extends Method {
|
||||
UriParseMethod() {
|
||||
this.hasName(["parse", "fromFile"]) and
|
||||
this.getDeclaringType().hasQualifiedName("android.net", "Uri")
|
||||
}
|
||||
}
|
||||
|
||||
class ExternalSource extends DataFlow::Node {
|
||||
ExternalSource() {
|
||||
sourceNode(this, "android-external-storage-dir") or
|
||||
this.asExpr().(MethodAccess).getMethod() instanceof UriParseMethod or
|
||||
this.asExpr().(StringLiteral).getValue().matches(["file://%", "http://%", "https://%"])
|
||||
}
|
||||
}
|
||||
|
||||
class ExternalSourceConfiguration extends DataFlow2::Configuration {
|
||||
ExternalSourceConfiguration() { this = "ExternalSourceConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof ExternalSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
// any(PackageArchiveMimeTypeConfiguration c).hasFlow(_, node)
|
||||
node instanceof SetDataSink
|
||||
}
|
||||
}
|
||||
|
||||
class PackageArchiveMimeTypeConfiguration extends TaintTracking::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
|
||||
// and any(ExternalSourceConfiguration c).hasFlow(_, node.(SetDataSink).getUri())
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, PackageArchiveMimeTypeConfiguration config
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Android APK installation"
|
||||
@@ -0,0 +1,38 @@
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class APKInstallation extends Activity {
|
||||
static final String APK_MIMETYPE = "application/vnd.android.package-archive";
|
||||
|
||||
public void installAPK(String path) {
|
||||
// BAD: the path is not checked
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(Uri.fromFile(new File(path)), "application/vnd.android.package-archive");
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public void downloadAPK(String url) {
|
||||
// BAD: the url is not checked
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(Uri.parse(url), "application/vnd.android.package-archive");
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public void installAPK2() {
|
||||
String path = "file:///sdcard/Download/MyApp.apk";
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setType("application/vnd.android.package-archive");
|
||||
intent.setData(Uri.parse(path));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public void installAPK3(String path) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setType(APK_MIMETYPE);
|
||||
intent.setData(Uri.fromFile(new File(path)));
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-094/ArbitraryAPKInstallation.ql
|
||||
@@ -1 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/validation-api-2.0.1.Final:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../stubs/apache-commons-logging-1.2:${testdir}/../../../stubs/mvel2-2.4.7:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/scriptengine:${testdir}/../../../stubs/jsr223-api:${testdir}/../../../stubs/apache-freemarker-2.3.31:${testdir}/../../../stubs/jinjava-2.6.0:${testdir}/../../../stubs/pebble-3.1.5:${testdir}/../../../stubs/thymeleaf-3.0.14:${testdir}/../../../stubs/apache-velocity-2.3
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/validation-api-2.0.1.Final:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../stubs/apache-commons-logging-1.2:${testdir}/../../../stubs/mvel2-2.4.7:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/scriptengine:${testdir}/../../../stubs/jsr223-api:${testdir}/../../../stubs/apache-freemarker-2.3.31:${testdir}/../../../stubs/jinjava-2.6.0:${testdir}/../../../stubs/pebble-3.1.5:${testdir}/../../../stubs/thymeleaf-3.0.14:${testdir}/../../../stubs/apache-velocity-2.3:${testdir}/../../..//stubs/google-android-9.0.0
|
||||
|
||||
Reference in New Issue
Block a user