{"id":17160221,"url":"https://github.com/obfusk/fdroid-fakesigner-poc","last_synced_at":"2025-04-06T06:07:05.878Z","repository":{"id":232270361,"uuid":"783435266","full_name":"obfusk/fdroid-fakesigner-poc","owner":"obfusk","description":"F-Droid Fake Signer PoC","archived":false,"fork":false,"pushed_at":"2025-01-22T14:20:17.000Z","size":123,"stargazers_count":124,"open_issues_count":0,"forks_count":5,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-03-30T05:03:54.740Z","etag":null,"topics":["android","apk","poc"],"latest_commit_sha":null,"homepage":"https://www.openwall.com/lists/oss-security/2024/04/08/8","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/obfusk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-07T21:39:45.000Z","updated_at":"2025-03-16T05:09:05.000Z","dependencies_parsed_at":"2025-01-02T17:37:22.502Z","dependency_job_id":"44794cf0-f3ad-4ae5-9725-fe7039193f7a","html_url":"https://github.com/obfusk/fdroid-fakesigner-poc","commit_stats":null,"previous_names":["obfusk/fdroid-fakesigner-poc"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obfusk%2Ffdroid-fakesigner-poc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obfusk%2Ffdroid-fakesigner-poc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obfusk%2Ffdroid-fakesigner-poc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obfusk%2Ffdroid-fakesigner-poc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/obfusk","download_url":"https://codeload.github.com/obfusk/fdroid-fakesigner-poc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247441045,"owners_count":20939239,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","apk","poc"],"created_at":"2024-10-14T22:24:06.791Z","updated_at":"2025-04-06T06:07:05.858Z","avatar_url":"https://github.com/obfusk.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# F-Droid Fake Signer PoC\n\nPoC for `fdroidserver` `AllowedAPKSigningKeys` certificate pinning bypass.\n\nPublished: 2024-04-08; updated: 2024-04-14, 2024-04-20, 2024-12-30, 2025-01-06,\n2025-01-08, 2025-01-09, 2025-01-10, 2025-01-19, 2025-01-21, 2025-01-22.\n\n**NB: no new updates will be provided solely to correct any further\ncounterfactual statements by F-Droid.  We implore them to take responsibility\nfor their mistakes instead of spreading misinformation in order to downplay our\nfindings.**\n\nNB: see also [`OVERVIEW.md`](OVERVIEW.md).\n\n## oss-security\n\n* https://www.openwall.com/lists/oss-security/2024/04/08/8\n* https://www.openwall.com/lists/oss-security/2024/04/20/3\n* https://www.openwall.com/lists/oss-security/2025/01/03/1\n* https://www.openwall.com/lists/oss-security/2025/01/20/1\n\n## Background\n\nWe started looking into Android APK Signing Block oddities at the request of\nF-Droid [1] on 2021-08-25; we opened F-Droid issue \"APK Signing Block\nconsiderations\" [2] on 2022-10-19.  No action was taken as a result.\n\nWe published the \"Android APK Signing Block Payload PoC\" [3] to the Reproducible\nBuilds mailing list [4] on 2023-01-31.\n\n\u003e But the Android APK Signature Scheme v2/v3 actually allows embedding arbitrary\n\u003e data (or code) in the signing block, meaning that two APKs with the exact same\n\u003e valid signature -- though not a bit-by-bit identical signing block -- can\n\u003e behave differently.\n\nJason Donenfeld reported \"Potential security hazard: `apk_signer_fingerprint()`\nlooks at certs in reverse order that Android checks them\" [5] on 2023-05-05; no\naction was taken to fix this bug.\n\n\u003e However, there's a discrepancy between how these certificates are extracted\n\u003e and how Android actually implements signature checks. [...] Notice how [the\n\u003e google flowchart [6]] checks v3, then v2, and then v1. Yet the [F-Droid] code\n\u003e above looks at v1, then v2, and then v3, in reverse order. So v1 could have a\n\u003e bogus signer that some versions of Android never even look at, yet fdroid\n\u003e makes a security decision based on it. Yikes! Also, it's worth noting that\n\u003e apk_signer_fingerprint() also does not bother validating that the signatures\n\u003e are correct.\n\nAndreas Itzchak Rehberg (IzzyOnDroid) reported about \"BLOBs in APK signing\nblocks\" in \"Ramping up security: additional APK checks are in place with the\nIzzyOnDroid repo\" [7] on 2024-03-25.  The accompanying German article\n\"Android-Apps auf dem Seziertisch: Eine vertiefte Betrachtung\" [8] points out\nthat we noticed that `apksigner` and `androguard` handle duplicate signing\nblocks rather differently: the former only sees the first, the latter only the\nlast, which allows all kinds of shenanigans.\n\n## Observations\n\nWe observed that embedding a v1 (JAR) signature file in an APK with `minSdk \u003e=\n24` will be ignored by Android/`apksigner`, which only checks v2/v3 in that\ncase.  However, since `fdroidserver` checks v1 first, regardless of `minSdk`,\nand does not verify the signature, it will accept a \"fake\" certificate and see\nan incorrect certificate fingerprint.\n\nWe also realised that the above mentioned discrepancy between `apksigner` and\n`androguard` (which `fdroidserver` uses to extract the v2/v3 certificates) can\nbe abused here as well.  Simply copying the v2/v3 signature from a different APK\nand appending it to the APK Signing Block will not affect `apksigner`'s\nverification, but `androguard`, and thus also `fdroidserver`, will see only the\nsecond block.  Again, the signature is not verified, a \"fake\" certificate\naccepted, and an incorrect fingerprint seen.\n\nAs a result, it is trivial to bypass the `AllowedAPKSigningKeys` certificate\npinning, as we can make `fdroidserver` see whatever certificate we want instead\nof the one Android/`apksigner` does.  Note that we don't need a valid signature\nfor the APK (we really only need a copy of the DER certificate, though having\nanother APK signed with the certificate we want to use makes things easy).\n\n### Update (2024-04-14)\n\nHaving been asked about multiple certificates in APK signatures [5], we realised\nthat, like v2/v3 signatures, v1 signatures can indeed also contain multiple\ncertificates (e.g. a certificate chain, though neither `jarsigner` nor\n`apksigner` seem to enforce any relationships between certificates).  However,\nunlike v2/v3 -- which guarantee that the certificate used for the signature is\nalways the first in the sequence -- v1 does not define an ordering: the\nsignature block file is a PKCS#7 DER-encoded ASN.1 data structure (per RFC 2315)\nand uses a SET for the list of certificates.\n\nAndroid/`apksigner` will find and use the first certificate that matches the\nrelevant `SignerInfo`, ignoring any other certificates, but `fdroidserver`\nalways returns the first certificate it finds in the signature block file.  Thus\nwe can once again trick it into seeing any certificate we want -- as long as it\nonly checks the v1 certificate (e.g. when the `fdroidserver.patch` has not been\napplied or the APK only has a v1 signature).\n\nNB: apps with `targetSdk \u003e= 30` are required to have a v2/v3 signature.\n\nNB: Android \u003c N will only check the first `SignerInfo`, later versions pick the\nfirst one that verifies if there are multiple.\n\n### Update (2024-04-20)\n\nDespite repeated warnings [5] that using the last certificate instead of the\nfirst one does not in any way fix the vulnerability described in the 2024-04-14\nupdate (PoC #3), the proposed patches for `fdroidserver` [10] and `androguard`\n[11] do exactly this.  With that patch, version A (which inserts the fake\ncertificate first) of the PoC now fails, but version B (which inserts it last)\nnow works.\n\n### Update (2024-12-30 #1)\n\nInstead of adopting the fixes we proposed, F-Droid wrote and merged their own\npatch [10], ignoring repeated warnings it had significant flaws (including an\nincorrect implementation of v1 signature verification and making it impossible\nto have APKs with rotated keys in a repository).  As a result it is possible to\nconstruct a valid v1 signature that `fdroidserver` matches to the wrong\ncertificate.\n\nWe do this by simply creating and prepending a second SignerInfo using our own\ncertificate, which has the same serial number and an almost identical issuer --\ne.g. a common name with a space (0x20) replaced by a tab (0x09) or a DEL (0x7f)\nappended -- to exploit an implementation that will match the SignerInfo against\nthe wrong certificate through incorrect canonicalisation.\n\nLuckily, the impact is lower than that of the other vulnerabilities as it does\nrequire a valid signature from the certificate one wishes to spoof.\n\n### Update (2024-12-30 #2)\n\nUnfortunately, we found another more severe vulnerability as well, caused by a\nregex incorrectly handling newlines in filenames.  This allows another trivial\nbypass of certificate pinning, as we can once again make `fdroidserver` see\nwhatever certificate we want instead of the one Android/`apksigner` does (as\nlong as we have a valid v1 signature for some other APK).\n\nThe regex in question, `^META-INF/.*\\.(DSA|EC|RSA)$`, is supposed to match all\nfilenames that start with `META-INF/` and end with `.DSA`, `.EC,` or `.RSA`.\nUnfortunately, the `.*` does not match newlines, and the `$` matches not just\nthe end of the string but \"the end of the string or just before the newline at\nthe end of the string\".  As a result we can use a newline in the filename of the\nreal signature files (before the extension), which Android/`apksigner` see but\n`fdroidserver` does not, and a newline after the `.RSA` extension for the\nspoofed signature files, which `fdroidserver` will see but Android/`apksigner`\nwill not.\n\nNB: `androguard` seems to use a similarly incorrect regex.\n\nWe can do almost the exact same thing with NUL bytes instead of newlines,\nindependently of the flawed regex, because Python's `ZipInfo.filename` is\nsanitised by removing any NUL byte and everything after it.  This will have the\nsame result for `fdroidserver` and `apksigner` (which happily accepts NUL bytes\nin filenames) as above, but luckily Android rejects APKs with NUL bytes in\nfilenames, and such an APK will thus fail to install.\n\nNB: in light of all of the above we reiterate that we strongly recommend using\nthe official `apksig` library (used by `apksigner`) to both verify APK\nsignatures and return the first signer's certificate to avoid these kind of\nimplementation mistakes and inconsistencies and thus further vulnerabilities.\nHandling common cases correctly is fairly easy, but handling edge cases\ncorrectly is hard; rolling your own implementation without the required\nexpertise and care to get it right is irresponsible.\n\n### Update (2025-01-06)\n\nF-Droid claims that the latest vulnerability \"does not affect the repository on\nf-droid.org\".  Which suggests that they do not understand the significance of\nthis certificate pinning bypass, or they do not believe certificate pinning is\nmeant to provide protection against a compromise of an upstream repository\nwithout a compromise of the upstream signing key.  Both are worrying.\n\nAll of the vulnerabilities described here were discovered by us *by accident*,\nlooking into things like key rotation, signature copying, and security scans for\nIzzyOnDroid, not vulnerabilities in `fdroidserver`.  The first one was\nindependently discovered and reported a year earlier, and subsequently ignored.\n\nThe latest vulnerability, the incorrect regex, was not something we specifically\npredicted.  But we warned F-Droid that their approach to handling certificate\npinning with custom code independent of the signature verification using\n`apksigner` was full of mistakes and fundamentally flawed, which proved to be\ncorrect again and again.\n\nWe recommended \"using the official `apksig` library (used by `apksigner`) to\nboth verify APK signatures and return the first signer's certificate to avoid\nthese kind of implementation inconsistencies and thus further vulnerabilities\nlike this one\".  We even provided a Python implementation for that.  All of our\nrecommendations were ignored.  We find this careless approach to security far\nmore worrying than the impact of the individual vulnerabilities described here.\n\nWe sincerely wish this document didn't have to exist.  We implore F-Droid to do\nbetter, to live up to its potential, and do right by its community.\n\n### Update (2025-01-08)\n\nF-Droid now claims PoC 5a is not an \"actionable security vulnerability\" because\n\"APKs signed by v1-only are not even installable on latest Android versions\".\nThis is false.  As long as `targetSdk \u003c 30` (and e.g. the official F-Droid\nclient has 29) they will install just fine.  We even confirmed this by\ninstalling the PoC APK on Android 13-15 just in case, something they apparently\nneglected to bother with before making that claim.\n\n### Update (2025-01-09)\n\nF-Droid now claims they can't use the patches as-is because of \"code quality\nissues\" (private APIs).  Which applies to exactly one patch: the one they\nalready merged 8 months ago (`fdroidserver.patch`).\n\nBecause the only way to fix the vulnerability was to monkey-patch `androguard`\n(and an updated version is still not available in Debian, nor has the Debian\nstable `fdroidserver` package received any patches, despite those packages being\nmaintained by the F-Droid team, so that monkey patch is still needed).\n\nThey are also downplaying the impact by insisting these vulnerabilities are only\na problem for third party repositories relying on `fdroidserver`; which, even if\ntrue, is showing a concerning disregard for the security of the repositories of\nother projects relying on `fdroidserver`.\n\nAgain, we find F-Droid's reaction and the security and code review processes on\ndisplay here to be highly concerning, far more than the vulnerabilities we\nreported.\n\n### Update (2025-01-19)\n\nQuoting the response of F-Droid's Technical Lead [12]:\n\n\u003e fdroidserver is fully safe for the tasks it was built for. It has been\n\u003e independently audited as well (we have two more audits coming up). If you have\n\u003e a trusted collection of APKs, then fdroidserver provides the entry point to a\n\u003e trustworthy pipe to the F-Droid client. It cannot protect against malicious\n\u003e upstreams, upstreams losing their signing keys, etc. It cannot fix the\n\u003e deprecated v1 signatures. Require v2+ signatures, and AllowedAPKSigningKeys\n\u003e works with no known weaknesses.\n\nBased on the above, we cannot but conclude that despite earlier claims that the\n\"goal of AllowedAPKSigningKeys is to make it easy for non-technical people to\nmanage binary APK repos securely\", certificate pinning is in fact not expected\nto provide any security against e.g. updates from compromised upstream\nrepositories as it assumes a \"trusted collection of APKs\".\n\nWe wonder what exactly the intended purpose of certificate pinning is if not to\nensure APKs can only be provided by someone in control of the upstream signing\nkey, as this is the kind of security repositories like IzzyOnDroid expected it\nto provide.  We also observe that the 2018 audit predates the implementation of\n`AllowedAPKSigningKeys` certificate pinning.\n\nAnother quote [13]:\n\n\u003e [...] why #fdroidserver implements somethings in #Python rather than scraping\n\u003e #apksigner output. Reliably and securely parsing CLI output over the long term\n\u003e is really hard to get right because deployed fdroidserver code has to be\n\u003e future proof, in that it has to support newer apksigner versions that might\n\u003e have changed its output. For example, #fdroidserver is coded against apksigner\n\u003e from build-tools version vX.0.0. Someone does `pip install fdroidserver`. Then\n\u003e at some point, the user upgrades apksigner to version vY.0.0 which breaks the\n\u003e parsing before fdroidserver supports apksigner vY.0.0. That breakage needs to\n\u003e fail gracefully, and that is really hard to do. Much harder than just writing\n\u003e pure Python code to extract the certificates which is tested against the\n\u003e apksigner test suite. [...]\n\nWe agree that parsing `apksigner` CLI output would be unreliable.  Which is why\nwe recommended using the underlying `apksig` library which has a stable API and\neven provided code to do exactly that [14].  This suggestion has been\nconsistently ignored with zero rationale given, other than clearly irrelevant\nobjections to parsing CLI output.\n\nWe vehemently disagree that the chosen approach of using custom Python code that\ndoes not verify the signatures and relies on matching specific *behaviour* of\nspecific versions of `apksigner` (e.g. whether or not and how it verifies v3.1\nsignatures) to extract the correct certificates is reliable or secure.  This is\nevidenced by the 6th PoC, which works because `fdroidserver` completely ignores\nthe APK Signature Scheme v3.1 block (and does not use any v1 signatures).\n\nWe find it concerning that F-Droid constantly chooses to move the goalposts and\ncontinues to rely on a fundamentally broken approach for certificate pinning,\nmerely patching [15] known vulnerabilities without ever addressing the\nunderlying cause.\n\nWe reiterate once again that we recommended \"using the official `apksig` library\n(used by `apksigner`) to both verify APK signatures and return the first\nsigner's certificate to avoid these kind of implementation inconsistencies and\nthus further vulnerabilities [...]\".\n\nUntil a proper reliable implementation of certificate pinning using `apksig` is\nprovided (if ever), we recommend repositories using `AllowedAPKSigningKeys`\nperform their own audit and assess whether the security they wish to provide\nrequires performing certificate pinning themselves or switching to e.g.\n`apkrepotool`.\n\n### Update (2025-01-21)\n\nQuoting F-Droid's Technical Lead 8 months ago [16]:\n\n\u003e This highlights one hazard of using multiple implementations of verification:\n\u003e different implementations can open up exploit vectors.\n\nAnd yet this is exactly what he did despite our repeated warnings against doing\nso, which were ignored.\n\n\u003e There is a public API in the Java apksig library, but fdroidserver is written\n\u003e in Python.\n\nWhich is exactly what we recommended using, even providing an example\nimplementation of using `apksig` from Python one month earlier, which was\nignored.\n\n\u003e For repos made with collections of binary APKs, this more of a concern.\n\u003e AllowedAPKSigningKeys was designed with these kinds of repos in mind.  Our\n\u003e goal is to make it as simple as possible to safely run app repos.\n\u003e AllowedAPKSigningKeys is a feature along those lines.  So this page is about\n\u003e documenting what needs to happen to make AllowedAPKSigningKeys as reliable as\n\u003e possible.\n\nWhich contradicts recent statements that certificate pinning is in fact not\nexpected to provide any such security as it assumes a \"trusted collection of\nAPKs\".  And no further action was taken to actually make `AllowedAPKSigningKeys`\n\"as reliable as possible\" despite the shortcomings identified.\n\n### Update (2025-01-22)\n\nIt seems F-Droid is now blaming the bugs in their own code on `apksigner` [17]:\n\n\u003e apksigner v33.0.x falsely verifies invalid APK which leads to wrong cert\n\u003e extracted for AllowedAPKSigningKeys\n\nThey are clearly aware that the APK, whilst indeed containing a v3.1 signature\nwithout a corresponding v3 signature, is considered perfectly valid by Android\nitself and installs without any problems on Android 13-15:\n\n\u003e I could install the poc-v6.apk in an SDK-34 emulator.\n\nYet they blame `apksigner` versions supporting v3.1 signatures but lacking an\nextra check that was added in later versions instead of acknowledging that the\nreason `fdroidserver` incorrectly returns the wrong certificate is that their\nown code is simply ignoring v3.1 signatures: yet another example that \"different\nimplementations can open up exploit vectors\" and still nothing done to address\nthat underlying issue.\n\nNB: `apksigner` is correct to have added this extra check as v3.1 signatures\nshould always have a corresponding v3 signature.  But the lack of a\ncorresponding v3 signature does not have any security implications for Android,\nwhich correctly considers such signatures valid.  And there would have been no\nsecurity implications for `fdroidserver` either if not for its fundamentally\nbroken approach to extracting the certificate using custom Python code instead\nof relying on `apksig` for both verification and extraction to guarantee the\nextracted certificate is the same one actually used for verification.\n\n## PoC\n\nNB: you currently need the `signing` branch of `apksigtool` [9].\n\nNB: the \"fake\" signer shown here is from the official F-Droid client (its APK\nhas a v1+v2+v3 signature), the one `apksigner` sees is randomly generated by\n`make-key.sh`; the `app.apk` used for testing had `minSdk 26` and a v2 signature\nonly.  Using APKs with other signature scheme combinations is certainly\npossible, but might require adjusting the PoC code accordingly.\n\n```bash\n$ ./make-key.sh             # generates a dummy key\n$ python3 make-poc-v1.py    # uses app.apk (needs minSdk \u003e= 24) as base, adds fake.apk .RSA\n$ python3 fdroid.py         # verifies and has fake.apk as signer according to F-Droid\nTrue\n43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab\n$ python3 make-poc-v2.py    # uses app.apk as base, adds signing block from fake.apk\n$ python3 fdroid.py         # verifies and has fake.apk as signer according to F-Droid\nTrue\n43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab\n$ apksigner verify -v --print-certs poc.apk | grep -E '^Verified using|Signer #1 certificate (DN|SHA-256)'\nVerified using v1 scheme (JAR signing): false\nVerified using v2 scheme (APK Signature Scheme v2): true\nVerified using v3 scheme (APK Signature Scheme v3): true\nVerified using v4 scheme (APK Signature Scheme v4): false\nSigner #1 certificate DN: CN=oops\nSigner #1 certificate SHA-256 digest: 029df1354735e81eb97c9bbef2185c8ead3bc78ae874c03a6e96e1e1435ac519\n```\n\n```bash\n$ mkdir fakesigner\n$ cd fakesigner\n$ fdroid init -d oops --repo-keyalias fakesigner\n$ mkdir metadata\n$ printf 'Name: MyApp\\nAllowedAPKSigningKeys: 43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab\\n' \u003e metadata/some.app.id.yml\n$ cp /path/to/poc.apk repo/\n$ fdroid update\n$ jq '.packages[].versions[].manifest.signer.sha256' \u003c repo/index-v2.json\n[\n  \"43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab\"\n]\n```\n\n### Update (2024-04-14)\n\nNB: version A, for `fdroidserver` using the first v1 certificate.\n\n```bash\n$ python3 make-poc-v3a.py   # uses app2.apk (needs targetSdk \u003c 30) as base, adds fake.apk .RSA cert\n$ python3 fdroid.py         # verifies and has fake.apk as signer according to F-Droid\nTrue\n43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab\n```\n\n### Update (2024-04-20)\n\nNB: version B, for `fdroidserver` using the last v1 certificate.\n\n```bash\n$ python3 make-poc-v3b.py   # uses app2.apk (needs targetSdk \u003c 30) as base, adds fake.apk .RSA cert\n$ python3 fdroid.py         # verifies and has fake.apk as signer according to F-Droid\nTrue\n43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab\n```\n\n### Update (2024-12-30 #1)\n\nNB: for convenience we generate our own key for the spoofed certificate as well;\nfor a real exploit we'd have a v1-signed APK to use here instead of signing one\nourselves.\n\n```bash\n$ ./make-key-v4.sh          # generates a dummy key\n$ sha256sum cert-rsa-fake.der cert-rsa-orig.der\n29c6fc6cfa20c2726721944a659a4293c5ac7e8090ab5faa8e26f64ba007bea4  cert-rsa-fake.der\n1e8a45fa677f82755b63edee209fee92081ba822d4f425c3792a1980bfa3fca9  cert-rsa-orig.der\n$ python3 make-poc-v4.py    # uses app3.apk (needs minSdk \u003e= 24 \u0026 targetSdk \u003c 30)\n$ python3 fdroid.py         # verifies and has the wrong signer according to F-Droid\nTrue\nERROR:root:\"Signature is invalid\", skipping:\n  1e8a45fa677f82755b63edee209fee92081ba822d4f425c3792a1980bfa3fca9\n  Common Name: Foo Bar\n1e8a45fa677f82755b63edee209fee92081ba822d4f425c3792a1980bfa3fca9\n$ apksigner verify -v --print-certs poc.apk | grep -E '^Verified using|Signer #1 certificate (DN|SHA-256)'\nVerified using v1 scheme (JAR signing): true\nVerified using v2 scheme (APK Signature Scheme v2): false\nVerified using v3 scheme (APK Signature Scheme v3): false\nVerified using v4 scheme (APK Signature Scheme v4): false\nSigner #1 certificate DN: CN=Foo        Bar\nSigner #1 certificate SHA-256 digest: 29c6fc6cfa20c2726721944a659a4293c5ac7e8090ab5faa8e26f64ba007bea4\n```\n\n### Update (2024-12-30 #2)\n\nNB: version A uses newlines, version B NUL bytes (which makes it fail to\nactually install on Android devices despite verifying with `apksigner`).\n\n```bash\n$ python3 make-poc-v5a.py   # uses app3.apk (needs targetSdk \u003c 30) as base, adds fake.apk .RSA\n$ python3 fdroid.py         # verifies and has fake.apk as signer according to F-Droid\nTrue\n43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab\n```\n\n```bash\n$ python3 make-poc-v5b.py   # uses app3.apk (needs targetSdk \u003c 30) as base, adds fake.apk .RSA\n$ python3 fdroid.py         # verifies and has fake.apk as signer according to F-Droid\nTrue\n43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab\n```\n\n### Update (2025-01-19)\n\nNB: see code comments for requirements.\n\n```bash\n$ python3 make-poc-v6.py    # uses app4.apk, adds signing block from fake.apk\n$ python3 fdroid.py         # verifies and has fake.apk as signer according to F-Droid\nTrue\n43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab\n```\n\n## Patch\n\nThe `fdroidserver.patch` changes the order so it matches Android's v3 before v2\nbefore v1, and monkey-patches `androguard` to see the first block instead of the\nlast one if there are duplicates.  This is still likely to be incomplete, but\nprevents the known bypasses described here.\n\n### Update (2024-04-14)\n\nThe `fdroidserver-multicert.patch` simply rejects any v1 signatures with\nmultiple certificates.  This may reject some valid APKs, but handling those\nproperly is nontrivial and there should be few APKs with multiple certificates\nand no v2/v3 signatures in the wild (e.g. the IzzyOnDroid repository found none\nin its catalog).  We recommend using the official `apksig` library (used by\n`apksigner`) to both verify APK signatures and return the first signer's\ncertificate to avoid these kind of implementation inconsistencies and thus\nfurther vulnerabilities like this one.\n\n### Update (2024-12-30)\n\nThe `fdroidserver-regex.patch` fixes the regex to correctly handle newlines.\n\nThe `fdroidserver-null-v1.patch` (for `fdroidserver` before the changes we\nrecommended against) and `fdroidserver-null-v2.patch` (for current\n`fdroidserver`) use `ZipInfo.orig_filename` to handle NUL bytes properly (and\navoid other potential issues).\n\n## Scanner (2024-04-15, 2024-04-20)\n\nThe `scan.py` script can check APKs for *possible* signature issues: it will\nflag APKs that are not clearly signed with a single unambiguous certificate,\nwhich *could* result in the kind of accidental misidentification of the signer\n-- despite successful verification by `apksigner` -- that we've demonstrated\nhere.  Unfortunately, such misidentification can easily happen as even the\nofficial documentation of the various signature schemes does not completely\ncover how Android/`apksigner` handles such cases.\n\nNB: this will flag some valid APKs too, e.g. those with certificate chains,\nthose having used key rotation, or those with multiple signers; as the\nIzzyOnDroid repository found none in its catalog, these cases luckily seem to be\nrelatively rare.\n\n```bash\n$ python3 scan.py poc*.apk\n'poc1.apk': Mismatch between v1 and v2/v3 certificates\n'poc2.apk': Duplicate block IDs\n'poc3a.apk': Multiple certificates in signature block file\n'poc3b.apk': Multiple certificates in signature block file\n```\n\n### Update (2024-12-30)\n\nThe `scan.py` script has been updated to check for APK Signature Scheme v3.1\nblocks (which will likely give false positives needing manual inspection as\nthose are expected to differ with key rotation) as well as NUL/LF/CR in\nfilenames and to use `ZipInfo.orig_filename`.\n\nNB: currently, neither `fdroidserver` nor `androguard` will see APK Signature\nScheme v3.1 blocks.\n\n```bash\n$ python3 scan.py poc[45]*.apk\n'poc4.apk': Multiple certificates in signature block file\n'poc5a.apk': NUL, LF, or CR in filename\n'poc5b.apk': NUL, LF, or CR in filename\n```\n\n### Update (2025-01-19)\n\n```bash\n$ python3 scan.py poc6.apk\n'poc6.apk': No v3 certs even though v3.1 cert is present\n```\n\n## References\n\n* [1] https://salsa.debian.org/reproducible-builds/diffoscope/-/issues/246\n* [2] https://gitlab.com/fdroid/fdroidserver/-/issues/1056\n* [3] https://github.com/obfusk/sigblock-code-poc\n* [4] https://lists.reproducible-builds.org/pipermail/rb-general/2023-January/002825.html\n* [5] https://gitlab.com/fdroid/fdroidserver/-/issues/1128\n* [6] https://source.android.com/docs/security/features/apksigning/v3\n* [7] https://android.izzysoft.de/articles/named/iod-scan-apkchecks\n* [8] https://www.kuketz-blog.de/android-apps-auf-dem-seziertisch-eine-vertiefte-betrachtung/\n* [9] https://github.com/obfusk/apksigtool\n* [10] https://gitlab.com/fdroid/fdroidserver/-/merge_requests/1466\n* [11] https://github.com/androguard/androguard/pull/1038\n* [12] https://floss.social/@IzzyOnDroid/113765504171758318\n* [13] https://social.librem.one/@eighthave/113820301078034374\n* [14] https://gist.github.com/obfusk/cfab950649631c3ece723b9eb277304b\n* [15] https://gitlab.com/fdroid/fdroidserver/-/issues/1251\n* [16] https://gitlab.com/fdroid/wiki/-/wikis/Internal/AllowedAPKSigningKeys\n* [17] https://gitlab.com/fdroid/fdroidserver/-/issues/1253\n\n## Links\n\n* https://github.com/obfusk/apksigcopier\n* https://github.com/obfusk/apkrepotool\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobfusk%2Ffdroid-fakesigner-poc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobfusk%2Ffdroid-fakesigner-poc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobfusk%2Ffdroid-fakesigner-poc/lists"}