Joshua Brindle

How to Win At Security

Joshua Brindle's Security Blog

    SE for Android on the GS4 Google Play Edition

    July 08, 2013

    GS4 Google Play Edition != Nexus

    Caveat: Everything here is based on the leaked images floating around and are not necessarilly represenative of what the final, released version will look like. That said, it is probably partially useful and my curiousity got the best of me...

    As you probably know by now the GS4 Google Play Edition is not a Nexus device. It does not get updates from Google and does not have only Google provided code. It was developed by Samsung but removes all of the TouchWiz features. However, it is an updated version of Android (4.3) and has some things that the standard GS4 does not.

    Since the standard GS4 SE for Android controls are Samsung-specific, and deeply embedded in the Knox libraries Google had to provide something else.

    Rather than using the management system proposed by the SE for Android community they invented another, third, system.

    It is a good bet that the interfaces I'll talk about here will show up in AOSP when 4.3 is released so getting a head start can't be a bad idea :)

    State of SE for Android on the GS4 GPE

    As you can see in screenshots released SELinux is present and set to permissive in the Settings app. The policy looks very similar to that of the standard GS4, including components that have been removed from this version:

    $ cat seapp_contexts | grep samsung
    user=_app seinfo=samsung domain=samsung_app type=app_data_file
    user=_app seinfo=samsung name=com.sec.android.app.samsungapps domain=samsung_app type=app_data_file
    user=_app seinfo=untrusted name=com.vlingo.midas domain=samsung_app type=app_data_file
    user=_app seinfo=untrusted name=tv.peel.samsung.app domain=samsung_app type=app_data_file
    user=_app seinfo=untrusted name=tv.peel.samsung.widget domain=samsung_app type=app_data_file
    

    So, since this is basically the same policy hopefully enforcing mode will work fine, as it does on the standard version:

    # setenforce 1
    

    Oh! The screen froze, I can't hit any buttons or even get back to home.

    # setenforce 0
    

    Doesn't fix it! Well... obviously this policy needs some work. Too bad the AVC messages are still removed from the kernel messages and no auditing is present... sigh

    Let's move on to the management API.

    Managing SE for Android on the GS4 GPE

    As I already said, the Knox components have been removed so the previous client I posted won't work. Looking at the code there is a new interface. It is in the services framework and is triggered by an Intent:

    Intent i = new Intent("android.intent.action.UPDATE_SEPOLICY");
    i.putExtra("CONTENT_PATH", bundle_file_path);
    i.putExtra("REQUIRED_HASH", "NONE");
    i.putExtra("SIGNATURE", "...");
    i.putExtra("VERSION", "1");
    

    The Intent can be triggered from anywhere, so a third party app can call it, a separate permission is not even required. The REQUIRED_HASH you see above short circuits the check against the previous policy and the VERSION must be higher than the last update (though can be 1 the first time you call it). The signature is the sticking point. The bundle (I'll get to that in a bit) has to be signed by the update certificate. The update certificate is stored in the Settings provider, though, and with root we can update that (you need to add sqlite3 to your system partition to do this):

    # cd /data/data/com.android.providers.settings/databases
    # sqlite3 settings.db "INSERT into secure (name, value) VALUES('config_update_certificate','<your public DER cert>');"
    

    Once you've done that can you sign the bundle, put it on the system where services can read it (/cache works well) and call the intent. Viola.

    Except not. After getting this to succeed (the certificate and signing stuff in here is really finicky) the policy was placed in /data/security/contexts with 'current' symlinked to it.. Strangely, none of the rest of the code appears to look there. Indeed the policy doesn't appear to be loaded either when the Intent is called or at boot time. Doh!

    Since this doesn't currently work I didn't see much value in posting code. I'll keep it up to date and when I get a real released version I'll post it if it works (or maybe if it doesn't)

    Enforcing mode is set with a global setting (selinux_status), and appears to be persistent, but since enforcing does not work it is not a good idea to set it.

    The Bundle

    So, Google made a new file format for bundling components of the policy, seapp_contexts, property_contexts, file_contexts, and the binary policy, in that order. Sadly mac_permissions.xml is not in the bundle, nor does there appear any way to update it.

    The bundle is simple, 4 32-bit (big endian!) integers representing the size of the policy component and then the components, base64 encoded.

    The Signature

    The signature isn't a standard openssl signature but it can be created in Java, using the standard API's:

    Signature signer = Signature.getInstance("SHA512withRSA");
    signer.initSign(privKey);
    signer.update(bundle);
    signer.update(Long.toString(version).getBytes());
    signer.update(prevHash.getBytes());
    byte [] signature = signer.sign();
    

    You'll note that the signature includes the hash of the bundle itself, and also the version number and hash sent in the intent. This is likely to prevent downgrading by sending the intent with a lower version and different hash.

    Conclusion

    So, my high hopes were dashed (I didn't really have high hopes... I've learned to have low expectations for these things).

    Enforcing mode doesn't work, auditing is still not present, the update API's are non-functional and even if they were functional, they can only be used by the person with the update certificate, which is the vendor. Once again, we have a device that does not allow its owner to protect themselves using mandatory access control, nor change the policy to meet different use cases than the vendor expected. It is very unfortunate that the best technology available for protecting our mobile devices has been locked away from us.

    SE for Android GS4 howto and exploit demo

    June 28, 2013

    The client

    I've uploaded my changes to the SEAdmin client to bitbucket (be sure to check out the samsung-API branch) for brave souls who want to experiment with this. I also have an apk for those who don't want to build their own. This must be installed to the system partition in order to work. It is also crazy hacked together so don't blame me if your phone blows up.

    After rooting your phone with something like motochopper put the apk onto /system:

    $ adb push SEAndroidAdminActivity.apk /sdcard/
    2146 KB/s (758399 bytes in 0.345s)
    
    $ adb shell
    shell@android:/ $ su
    su
    root@android:/ # mount -orw,remount /system
    mount -orw,remount /system
    root@android:/ # cp /sdcard/SEAndroidAdminActivity.apk /system/app
    

    If it doesn't show up in your app list go ahead and reboot.

    root@android:/ # reboot
    

    Run it and enable it as a device administrator by sliding the top slider and hitting activate. It isn't technically a device admin anymore but I didn't disable that code.

    After that go to SELinux Administration:

    SELinux Administration - permissive

    Before you click on enforce it is a good idea to get an adb shell running as system so that you don't have to continually reboot your device:

    $ adb shell
    shell@android:/ $ su
    su
    root@android:/ # runcon u:r:system:s0 sh
    runcon u:r:system:s0 sh
    root@android:/ # id -Z
    id -Z
    uid=0(root) gid=0(root) context=u:r:system:s0
    

    We are all set, go ahead and click enforce:

    SELinux Administration - enforcing

    It will be checked now, unfortunately if you leave this screen and come back it won't be checked. This is due to the app not having permission to check enforcing status. You can always check whether you are enforcing by going to settings->More->About Device and scrolling to the bottom:

    SELinux settings

    The exploit

    Now, lets try to run motochopper on an phone in enforcing:

    $ ./run.sh 
    <snip>
    [*] 
    [*] Waiting for device...
    * daemon not running. starting it now on port 5037 *
    * daemon started successfully *
    [*] Device found.
    [*] Pushing exploit...
    4382 KB/s (1283460 bytes in 0.286s)
    [*] Pushing root tools...
    5119 KB/s (366952 bytes in 0.070s)
    4373 KB/s (1867568 bytes in 0.417s)
            pkg: /data/local/tmp/Superuser.apk
    Success
    4520 KB/s (1578585 bytes in 0.341s)
    [*] Rooting phone...
    [+] This may take a few minutes.
    [-] Failure.
    [*] Cleaning up...
    [*] Exploit complete. Press enter to reboot and exit.
    Press any key to continue . . .   
    

    As you can see it failed.

    Howto

    You can continue running it with the default policy but apps that require root will not work. If you are interested in working on the policy you can use adb to pull it:

    $ adb pull /sepolicy
    3379 KB/s (134977 bytes in 0.039s)
    

    And then use the policy injector to add rules. Afterward you'll need to reload the policy manually:

    $ adb push sepolicy /sdcard/
    3138 KB/s (134977 bytes in 0.042s)
    

    Then on your system shell (you did keep a system shell handy, right?)

    # cd /data/security
    # cp /sdcard/sepolicy .
    # setprop selinux.reload_policy 1
    

    The system shell can also toggle enforcing and permissive (I know, that would have been easier than using a client but I wanted to see what real support there was first)

    root@android:/ # setenforce 0
    setenforce 0
    root@android:/ # getenforce
    getenforce
    Permissive
    root@android:/ # setenforce 1
    setenforce 1
    root@android:/ # getenforce
    getenforce
    Enforcing
    

    Without denials working on the policy will be quite tedious. Hopefully I'll be able to post a modified kernel soon to turn auditing back on. In the mean time, have fun.

    Using SE for Android on the Samsung Galaxy S4

    June 27, 2013

    So, you want to secure your Galaxy S4?

    After my last blog post I really wanted to get my hands on a Galaxy S4 to see what we could do with it, from an SE for Android perspective. Well, I got one yesterday and the answer is, unfortunately, not a whole lot, but not nothing, either.

    First, I wanted to try turning on enforcing from an MDM app without rooting the device. Turns out the APIs for working with SE for Android aren't part of the Samsung MDM API (nor do they have the upstream MDM API present). Instead they are calls to a service that require the calling app to either be signed by the platform certificate or on the system image. Doh.

    So now, time to root. I adapted the SEAdmin app from upstream SE for Android to use the Samsung APIs instead and put it on the system image. After some kajiggering I got the app to set enforcing (yay!). Fun fact: you have to set the logging level all the way up for it to work... hrm...

    So, now let's set it back to permissive. Oh, there isn't an API for that; can't use JNI either, since the app isn't running as system. Well, I guess that is more secure anyway. Too bad it makes development very difficult.

    Speaking of making development very difficult, fun fact #2 is that the audit logs for SELinux are turned off in the kernel so you can't get denials if you plan on doing policy development. Guess I'll have to rebuild the kernel when I get to that stage.

    Time for a demo:

    shell@android:/ $ getenforce
    getenforce
    Permissive
    

    Here's where I hit the button to go into enforcing

    shell@android:/ $ getenforce
    getenforce
    Could not get enforcing status:  Permission denied
    

    So far, so good; to get out of enforcing just reboot. Fun fact #3, there is no way to keep enforcing persistent across reboots. I guess I'll have to write a service or something that runs at boot.

    So, what can we do?

    Okay, I found an interface that lets me load a new policy... at least I thought I did.

    I updated the admin app and tried pushing policy, "success" it says... how do I test though? Pushing a random policy and going into enforcing is likely to cause all manners of breakage so I spent a couple hours writing a rule injector.

    Run that baby:

    joshua@QuarkZilla:~/sepolicy-inject$ sesearch -A -s shell -t selinuxfs -c file sepolicy                                    
    Found 1 semantic av rules:
       allow appdomain fs_type : file getattr ; 
    
    joshua@QuarkZilla:~/sepolicy-inject$ ./sepolicy-inject -s shell -t selinuxfs -c file -p open -P sepolicy -o sepolicy2      
    joshua@QuarkZilla:~/sepolicy-inject$ ./sepolicy-inject -s shell -t selinuxfs -c file -p read -P sepolicy2 -o sepolicy
    
    joshua@QuarkZilla:~/sepolicy-inject$ sesearch -A -s shell -t selinuxfs -c file sepolicy                              
    Found 2 semantic av rules:
       allow shell selinuxfs : file { read open } ; 
       allow appdomain fs_type : file getattr ;
    

    woohoo!

    Let's test this out. Push the policy with the interface and test it out. Fail. Hrm...

    shell@android:/ $ getenforce
    getenforce
    Could not get enforcing status:  Permission denied
    

    Well, we don't have denials so this is tricky. Let's manually load the policy. The policy was put in /data/security by the interface, it just wasn't loaded:

    root@android:/ # setprop selinux.reload_policy 1
    

    How about now?

    shell@android:/ $ getenforce
    getenforce
    Enforcing
    

    Success. Well, at least I know my tool works :)

    There are other interfaces to push down other policy files, such as seapp_contexts (fun fact #4: that interface actually misnames the target file as "seapps_context" ... sigh) but since they aren't reloaded there isn't much value there. Libselinux will read a properly named seapp_contexts from /data/security at boot time so you can put one there manually. mac_permissions.xml does not get read from /data/security, so adding signatures won't work there. Luckily that file is on /system which we are already committed to modifying.

    Correction: mac_permissions.xml does get read from /data/security at boot time. MMAC is not in enforcing by default but can be put into enforcing mode:

    adb shell su -c "setprop persist.mmac.enforce 1"
    adb reboot
    

    Summary

    Here's the bottom line:

    The Good:

    1. SE for Android is present
    2. SE for Android can be turned to enforcing and the phone doesn't catch on fire
    3. There are APIs for updating policies and setting enforcing

    The Bad:

    1. Enforcing not on by default
    2. APIs don't actually work
    3. Setting enforcing does not persist across reboots
    4. No denials
    5. No way to set back to permissive programatically
    6. Strange side effects like requiring max log level
    7. Root required to do anything with SE for Android
    8. No way to get or set SELinux booleans

    The Ugly:

    1. These APIs are actually not public, meaning they can go away or change at any point
    2. Without vulnerabilities available users won't be able to secure their devices...
    3. Real SE for Android development on the GS4 means rerolling the kernel and boot image
    4. If the management APIs are any indication the SE for Android support is still half-baked on the GS4
    5. SE for Android isn't actually a supported feature on standard GS4's, technically it is a Knox feature, and we don't have Knox yet

    Older posts... (see archive for more)