

Exploit (and Fix) Android "Master Key" - djent
http://www.saurik.com/id/17

======
ctz
I would say that the root cause is not the multiple implementations of zipfile
reading existing, but that the JAR signing is just mis-designed.

A significantly simpler, faster, and more secure method would be to sign the
whole ZIP file, and include the signature as an appendix (like a PGP detached
signature). This wouldn't involve interpreting the contents of the
unauthenticated ZIP file while authenticating it, and would never be
susceptible to these two vulnerabilities.

Schneier gave this design rule the name Horton's Principle[1].

[1] [http://www.schneier.com/paper-ssl-
revised.pdf](http://www.schneier.com/paper-ssl-revised.pdf)

~~~
saurik
(I really wish people would respond instead of just downvote; I spent a long
time putting together both the argument in my article, as well as the argument
in this comment, and as I'm the person who wrote the article in question that
people seem to have liked enough to have held on the home page all day, I feel
like there's probably some merit to my thoughts: the least someone could do is
explain why they think I'm wrong. Personally, I think this is an interesting
philosophical discussion.)

I disagree (with you, not with Schneier, whom I do not believe was making
quite the same point you are): even though the jar verification algorithm is
quite arguably horribly complex (in a way that is likely to lead to
implementation skew), if Android used one or the other algorithm for managing
the contents of a zip file, there would not have a been a problem: there was
nothing intrinsically wrong with their signature checker, it simply didn't
work identically the same to the code that later extracted files.

To demonstrate why I believe this, I actually have another story to tell about
signature verification vulnerabilities on this platform: Android also uses zip
files for OTA system software image updates, and they actually do whole-file
signatures for those archives; yet, they also managed to do it wrong, and
there was a similar signature vulnerability issue that targeted the recovery
system. This is the exploit used in 2009 to root the Motorola Droid.

[http://evancharlton.com/thoughts/how-droid-was-
rooted](http://evancharlton.com/thoughts/how-droid-was-rooted)

The cause? Once again, multiple implementations: in this case, of the system
for finding the signature itself, which was attached to the end of the zip
file (conceptually a zip file "comment", as they wanted to use a separate off-
the-shelf implementation of unzip); the implementation of unzip was using the
normal "scan backwards" algorithm, while the verification code took the length
of the signature and subtracted it from the length of the file. This caused
the unzip code to use a different whole-file than the signature verification
did.

You really can't implement a signature system and feel safe that you did it
right if the code that is using the file is different than the code that is
verifying the file: that's the core issue that is behind this kind of
vulnerability. It's a sloppy kind of mistake, and these "multiple
implementation" situations are always the first place to look for bugs as you
attack a system: maybe the bootloader has a one-off read-only implementation
of the filesystem parser, or maybe the kernel has a different implementation
of XML (such as to parse Apple plist files) than userland.

You might argue that it is easier to have multiple implementations that are
more likely to be implemented in the same way if the specification is simpler,
but that is a mitigation, not a solution: at some point, the file is always
going to be in some kind of container (such as on the filesystem, or in a
flash memory partition, or in a MIME-encoded e-mail message, or yes: inside of
a zip file) and if the implementation of the code that works with that content
is different in one place than in the other place, that is how this kind of
mistake is made.

While complex, jarsign has been around a long time and is fairly battle-
hardened: attackers have had well over a decade to find holes in its
specification, and it has been used in some rather security-critical deployed-
over-insecure-Internet situations; the only reason we'd expect to find a
mistake today is because the implementation was wrong. As an example, we might
expect to find "extra files are allowed in the zip file that were not signed"
(the kind of mistake that at least was, if not still is, made by Apple's
codesign implementation).

To be clear: yes, you should strive to use as whole-file verification
techniques as possible. (I am not disagreeing with Scheier's point: in fact, I
agree with him whole-heartedly, I just believe that "whole-file" is not the
root cause of this particular mistake that Google made with jarsign.) I was,
truly, very disappointed that the APK algorihthm was not a whole-file
signature verification, especially given that the tool that does the whole-
file OTA update signatures is "SignApk" (like, I thought they were using the
same signature in both places).

However, in this case, the signature mechanism was implemented correctly on
top of the Java ZipFile abstraction: the problem is that that underlying
abstraction was incompatible with Dalvik's, and that kind of problem can
happen even to "whole-file" solutions. The underlying philosophical issue is
that, much like "you can't not choose partition tolerance", you can't not have
a container: we aren't signing the whole filesystem, we are signing one file
on the filesystem, and somewhere there will be code that decides which file is
being signed by what signature.

(I'd even argue that we are in "screaming agreement" at some level: in
essence, I feel that the only way to truly know you have "signed the thing you
meant, not the thing you said"\--which requires feeling comfortable that the
interpretation of the meaning in all of the places that you attempt to
understand that meaning is the same--is to have one single implementation in
charge of meaning extraction. In real-world encryption systems, you will
always have a container: to be responsible, you have to not subject that
container to sloppy implementation skew.)

