Skip to content

Commit 618b608

Browse files
committed
Arbitrary APK Installation MVP
1 parent ce937e7 commit 618b608

File tree

4 files changed

+145
-1
lines changed

4 files changed

+145
-1
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* @name Android APK installation
3+
* @description Installing an APK from an untrusted source.
4+
* @kind path-problem
5+
* @tags security
6+
*/
7+
8+
import java
9+
import semmle.code.java.frameworks.android.Intent
10+
import semmle.code.java.dataflow.DataFlow
11+
import semmle.code.java.dataflow.TaintTracking
12+
private import semmle.code.java.dataflow.ExternalFlow
13+
14+
class PackageArchiveMimeTypeLiteral extends StringLiteral {
15+
PackageArchiveMimeTypeLiteral() { this.getValue() = "application/vnd.android.package-archive" }
16+
}
17+
18+
class SetTypeMethod extends Method {
19+
SetTypeMethod() {
20+
this.hasName(["setType", "setTypeAndNormalize"]) and
21+
this.getDeclaringType().getASupertype*() instanceof TypeIntent
22+
}
23+
}
24+
25+
class SetDataAndTypeMethod extends Method {
26+
SetDataAndTypeMethod() {
27+
this.hasName(["setDataAndType", "setDataAndTypeAndNormalize"]) and
28+
this.getDeclaringType().getASupertype*() instanceof TypeIntent
29+
}
30+
}
31+
32+
class SetDataMethod extends Method {
33+
SetDataMethod() {
34+
this.hasName(["setData", "setDataAndNormalize", "setDataAndType", "setDataAndTypeAndNormalize"]) and
35+
this.getDeclaringType().getASupertype*() instanceof TypeIntent
36+
}
37+
}
38+
39+
class SetDataSink extends DataFlow::ExprNode {
40+
SetDataSink() { this.getExpr().(MethodAccess).getMethod() instanceof SetDataMethod }
41+
42+
DataFlow::ExprNode getUri() { result.asExpr() = this.getExpr().(MethodAccess).getArgument(0) }
43+
}
44+
45+
class UriParseMethod extends Method {
46+
UriParseMethod() {
47+
this.hasName(["parse", "fromFile"]) and
48+
this.getDeclaringType().hasQualifiedName("android.net", "Uri")
49+
}
50+
}
51+
52+
class ExternalSource extends DataFlow::Node {
53+
ExternalSource() {
54+
sourceNode(this, "android-external-storage-dir") or
55+
this.asExpr().(MethodAccess).getMethod() instanceof UriParseMethod or
56+
this.asExpr().(StringLiteral).getValue().matches(["file://%", "http://%", "https://%"])
57+
}
58+
}
59+
60+
class ExternalSourceConfiguration extends DataFlow2::Configuration {
61+
ExternalSourceConfiguration() { this = "ExternalSourceConfiguration" }
62+
63+
override predicate isSource(DataFlow::Node node) { node instanceof ExternalSource }
64+
65+
override predicate isSink(DataFlow::Node node) {
66+
// any(PackageArchiveMimeTypeConfiguration c).hasFlow(_, node)
67+
node instanceof SetDataSink
68+
}
69+
}
70+
71+
class PackageArchiveMimeTypeConfiguration extends TaintTracking::Configuration {
72+
PackageArchiveMimeTypeConfiguration() { this = "PackageArchiveMimeTypeConfiguration" }
73+
74+
override predicate isSource(DataFlow::Node node) {
75+
node.asExpr() instanceof PackageArchiveMimeTypeLiteral
76+
}
77+
78+
override predicate isAdditionalTaintStep(
79+
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
80+
DataFlow::FlowState state2
81+
) {
82+
state1 instanceof DataFlow::FlowStateEmpty and
83+
state2 = "typeSet" and
84+
exists(MethodAccess ma |
85+
ma.getQualifier() = node2.asExpr() and
86+
(
87+
ma.getMethod() instanceof SetTypeMethod and
88+
ma.getArgument(0) = node1.asExpr()
89+
or
90+
ma.getMethod() instanceof SetDataAndTypeMethod and
91+
ma.getArgument(1) = node1.asExpr()
92+
)
93+
)
94+
}
95+
96+
override predicate isSink(DataFlow::Node node, DataFlow::FlowState state) {
97+
state = "typeSet" and
98+
node instanceof SetDataSink
99+
// and any(ExternalSourceConfiguration c).hasFlow(_, node.(SetDataSink).getUri())
100+
}
101+
}
102+
103+
from DataFlow::PathNode source, DataFlow::PathNode sink, PackageArchiveMimeTypeConfiguration config
104+
where config.hasFlowPath(source, sink)
105+
select sink.getNode(), source, sink, "Android APK installation"
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import android.app.Activity;
2+
import android.content.Intent;
3+
import android.net.Uri;
4+
5+
import java.io.File;
6+
7+
public class APKInstallation extends Activity {
8+
static final String APK_MIMETYPE = "application/vnd.android.package-archive";
9+
10+
public void installAPK(String path) {
11+
// BAD: the path is not checked
12+
Intent intent = new Intent(Intent.ACTION_VIEW);
13+
intent.setDataAndType(Uri.fromFile(new File(path)), "application/vnd.android.package-archive");
14+
startActivity(intent);
15+
}
16+
17+
public void downloadAPK(String url) {
18+
// BAD: the url is not checked
19+
Intent intent = new Intent(Intent.ACTION_VIEW);
20+
intent.setDataAndType(Uri.parse(url), "application/vnd.android.package-archive");
21+
startActivity(intent);
22+
}
23+
24+
public void installAPK2() {
25+
String path = "file:///sdcard/Download/MyApp.apk";
26+
Intent intent = new Intent(Intent.ACTION_VIEW);
27+
intent.setType("application/vnd.android.package-archive");
28+
intent.setData(Uri.parse(path));
29+
startActivity(intent);
30+
}
31+
32+
public void installAPK3(String path) {
33+
Intent intent = new Intent(Intent.ACTION_VIEW);
34+
intent.setType(APK_MIMETYPE);
35+
intent.setData(Uri.fromFile(new File(path)));
36+
startActivity(intent);
37+
}
38+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Security/CWE/CWE-094/ArbitraryAPKInstallation.ql
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +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
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:${testdir}/../../..//stubs/google-android-9.0.0

0 commit comments

Comments
 (0)