(This is a tracking bug. The issue has already been fixed upstream by the Google Software Updater.)
Google software updater ships with Chrome on MacOS and under certain circumstances installs a root service (com.google.Keystone.Daemon.UpdateEngine)
which lives here: /Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/GoogleSoftwareUpdateDaemon
(Note that the system version of this service is not installed under all circumstances - it will installed be if chrome is configured to update for all users, or if some other google software is installed eg Chrome Remote Desktop.)
This service vends a Distributed Object which exposes an API for updating google software running on the machine.
Distributed Objects are very very hard to safely use across a privileged boundary.
The GoogleSoftwareUpdateDaemon process attempts to "sanitize" objects passed to it by serializing
and deserializing them to a plist, however this still means we can attack the plist serializing code!
Specifically, with D.O. we can pass proxy objects which allow us to overload all objective-c
method calls. We can make the plist code think it's serializing a CFString, and then change our behaviour
to return a different CFTypeID so we become a dictionary for example.
The plist serialization code is not written to defend against such proxy objects, because D.O. should not be
used across a privilege boundary.
In this case I'm targetting the following code in CoreFoundation:
static void _flattenPlist(CFPropertyListRef plist, CFMutableArrayRef objlist, CFMutableDictionaryRef objtable, CFMutableSetRef uniquingset);
plist will be a proxy for the FakeCFObject I define. We can first pretend to be a CFString to pass some other type checks, then become a CFDictionary
(by simply returning a different return value for the _cfTypeID method.) We can then reach the following code:
CFIndex count = CFDictionaryGetCount((CFDictionaryRef)plist);
STACK_BUFFER_DECL(CFPropertyListRef, buffer, count <= 128 ? count * 2 : 1);
CFPropertyListRef *list = (count <= 128) ? buffer : (CFPropertyListRef *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2 * count * sizeof(CFTypeRef), __kCFAllocatorGCScannedMemory);
CFDictionaryGetKeysAndValues((CFDictionaryRef)plist, list, list + count);
for (CFIndex idx = 0; idx < 2 * count; idx++) {
_flattenPlist(list[idx], objlist, objtable, uniquingset);
}
Since we're not a real CFDictionary we can return an arbitrary value for count. If we return a value < 0 it will be used to calculate the size of a stack buffer.
By passing a carefully chosen value this lets you move the stack pointer down an arbitrary amount, off the bottom of the stack and potentially into another thread's stack
or on to the heap, allowing memory corruption.
There will be dozens of other places where attack-controlled proxy objects will be able to interact with system code that was not written expecting to have
to deal with proxy objects.
The correct fix is to not use Distributed Objects across a privilege boundary, as per Apple's advice:
https://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/DesigningDaemons.html
build this PoC:
clang -o ks_r00t ks_r00t.m -framework Foundation -framework CoreFoundation
This PoC exploit will run the shell script /tmp/x.sh as root.
|
Deleted:
ks_r00t.m
9.3 KB
|
Comment 1 by awhalley@google.com
, Mar 15 2018