|
Message-ID: <Z3bEuhwZJuIYpTqg@nihonium> Date: Thu, 2 Jan 2025 17:54:18 +0100 From: Fay Stegerman <flx@...usk.net> To: oss-security@...ts.openwall.com Subject: Another fdroidserver AllowedAPKSigningKeys certificate pinning bypass Hi! Another update for [1,2], still published at [3]: two more PoCs (bringing the total to 5), patches for the 5th PoC, and an updated script to scan for potentially affected APKs. 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://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. [...] ### [Observations] Update (2024-12-30 #1) Instead of adopting the fixes we proposed, F-Droid wrote and merged their own patch [10], ignoring repeated warnings it had significant flaws (including an incorrect implementation of v1 signature verification and making it impossible to have APKs with rotated keys in a repository). As a result it is possible to construct a valid v1 signature that fdroidserver matches to the wrong certificate. We do this by simply creating and prepending a second SignerInfo using our own certificate, which has the same serial number and an almost identical issuer -- e.g. a common name with a space (0x20) replaced by a tab (0x09) or a DEL (0x7f) appended -- to exploit an implementation that will match the SignerInfo against the wrong certificate through incorrect canonicalisation. Luckily, the impact is lower than that of the other vulnerabilities as it does require a valid signature from the certificate one wishes to spoof. ### [Observations] Update (2024-12-30 #2) Unfortunately, we found another more severe vulnerability as well, caused by a regex incorrectly handling newlines in filenames. This allows another trivial bypass of certificate pinning, as we can once again make fdroidserver see whatever certificate we want instead of the one Android/apksigner does (as long as we have a valid v1 signature for some other APK). The regex in question -- ^META-INF/.*\.(DSA|EC|RSA)$ -- is supposed to match all filenames that start with META-INF/ and end with .DSA, .EC, or .RSA. Unfortunately, the ".*" does not match newlines, and the "$" matches not just the end of the string but "the end of the string or just before the newline at the end of the string". As a result we can use a newline in the filename of the real signature files (before the extension), which Android/apksigner see but fdroidserver does not, and a newline after the .RSA extension for the spoofed signature files, which fdroidserver will see but Android/apksigner will not. NB: androguard seems to use a similarly incorrect regex. We can do almost the exact same thing with NUL bytes instead of newlines, independently of the flawed regex, because Python's ZipInfo.filename is sanitised by removing any NUL byte and everything after it. This will have the same result for fdroidserver and apksigner (which happily accepts NUL bytes in filenames) as above, but luckily Android rejects APKs with NUL bytes in filenames, and such an APK will thus fail to install. NB: in light of all of the above we reiterate that we strongly recommend 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 mistakes and inconsistencies and thus further vulnerabilities. Handling common cases correctly is fairly easy, but handling edge cases correctly is hard; rolling your own implementation without the required expertise and care to get it right is irresponsible. [...] ### [PoC] Update (2024-12-30 #1) NB: for convenience we generate our own key for the spoofed certificate as well; for a real exploit we'd have a v1-signed APK to use here instead of signing one ourselves. ```bash $ ./make-key-v4.sh # generates a dummy key $ sha256sum cert-rsa-fake.der cert-rsa-orig.der 29c6fc6cfa20c2726721944a659a4293c5ac7e8090ab5faa8e26f64ba007bea4 cert-rsa-fake.der 1e8a45fa677f82755b63edee209fee92081ba822d4f425c3792a1980bfa3fca9 cert-rsa-orig.der $ python3 make-poc-v4.py # uses app3.apk (needs minSdk >= 24 & targetSdk < 30) $ python3 fdroid.py # verifies and has the wrong signer according to F-Droid True ERROR:root:"Signature is invalid", skipping: 1e8a45fa677f82755b63edee209fee92081ba822d4f425c3792a1980bfa3fca9 Common Name: Foo Bar 1e8a45fa677f82755b63edee209fee92081ba822d4f425c3792a1980bfa3fca9 $ apksigner verify -v --print-certs poc.apk | grep -E '^Verified using|Signer #1 certificate (DN|SHA-256)' Verified using v1 scheme (JAR signing): true Verified using v2 scheme (APK Signature Scheme v2): false Verified using v3 scheme (APK Signature Scheme v3): false Verified using v4 scheme (APK Signature Scheme v4): false Signer #1 certificate DN: CN=Foo Bar Signer #1 certificate SHA-256 digest: 29c6fc6cfa20c2726721944a659a4293c5ac7e8090ab5faa8e26f64ba007bea4 ``` ### [PoC] Update (2024-12-30 #2) NB: version A uses newlines, version B NUL bytes (which makes it fail to actually install on Android devices despite verifying with apksigner). ```bash $ python3 make-poc-v5a.py # uses app3.apk (needs targetSdk < 30) as base, adds fake.apk .RSA $ python3 fdroid.py # verifies and has fake.apk as signer according to F-Droid True 43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab ``` ```bash $ python3 make-poc-v5b.py # uses app3.apk (needs targetSdk < 30) as base, adds fake.apk .RSA $ python3 fdroid.py # verifies and has fake.apk as signer according to F-Droid True 43238d512c1e5eb2d6569f4a3afbf5523418b82e0a3ed1552770abb9a9c9ccab ``` [...] ### [Patch] Update (2024-12-30) The fdroidserver-regex.patch fixes the regex to correctly handle newlines. The fdroidserver-null-v1.patch (for fdroidserver before the changes we recommended against) and fdroidserver-null-v2.patch (for current fdroidserver) use ZipInfo.orig_filename to handle NUL bytes properly (and avoid other potential issues). [...] ### [Scanner] Update (2024-12-30) The scan.py script has been updated to check for APK Signature Scheme v3.1 blocks (which will likely give false positives needing manual inspection as those are expected to differ with key rotation) as well as NUL/LF/CR in filenames and to use ZipInfo.orig_filename. NB: currently, neither fdroidserver nor androguard will see APK Signature Scheme v3.1 blocks. ```bash $ python3 scan.py poc[45]*.apk 'poc4.apk': Multiple certificates in signature block file 'poc5a.apk': NUL, LF, or CR in filename 'poc5b.apk': NUL, LF, or CR in filename ``` ## References [...] * [10] https://gitlab.com/fdroid/fdroidserver/-/merge_requests/1466 [...] View attachment "fdroidserver-null-v1.patch" of type "text/x-diff" (1054 bytes) View attachment "fdroidserver-null-v2.patch" of type "text/x-diff" (2172 bytes) View attachment "fdroidserver-regex.patch" of type "text/x-diff" (757 bytes) Download attachment "make-key-v4.sh" of type "application/x-sh" (458 bytes) View attachment "make-poc-v4.py" of type "text/x-python" (4087 bytes) View attachment "make-poc-v5a.py" of type "text/x-python" (1095 bytes) View attachment "make-poc-v5b.py" of type "text/x-python" (1180 bytes) View attachment "scan.py" of type "text/x-python" (6336 bytes)
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.