Mastodon IzzyOnDroid

Say thanks!
Privacy Links
↓ Your product here? ↓
Das inoffizielle Android-HandbuchDas inoffizielle Android-Handbuch
Buy at Amazon for €16.99
Die Daten, die ich rief: Wie wir unsere Freiheit an Großkonzerne verkaufenDie Daten, die ich rief: Wie wir unsere Freiheit an Großkonzerne verkaufen
Buy at Amazon for €9.00
Sie kennen dich! Sie haben dich! Sie steuern dich!: Die wahre Macht der DatensammlerSie kennen dich! Sie haben dich! Sie steuern dich!: Die wahre Macht der Datensammler
Buy at Amazon for €9.99
Sie wissen alles: Wie Big Data in unser Leben eindringt und warum wir um unsere Freiheit kämpfen müssen -Sie wissen alles: Wie Big Data in unser Leben eindringt und warum wir um unsere Freiheit kämpfen müssen -
Buy at Amazon for €2.76
Android Forensics: Investigation, Analysis and Mobile Security for Google AndroidAndroid Forensics: Investigation, Analysis and Mobile Security for Google Android
Buy at Amazon for €57.73
Android Apps SecurityAndroid Apps Security
Buy at Amazon for €27.61
Bitdefender Total Security 2021 1 Device / 18 Months (Code in a Box)|Standard|1|18 Months|PC/Mac/Android|DownloadBitdefender Total Security 2021 1 Device / 18 Months (Code in a Box)|Standard|1|18 Months|PC/Mac/Android|Download
Buy at Amazon for €40.90
The Android Malware Handbook: Detection and Analysis by Human and MachineThe Android Malware Handbook: Detection and Analysis by Human and Machine
Buy at Amazon for €31.99
Android Apps Security: Mitigate Hacking Attacks and Security BreachesAndroid Apps Security: Mitigate Hacking Attacks and Security Breaches
Buy at Amazon for €48.14
As of 2024-07-21 19:51
prices & availability might be subject to change.

Ramping up security: additional APK checks are in place with the IzzyOnDroid repo

Security Lock

We use apps because we value their functionality. But sometimes they also include components we wouldn’t have chosen ourselves had we been asked. Pieces of code which are either not needed for the app’s valued functionality (such as ads or trackers) – or modules which are not FOSS, thus making the FOSS app a tainted one that strictly speaking, now is no longer FOSS. Two previous articles on this site give you some insights into that:

The library scanner active in the IzzyOnDroid repo, which is described in the latter article and available under a FOSS license, deals with those and makes transparent which libraries/modules an APK file contains. An overview on what measures are in place you can always find in the What about security? section of the repository’s info page.

In January 2024, additional security measures were established at the IzzyOnDroid repository. It is these that this article not only wants to introduce to you, but also give you some background about.

Scanning the AndroidManifest

Wikipedia introduces the term Manifesto as:

A manifesto is a written declaration of the intentions, motives, or views of the issuer

an AndroidManifest
Example AndroidManifest.xml

In computing there‘s a similar thing: the manifest file. With an Android app, the issuer of the manifest is the developer of the app. This app usually holds more than one manifest. The one we’re talking about here is the AndroidManifest.xml. In it, the developer(s) declare the intentions and activities of the app:

Inside the APK file, this AndroidManifest.xml is stored in binary form (called „AXML“), which can be extracted e.g. using androguard axml <file.apk>, and then analyzed. This is being done for each APK file entering the IzzyOnDroid repo, were the following checks are being performed:

Checking for Flags

The official documentation calls them „attributes“. They declare each of the app’s components and some of its features. Most of them one wouldn’t consider sensitive – e.g. the icon, whether it allows to be backed up, which category it belongs to. But some of them are, and those are checked for now:

Sensitive permissions

These will trigger a warning, which will cause a manual verification. Justified ones will be added to the „allow list“ of the app, like CONTACTS for an address book, or CALL_PHONE for a dialer. Others are highlighted in the corresponding APK’s permission section – or might lead to the removal (or non-inclusion) of the app. A list of those permissions can be found in issue 475 and in the corresponding library file. These cover security topics as well as privacy concerns.

A specific permission is REQUEST_INSTALL_PACKAGES which usually indicates an integrated self-updater, violating the inclusion criteria of the repo as that would bypass the security checks in place. Here I reach out to the corresponding developers to verify (the permission could well be justified; e.g. a file explorer might need it to let you install APK files from your local storage) and, in case it is a self-updater, ask for either a separate build flavor having the self-updater removed, or at least make it opt-in with a proper warning, as shown by the example screenshots from RiMusic (very few exceptions are made for dedicated testing variants of apps, e.g. „beta channels“, which must be clearly marked as such). So far, my requests were honored with understanding.

Opt-In 1 Opt-In 2
Opt-In: Properly asking for consent, including giving context
(click screenshots for larger variants)

