Merge pull request #11915 from egregius313/egregius313/arbitrary-apk-installation

Java: Arbitrary APK installation
This commit is contained in:
Edward Minnix III
2023-03-14 06:23:51 -04:00
committed by GitHub
12 changed files with 459 additions and 1 deletions

View File

@@ -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>

View 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."

View 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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -0,0 +1,5 @@
---
category: newQuery
---
* Added a new query, `java/android/arbitrary-apk-installation`, to detect installation of APKs from untrusted sources.