Anatomy of another Android hole – Chinese researchers claim new code verification bypass

Hot on the heels of the so-called “master key” hole in Android comes what Chinese Android researchers are calling “a similar vulnerability.”

They’ve definitely found a bug, and an another embarrassing one for Google’s coders, too.

But is it really a similar vulnerability? How does this one work?

Understanding the bug

The bug is a signed-unsigned integer mismatch.

I’ll try to explain by using diagrams.

As we mentioned before, Android apps are installed using APK files (Android packages), which are just ZIP files with a different extension and some specially-named files inside.

ZIP archives consist of a header, a sequence of file objects, and a central directory, though the central directory is at the end of the file, not in the middle.

Each file object looks something like this:

→ ZIP files are little-endian, meaning that multibyte numbers are stored with the least-significant byte first. So the compressed size of 11,141 bytes above translates into a 32-bit (4-byte) hex number as 0x00002B85, which is stored in the file as 85 2B 00 00. The “magic number” isn’t really a number, it’s a text string: PK is the late Phil Katz, inventor of the file format.

The actual byte values in Fig 1 are taken from a sample app from the Android Game Frame project. I chose this app simply for the modest size of its APK. (Thanks, guys!)

The file in question is classes.dex, the Android bytecode compiled from the app’s Java source.

This is what actually executes on top of Android when the app is launched.

Between the filename and the raw file data (a compressed version of the DEX file) is what the ZIP format calls an “extra field”, used to store arbitrary metadata about the file.

Here, the extra field length (X) is zero, so the extra field itself is zero bytes long, i.e. absent.

The researchers noticed that while Android is verifying an APK, the package is traversed using Java-based utility code for managing ZIP archives.

This code treats the sizes stored in fields F and X as signed 16-bit integers.

But that code that actually loads the DEX file from the package treats these lengths correctly, as unsigned 16-bit integers.

In other words, patching in an extra field length of 0xFFFD, as shown in Fig 2, creates an ambiguous APK.

Treated as unsigned, 0xFFFD translates into 65533 in decimal; as a signed integer, it comes out as -3.

→ 16-bit integers “count” as follows:

0x0000  Unsigned = 0 / Signed = 0 
0x0001  Unsigned = 1 / Signed = 1 
0x0002  Unsigned = 2 / Signed = 2
0x7FFE  Unsigned = 32,766 / Signed = 32,766
0x7FFF  Unsigned = 32,767 / Signed = 32,767
0x8000  Unsigned = 32,768 / Signed = -32,768
0x8001  Unsigned = 32,769 / Signed = -32,767
0xFFFD  Unsigned = 65,533 / Signed = -3
0xFFFE  Unsigned = 65,534 / Signed = -2 
0xFFFF  Unsigned = 65,535 / Signed = -1

So, when the file is checked to validate the cryptographic checksum of the classes.dex file, the verifier treats X as -3.

Instead of jumping forwards in the file as you might expect, it “skips” the extra field by jumping backwards three bytes.

As a result, it reads in the last three bytes of the filename and what comes after it in the extra field, as shown in Fig 3, instead of skipping the extra field and reading the file data that follows.

But when the APK is loaded, 65533 bytes of extra field are skipped forwards, because X is correctly handled as an unsigned number, as shown in Fig 4.

That’s a bug in any language, and an discomfiting one for Google, whose security teams will surely consider this an elementary mistake that ought to have been caught in testing, if not during code review.

Exploiting the flaw

The researchers suggest that you can exploit the verifier’s signed/unsigned U-turn in the file because the characters dex at the end of the filename just happen to match the identification signature bytes found at the start of every DEX bytecode file.

So, they say, all you have to do is to decompress the original classes.dex file from the APK, pad it out to 65536 bytes in length (3 + 65533), and drop it into your hacked APK so it overlaps the end of the filename: the red bytes shown in Fig 3.

Change X to 0xFFFD, and set the compression type to Stored so the verifier knows to take the DEX file as it finds it, and, “Bingo.”

The verifier sees the original classes.dex but the loader skips over this part and read in the attacker’s tampered DEX file.

There’s your exploit.

The earlier “master key” hole involves stashing two different versions of a file of your choice in an APK, relying on a feature that is explicitly supported, albeit not intentionally, by the ZIP format.

The “extra field” flaw involves stashing two versions of the classes.dex file, using a fault in the ZIP handling code that was definitely not intended.

As a result, the “extra field” flaw is not as generic as the “master key” bug.

In particular, any APK hacked in this way must start with a classes.dex that fits into 65536 bytes when decompressed.

That narrows things down a bit, which is a small mercy: out of 96 APK files extracted from my personal Android device, for example, 75 are ineligible for attack by this flaw.

Comparing flaws

This flaw and the recently disclosed “master key” hole are indeed of a similar sort: instead of cracking the cryptographic verifier, they simply mislead it into seeing what it expects, not what will later actually be loaded.

Both attacks involve stashing both clean and hacked versions of the same file object inside a modified APK.

→ This is not a new trick: malware has used anti-anti-virus subterfuges of this sort, known as stealth, since the earliest days. In fact, the first recorded PC virus, Brain, replaced the boot sector of your startup disks, but kept the original copy to show you if ever you went looking for signs of infection. That was back in the mid-1980s.

What next?

Google has published patches already, under the laconic comment "Values in ZIP files are unsigned."

Of course, Google recently announced that seven days is an achievable timeframe for responding to vulnerability reports, down from the 60 days it accepted before.

Although Google has indeed responded quicklly by patching both holes, and should be commended for its efficiency, that doesn’t get the fixes out into the wider world.

It remains to be seen how hard Mountain View will lean on its many handset licensees to push out firmware updates for the “extra field” and “master key” flaws, since they go to the heart of application verification on the Android platform.