Sensitive permissions are checked manually before a new app gets added to the repo, but also automatically with each incoming update. Justified ones are added to the app’s „allow list“ (and removed from there if they were removed from the app). Unclear ones are reached out about to the corresponding developer(s) which then either give clarification, or care for their removal/replacement (a process that worked out very fine so far). Remaining ones, as pointed out above, are clearly marked or may lead, in serious cases, to the app’s removal from the repo.

Sensitive intent filters

With Android apps, an „intent“ is an abstract description of an operation to be performed. Basically, there are three main use cases for them: starting an Activity, a Service, and to deliver messages to a BroadcastReceiver other apps can subscribe to in order to receive these messages.

package details
Example of details shown for an APK

There are some intent-filters which will trigger warnings, analog to sensitive permissions, as they might indicate unwanted behavior. These are handled similar to the above: manual check, asking the authors for clarification if needed. Then, either adding them to the allow-list, raise a specific anti-feature – or lead to exclusion of the app.

Intent filters are scanned for with the app inclusion checks manually, but also automatically by the update checker for each incoming APK file.

If any of those are used by an app, this must be made transparent. So this „overhaul“ includes to guarantee exactly that: make the reasons visible (in a separate block of the app’s details page on the website of the repo labeled „App configuration & Special Access“, together with above outlined „flags“).

Certificates and Signing

Certificates are used to sign apps. They are usually created by the developers themselves and stored in their private keystores – at least for the APK files provided via the IzzyOnDroid repository. Exceptions are Google’s PlayStore, where Google wants to perform the signing itself – and also F-Droid‘s own repo where F-Droid generates the corresponding certificates if reproducible builds can not be established.

App signing is a security feature. Its purpose is to ensure that the APK cannot be tampered with (so nobody can inject e.g. malicious elements), and that you can check the APK was really provided by the corresponding developer. Updates to an installed app are only accepted if the signature of the corresponding APK file matches that of the already installed app. Which is only the case if both have been signed using the very same certificate. You can check the certificates of APK files yourself, the tool for that is called apksigner:

$ apksigner verify --verbose --print-certs org.fdroid.fdroid.apk
Verified using v1 scheme (JAR signing): true
Verified using v2 scheme (APK Signature Scheme v2): true
Verified using v3 scheme (APK Signature Scheme v3): true
Number of signers: 1
Signer #1 certificate DN: CN=Ciaran Gultnieks, OU=Unknown, O=Unknown, L=Wetherby, ST=Unknown, C=UK
Signer #1 certificate SHA-256 digest: 43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab
Signer #1 certificate SHA-1 digest: 05f2e65928088981b317fc9a6dbfe04b0fa13b4e
Signer #1 certificate MD5 digest: 17c55c628056e193e95644e989792786
Signer #1 key algorithm: RSA
Signer #1 key size (bits): 2048
Signer #1 public key SHA-256 digest: e3d2cc87a245da2e84d4fb71e527c164e084d48bccf76ffad46ad17f1bfde388
Signer #1 public key SHA-1 digest: 26ef7882633282a9b04688178ee7f372fbec7c3d
Signer #1 public key MD5 digest: 9225fccafb33b605a86cfc09d7f38ec6

When an app is added to the IzzyOnDroid repo, its certificate is pinned: AllowedSigningKeys is set to the value of Signer #1 certificate SHA-256 digest. So updates are only accepted into the repository if the certificates match. This is done to prevent an APK that was potentially tampered with from even entering the repository. While such an APK would not be installed (or even offered as an update) for those having the previous version running on their device, keeping it from entering the repository protects those who’d install the app for the first time.

But this is not the only thing checked for at this level. Let’s see what else is done:

Debug Certificates & Weak Certificates

These are only checked for at inclusion time – apps with debug certificates are no longer accepted into the repo. Updates won’t bring those in as the repo already has AllowedSigningKeys in place for all apps and thus certificates are „pinned“ (see above); APKs with a different signature (switching the signing key definitely causes this) thus would be rejected straight away and not even reach the index. A scan was run over all APKs currently in the repo to find those using a debug key (see scan existing APKs for use of debug keys) and showed there were 112 such APKs initially, 40 of them even with expired debug keys (that does not mean 112 resp. 40 apps, as there can be up to 3 APKs per app in the repo). The latter 40 have been started with: 23 apps obviously no longer maintained (repositories deleted/archived, author not seen for years etc.) have been removed, for the remaining ones I reached out to their authors. Some of them have been fixed within a week, others received no answer at all. The latter were then removed as well if there was no response within a month (or if the authors stated not to address this).

The remaining 72 were addressed in a separate run. Most of those are already solved: usually developers switched to a proper release key, and the debug versions have been removed here. The last APKs signed with a debug key should be gone end of March, 2024.

For those who wonder why this should be an issue, please see:

Further, a proper release key points out in their DN who created the certificate, while debug keys always say it was „Android debug“:

