Safely Unzipping ZIP Archives
ZIP archives are a convenient packaging format, and many developers use
them. However, ZIP archives are very generous as to what they support,
such as relative paths (e.g., a ZIP archive containing an entry whose path
is ../../../let/us/overwite/something
). This particular bit of flexibility
can represent a security flaw, if a Martian-in-the-middle (MITM) attack replaces
some downloaded ZIP with another one.
NowSecure pointed this out last week,
demonstrating how a malformed ZIP, a MITM attack, and the multidex
backport allow attackers to execute arbitrary code on devices.
I can’t fix multidex. I have been begging people to use SSL properly (thereby making MITM harder) for a while now. So, there is not a lot that I can do to help on those issues.
I can help on unpacking a ZIP archive, though.
CERT published sample safe-unziping code
a while back. I have made a variation on this code, to make it more
Android friendly, and added it as
the unzip()
method on the ZipUtils
class
on my CWAC-Security library.
As with the CERT code, my unzip()
fails on three types of malformed
ZIP archive:
-
Does the ZIP archive attempt to use relative paths to write to locations outside of the directory where we are trying to unzip the contents? This is the attack that NowSecure outlined.
-
Does the ZIP archive expand to more than X bytes? This is the so-called ZIP bomb attack.
-
Does the ZIP archive contain more than X entries?
In the case of any of these failures, or a more generic exception
(e.g., some IOExecption
), unzip()
will delete all that was previously
unzipped, to not leak storage space.
As this code is doing heavy disk I/O and decompression logic, please call
unzip()
on a background thread.
If you find flaws in the unzip()
logic, please
file an issue, and I
will work on it.