mirror of
https://github.com/github/codeql.git
synced 2026-04-25 16:55:19 +02:00
Merge pull request #11915 from egregius313/egregius313/arbitrary-apk-installation
Java: Arbitrary APK installation
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Android allows an application to install an Android Package Kit (APK)
|
||||
using an <code>Intent</code> with
|
||||
the <code>"application/vnd.android.package-archive"</code> MIME type. If
|
||||
the file used in the <code>Intent</code> is from a location that is not
|
||||
controlled by the application (for example, an SD card that is
|
||||
universally writable), this can result in the unintended installation of untrusted applications.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
You should install packages using
|
||||
the <code>PackageInstaller</code> class.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you need to install from a file, you should use
|
||||
a <code>FileProvider</code>. Content providers can provide more specific
|
||||
permissions than file system permissions can.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
When your application does not require package installations, do not add
|
||||
the <code>REQUEST_INSTALL_PACKAGES</code> permission in the manifest file.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
|
||||
<p>
|
||||
In the following (bad) example, the package is installed from a file which
|
||||
may be altered by another application:
|
||||
</p>
|
||||
|
||||
<sample src="InstallApkWithFile.java"/>
|
||||
|
||||
<p>
|
||||
In the following (good) example, the package is installed by using
|
||||
a <code>FileProvider</code>:
|
||||
</p>
|
||||
|
||||
<sample src="InstallApkWithFileProvider.java"/>
|
||||
|
||||
<p>
|
||||
In the following (good) example, the package is installed using an
|
||||
instance of the <code>android.content.pm.PackageInstaller</code> class:
|
||||
</p>
|
||||
|
||||
<sample src="InstallApkWithPackageInstaller.java"/>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Android Developers: <a href="https://developer.android.com/reference/android/content/Intent#ACTION_INSTALL_PACKAGE">Intent.ACTION_INSTALL_PACKAGE</a>.
|
||||
</li>
|
||||
<li>
|
||||
Android Developers: <a href="https://developer.android.com/reference/android/Manifest.permission#REQUEST_INSTALL_PACKAGES">Manifest.permission.REQUEST_INSTALL_PACKAGES</a>.
|
||||
</li>
|
||||
<li>
|
||||
Android Developers: <a href="https://developer.android.com/reference/android/content/pm/PackageInstaller">PackageInstaller</a>.
|
||||
</li>
|
||||
<li>
|
||||
Android Developers: <a href="https://developer.android.com/reference/androidx/core/content/FileProvider">FileProvider</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
19
java/ql/src/Security/CWE/CWE-094/ArbitraryApkInstallation.ql
Normal file
19
java/ql/src/Security/CWE/CWE-094/ArbitraryApkInstallation.ql
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @id java/android/arbitrary-apk-installation
|
||||
* @name Android APK installation
|
||||
* @description Creating an intent with a URI pointing to a untrusted file can lead to the installation of an untrusted application.
|
||||
* @kind path-problem
|
||||
* @security-severity 9.3
|
||||
* @problem.severity error
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-094
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.ArbitraryApkInstallationQuery
|
||||
import ApkInstallationFlow::PathGraph
|
||||
|
||||
from ApkInstallationFlow::PathNode source, ApkInstallationFlow::PathNode sink
|
||||
where ApkInstallationFlow::hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Arbitrary Android APK installation."
|
||||
14
java/ql/src/Security/CWE/CWE-094/InstallApkWithFile.java
Normal file
14
java/ql/src/Security/CWE/CWE-094/InstallApkWithFile.java
Normal file
@@ -0,0 +1,14 @@
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/* Get a file from external storage */
|
||||
File file = new File(Environment.getExternalStorageDirectory(), "myapp.apk");
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
/* Set the mimetype to APK */
|
||||
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
|
||||
|
||||
startActivity(intent);
|
||||
@@ -0,0 +1,31 @@
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
String tempFilename = "temporary.apk";
|
||||
byte[] buffer = new byte[16384];
|
||||
|
||||
/* Copy application asset into temporary file */
|
||||
try (InputStream is = getAssets().open(assetName);
|
||||
FileOutputStream fout = openFileOutput(tempFilename, Context.MODE_PRIVATE)) {
|
||||
int n;
|
||||
while ((n=is.read(buffer)) >= 0) {
|
||||
fout.write(buffer, 0, n);
|
||||
}
|
||||
}
|
||||
|
||||
/* Expose temporary file with FileProvider */
|
||||
File toInstall = new File(this.getFilesDir(), tempFilename);
|
||||
Uri applicationUri = FileProvider.getUriForFile(this, "com.example.apkprovider", toInstall);
|
||||
|
||||
/* Create Intent and set data to APK file. */
|
||||
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
|
||||
intent.setData(applicationUri);
|
||||
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
|
||||
startActivity(intent);
|
||||
@@ -0,0 +1,32 @@
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInstaller;
|
||||
|
||||
private static final String PACKAGE_INSTALLED_ACTION =
|
||||
"com.example.SESSION_API_PACKAGE_INSTALLED";
|
||||
|
||||
/* Create the package installer and session */
|
||||
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
|
||||
PackageInstaller.SessionParams params =
|
||||
new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
|
||||
int sessionId = packageInstaller.createSession(params);
|
||||
session = packageInstaller.openSession(sessionId);
|
||||
|
||||
/* Load asset into session */
|
||||
try (OutputStream packageInSession = session.openWrite("package", 0, -1);
|
||||
InputStream is = getAssets().open(assetName)) {
|
||||
byte[] buffer = new byte[16384];
|
||||
int n;
|
||||
while ((n = is.read(buffer)) >= 0) {
|
||||
packageInSession.write(buffer, 0, n);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create status receiver */
|
||||
Intent intent = new Intent(this, InstallApkSessionApi.class);
|
||||
intent.setAction(PACKAGE_INSTALLED_ACTION);
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
|
||||
IntentSender statusReceiver = pendingIntent.getIntentSender();
|
||||
|
||||
/* Commit the session */
|
||||
session.commit(statusReceiver);
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `java/android/arbitrary-apk-installation`, to detect installation of APKs from untrusted sources.
|
||||
|
||||
Reference in New Issue
Block a user