Signer #1 certificate DN: C=US, O=Android, CN=Android Debug

For those interested in technical details, these were the commands used to ferret them out:

Hint for developers: as using a different signing key means all folks already using your app have to uninstall the old version before they can install the new one, you might consider using „key rotation“. Key rotation can be done using apksigner if one has access to both, the original and the new private key. I have not yet tested whether fdroidserver supports this, so be welcome to reach out to me and I can run a test. As key rotation was only introduced with Android 9, this is only possible if the minSdkVer of your app points to Android 9 or later.

BLOBs in APK signing blocks

APK signing blocks are where signing details are stored in. Fay drew my attention to the fact that there could be "weird stuff" included in APK signing blocks – something few people are aware of (I was not until then). Of course there are also things one would expect here:

OK_BLOCKS = dict(

I dug into it, and found even more weird stuff. So checks for that were implemented as well:

There might be other block types, as a developer could just create their own block type (or abuse an existing one) and put anything they want in there, including code. Which is why „unknown blocks“ will also raise a warning, again sent with a report by mail to me. As with sensitive permissions and intent filters, this check is run manually before a new app gets added to the repo, but also automatically for each incoming update. If you want to check an APK for these yourselves, you can find the script used for that here. In this script you also find all currently known blocks, with links to details on them.

As pointed out on DEPENDENCY_INFO_BLOCK, I recommend using apksigner for signing to avoid some of those BLOBs right away. According to the official documentation, to keep Android Studio and also IntelliJ IDEA from inserting this block, including the following snippet with your build.gradle.kts should help:

android {
    dependenciesInfo {
        // Disable including dependency metadata when building APKs
        includeInApk = false
        // Disable including dependency metadata when building Android App Bundles
        includeInBundle = false

Thanks to Naveen for pointing this out, and to soupslurpr for the note to handle AABs as well! Meanwhile the approach to exclude the DEPENDENCY_INFO_BLOCK via the outlined modification of the build.gradle has been confirmed as a success, and was applied in the source code of several apps present in the IzzyOnDroid repository.

Currently, SigningBlock BLOBs are only recorded to the repository’s APK-Checker database and made visible on the website. How this shall be dealt with in the future is tracked in a separate issue: Dealing with BLOBs found in APK signing blocks.

One more word on this: as the payload example of Meituan shows, this is not just „cosmetic“. One can take an APK that is properly signed by the developer’s key, add something to the signing block – and verification with apksigner still fully succeeds as if nothing was wrong. The modified app even installs properly on an Android device – so this is something to take quite seriously. Details on this can be found in this POC. A short quote from the POC:

Whether the payload is present or not does not affect the validity of the signature. Thus we get two APKs -- with an identical valid v1+v2+v3 signature -- but one says "nothing to see here..." when you run it, whereas the other says e.g. "This is the payload".

In a test, the EICAR sample was included with an APK modified as outlined above, and uploaded to VirusTotal. Only 2 out of its 64 scanner engines detected it, which means in many cases such a tampering could go totally undetected. But even if more malware scanners would detect it it would be a bad sign as long as signature verification does not: a malicious actor could take the APK of a reputable author, add malware to a signing block and spread the resulting APK, thus affecting the reputation of the original author – who will be thought responsible as the signature „proves“ it was them.

In another test, an ELF binary was injected into a signing block. VirusTotal did not seem to see that at all. Pithus in those cases at least points out the „unknown“ blocks.

So why is this a security issue? Android apps have access to their own APK file, so they can hide payload here (and as the case of the MEITUAN_APK_CHANNEL shows, some even do). With most scanners ignoring the signing blocks entirely, this poses a risk that should be addressed.

Thanks to Fay for the efforts she put into this!

Reactions by developers I contacted

So far all of my reports, when answered, have been received well and acted upon positively. Either the „potential offenders“ have been clarified (with links to the code and a proper explanation) – or my reports led to improvements of the app itself, as those „offenders“ were removed (partly with code rewrites using less offensive implementations), or by making justified ones more transparent and explaining them to those using the app when the related functionality was triggered (e.g. with android:usesCleartextTraffic, when a non-secured URL was entered, asking if this was really intended or just a typo (missing the s in https) – and sometimes both, as in the described example.


Security is no „set-and-forget“, but an ongoing process. This is the third „big round“ of checks put into place at the IzzyOnDroid repository, but it will certainly not be the last. As shown, transparency is an important aspect of this repo, so you’ll be shown „what’s inside“ even if it’s not a security issue – so when encountering something that „looks suspicious“ you’ll at least have an explanation why it is there, hopefully.

Also, „making things safe(r)“ is a learning process. I’m thankful for all help I received from developers explaining me (who’s not an Android dev) backgrounds and whereabouts. And I’m glad I could help some developers with what I’ve learned on the path. This „working together“ I feel is very important as it helps us all to improve – and to make our devices safer places. Let’s keep that up!