Lekensteyn / make-gapps-zip

Documentation and tools for reproducible update.zip builds
MIT License
12 stars 9 forks source link

System apps do not require valid signatures for files that are not listed in MANIFEST.MF #3

Open Lekensteyn opened 8 years ago

Lekensteyn commented 8 years ago

While trying to figure out how to put classes.dex in an apk file without the invalidating the signatures in the APK file, I found that apks installed to /system/app/ do not require a valid signature for each file. Tested as follows:

Now I tried to install such a modified on Android 5.1.1 by putting it into /system/app/TestCmd/TestCmd.apk from recovery. Then I restarted and to my surprise the tampered app is still available!

Digging in the framework code, I found the message referenced in PackageParser.java with a special case for PARSE_IS_SYSTEM. According to Yuri, this flag is set for apps from /system/app. This code is from PackageParser.java:

        private static void collectCertificates(Package pkg, File apkFile, int flags)
            throws PackageParserException {
        final String apkPath = apkFile.getAbsolutePath();

        StrictJarFile jarFile = null;
        try {
            jarFile = new StrictJarFile(apkPath);

            // Always verify manifest, regardless of source
            final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
            if (manifestEntry == null) {
                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                        "Package " + apkPath + " has no manifest");
            }

            final List<ZipEntry> toVerify = new ArrayList<>();
            toVerify.add(manifestEntry);

            // If we're parsing an untrusted package, verify all contents
            if ((flags & PARSE_IS_SYSTEM) == 0) {
                final Iterator<ZipEntry> i = jarFile.iterator();
                while (i.hasNext()) {
                    final ZipEntry entry = i.next();

                    if (entry.isDirectory()) continue;
                    if (entry.getName().startsWith("META-INF/")) continue;
                    if (entry.getName().equals(ANDROID_MANIFEST_FILENAME)) continue;

                    toVerify.add(entry);
                }
            }

            // Verify that entries are signed consistently with the first entry
            // we encountered. Note that for splits, certificates may have
            // already been populated during an earlier parse of a base APK.
            for (ZipEntry entry : toVerify) {
                final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
                if (ArrayUtils.isEmpty(entryCerts)) {
                    throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                            "Package " + apkPath + " has no certificates at entry "
                            + entry.getName());
                }
...

As you can see, files which do not occur in the manifest file (META-INF/MANIFEST.MF) are not checked if they are in /system/app/... which results in the observed behavior.

The relevance of this for make-gapps-zip is that classes.dex can additionally be included in the apk file without breaking the application signature. Another possible result is that adversaries can distribute malicious modifications (additions) to the APKs which are accepted by the system.

To verify the authenticity of APKs, validate the certificate manually and verify the authenticity using jarsigner as described by Chris Stratton at How can I verify the authenticity of an apk file I downloaded?.

mfonville commented 8 years ago

Btw, this only applies up to Lollipop. In Marshmallow many ROMs (but not all) do require the APKs content to be equal to filelist stated in the manifest.