Last month Patrick Wardle presented “Exposing Gatekeeper” at VB2015 Prague.
The core of the presentation deals with Gatekeeper bypasses originating in the fact that Gatekeeper only verifies the code signatures of the main binary and not of any linked libraries/frameworks/bundles.
This means it is possible to run unsigned code using dynamic library hijacking techniques also presented by Patrick in code that should be protected by Gatekeeper. His exploit uses an Apple code signed application that is vulnerable to dylib hijacking and his modified to run unsigned code when downloaded from the Internet. In this scenario Gatekeeper enters into action and should verify if the download is code signed (assuming the default OS X scenario where it is enabled). But in this case Gatekeeper will fail to verify the linked code and effectively is bypassed.
The core of the problem is that Gatekeeper only deals with the main binary code, and never verifies any linked code. This is obviously a flaw and hopefully a fix by Apple should be out sooner or later. Meanwhile we can try to build ourselves a fix using the TrustedBSD framework. For this I created Gatekeerper, a proof of concept kernel extension for Yosemite 10.10.5 (can be easily adapted to work with El Capitan, but I don’t want to release that code).
The rootpipe vulnerability was finally fully disclosed last week after a couple of months of expectation since the first announcement. It was disclosed as a hidden backdoor but it’s really more something related to access control and crap design than a backdoor. Although keep in mind that good backdoors should be hard to distinguish from simple errors. In this case there are a lot of services using this feature so it’s hardly a hidden backdoor that just sits there waiting for some evil purpose. Apple doesn’t have a stellar security record so the simple explanation has a good chance to prevail over the backdoor story.
Anyway that’s not what really matter for this post. The most important issue is that a fix was made available only for Yosemite 10.10.3. Every other OS X version is left vulnerable. While this is a local privilege escalation vulnerability there are many scenarios where it can be used (you don’t audit every single installer and software that runs on your Mac, do you?). It is extremely reliable and can be used in different ways other than just creating a suid binary.
The vulnerability author wrote the following regarding this issue:
“Apple indicated that this issue required a substantial amount of changes on their side, and that they will not back port the fix to 10.9.x and older.”
So essentially Apple refuses to patch this in all versions except the latest one because it’s apparently too much work. There is no official statement from Apple regarding the EOL (End of Life) status about all previous OS X versions so this course of action is quite strange. Even stranger when Apple backports some security patches to those older versions so they are implicitly not yet dead versions.
In this situation what can we do?
We can try to verify what is the real impact of Apple’s fix and call their bluff if we can prove that we are able to produce a fix without significant changes to the operating system. Challenge accepted!
Let me present you another TrustedBSD policy module, this time to control execution of suid enabled binaries.
The idea to create this started with nemo’s exploitation of bash’s shellshock bug and VMWare Fusion. It was an easy local privilege escalation because there are many Fusion suid enabled binaries. This got me thinking that I want to know when this kind of binaries are executed and if possible control access to them. For the first part you don’t really need this module because the audit features available in OS X can give you this information. I’m more interested in having decision power over what is executed. Or it’s just another nice example of how TrustedBSD is so interesting and powerful and why it’s annoying that Apple is closing KPI access to it (latest SDK warnings say it was never meant to be a KPI).
There are two parts to this. The first is the kernel driver that detects execution, controls access, and notifies userland. The second is a small userland application that receives the kernel notifications, asks the user for a decision, and sends back the answer to the kernel.
The execution is blocked until a decision is made or a timeout is reached (default is 5 seconds, you probably want to increase this value).
To avoid any deadlocks all binaries before userland application is connected are authorized. When the userland finally connects it receives a list (displayed as user notifications) of all the suid binaries that executed during the boot process (from my tests this is zero in Mavericks).
Because of the changing nature of TrustedBSD started in Mavericks, this code only works with Mavericks as it is. If you want to make it work in Mountain Lion or Yosemite, you need to change the hook prototype and compile it with the correspondent SDK.
The userland application needs some work due to my crap Cocoa skills :-). The kernel extension has a few points that need a decision (what to do in error cases mostly), authentication of the userland process (it’s not hard to do), and probably more process information.
A friendly tip: The kernel control interface is used for userland/kernel communication. Because the volume of data exchanged is very low it’s ok for this. If you are thinking about using this interface for high volumes forget about it. It has some weird bug where it starts losing data and not able to keep throughtput (error 55 is what you start to experience).
Source available at Github, https://github.com/gdbinit/can_I_suid.
After surviving the five shots at SyScan’s WhiskeyCon I am finally back home and you get a chance to see the slides and code for the TrustedBSD module I presented.
The goal of REX vs The Romans is to work as detection and prevention tool of Hacking Team’s OS X malware. The TrustedBSD hook allows to detect if the system is already infected, and the Kauth listener to warn about any future infection.
The code has a strong assumption, which is that the malware binaries are installed into /Users/username/Library/Preferences. This has been true for all past known samples found in the wild. I do have better work than this but it is embedded in a commercial product so I can’t disclose its code.
The kernel extension will generate a user alert when something wrong is detected, either on installation or already infected system. A message starting with [WARNING] will also be printed to the system log. The following screenshot demonstrates the execution and infection from the dropper in a Lion 10.7.5 system.
You are encouraged to improve this code. Unfortunately I can’t do much more because of the commercial product conflict. If you do so please tell me about it, I might be able to help with some hints and/or fixes.
I am going to try to get a personal kernel extension certificate so I can distribute a ready to use binary version of this extension. That would be the most helpful case for the common users out there. Let’s see if Apple allows me to do so.
The slides are available here. The code is available at Github.
If you have any issues or questions feel free to mail me or post a comment.
SyScan 2014 was awesome, thanks to everyone who attended and made it possible.
P.S.: The MPRESS dumper will hopefully be released when I do the full presentation on Hacking Team’s OS X malware this year.
I like things well done and the healthy discussion with snare about this topic remembered me this PoC was a bit incomplete. So I decided to close the missing gaps.
The fix is pretty simple. Retrieve a new kauth credential with uid and gid equal to 0 and replace the old one (the code seems stable even without process locks). It also seems to work fine without the allproc lock.
The backdoor also had a small “bug” that I didn’t noticed due to a coincidence. If you are using iStat Menus then you have a daemon running as root that is collecting info from processes and uses task_for_pid() on them. So the trick of getting the task_for_pid for any process even without permissions worked because of this coincidence (the backdoor failed but iStat daemon called task_for_pid() on the process and so backdoor was activated, duh!). The fix is to do a task_for_pid() on itself. It was one of those things that you don’t feel it’s right but you don’t pay much attention to.
The only catch is that the symbol for kauth_cred_setuidgid() is not exported so it’s manually configured for Snow Leopard 10.6.8. To resolve the kernel symbols is another project 😉
All previous versions do not work with Lion because proc structures changed (check xnu/bsd/sys/proc_internal.h).
Version 0.3 adds support to Lion 10.7.1. Edit the main source file and change the define accordingly.
While poking around OS X implementation of TrustedBSD to write the sandbox guide I had the idea of trying to abuse it for backdooring purposes. It’s kind of funny that something designed to protect can be so “easily” abused to install backdoors.
This is not rocket science or a big breakthru post – I was just curious about the possibility to abuse the framework. You still need to find a way to install the kernel module!
So without further delay, I present you Rex, The Wonder Dog. It is a very simple policy module for TrustedBSD that gives r00t privileges to a process named “xyz”, if it calls task_for_pid(). For some unknown reason I couldn’t yet do the same with fork() (it was only working for Safari). I was doing this at 3am so I really didn’t bothered too much about it. It is based on SEDarwin sample policies code.
I had some trouble to compile the policy module (duplicate symbols) if I try to use the macro to initialize the module. I strongly suspect this is because I am using XCode’s kernel extension template. This is just a lazy PoC 😉
The code is unstable. Processes start crashing and crash reporter isn’t executing. It could be due to the very lazy way that r00t privileges are changed for the target process (it only starts to happen after backdoor is activated). Kernel land is dangerous territory! It is tested only with Snow Leopard 10.6.8. Might work with Lion without any problems. Load it as a normal kernel module, with kextload.
Dmesg log when module is loaded:
calling mpo_policy_init for rex_the_wonder_dog
calling mpo_policy_initbsd for rex_the_wonder_dog
Security policy loaded: Rex, the wonder dog! (rex_the_wonder_dog)
Starting the backdoor and getting a r00t shell:
[info] calling task_for_pid()
[info] task for pid returned 0
[info] uid 501 euid 0
[info] setting uid to 0...
[info] uid 0 euid 0
[info] executing r00t shell...
uid=0(root) gid=0(wheel) egid=20(staff) groups=0(wheel),204(_developer),100(_lpoperator),98(_lpadmin),80(admin),
Policy modules open interesting possibilities for implementing other things. Maybe your own binary integrity check module? Or maybe some nice anti-debug for software who must use kernel modules 😉
Sorry for the lazy and unstable code. I’m not that much interested in backdoors, I was just interested in testing the possibility. It’s (just) a clean way to activate a kernel backdoor. If you improve and want to share your code feel free to send it!
Here are the goodies.