Initial FDroid Audit by pd0x

(this is the notes from the Guardian Project's initial audit of FDroid as part of the Bazaar project)

h2.

Rough notes being transformed from a research journal to a more finely honed state.

Kerplapp P.O.C

  • Kerplapp - dropping apps onto droids.
  • In-progress code dumped to a Github branch

F-droid Notes

  • Results of looking at the F-Droid model for app delivery, specifically from a security audit angle.

Overview

  • How does F-Droid Work?

Server

  • Simple design that requires only a static HTTP server.
  • Repos can be either so called "binary repos" or built from source
    • Binary repos host pre-built APKs (the Guardian Project repo is one such binary repo)
    • Source repos build APKs from source, signing all apps with a unique-per-pkg randomly generated signing key
  • Quickly stand up an F-droid repo dir with:
    cd /repo/dir; python -m http.server 
    
  • Metadata contained in index.xml
    • Signed repos also have a index.jar (detail later)
    • Hierarchy of repo -> app -> apk
    • Create a signer repo out of an unsigned repo with:
      jar cf index.jar index.xml
      jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore mytest.keystore index.jar mykeyalias
      
    • If an index.jar is present the signed index.xml located within the JAR should be considered authoritative
    • The index.xml is loaded on first-add of the repository, if it defines a public key attribute for the repository subsequent repository requests will use index.jar pinned to the public key (TOFU).
  • APK Signing for from-source repos has issues:
    • When a new pkg is seen and there isn't a key in the keystore for the pkg (more on that follows) a new key must be generated and put in the keystore
    • Keygen defaults are good, RSA2048
    • Key alias is auto generated by taking the MD5 of the pkg string and truncating it
      • Decreases resilience of the hash
      • Thankfully relies on second preimage resistance of MD5 to find a collision with another pkg string
        • Should still be redesigned, MD5 has been demonstrated broken for realistic first pre-image attacks in the SSL/X509 space.
      • If you could find a colliding pkg string you could have the source code for your app built and signed with the private key of another app in the same repo, allowing you to utilize the shareduserid feature of Android to access the data of the other app.
  • Server relies on HTTPS for transport security
    • Should pin certificates, does not
  • Due to the way the index.xml metadata is signed, replay and freeze attacks are possible.
    • An adversary can archive the index.jar and matching APKs from the signed repo across several updates/releases.
    • On a subsequent MITM they can replace the current signed index.jar with a previous version. The signature on the metadata will verify and the APKs can be replaced with the version that matched the old index.jar.
    • This attack allows an adversary to perform a 'freeze' attack (i.e. keep all clients at a fixed repository state, regardless of updates)
    • This attack also allows an adversary to pick an exact moment in history to freeze clients at. This gives the adversary the flexibility to decide which version of apps 'first install' clients get (choosing an old version of an apk only works on first install clients as we assume anyone running a version newer than the frozen app at install time can't be forced to downgrade their installation due to Android update semantics).

Client

  • Clients add repositories by URL
    • Index.xml is fetched first, used to TOFU a pubkey for the index.jar
      • No technical reason why the index.xml must exist. Perhaps facilitates in-browser viewing, but an attacker can replace index.xml without knowing the signing key, meaning it shouldn't be used to make decisions about the repo in the presence of a index.jar
  • APKs are subject to normal Android code signing policy
    • MUST be signed
    • TOFU, first install the pkg is associated with the certs used to sign it
    • Subsequent updates to the pkg must be signed with exact same set of certs
  • APK hashes and the first signature on the APK are included in the index.xml metadata for the app. Client app verifies that the hash of the binary matches, and that the hash of the first signature on the APK matches. Note: F-Droid only processes single certificates for signed APKs. This seems to be an assumption based on the F-droid build process signing all pkgs with one unique randomly generated signing certificate. A so called "binary" repo could have apps that are signed with multiple certificates, only the first is included in the index.xml and authenticated by clients
    • This allows a (rather pointless) attack in which an adversary can add their signature to applications from the repo in a MITM attack. If they MITM the first install, the APK will install as the first certificate matches the one in the index.xml and the second signature is ignored. Subsequent updates from the un-MITM'd repo are unable to be installed as they lack the 2nd signature and break the Android code signing policy. If the client had previously installed the single-signed app, the adversaries MITM'd double signed copy won't install. An adversary in a persistent MITM role could repeat this process for every APK on the wire, preventing timely updates.

Also available in: PDF HTML TXT