Git Product home page Git Product logo

google-security-research's People

google-security-research's Issues

OS X IOKit kernel code execution due to bad free in IOBluetoothFamily

IOBluetoothFamily implements its own queuing primitive: IOBluetoothDataQueue 
(doesn't appear to inherit from IODataQueue, but I could be wrong about that?)

IOBluetoothHCIPacketLogUserClient is userclient type 1 of 
IOBluetoothHCIController.

The IOBluetoothDataQueue free method uses the queue size field which was mapped 
into userspace
when freeing the queue - a userspace client can modify this field forcing a bad 
kmem_free.

Original issue reported on code.google.com by [email protected] on 23 Jun 2014 at 11:21

Attachments:

OS X IOKit kernel code execution due to lack of bounds checking in AppleMultitouchIODataQueue

The clientMemoryForType method of AppleUSBMultitouchUserClient creates an 
AppleMultitouchIODataQueue
and maps it into kernel/user shared memory. AppleMultitouchIODataQueue inherits 
from IODataQueue.

The memory which is mapped into userspace is represented by the variable-sized 
struct IODataQueueMemory:

typedef struct _IODataQueueMemory {
      UInt32 queueSize;
      volatile UInt32 head;
      volatile UInt32 tail;
      IODataQueueEntry queue[1];
} IODataQueueMemory;

These queueSize, head and tail values are used to ensure that the enqueued 
items stay within the bounds of the queue. Userspace can modify the queueSize, 
head and tail values such that the kernel will try to enqueue a value to the 
queue outside of the allocated memory.

Original issue reported on code.google.com by [email protected] on 20 Jun 2014 at 3:58

Attachments:

OS X IOKit kernel code execution due to lack of bounds checking in GPU command buffers

The Intel GPU driver uses shared memory for drawing commands. The userspace
client of the driver calls IOConnectMapMemory to map a shared page which it 
will use,
calling selector 2 of the IOAccelerator userclient (submit_data_buffers) to 
signal to the driver that it should
consume the commands (tokens) written there by the client.

The first 0x10 bytes of the shared memory are some kind of header, the rest is 
filled with
tokens of the form:

+0x00 2-byte token ID
+0x02 length of token (in 4 byte words, including this header)
+0x04 4 byte output offset??
+0x08 body of token
..

I'm still not completely sure what the 4 byte output offset field is actually 
for,
but after processing all the tokens the driver calls 
IGAccelFIFOChannel::submitBuffer,
and writes two words (maybe end of buffer delimiters?) using a value derived 
from those offset fields
as an index and there's no bounds checking, so by specifying a large output 
offset for a token
you can get this function to write the two words: 0x05000000 0x00000000 at a 
controlled offset.

tested on: MacBookAir5,2 w/ 10.9.3/13d64

(it appears to crash the GeForce driver too with what looks at first glance 
like a similar issue. I haven't had a chance to look at it yet but running this 
repro on a MacBookPro10,1 crashes in nvFermiGLContext::UpdateDrawableOffsets 
with an OOB write)

Original issue reported on code.google.com by [email protected] on 6 Jun 2014 at 7:03

Attachments:

OS X IOKit kernel code execution due to controlled kmem_free size in IOSharedDataQueue

IOSharedDataQueue is used by OS X kernel drivers to implement a user/kernel 
queue in shared memory.

The memory which is mapped into userspace is represented by the variable-sized 
struct IODataQueueMemory:

typedef struct _IODataQueueMemory {
      UInt32 queueSize;
      volatile UInt32 head;
      volatile UInt32 tail;
      IODataQueueEntry queue[1];
} IODataQueueMemory;

This is allocated on the kernel heap with IOMallocAligned (the size is rounded 
up to the nearest page_size multiple.) This size is stored in the queueSize 
field.

Kernel code can call IOSharedDataQueue::getMemoryDescriptor to wrap these pages 
in an IOMemoryDescriptor which can then be mapped into the userspace task (via 
IOConnectMapMemory.)

When the IOSharedDataQueue is destructed its ::free method passes the queueSize 
to kmem_free, which simply removes the corresponding number of pages from the 
kernel_map. If userspace increased the value of the queueSize field this will 
remove more pages than were allocated - potentially removing other live 
allocations from the map.

This could be leveraged for code execution by, for example, forcing these free 
pages to be reallocated with controlled data before they are accessed.

[[ Note that due to the nature of this bug this PoC will crash in weird ways - 
break at IODataQueue::free to see the bad size]]

Original issue reported on code.google.com by [email protected] on 18 Jun 2014 at 4:11

Attachments:

launchd heap corruption due to integer overflow in launch_data_unpack

launchd is the OS X equivalent of init. It runs as root and is the parent of 
all other processes. On OS X it also serves as the bootstrap server, allowing 
any process with a send right to the launchd bootstrap mach port (by default 
all processes, including chrome/safari renderers) to request and register 
services provided via mach ports.

The MIG methods legacy_ipc_request and swap_complex pass a pointer to an 
out-of-line buffer containing arguments in a serialized in a custom format, 
which is deserialized by the function launch_data_unpack:

liblaunch.c:

launch_data_t
launch_data_unpack(void *data, size_t data_size, int *fds, size_t fd_cnt, 
size_t *data_offset, size_t *fdoffset)
{
  launch_data_t r = data + *data_offset;
  size_t i, tmpcnt;

  if ((data_size - *data_offset) < sizeof(struct _launch_data))
    return NULL;
  *data_offset += sizeof(struct _launch_data);
...


data points to a user-controlled buffer. launch_data_t is typedef'd as a 
pointer to a struct _launch_data:

liblaunch.c:

struct _launch_data {
  uint64_t type;
  union {
    struct {
      union {
        launch_data_t *_array;
        char *string;
        void *opaque;
        int64_t __junk;
      };
      union {
        uint64_t _array_cnt;
        uint64_t string_len;
        uint64_t opaque_size;
      };
    };
    int64_t fd;
    uint64_t  mp;
    uint64_t err;
    int64_t number;
    uint64_t boolean;
    double float_num;
  };
};

launch_data_unpack unpacks each of the _launch_data structures in the input 
buffer (data.) If the type is one of: LAUNCH_DATA_DICTIONARY,
LAUNCH_DATA_ARRAY, LAUNCH_DATA_STRING or LAUNCH_DATA_OPAQUE then the struct 
_launch_data is followed by the payload. launch_data_unpack fixes up the
pointer (_array, string or opaque) to point to the payload.

The following code unpack arrays and dictionaries:

liblaunch.c:

...
  case LAUNCH_DATA_DICTIONARY:
  case LAUNCH_DATA_ARRAY:
    tmpcnt = big2wire(r->_array_cnt);
    if ((data_size - *data_offset) < (tmpcnt * sizeof(uint64_t))) {                               <-- (a)
      errno = EAGAIN;
      return NULL;
    }
    r->_array = data + *data_offset;
    *data_offset += tmpcnt * sizeof(uint64_t);
    for (i = 0; i < tmpcnt; i++) {
      r->_array[i] = launch_data_unpack(data, data_size, fds, fd_cnt, data_offset, fdoffset);     <-- (b)
      if (r->_array[i] == NULL)                                                                   <-- (c)
        return NULL;
    }
    r->_array_cnt = tmpcnt;
    break;
...

The first bug is the lack of an integer overflow check on (tmpcnt * 
sizeof(uint64_t)). Although this result isn't used for an allocation size, we 
can still
leverage it for heap corruption:

By setting tmpcnt to (UINT64_MAX + 1) / 8 we can force the multiplication to 
overflow to 0. If we also ensure that (data_size - *data_offset) is 0 (that is,
there is no actual payload data) then r->_array will point to the first byte 
after the data buffer. At (b) a NULL pointer will then be written to 
r->_array[0],
setting the 8 bytes following the data buffer to 0. By crafting a buffer 
containing an array of strings and arrays its possible to control the size of 
the data
buffer exactly.

Exploitation would involve finding something interesting to corrupt, the 
canonical example would be finding something which was reference counted and
overwriting the reference count.

Original issue reported on code.google.com by [email protected] on 3 Apr 2014 at 6:51

Lack of bounds checking in notifyd

notifyd is an OS X service running as root. Chrome and Safari sandboxed 
renderers have a mach port to talk to notifyd.

The prev_slot argument of the _notify_server_regenerate MIG IPC method isn't 
bounds checked:

notify_ipc.defs:

routine _notify_server_regenerate
(
  server : mach_port_t;
  name : notify_name;
  token : int;
  reg_type : uint32_t;
  port : mach_port_make_send_t;
  sig: int;
  prev_slot: int;
  prev_state : uint64_t;
  prev_time : uint64_t;
  path : notify_name;
  path_flags: int;
  out new_slot : int;
  out new_name_id : uint64_t;
  out status : int;
  ServerAuditToken audit : audit_token_t
);

server implementation in notify_proc.c:

    case NOTIFY_TYPE_MEMORY:
    {
      kstatus = __notify_server_register_check_2(server, name, nameCnt, token, &size, new_slot, new_nid, status, audit);
      if (*status == NOTIFY_STATUS_OK)
      {
        if ((*new_slot != UINT32_MAX) && (prev_slot != UINT32_MAX) && (global.last_shm_base != NULL))                         <-- (a)
        {
          global.shared_memory_base[*new_slot] = global.shared_memory_base[*new_slot] + global.last_shm_base[prev_slot] - 1;  <-- (b)
          global.last_shm_base[prev_slot] = 0;                                                                                <-- (c)
        }
      }
      break;
    }


If global.last_shm_base is not NULL (a) then prev_slot is used as in index to 
read (b) and write (c). prev_slot is only checked to
not be UINT32_MAX; it isn't validated to fall within the bounds of 
global.last_shm_base.

global.shared_memory_base will only not be NULL if the notifyd service has 
restarted - I don't know under what circumstances this would
occur, therefore severity is lower.

Original issue reported on code.google.com by [email protected] on 3 Apr 2014 at 7:16

Safari sandbox IPC memory corruption with WebEvent::Wheel

The handler for the WebPageProxy::DidReceiveEvent IPC message fails to check 
that the WTF::Deque m_currentlyProcessedWheelEvents is not empty before calling 
takeFirst() when processing an event of type WebEvent::Wheel:

void WebPageProxy::didReceiveEvent(uint32_t opaqueType, bool handled)
...
    case WebEvent::Wheel: {
        ASSERT(!m_currentlyProcessedWheelEvents.isEmpty());

        OwnPtr<Vector<NativeWebWheelEvent>> oldestCoalescedEvent = m_currentlyProcessedWheelEvents.takeFirst();
...

That debug ASSERT should be a runtime CHECK.

takeFirst() simply returns the head of the queue and bumps the pointer (mod the 
buffer size,) there is no check that the queue isn't empty.

When this scope is left that OwnPtr will be freed. Exploitability depends on 
the contents of the buffer backing the queue, a crash stack is show below where 
the faulting address is clearly a poison value, however the memory pointed to 
by that poisoned pointer doesn't seem to be reserved, and could probably be 
easily heap-sprayed too. Further investigation of the operation of WTF::Deque 
would be required to understand if this could be exploited reliably.

Crash stack:
(lldb) attach 9724
Process 9724 stopped
Executable module set to 
"/Applications/Safari.app/Contents/MacOS/SafariForWebKitDevelopment".
Architecture set to: x86_64-apple-macosx.
(lldb) c
Process 9724 resuming
Process 9724 stopped
* thread #1: tid = 0x9ef7, 0x0000000101bd1513 
WebKit2`WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, 
WTF::CrashOnOverflow>::~Vector(this=0x00000001badbeef5) + 9 at Vector.h:595, 
queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, 
address=0x1badbef01)
    frame #0: 0x0000000101bd1513 WebKit2`WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow>::~Vector(this=0x00000001badbeef5) + 9 at Vector.h:595
   592  
   593      ~Vector()
   594      {
-> 595          if (m_size)
   596              shrink(0);
   597      }
   598  
WebKit2`WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, 
WTF::CrashOnOverflow>::~Vector() + 9 at Vector.h:595:
-> 0x101bd1513:  cmp    DWORD PTR [RBX + 12], 0
   0x101bd1517:  je     0x101bd1523               ; WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow>::~Vector() + 25 [inlined] WTF::VectorBufferBase<WebKit::NativeWebWheelEvent>::buffer() at Vector.h:373
WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow>::~Vector() 
+ 25 [inlined] WTF::VectorBuffer<WebKit::NativeWebWheelEvent, 
0ul>::~VectorBuffer() at Vector.h:597
WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow>::~Vector() 
+ 25 at Vector.h:597
   0x101bd1519:  mov    RDI, RBX
(lldb) bt
* thread #1: tid = 0x9ef7, 0x0000000101bd1513 
WebKit2`WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, 
WTF::CrashOnOverflow>::~Vector(this=0x00000001badbeef5) + 9 at Vector.h:595, 
queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, 
address=0x1badbef01)
    frame #0: 0x0000000101bd1513 WebKit2`WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow>::~Vector(this=0x00000001badbeef5) + 9 at Vector.h:595
    frame #1: 0x0000000101bd14f4 WebKit2`WTF::OwnPtr<WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow> >::~OwnPtr() [inlined] WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow>::~Vector(this=0x00000001badbeef5, p=0x00000001badbeef5) + 8 at Vector.h:594
    frame #2: 0x0000000101bd14ec WebKit2`WTF::OwnPtr<WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow> >::~OwnPtr() [inlined] void WTF::deleteOwnedPtr<WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow> >(ptr=0x00000001badbeef5) at OwnPtrCommon.h:37
    frame #3: 0x0000000101bd14ec WebKit2`WTF::OwnPtr<WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow> >::~OwnPtr(this=<unavailable>) + 14 at OwnPtr.h:47
    frame #4: 0x0000000101bbd198 WebKit2`WebKit::WebPageProxy::didReceiveEvent(unsigned int, bool) [inlined] WTF::OwnPtr<WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow> >::~OwnPtr(this=0x00000001badbeef5) + 776 at OwnPtr.h:47
    frame #5: 0x0000000101bbd193 WebKit2`WebKit::WebPageProxy::didReceiveEvent(this=<unavailable>, opaqueType=<unavailable>, handled=<unavailable>) + 771 at WebPageProxy.cpp:3699
    frame #6: 0x0000000101bdb648 WebKit2`void IPC::handleMessage<Messages::WebPageProxy::DidReceiveEvent, WebKit::WebPageProxy, void (WebKit::WebPageProxy::*)(unsigned int, bool)>(IPC::MessageDecoder&, WebKit::WebPageProxy*, void (WebKit::WebPageProxy::*)(unsigned int, bool)) [inlined] void IPC::callMemberFunctionImpl<WebKit::WebPageProxy, void (args=0x0000000100000003, __t=0x0000000100000003, object=<unavailable>)(unsigned int, bool), std::__1::tuple<unsigned int, bool>, 0ul, 1ul>(WebKit::WebPageProxy*, void (WebKit::WebPageProxy::*)(unsigned int, bool), std::__1::tuple<unsigned int, bool>&&, std::index_sequence<0ul, 1ul>) + 73 at HandleMessage.h:16
    frame #7: 0x0000000101bdb62a WebKit2`void IPC::handleMessage<Messages::WebPageProxy::DidReceiveEvent, WebKit::WebPageProxy, void (WebKit::WebPageProxy::*)(unsigned int, bool)>(IPC::MessageDecoder&, WebKit::WebPageProxy*, void (WebKit::WebPageProxy::*)(unsigned int, bool)) [inlined] void IPC::callMemberFunction<WebKit::WebPageProxy, void (args=0x0000000100000003, __t=0x0000000100000003)(unsigned int, bool), std::__1::tuple<unsigned int, bool>, std::make_index_sequence<2ul> >(std::__1::tuple<unsigned int, bool>&&, WebKit::WebPageProxy*, void (WebKit::WebPageProxy::*)(unsigned int, bool)) at HandleMessage.h:22
    frame #8: 0x0000000101bdb62a WebKit2`void IPC::handleMessage<Messages::WebPageProxy::DidReceiveEvent, WebKit::WebPageProxy, void (decoder=<unavailable>, object=<unavailable>, function=<unavailable>)(unsigned int, bool)>(IPC::MessageDecoder&, WebKit::WebPageProxy*, void (WebKit::WebPageProxy::*)(unsigned int, bool)) + 43 at HandleMessage.h:117
    frame #9: 0x0000000101bd8c8e WebKit2`WebKit::WebPageProxy::didReceiveMessage(this=0x00007fc8eb80ea18, connection=<unavailable>, decoder=0x0000000107206820) + 916 at WebPageProxyMessageReceiver.cpp:214
    frame #10: 0x0000000101abf7d5 WebKit2`IPC::MessageReceiverMap::dispatchMessage(this=<unavailable>, connection=0x000000010720d780, decoder=0x0000000107206820) + 125 at MessageReceiverMap.cpp:87
    frame #11: 0x0000000101a6c947 WebKit2`WebKit::ChildProcessProxy::dispatchMessage(this=<unavailable>, connection=<unavailable>, decoder=<unavailable>) + 13 at ChildProcessProxy.cpp:118
    frame #12: 0x0000000101c05328 WebKit2`WebKit::WebProcessProxy::didReceiveMessage(this=0x000000010591ea80, connection=0x000000010720d780, decoder=0x0000000107206820) + 24 at WebProcessProxy.cpp:364
    frame #13: 0x0000000101a6dafc WebKit2`IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::MessageDecoder, std::__1::default_delete<IPC::MessageDecoder> >) [inlined] IPC::Connection::dispatchMessage(decoder=<unavailable>, this=<unavailable>) + 94 at Connection.cpp:770
    frame #14: 0x0000000101a6daef WebKit2`IPC::Connection::dispatchMessage(this=0x000000010720d780, message=0x00007fff5eb85020) + 81 at Connection.cpp:791
    frame #15: 0x0000000101a6fb70 WebKit2`IPC::Connection::dispatchOneMessage(this=0x000000010720d780) + 106 at Connection.cpp:817
    frame #16: 0x0000000101463a45 JavaScriptCore`WTF::RunLoop::performWork() [inlined] std::__1::function<void (this=0x00007fff5eb850e0)>::operator()() const + 421 at functional:1435
    frame #17: 0x0000000101463a3b JavaScriptCore`WTF::RunLoop::performWork(this=0x000000010590df30) + 411 at RunLoop.cpp:104
    frame #18: 0x0000000101464122 JavaScriptCore`WTF::RunLoop::performWork(context=<unavailable>) + 34 at RunLoopCF.cpp:38
    frame #19: 0x00007fff871bb731 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #20: 0x00007fff871acea2 CoreFoundation`__CFRunLoopDoSources0 + 242
    frame #21: 0x00007fff871ac62f CoreFoundation`__CFRunLoopRun + 831
    frame #22: 0x00007fff871ac0b5 CoreFoundation`CFRunLoopRunSpecific + 309
    frame #23: 0x00007fff90fb1a0d HIToolbox`RunCurrentEventLoopInMode + 226
    frame #24: 0x00007fff90fb17b7 HIToolbox`ReceiveNextEventCommon + 479
    frame #25: 0x00007fff90fb15bc HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 65
    frame #26: 0x00007fff8db9e3de AppKit`_DPSNextEvent + 1434
    frame #27: 0x00007fff8db9da2b AppKit`-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 122
    frame #28: 0x00007fff8a241290 Safari`-[BrowserApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 161
    frame #29: 0x00007fff8db91b2c AppKit`-[NSApplication run] + 553
    frame #30: 0x00007fff8db7c913 AppKit`NSApplicationMain + 940
    frame #31: 0x00007fff8a41306d Safari`SafariMain + 267
    frame #32: 0x00007fff83a405fd libdyld.dylib`start + 1
    frame #33: 0x00007fff83a405fd libdyld.dylib`start + 1
(lldb) register read
General Purpose Registers:
       rax = 0x0000000000000000
       rbx = 0x00000001badbeef5
       rcx = 0x000000000000000f
       rdx = 0x0000000000000000
       rdi = 0x00000001badbeef5
       rsi = 0x0000000000000003
       rbp = 0x00007fff5eb84d50
       rsp = 0x00007fff5eb84d40
        r8 = 0x0000000000000074
        r9 = 0x0000000000000000
       r10 = 0x00007fc8eb4caf9c
       r11 = 0x00007fff73da4db8  (void *)0x00007fff73da4d90: NSAutoreleasePool
       r12 = 0x00007fc8eb80ea18
       r13 = 0x00007fff5eb85200
       r14 = 0x0000000000000001
       r15 = 0x00000001badbeef5
       rip = 0x0000000101bd1513  WebKit2`WTF::Vector<WebKit::NativeWebWheelEvent, 0ul, WTF::CrashOnOverflow>::~Vector() + 9 at Vector.h:595
    rflags = 0x0000000000010206
        cs = 0x000000000000002b
        fs = 0x0000000000000000
        gs = 0x0000000000000000


This issue can be easily reproduced by applying following patch to the renderer 
code then scrolling using the mouse on a page:

Source/WebKit2/WebProcess/WebPage/EventDispatcher.cpp:

         ScrollingTree::EventResult result = scrollingTree->tryToHandleWheelEvent(platformWheelEvent);
         if (result == ScrollingTree::DidHandleEvent || result == ScrollingTree::DidNotHandleEvent) {
-            sendDidReceiveEvent(pageID, wheelEvent, result == 
ScrollingTree::DidHandleEvent);
+            for (int i = 0; i < 128; i++){
+                sendDidReceiveEvent(pageID, wheelEvent, result == 
ScrollingTree::DidHandleEvent);
+            }
             return;
         }
     }

Original issue reported on code.google.com by [email protected] on 3 Apr 2014 at 5:57

OS X IOKit kernel code execution due to lack of bounds checking in IOAccel2DContext2::blit

IOAccel2DContext2::blit is implemented in IOAcceleratorFamily2.kext - the 
vulnerable code can be reached from the chrome GPU process sandbox and the 
safari renderer sandbox.

The kernel code fails to validate an index passed from userspace which is used 
to index an array of pointers to Surfaces.

Provided a suitable structure can be constructed in kernel memory this bug can 
be leveraged for code execution since the code will call an attacker controller 
function pointer.

See attached blit.c for a crashing poc.

Original issue reported on code.google.com by [email protected] on 2 May 2014 at 3:01

Attachments:

OS X IOKit kernel code execution due to lack of bounds checking in IOAccelDisplayPipeTransaction2::set_plane_gamma_table

IOAccelDisplayPipe2::transaction_set_plane_gamma_table fails to verify the 
second dword of IOAccelDisplayPipeGammaTableArgs which can be controlled by 
calling the external method with selector 5 of IOAccelDisplayPipeUserClient2.

This unchecked dword is passed to 
IOAccelDisplayPipeTransaction2::set_plane_gamma_table where it is used as an 
index to read a pointer to a c++ object from an array. By specifying a large 
index this will read a c++ object pointer out-of-bounds. The code then calls a 
virtual function on this object.

Impact:
This userclient can be instantiated in the chrome GPU process sandbox and the 
safari renderer sandbox.

Original issue reported on code.google.com by [email protected] on 16 Jun 2014 at 1:55

Attachments:

OS X IOKit kernel code execution due to NULL pointer dereference in IOThunderboltFamily

IOThunderboltFamilyUserClient::xDomainRequestAction doesn't verify that a 
pointer is non-NULL before calling a virtual function, giving trivial kernel 
RIP control if the user process maps the NULL page, as this PoC demonstrates.

IOThunderboltFamilyUserClient::xDomainRequestAction is called by 
IOThunderboltFamilyUserClient::xDomainRequest which is selector 13 of 
IOThunderboltController

Original issue reported on code.google.com by [email protected] on 22 May 2014 at 8:03

Attachments:

OS X IOKit kernel code execution due to multiple bounds checking issues in IGAccelGLContext token parsing (x3)

The functions IGAccelGLContext::process_token_BindConstantBuffers, 
IGAccelGLContext::process_token_BindDrawFBOColor and 
GAccelGLContext::process_token_BindTextures fail to bounds-check the dword at 
offset 0x10 of the token they're parsing - this value is read from user/kernel 
shared memory and is thus completely attacker controlled. The value is used as 
the index for a kernel memory write.

(See previous token parsing bugs for more details of the IOAccelerator token 
structures.)

These PoCs find the tokens in shared memory and set the offset to a large value 
to cause a kernel panic.

IMPACT:
This userclient can be instantiated from the chrome gpu sandbox and the safari 
renderer sandbox

Original issue reported on code.google.com by [email protected] on 17 Jun 2014 at 4:35

Attachments:

Flash leak of uninitialized data whilst rendering JPEGs

This is probably another instance of CVE-2013-6629, reference:

http://seclists.org/fulldisclosure/2013/Nov/83

A SWF to reproduce is attached, along with source. To reproduce, host 
JPEGLeak.swf on the same web server / directory as 55.jpg.

Since this is uninitialized data, you can reload the SWF and see the rendered 
JPEG change either slightly or sometimes dramatically. It seems to work best 
with a Flash process that is not fresh. A couple of screenshots are attached to 
illustrate that the rendered images can sometimes differ significantly. The SWF 
file also demonstrates that the uninitialized data can be leaked to script, 
which makes the issue interesting / more serious.

If this is indeed the same underlying issue as CVE-2013-6629, then this patch 
may be useful:

http://src.chromium.org/viewvc/chrome/trunk/src/third_party/libjpeg/jdmarker.c?r
1=228354&r2=228353&pathrev=228354

Original issue reported on code.google.com by [email protected] on 8 Jul 2014 at 7:35

Attachments:

OS X IOKit kernel code execution due to heap overflow in IOHIKeyboardMapper::parseKeyMapping

The function IOHIKeyboardMapper::parseKeyMapping(const UInt8 * map, UInt32 
mappingLen, NXParsedKeyMapping * parsedMapping)
can be reached by a regular user by setting the HIDKeyMapping property of 
IOHIDKeyboard in the IOKit registry.

This function is responsible for parsing a binary keymapping file.

The function NextNum reads either 1 or two bytes from map depending on whether 
the first two bytes in the file are non-zero.

  IOHIKeyboardMapper::parseKeyMapping(const UInt8 * map, UInt32 mappingLen, NXParsedKeyMapping * parsedMapping)
    ...
    parsedMapping->numSeqs = NextNum(&nmd);         <-- (a)
    if (parsedMapping->numSeqs <= maxSeqNum)        <-- (b)
      return false;

    for(i = 0; i < parsedMapping->numSeqs; i++)
    {
      parsedMapping->seqDefs[i] = (unsigned char *)nmd.bp; <-- (c)
      for(j=0, l=NextNum(&nmd); j<l; j++)
      {
              NextNum(&nmd);
              NextNum(&nmd);
      }
    }

At (a) this function reads the number of sequence definitions follow, at (b) 
this number is checked against a lower bound however
there is no upper-bound check. By specifying a large value for numSeqs (can be 
up to 0xffff) we can overflow the fixed-size seqDefs buffer at (c).

seqDefs is defined here:

  typedef struct _NXParsedKeyMapping_ {
    short shorts;
    char  keyBits[NX_NUMKEYCODES];
    int     maxMod;
    unsigned char *modDefs[NX_NUMMODIFIERS];
    int     numDefs;
    unsigned char *keyDefs[NX_NUMKEYCODES];
    int     numSeqs;
    unsigned char *seqDefs[NX_NUMSEQUENCES];         <-- seqDefs ( #define NX_NUMSEQUENCES  128 )
    int     numSpecialKeys;
    unsigned short specialKeys[NX_NUMSPECIALKEYS];
    const unsigned char *mapping;
    int mappingLen;
  } NXParsedKeyMapping;

It's actually very easy to get code execution with this buffer overflow:

This NXParsedKeyMapping is a member var of IOHIKeyboardMapper:

class IOHIKeyboardMapper : public OSObject
{
private:
  IOHIKeyboard *    _delegate;
  bool        _mappingShouldBeFreed;
  NXParsedKeyMapping  _parsedMapping;                       <-- _parsedMapping.seqDefs will be overflowed
  IOHIDSystem  *    _hidSystem;
...
  ExpansionData * _reserved;
...
  UInt32              _stickyKeys_State; 
  int           _stickyKeys_NumModifiersDown;
  UInt8         _stickyKeys_Modifiers[kMAX_MODIFIERS];
  StickyKeys_ToggleInfo * _stickyKeys_ShiftToggle;
  StickyKeys_ToggleInfo * _stickyKeys_OptionToggle;
  bool        _stateDirty;
  OSDictionary *    _onParamDict;                           <-- pointer to an object with virtual method

We can easily cause a virtual method to be called on _onParamDict by ensuring 
that parseKeyMapping returns false indicating
an error parsing the keyfile. This will cause a call to: 
IOHIKeyboardMapper::free which will call stickyKeysFree:

   void IOHIKeyboardMapper::stickyKeysfree (void)
  {
    ...
    if (_onParamDict)            <-- we overwrite this pointer
      _onParamDict->release();   <-- virtual method ( offset 0x28 in the vtable)

Some offsets:

_onParamDict is at offset 0xe30 in the IOHIDKeyboardMapper

seqDefs is at offset 0x998 in NXParsedKeyMapping, and NXParsedKeyMapping is at 
offset 0x20 in IOHIKeyboardMapper:

+0x000: IOHIKeyboardMapper
...
+0x020:   NXParsedKeyMapping _parsedMapping
...
+0x9b8:     unsigned char* seqDefs[]  -+
...                                    | 0x478 bytes -> 0x8f pointers
+0xe30:   OSDictionary* _onParamDict  -+


So the 0x90th seqDef entry will overlap with the _onParamDict pointer.

Looking again at the parseKeyMapping function snippet where the overflow takes 
place:

  for(i = 0; i < parsedMapping->numSeqs; i++)
  {
    parsedMapping->seqDefs[i] = (unsigned char *)nmd.bp;
    for(j=0, l=NextNum(&nmd); j<l; j++)
    {
            NextNum(&nmd);
            NextNum(&nmd);
    }

(nmb.bp points to the next number to parse.)

This loop is parsing a length followed by twice that number of Nums.

We can supply 0x8f entries of: 0x00 0x00 (len=0)
followed by one entry of: 0x00 0x02 (len = 0x2)
                          0x00 0x78 0x56 0x00
                          0x00 0x00 0x00 0x00

which will overwrite the _onParamDict pointer with a pointer to: 0x5678000000 
which will be intepreted as a pointer to
a vtable :-) We can then map that page in userspace and write a fake vtable at 
0x5678000200.

We can then force the parseKeyMapping function to return false here by 
supplying a value larger than NX_NUMSPECIALKEYS:
  numMods = NextNum(&nmd);
  parsedMapping->numSpecialKeys = numMods;
  if ( numMods > NX_NUMSPECIALKEYS )
    return false;

PoC attached.

Original issue reported on code.google.com by [email protected] on 30 Jun 2014 at 2:47

Attachments:

Flash leak of uninitialized data whilst rendering a 2-component JPEG

A SWF to reproduce is attached, along with source. To reproduce, host 
JPEGLeak2.swf on the same web server / directory as twocomps.jpg. A screenshot 
of the PoC in action is also attached.

twocomps.jpg is a weird JPEG file that has all sorts of problems (truncated, 
etc.,) but the main problem is that no software really knows how to handle 
2-component JPEGs, as these do not exist in the wild. It looks like Flash's 
response to not knowing how to handle it is to leave the image canvas 
uninitialized. This can be a significant security issue.

The PoC goes most of the way to pulling a pointer value (ASLR defeat) out of 
the uninitialized canvas -- for the 64-bit Linux platform. But you can get the 
point just by refreshing the PoC a lot and seeing the rendered content change.

Since it's very easy to use this vulnerability to read uninitialized memory 
content, a 90-day disclosure deadline applies.

Original issue reported on code.google.com by [email protected] on 9 Jul 2014 at 11:22

Attachments:

OS X IOKit kernel code execution due to NULL pointer dereference in IOHIKeyboardMapper::stickyKeysfree

When setting a new keyboard mapping the following code will be reached:

    IOHIKeyboardMapper * IOHIKeyboardMapper::keyboardMapper(
                                                            IOHIKeyboard * delegate,
                                                            const UInt8 *  mapping,
                                                            UInt32       mappingLength,
                                                            bool       mappingShouldBeFreed )
    {
      IOHIKeyboardMapper * me = new IOHIKeyboardMapper;

      if (me && !me->init(delegate, mapping, mappingLength, mappingShouldBeFreed))
      {
        me->free();

If the init method returns false, IOHIKeyboardMapper::free will be called.

    bool IOHIKeyboardMapper::init(  IOHIKeyboard *delegate,
                                  const UInt8 *map,
                                  UInt32 mappingLen,
                                  bool mappingShouldBeFreed )
    {
      if (!super::init())  return false;

      _delegate         = delegate;

      if (!parseKeyMapping(map, mappingLen, &_parsedMapping)) return false;
    ...
      _reserved = IONew(ExpansionData, 1);

If the parseKeyMapping method returns false (by supplying a malformed key 
mapping),
the init function will return early, and won't initialize the _reserved member.

The IOHIKeyboardMapper::free method will call stickyKeysfree() (both 
_parsedMapping.mapping and _parsedMapping.mappingLen
are non-zero) :

    void IOHIKeyboardMapper::free()
    {
        if (!_parsedMapping.mapping || !_parsedMapping.mappingLen)
            return;

        stickyKeysfree();

stickyKeysfree attempts to release all member objects which have been 
initialized:

    void IOHIKeyboardMapper::stickyKeysfree (void)
    {
      // release shift toggle struct
      if (_stickyKeys_ShiftToggle)
        stickyKeysFreeToggleInfo(_stickyKeys_ShiftToggle);

      // release option toggle struct
      if (_stickyKeys_OptionToggle)
        stickyKeysFreeToggleInfo(_stickyKeys_OptionToggle);

      // release on param dict
      if (_onParamDict)
        _onParamDict->release();

      // release off param dict
      if (_offParamDict)
        _offParamDict->release();

    // release off fn param dict
    if (_offFnParamDict)                 <-- (a)
    _offFnParamDict->release();          <-- (b)

However, at (a) _offFnParamDict isn't actually a member but the following macro:

    #define _offFnParamDict       _reserved->offFnParamDict


Since we returned early from IOHIKeyboardMapper::init before _reserved was 
allocated it's null.
By mapping the null page we can control the value of the offFnParamDict pointer 
and therefore
control the virtual function call at (b)

Original issue reported on code.google.com by [email protected] on 30 Jun 2014 at 3:25

Attachments:

OS X IOKit kernel code execution due to integer overflow in IOBluetoothDataQueue (root only)

IOBluetoothFamily implements its own queuing primitive: IOBluetoothDataQueue

IOBluetoothHCIPacketLogUserClient is userclient type 1 of 
IOBluetoothHCIController. Its clientMemoryForType
method uses the type argument as a length and calls 
IOBluetoothDataQueue::withCapacity, which in turn calls
IOBluetoothDataQueue::initWithCapacity which uses the following code to 
calculate the buffer size to allocate:

(r14d is controlled size)

  lea     edi, [r14+100Bh] ; overflow
  and     edi, 0FFFFF000h
  mov     esi, 1000h
  call    _IOMallocAligned

Calling selector 0 will cause the kernel to enqueue data to the undersized 
queue. This selector is restricted to
root, so this doesn't actually get you an EoP on OS X hence Severity-None.

Original issue reported on code.google.com by [email protected] on 23 Jun 2014 at 11:25

Attachments:

Flash leak of uninitialized data when JPEG image alpha channel zlib stream ends prematurely

A SWF to reproduce is attached, along with source. To reproduce, host the 
additional resource SWF "jpgswfalpha.swf" on the same web server / directory as 
JPEGLeakAlpha.swf

For JPEG images in Flash, there's an optional zlib-compressed alpha channel 
component after the JPEG data. If we supply a zlib stream that terminates 
early, uninitialized alpha channel values are used and these can be leaked to 
script.

The demo SWF file grabs a pointer value and displays it (64-bit Linux) to 
illustrate the point.

A screenshot is attached for convenience.

Since it's very easy to use this vulnerability to read uninitialized memory 
content, a 90-day disclosure deadline applies.

Original issue reported on code.google.com by [email protected] on 14 Jul 2014 at 9:22

Attachments:

OS X IOKit kernel code execution due to lack of bounds checking in IGAccelVideoContextMain::process_token_ColorSpaceConversion

IGAccelVideoContextMain is the userclient used for GPU accelerated video 
encoding on the Intel HD integrated GPUs. It's userclient 0x100 of the 
IntelAccelerator IOService. IOConnectMapMemory type=0 of this userclient is a 
shared token buffer. Token 0x8a is ColorSpaceConversion, implemented in 
IGAccelVideoContextMain::process_token_ColorSpaceConversion
The dword at offset 0x14 of this token is used to compute the offset for a 
write without checking the bounds, allowing a controlled kernel memory write.

Triggering this is a bit annoying, sorry, haven't had time to make a 
self-contained repro for this bug yet:

Compile this dylib:
  $ clang -Wall -dynamiclib -o ig_video_main_ColorSpaceConversion.dylib ig_video_main_ColorSpaceConversion.c -framework IOKit -arch i386 -arch x86_64 
Load it into Quicktime:
  $ DYLD_INSERT_LIBRARIES=./ig_video_main_ColorSpaceConversion.dylib /Applications/QuickTime\ Player.app/Contents/MacOS/QuickTime\ Player
Start a screen recording:
  File -> New Screen Recording -> Click the red circle -> start the recording
This interpose library will look for the ColorSpaceConversion token in the 
shared memory and trigger the bug.

Impact:
This userclient can be instantiated from the Chrome GPU process sandbox and the 
Safari renderer sandbox

Original issue reported on code.google.com by [email protected] on 13 Jun 2014 at 12:47

Attachments:

OS X IOKit kernel code execution due to unchecked pointer parameter in IGAccelCLContext::unmap_user_memory

The Intel OpenCL IOKit userclient has pretty much exactly the same bug as the 
OpenGL one - they trust a user-supplied pointer and call a virtual function off 
of it.

Specifically the function IGAccelCLContext::unmap_user_memory is reachable as 
selector 0x101.

Attached poc hello.c (uses the apple OpenCL hello world example to initialize 
OpenCL and get the correct userclient) will kernel panic dereferencing 
0x4141414141414141. Compile with -framework OpenCL -framework IOKit

This should be reachable from the chrome gpu process sandbox and the safari 
renderer sandbox.

Original issue reported on code.google.com by [email protected] on 2 May 2014 at 11:00

Attachments:

Safari sandbox IPC memory corruption with WebEvent::Char

The handler for the WebPageProxy::DidReceiveEvent IPC message fails to check 
that the WTF::Deque m_keyEventQueue is not empty before calling first() when 
processing an event of type WebEvent::Char:

void WebPageProxy::didReceiveEvent(uint32_t opaqueType, bool handled)
...
    case WebEvent::Char: {
        LOG(KeyHandling, "WebPageProxy::didReceiveEvent: %s", webKeyboardEventTypeString(type));

        NativeWebKeyboardEvent event = m_keyEventQueue.first();
        MESSAGE_CHECK(type == event.type());

        m_keyEventQueue.removeFirst();

        if (!m_keyEventQueue.isEmpty())
            m_process->send(Messages::WebPage::KeyEvent(m_keyEventQueue.first()), m_pageID);

        m_pageClient.doneWithKeyEvent(event, handled);
        if (handled)
            break;

        if (m_uiClient->implementsDidNotHandleKeyEvent())
            m_uiClient->didNotHandleKeyEvent(this, event);
        break;
...

Not sure about the security implications. I haven't looked into this one very 
much, but the NativeWebKeyboardEvent does contain some interesting pointers on 
some platforms.

Repro patch, apply and scroll a webpage:
Source/WebKit2/WebProcess/WebPage/EventDispatcher.cpp:

         ScrollingTree::EventResult result = scrollingTree->tryToHandleWheelEvent(platformWheelEvent);
         if (result == ScrollingTree::DidHandleEvent || result == ScrollingTree::DidNotHandleEvent) {
-            sendDidReceiveEvent(pageID, wheelEvent, result == 
ScrollingTree::DidHandleEvent);
+            for (int i = 0; i < 128; i++){
+                
WebProcess::shared().parentProcessConnection()->send(Messages::WebPageProxy::Did
ReceiveEvent(static_cast<uint32_t>(7), false), pageID);
+            }
             return;
         }
     }

Crash Stack (null pointer dereference)
(lldb) bt
* thread #1: tid = 0x15bcc, 0x000000010b0b4f00 
WebKit2`WebKit::WebPageProxy::didReceiveEvent(unsigned int, bool) [inlined] 
WebKit::WebKeyboardEvent::WebKeyboardEvent(WebKit::WebKeyboardEvent const&) at 
WebEvent.h:214, queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS 
(code=1, address=0x0)
    frame #0: 0x000000010b0b4f00 WebKit2`WebKit::WebPageProxy::didReceiveEvent(unsigned int, bool) [inlined] WebKit::WebKeyboardEvent::WebKeyboardEvent(WebKit::WebKeyboardEvent const&) at WebEvent.h:214
    frame #1: 0x000000010b0b4f00 WebKit2`WebKit::WebPageProxy::didReceiveEvent(unsigned int, bool) [inlined] WebKit::NativeWebKeyboardEvent::NativeWebKeyboardEvent(this=0x00007fff55691e3c) at NativeWebKeyboardEvent.h:60
    frame #2: 0x000000010b0b4f00 WebKit2`WebKit::WebPageProxy::didReceiveEvent(unsigned int, bool) [inlined] WTF::Deque<WebKit::NativeWebKeyboardEvent>::first(this=0x00007fff55691e3c) + 20 at NativeWebKeyboardEvent.h:60
    frame #3: 0x000000010b0b4eec WebKit2`WebKit::WebPageProxy::didReceiveEvent(this=0x00007fa3a1824818, opaqueType=7, handled=false) + 92 at WebPageProxy.cpp:3707
    frame #4: 0x000000010b0d3648 WebKit2`void IPC::handleMessage<Messages::WebPageProxy::DidReceiveEvent, WebKit::WebPageProxy, void (WebKit::WebPageProxy::*)(unsigned int, bool)>(IPC::MessageDecoder&, WebKit::WebPageProxy*, void (WebKit::WebPageProxy::*)(unsigned int, bool)) [inlined] void IPC::callMemberFunctionImpl<WebKit::WebPageProxy, void (args=0x0000000000000007, __t=0x0000000000000007, object=<unavailable>)(unsigned int, bool), std::__1::tuple<unsigned int, bool>, 0ul, 1ul>(WebKit::WebPageProxy*, void (WebKit::WebPageProxy::*)(unsigned int, bool), std::__1::tuple<unsigned int, bool>&&, std::index_sequence<0ul, 1ul>) + 73 at HandleMessage.h:16
    frame #5: 0x000000010b0d362a WebKit2`void IPC::handleMessage<Messages::WebPageProxy::DidReceiveEvent, WebKit::WebPageProxy, void (WebKit::WebPageProxy::*)(unsigned int, bool)>(IPC::MessageDecoder&, WebKit::WebPageProxy*, void (WebKit::WebPageProxy::*)(unsigned int, bool)) [inlined] void IPC::callMemberFunction<WebKit::WebPageProxy, void (args=0x0000000000000007, __t=0x0000000000000007)(unsigned int, bool), std::__1::tuple<unsigned int, bool>, std::make_index_sequence<2ul> >(std::__1::tuple<unsigned int, bool>&&, WebKit::WebPageProxy*, void (WebKit::WebPageProxy::*)(unsigned int, bool)) at HandleMessage.h:22
    frame #6: 0x000000010b0d362a WebKit2`void IPC::handleMessage<Messages::WebPageProxy::DidReceiveEvent, WebKit::WebPageProxy, void (decoder=<unavailable>, object=<unavailable>, function=<unavailable>)(unsigned int, bool)>(IPC::MessageDecoder&, WebKit::WebPageProxy*, void (WebKit::WebPageProxy::*)(unsigned int, bool)) + 43 at HandleMessage.h:117
    frame #7: 0x000000010b0d0c8e WebKit2`WebKit::WebPageProxy::didReceiveMessage(this=0x00007fa3a1824818, connection=<unavailable>, decoder=0x00000001139b4958) + 916 at WebPageProxyMessageReceiver.cpp:214
    frame #8: 0x000000010afb77d5 WebKit2`IPC::MessageReceiverMap::dispatchMessage(this=<unavailable>, connection=0x0000000110707780, decoder=0x00000001139b4958) + 125 at MessageReceiverMap.cpp:87
    frame #9: 0x000000010af64947 WebKit2`WebKit::ChildProcessProxy::dispatchMessage(this=<unavailable>, connection=<unavailable>, decoder=<unavailable>) + 13 at ChildProcessProxy.cpp:118
    frame #10: 0x000000010b0fd328 WebKit2`WebKit::WebProcessProxy::didReceiveMessage(this=0x000000010ee18a80, connection=0x0000000110707780, decoder=0x00000001139b4958) + 24 at WebProcessProxy.cpp:364
    frame #11: 0x000000010af65afc WebKit2`IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::MessageDecoder, std::__1::default_delete<IPC::MessageDecoder> >) [inlined] IPC::Connection::dispatchMessage(decoder=<unavailable>, this=<unavailable>) + 94 at Connection.cpp:770
    frame #12: 0x000000010af65aef WebKit2`IPC::Connection::dispatchMessage(this=0x0000000110707780, message=0x00007fff55692020) + 81 at Connection.cpp:791
    frame #13: 0x000000010af67b70 WebKit2`IPC::Connection::dispatchOneMessage(this=0x0000000110707780) + 106 at Connection.cpp:817
    frame #14: 0x000000010a95aa45 JavaScriptCore`WTF::RunLoop::performWork() [inlined] std::__1::function<void (this=0x00007fff556920e0)>::operator()() const + 421 at functional:1435
    frame #15: 0x000000010a95aa3b JavaScriptCore`WTF::RunLoop::performWork(this=0x000000010ee07f30) + 411 at RunLoop.cpp:104
    frame #16: 0x000000010a95b122 JavaScriptCore`WTF::RunLoop::performWork(context=<unavailable>) + 34 at RunLoopCF.cpp:38
    frame #17: 0x00007fff871bb731 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #18: 0x00007fff871acea2 CoreFoundation`__CFRunLoopDoSources0 + 242
    frame #19: 0x00007fff871ac62f CoreFoundation`__CFRunLoopRun + 831
    frame #20: 0x00007fff871ac0b5 CoreFoundation`CFRunLoopRunSpecific + 309
    frame #21: 0x00007fff90fb1a0d HIToolbox`RunCurrentEventLoopInMode + 226
    frame #22: 0x00007fff90fb17b7 HIToolbox`ReceiveNextEventCommon + 479
    frame #23: 0x00007fff90fb15bc HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 65
    frame #24: 0x00007fff8db9e3de AppKit`_DPSNextEvent + 1434
    frame #25: 0x00007fff8db9da2b AppKit`-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 122
    frame #26: 0x00007fff8a241290 Safari`-[BrowserApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 161
    frame #27: 0x00007fff8db91b2c AppKit`-[NSApplication run] + 553
    frame #28: 0x00007fff8db7c913 AppKit`NSApplicationMain + 940
    frame #29: 0x00007fff8a41306d Safari`SafariMain + 267
    frame #30: 0x00007fff83a405fd libdyld.dylib`start + 1
    frame #31: 0x00007fff83a405fd libdyld.dylib`start + 1

Original issue reported on code.google.com by [email protected] on 3 Apr 2014 at 6:13

launchd heap corruption due to incorrect rounding in launch_data_unpack

(see issue 12 for background on launchd and launch_data_unpack)

liblaunch.c:
launch_data_unpack:
...
  case LAUNCH_DATA_STRING:
    tmpcnt = big2wire(r->string_len);
    if ((data_size - *data_offset) < (tmpcnt + 1)) {
      errno = EAGAIN;
      return NULL;
    }
    r->string = data + *data_offset;
    r->string_len = tmpcnt;
    *data_offset += ROUND_TO_64BIT_WORD_SIZE(tmpcnt + 1);
    break;
  case LAUNCH_DATA_OPAQUE:
    tmpcnt = big2wire(r->opaque_size);
    if ((data_size - *data_offset) < tmpcnt) {
      errno = EAGAIN;
      return NULL;
    }
    r->opaque = data + *data_offset;
    r->opaque_size = tmpcnt;
    *data_offset += ROUND_TO_64BIT_WORD_SIZE(tmpcnt);
    break;
...

Both these cases check that there is enough space remaining in the buffer for 
the given payload size. However, they both then round up the given size to the 
nearest 8 bytes.
This rounding can cause data_offset to become larger than data_size if the 
data_size was not a multiple of 8 bytes. This causes (data_size - *data_offset) 
to underflow, meaning
that the code will continue to read and deserialize _launch_data structures off 
the end of the data buffer.

Original issue reported on code.google.com by [email protected] on 3 Apr 2014 at 6:58

Flash heap buffer overflow calling copyPixelsToByteArray() on a large ByteArray

A SWF to reproduce is attached, along with source.

This is probably due to an integer overflow.

Note that this bug is almost certainly 64-bit only. The PoC relies on creating 
a ByteArray that is almost 4GB in size, and obviously such an allocation is 
never going to succeed in a 32-bit address space.

Also, the bug does not work in Chrome 64-bit Linux, because Chrome 64-bit Linux 
has a defense that limits total allocations to 4GB.

In order to repro, use 64-bit Flash in 64-bit IE, or run Chrome 64-bit Linux 
with the --no-sandbox flag (which disabled the 4GB limit).

Original issue reported on code.google.com by [email protected] on 14 Jul 2014 at 7:36

Attachments:

OS X IOKit kernel memory disclosure due to lack of bounds checking in IOHIKeyboardMapper::modifierSwapFilterKey

By setting the HIDKeyboardModifierMappingPairs property of IOHIDKeyboard we can 
reach the following code in
IOHIKeyboardMapper.cpp:

  if ((array = OSDynamicCast(OSArray, dict->getObject(kIOHIDKeyboardModifierMappingPairsKey))) && (_parsedMapping.maxMod != -1))
  {
    UInt32 count = array->getCount();    
    if ( count )
    {
      for ( unsigned i=0; i<count; i++)
      {
        OSDictionary *  pair      = OSDynamicCast(OSDictionary, array->getObject(i));
        SInt32      src       = 0;
        SInt32      dst       = 0;

        if ( !pair ) continue;

        number = OSDynamicCast(OSNumber, pair->getObject(kIOHIDKeyboardModifierMappingSrcKey));

        if ( !number ) continue;

        src = number->unsigned32BitValue();

        number = OSDynamicCast(OSNumber, pair->getObject(kIOHIDKeyboardModifierMappingDstKey));

        if ( !number ) continue;

        dst = number->unsigned32BitValue();

        ...       

        if ((src >= NX_MODIFIERKEY_ALPHALOCK) && (src <= NX_MODIFIERKEY_RCOMMAND))
          _modifierSwap_Modifiers[src] = dst;                                       <-- (a)

This code expects an array of dictionaries, each containing two keys 
(IOHIDKeyboardModifierMappingSrc and
IOHIDKeyboardModifierMappingDst) which have unsigned integer values. The 
purpose of this code is to allow
swapping of modifier keys. The value dst at (a) is controlled and there is no 
bounds check to ensure that
it corrisponds to a valid modifier key index.

_modifierSwap_Modifiers is defined as:

#define _modifierSwap_Modifiers    _reserved->modifierSwap_Modifiers

where _reserved->modifierSwap_Modifiers is:

        SInt32      modifierSwap_Modifiers[NX_NUMMODIFIERS];

These values are read here when a modifier key is pressed:
  bool IOHIKeyboardMapper::modifierSwapFilterKey(UInt8 * key)
  {
    unsigned char thisBits = _parsedMapping.keyBits[*key];
    SInt16      modBit = (thisBits & NX_WHICHMODMASK);
    SInt16      swapBit;
    unsigned char *map;
    ... 
    swapBit = _modifierSwap_Modifiers[modBit];              <-- (b)
    ...
    if (((map = _parsedMapping.modDefs[swapBit]) != 0 ) &&  <-- (c)
      ( NEXTNUM(&map, _parsedMapping.shorts) ))
      *key = NEXTNUM(&map, _parsedMapping.shorts);          <-- (d)

swapBit (read at (b)) is completely controlled and there's no bounds checking 
at (c) to ensure that it falls
within the bounds of _parsedMapping.modDefs.

modDefs is defined as:

  unsigned char *modDefs[NX_NUMMODIFIERS];

and NEXTNUM as:

  static inline int NEXTNUM(unsigned char ** mapping, short shorts)
  {
    int returnValue;    
    if (shorts)
    {
      returnValue = OSSwapBigToHostInt16(*((unsigned short *)*mapping));
      *mapping += sizeof(unsigned short);
    }
    else
    {
      returnValue = **((unsigned char  **)mapping);
      *mapping += sizeof(unsigned char);
    }
    return returnValue;
  }

Here we can see that if we can control a value at a fixed offset from modDefs 
it will be treated as a char* from
which two values will be read (depening on value of _parsedMapping.shorts 
either bytes or shorts.) If the first
value is non-zero then the second will be interpreted as a keycode. If it's a 
valid keycode then this value can
be read by a userspace application, for example like this:

    [NSEvent addLocalMonitorForEventsMatchingMask:NSKeyDownMask
                                          handler:
     ^NSEvent *(NSEvent * event) {
         printf("%02x\n", [event keyCode]);
         return event;
     } ];

We can quite easily fake a pointer to read from at a fixed offset from modDefs 
by setting our own keymap
and encoding the read target in the specialKeys, since looking at the 
definition of NXParsedKeyMapping
this array of shorts is at a fixed offset from modDefs:

  typedef struct _NXParsedKeyMapping_ {
    short shorts;
    char  keyBits[NX_NUMKEYCODES];
    int     maxMod;
    unsigned char *modDefs[NX_NUMMODIFIERS];
    int     numDefs;
    unsigned char *keyDefs[NX_NUMKEYCODES];
    int     numSeqs;
    unsigned char *seqDefs[NX_NUMSEQUENCES];
    int     numSpecialKeys;
    unsigned short specialKeys[NX_NUMSPECIALKEYS];
    const unsigned char *mapping;
    int mappingLen; 
  } NXParsedKeyMapping;

This PoC creates and applies such a keymap, see the code for details.

By reading modified keycodes (using for example snippet above in a cocoa app) 
you can leak individual bytes of kernel memory.

Original issue reported on code.google.com by [email protected] on 7 Jul 2014 at 6:51

Attachments:

OS X IOKit Multiple exploitable kernel NULL dereferences (x4)

I took another look at the mach-o loading code yesterday and noticed that OS X 
only raises the vm_map min_addr for 64-bit programs (this can be trivially 
avoided, but *should* provide some protection for sandboxed 64-bit 
processes...) Chrome is still 32-bit, which means you can literally just 
vm_deallocate the --- page at address 0 and vm_allocate a rw- one there :-(

Relevant code from the loader:

**************
mach_loader.c
load_segment(
    boolean_t       prohibit_pagezero_mapping = FALSE;
...

        /* XXX (4596982) this interferes with Rosetta, so limit to 64-bit tasks */
        if (scp->cmd == LC_SEGMENT_64) {
                prohibit_pagezero_mapping = TRUE;
        }

        if (prohibit_pagezero_mapping) {
...
            ret = vm_map_raise_min_offset(map, seg_size); //only place this is called
*************

This leads to some quite nice bugs actually - I'm reporting four to Apple now 
which are all reachable from the chrome GPU process sandbox.

The first two are in the Intel graphics driver again - it's possible to call an 
external method before the pointer to the methodDescs has been initialized 
(which is done in contextStart.) If you set things up correctly you can craft a 
fake IOExternalMethod near NULL which will get passed to 
shim_io_connect_method_scalarI_scalarO where you can reach this code:

                    err = (object->*func)(  ARG32(input[0]), ARG32(input[1]), ARG32(input[2]),
                                          ARG32(input[3]), ARG32(input[4]), ARG32(input[5]) );

where func and input are user controlled (they're read from the 
IOExternalMethod.) This is pretty cool as you get control of a c++ pointer to 
member method :-) I didn't actually know how those were implemented until I 
looked at the disassembly of this - if the least significant bit of func is set 
then it's treated as an offset into the vtable of object (after subtracting 1.) 
If the least significant bit is clear then it's just a regular function 
pointer. You could easily get a UaF from this by calling the destructor but 
there's almost certainly loads you could do with this.

I've attached two PoCs (both different bugs) which demonstrate both 
pointer-to-virtual-member-method and function pointer crashes.

I've also attached two more PoCs which show similar issues in the nVidia 
graphics driver (used in the MacBookPro.) These also give trivial instruction 
pointer control (they actually crash trying to take a lock, but the address of 
that lock is controlled. Right after that the call virtual functions from a 
controlled vtable address.)

Original issue reported on code.google.com by [email protected] on 6 May 2014 at 7:41

Attachments:

OS X IOKit kernel code execution due to NULL pointer dereference in IOAccelContext2::clientMemoryForType

Calling IOConnectMapMemory with type=0 of userclient 0x100 of IOService 
"IntelAccelerator" hits the following exploitable kernel NULL pointer 
dereference:

 mov rdi, [r12+1D8h]      ; rdi := NULL
 mov rax, [rdi]           ; read vtable pointer from NULL
 call qword ptr [rax+20h] ; controlled call

See attached PoC which maps the NULL page and kernel panics calling a virtual 
function near 0x4141414141414141.

This userclient is reachable from the chrome GPU process sandbox and the safari 
renderer sandbox.

Original issue reported on code.google.com by [email protected] on 12 Jun 2014 at 4:37

Attachments:

OS X IOKit kernel code execution due to integer overflow in IODataQueue::enqueue

The class IODataQueue is used in various places in the kernel. There are a 
couple of exploitable integer overflow issues in the ::enqueue method:

Boolean IODataQueue::enqueue(void * data, UInt32 dataSize)
{
  const UInt32       head      = dataQueue->head;  // volatile
  const UInt32       tail      = dataQueue->tail;
  const UInt32       entrySize = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE;  <-- (a)
  IODataQueueEntry * entry;

  if ( tail >= head )
  {
    // Is there enough room at the end for the entry?
    if ( (tail + entrySize) <= dataQueue->queueSize )                      <-- (b)
    {
      entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);

      entry->size = dataSize;
      memcpy(&entry->data, data, dataSize);                                <-- (c)


The additions at (a) and (b) should be checked for overflow. In both cases, by 
supplying a large value for dataSize an attacker can reach the memcpy call at 
(c) with a length argument which is larger than the remaining space in the 
queue buffer.

The majority of this PoC involves setting up the conditions to actually be able 
to reach a call to ::enqueue with a controlled dataSize argument, the bug 
itself it quite simple.

This PoC creates an IOHIDLibUserClient (IOHIDPointingDevice) and calls the 
create_queue externalMethod to create an IOHIDEventQueue (which inherits from 
IODataQueue.) This is the queue which will have the ::enqueue method invoked 
with the large dataSize argument.

The PoC then calls IOConnectMapMemory with a memoryType argument of 0 which 
maps an array of IOHIDElementValues into userspace:

typedef struct _IOHIDElementValue
{
  IOHIDElementCookie  cookie;
  UInt32        totalSize;
  AbsoluteTime    timestamp;
  UInt32        generation;
  UInt32        value[1];
}IOHIDElementValue;

The first dword of the mapped memory is a cookie value and the second is a size.

When the IOHIDElementPrivate::processReport method is invoked (in response to 
an HID event) if there are any listening queues then the IOHIDElementValue will 
be enqueued - and the size is in shared memory :-)

The PoC calls the startQueue selector to start the listening queue then calls 
addElementToQueue passing the cookie for the first IOHIDElementValue and the ID 
of the listening queue.

A loop then overwrites the totalSize field of the IOHIDElementValue in shared 
memory with 0xfffffffe. When the processReport method is called this will call 
IODataQueue::enqueue and overflow the calculation of entry size such that it 
will attempt to memcpy 0xfffffffe bytes. Note that the size of the queue buffer 
is also attacked controlled, and the kernel is 64-bit, so a 4gb memcpy is 
almost certainly exploitable.

Note that lldb seems to get confused by the crash - the memcpy implementation 
uses rep movsq and lldb doesn't seem to understand the 0xf3 (rep) prefix - IDA 
disassembles the function fine though. Also the symbols for memcpy and 
real_mode_bootstrap_end seem to have the same address so the lldb backtrace 
looks weird, but it is actually memcpy.

Original issue reported on code.google.com by [email protected] on 26 Jun 2014 at 1:33

Attachments:

OS X IOKit kernel code execution due to incorrect bounds checking in Intel GPU driver ( x2 )

IGAccelGLContext::getTargetAndMethodForIndex doesn't correctly bounds check the 
selector:

__text:000000000000873A ; 
IGAccelGLContext::getTargetAndMethodForIndex(IOService **, unsigned int)
__text:000000000000873A                 push    rbp
__text:000000000000873B                 mov     rbp, rsp
__text:000000000000873E                 mov     [rsi], rdi
__text:0000000000008741                 lea     eax, [rdx-106h]    ; rdx is 
controlled selector index
__text:0000000000008747                 cmp     eax, 100h
__text:000000000000874C                 jnb     short loc_8765     ; pass 
selectors 0..0x105 && 0x207+ to superclass
__text:000000000000874E                 add     edx, 0FFFFFE00h    ; selectors 
in the range 0x106..0x206 pass the test,
                                                                   ; this then subtracts 0x200, underflowing for any selector
                                                                   ; in the range 0x106..0x1ff
__text:0000000000008754                 lea     rax, [rdx+rdx*2]
__text:0000000000008758                 shl     rax, 4
__text:000000000000875C                 add     rax, [rdi+1098h]   ; 
underflowed value used as index into array of IOExternalMethods
__text:0000000000008763                 pop     rbp
__text:0000000000008764                 retn

the correct range of selectors is 0x200..0x206, however the selector is only 
checked to be in the range 0x106..0x206 :-)

Exploitation of this issue depends on the value stored at this+0x1098 (which 
should point to the static methodDescs array.) I've already reported another bug
where this field can be NULL. Combined with this new bug you get a nice way to 
exploit that NULL pointer deref without having to be able to map the NULL page:

Since the index will underflow a 32-bit value then be extended to 64-bits and 
multiplied by 48 you end up with a pointer something like this: 
0x0000002fffffdf18
which is a mappable address for a 64-bit process, even one which is sandboxed 
and has a PAGEZERO segment since it's above 4GB. By crafting a valid 
IOExternalMethod
struct there you can trivially get kernel RIP control.

Once the NULL methodDescs bug is fixed, exploitability of this bug will depend 
on what data happens to be in the oob region and whether it could be interpreted
as a valid IOExternalMethod structure. (I haven't tried to exploit this 
approach yet, but the methodDescs array is near various vtables and other
IOExternalMethod arrays so I wouldn't rule it out.)

The OpenCL userclient (different code path) suffers from a similar bad bounds 
checking issue, I've attached a PoC for that too.

Original issue reported on code.google.com by [email protected] on 20 May 2014 at 4:23

Attachments:

OS X IOKit kernel code execution due to off-by-one error in IGAccelGLContext::processSidebandToken

The Intel GPU driver uses shared memory for drawing commands. The userspace
client of the driver calls IOConnectMapMemory to map a shared page which it 
will use,
calling selector 2 (submit_data_buffers) to signal to the driver that it should
consume the commands (tokens) written there by the client.

The function IGAccelGLContext::processSidebandToken checks the token ID and 
length then
jumps to the function responsible for actually parsing the token:

; IGAccelGLContext::processSidebandToken(IOAccelCommandStreamInfo &)
       push    rbp
       mov     rbp, rsp
       mov     ax, [rsi+18h]   ; this is the token id
       test    ax, ax
       jns     short not_us    ; jump if not sign - token must be >= 0x8000
       cmp     ax, 0A1FFh   
       ja      short err       ; jump if token > 0xa1ff
       movzx   ecx, ax         ; otherwise, take the upper 8 bits, subtract 0x80 and use as an index into s_cTokenInfo array of token function descriptors
       mov     r8, [rdi+1090h] ; 
       shr     ecx, 8
       add     ecx, 0FFFFFF80h
       lea     rcx, [rcx+rcx*2]
       mov     edx, [r8+rcx*8+10h] ; get pointer to descriptor

s_cTokenInfo points to an array of 0x21 descriptors (each 0x18 bytes) therefore 
the maximum index allowed should be 0x20. Supplying a token
with a token id field of 0xa100 will read a descriptor off the end of the 
s_cTokenInfo array. The bytes following the array happen to be zero, which means
that the code will reach a jmp rax where rax is zero.

Exploitability depends on two things: being able to map the zero page and being 
able to execute it - mapping the zero page is possible (see previous
bug reports) but SMEP (Supervisor Mode Execution Prevention) will stop 
exploitation of this for Ivy Bridge and newer cpus. On Sandy Bridge and older 
hardware
this bug will be exploitable (eg MacBookPro <= 8,3 which so far as I understand 
is still completely supported hardware.)

Original issue reported on code.google.com by [email protected] on 10 Jun 2014 at 12:06

Attachments:

Safari sandbox logic error enables reading of arbitrary files

The description we have so far is in the form of some raw notes that describe 
the logic error:

Goal: get UniversalFileReadSandboxExtension

This IPC will get it for us, providing we pass a few checks:
WebPageProxy::
    BackForwardGoToItem(uint64_t itemID) -> (WebKit::SandboxExtension::Handle sandboxExtensionHandle)

void WebPageProxy::backForwardGoToItem(uint64_t itemID, 
SandboxExtension::Handle& sandboxExtensionHandle)
{
    WebBackForwardListItem* item = m_process->webBackForwardItem(itemID); <-- //check 1: we need to get a file url as an itemID
    if (!item)
        return;

    bool createdExtension = maybeInitializeSandboxExtensionHandle(URL(URL(), item->url()), sandboxExtensionHandle); <-- //*** [1]
    if (createdExtension)
        m_process->willAcquireUniversalFileReadSandboxExtension();
    m_backForwardList->goToItem(item);
}

//[1]
//getting the sandbox extension looks easy; just call this function with the 
file:// URL (and if inspector is enabled, get isInspectorPage(this) to return 
false
bool WebPageProxy::maybeInitializeSandboxExtensionHandle(const URL& url, 
SandboxExtension::Handle& sandboxExtensionHandle)
{
    if (!url.isLocalFile())
        return false;

#if ENABLE(INSPECTOR)
    // Don't give the inspector full access to the file system.
    if (WebInspectorProxy::isInspectorPage(this)) <-- //this will be important later
        return false;
#endif

    SandboxExtension::createHandle("/", SandboxExtension::ReadOnly, sandboxExtensionHandle); <-- //very importantly, this is actually going to grant us read access to the WHOLE FS, not just
                                                                                                 //the file:// url we are trying to get an extension for :-)
    return true;
}


//So check 1: get a file:// url as a webBackForwardItem:

WebBackForwardListItem* WebProcessProxy::webBackForwardItem(uint64_t itemID) 
const
{
    return m_backForwardListItemMap.get(itemID);
}

//just get it inserted in that map;

// easy with this IPC
WebProcessProxy::
    AddBackForwardItem(uint64_t itemID, WTF::String originalURL, WTF::String url, WTF::String title, CoreIPC::DataReference backForwardData)

void WebProcessProxy::addBackForwardItem(uint64_t itemID, const String& 
originalURL, const String& url, const String& title, const 
CoreIPC::DataReference& backForwardData)
{
    MESSAGE_CHECK_URL(originalURL); <-- //EXCEPT, what are these checks
    MESSAGE_CHECK_URL(url);         <-- //.... ? [2]

    WebBackForwardListItemMap::AddResult result = m_backForwardListItemMap.add(itemID, nullptr);
    if (result.isNewEntry) {
        result.iterator->value = WebBackForwardListItem::create(originalURL, url, title, backForwardData.data(), backForwardData.size(), itemID);
        return;
    }

    // Update existing item.
    result.iterator->value->setOriginalURL(originalURL);
    result.iterator->value->setURL(url);
    result.iterator->value->setTitle(title);
    result.iterator->value->setBackForwardData(backForwardData.data(), backForwardData.size());
}


[2]

#define MESSAGE_CHECK_URL(url) 
MESSAGE_CHECK_BASE(m_process->checkURLReceivedFromWebProcess(url), 
m_process->connection())
//it's a runtime check
#define MESSAGE_CHECK_BASE(assertion, connection) do \
    if (!(assertion)) { \
        ASSERT(assertion); \
        (connection)->markCurrentlyDispatchedMessageAsInvalid(); \
        return; \
    } \
while (0)

//what is it actually checking? we have to be able to return true from this 
function to proceed:

bool WebProcessProxy::checkURLReceivedFromWebProcess(const URL& url)
{
    // FIXME: Consider checking that the URL is valid. Currently, WebProcess sends invalid URLs in many cases, but it probably doesn't have good reasons to do that.

    // Any other non-file URL is OK.
    if (!url.isLocalFile())  <-- //it's okay if this isn't a file:// url, well, we want a file:// url so this doesn help
        return true;

    // Any file URL is also OK if we've loaded a file URL through API before, granting universal read access.
    if (m_mayHaveUniversalFileReadSandboxExtension) <-- //we don't have this yet so this is no help...
        return true;

    // If we loaded a string with a file base URL before, loading resources from that subdirectory is fine.
    // There are no ".." components, because all URLs received from WebProcess are parsed with URL, which removes those.
    String path = url.fileSystemPath();
    for (HashSet<String>::const_iterator iter = m_localPathsWithAssumedReadAccess.begin(); iter != m_localPathsWithAssumedReadAccess.end(); ++iter) {
        if (path.startsWith(*iter))
            return true;        //THIS looks more promising :-) if our file://XXX url is in a subdirectory in m_localPathsWithAssumedReadAccess then we can load it
                                // this doesn't sound so exciting, except that remember if we can get ANY file:// url through here then we're actually going to get
                                // a sandbox extension for '/'.... so how can we get something in this list?
    }
...
  return false;
}

ENTER INSPECTOR

//that m_localPathsWithAssumedReadAccess is only modified here:

void WebProcessProxy::assumeReadAccessToBaseURL(const String& urlString)
{
    URL url(URL(), urlString);
    if (!url.isLocalFile())
        return;

    // There's a chance that urlString does not point to a directory.
    // Get url's base URL to add to m_localPathsWithAssumedReadAccess.
    URL baseURL(URL(), url.baseAsString());

    // Client loads an alternate string. This doesn't grant universal file read, but the web process is assumed
    // to have read access to this directory already.
    m_localPathsWithAssumedReadAccess.add(baseURL.fileSystemPath());
}

//which in turn is only called here in code reachable via IPC:
IPC::
    CreateInspectorPage() -> (uint64_t inspectorPageID, WebKit::WebPageCreationParameters inspectorPageParameters)

void WebInspectorProxy::createInspectorPage(uint64_t& inspectorPageID, 
WebPageCreationParameters& inspectorPageParameters)
{
... //a lot of important stuff happens here; come back to that later
    m_page->process()->assumeReadAccessToBaseURL(inspectorBaseURL());  //importantly, this is getting set for the process, not for the page

//the path which is getting passed to assumeReadAccessToBaseURL
//it doesn't really matter what that path is, so long as it's actually a 
file:// url (and fixed or guessable)
String WebInspectorProxy::inspectorBaseURL() const
{
    // Call the soft link framework function to dlopen it, then [NSBundle bundleWithIdentifier:] will work.
    WebInspectorUILibrary();

    NSString *path = [[NSBundle bundleWithIdentifier:@"com.apple.WebInspectorUI"] resourcePath];
    ASSERT([path length]);

    return [[NSURL fileURLWithPath:path] absoluteString];
}


//need to debug to check but it's almost certainly under here: 
/System/Library/PrivateFrameworks/WebInspectorUI.framework/Resources
//see Main.html, Main.js etc

//need to check exactly how we route messages to a particular 
WebPageProxy/WebProcessProxy

//at this point only really have to make sure we pass the 
//WebInspectorProxy::isInspectorPage(this) check
//note that this here corrisponds to a WebPageProxy - not a WebProcessProxy:


bool WebPageProxy::maybeInitializeSandboxExtensionHandle(const URL& url, 
SandboxExtension::Handle& sandboxExtensionHandle)
{
    if (!url.isLocalFile())
        return false;

#if ENABLE(INSPECTOR)
    // Don't give the inspector full access to the file system.
    if (WebInspectorProxy::isInspectorPage(this)) //we have created an Inspector page through createInspectorPage, so we'll have to be careful getting around this
        return false;


bool WebInspectorProxy::isInspectorPage(WebPageProxy* page)
{
    return WebInspectorPageGroups::shared().isInspectorPageGroup(page->pageGroup());
}

//this WebInspectorPageGroups thing seems to be there to allow nesting of 
inspectors (since the inspector itself is just another Page)
in WebInspectorProxy.cpp

class WebInspectorPageGroups {
public:
    static WebInspectorPageGroups& shared()
    {
        //so this is just a singleton
        DEFINE_STATIC_LOCAL(WebInspectorPageGroups, instance, ());
        return instance;
    }


//will be called with page->pageGroup() as group when called by isInspectorPage
    bool isInspectorPageGroup(WebPageGroup* group)
    {
        return m_pageGroupLevel.contains(group);
    }

    typedef HashMap<WebPageGroup*, unsigned> PageGroupLevelMap;
    PageGroupLevelMap m_pageGroupLevel;

//so a page is considered an inspector if it's pageGroup pointer is a key in 
m_pageGroupLevels

//this is the only place keys are added to m_pageGroupLevel
    WebPageGroup* inspectorPageGroupForLevel(unsigned level)
    {
        // The level is the key of the HashMap, so it cannot be 0.
        ASSERT(level);

        auto iterator = m_pageGroupByLevel.find(level);
        if (iterator != m_pageGroupByLevel.end())
            return iterator->value.get();

        RefPtr<WebPageGroup> group = createInspectorPageGroup(level); //note that it's always adding a new PageGroup
        m_pageGroupByLevel.set(level, group.get());
        m_pageGroupLevel.set(group.get(), level);
        return group.get();
    }

WebPageProxy::WebPageProxy(PageClient* pageClient, PassRefPtr<WebProcessProxy> 
process, WebPageGroup* pageGroup, uint64_t pageID){
...
#if ENABLE(INSPECTOR)
    m_inspector = WebInspectorProxy::create(this);
#endif
...
}

//the constructor of the WebInspectorProxy (invoked from the WebPageProxy 
constructor)
WebInspectorProxy::WebInspectorProxy(WebPageProxy* page){
...
//does this add m_page->pageGroup to the m_pageGroupLevel map?
    m_level = WebInspectorPageGroups::shared().inspectorLevel(m_page->pageGroup()); // <-- m_level will be 1
//adds itself as a messageReceived for this process+pageID combo
    m_page->process()->addMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_page->pageID(), this);
}

    unsigned inspectorLevel(WebPageGroup* inspectedPageGroup)
    {
        //the first time around m_pageGroupLevel will be empty so isInspectorPageGroup must be empty -> this returns 1 (and nothing gets added to m_pageGroupLevel)
        return isInspectorPageGroup(inspectedPageGroup) ? inspectorPageGroupLevel(inspectedPageGroup) + 1 : 1;
    }


//going back to createInspectorPage we can see when that map is maipulated to 
add entries:

void WebInspectorProxy::createInspectorPage(uint64_t& inspectorPageID, 
WebPageCreationParameters& inspectorPageParameters)
{
    inspectorPageID = 0;

    if (!m_page)
        return;

    m_isAttached = shouldOpenAttached();
    m_attachmentSide = static_cast<AttachmentSide>(inspectorPageGroup() <--
...

WebPageGroup* WebInspectorProxy::inspectorPageGroup() const
{
    return WebInspectorPageGroups::shared().inspectorPageGroupForLevel(m_level);
}

//this will call createInspectorPageGroup

    static PassRefPtr<WebPageGroup> createInspectorPageGroup(unsigned level)
    {
        RefPtr<WebPageGroup> pageGroup = WebPageGroup::create(String::format("__WebInspectorPageGroupLevel%u__", level), false, false);
        ...
    }
//which creates a new WebPageGroup corrisponding to the correct WebInspector 
level


//so what was the point of all that:
class WebProcessProxy {
...
    typedef HashMap<uint64_t, WebPageProxy*> WebPageProxyMap;

    WebPageProxyMap m_pageMap; <-- // WebProcessProxies have multiple WebPageProxies - identified by pageIDs (uint64_t's)

    HashSet<String> m_localPathsWithAssumedReadAccess; <-- //this is also defined on a per process basis

    WebBackForwardListItemMap m_backForwardListItemMap; // each WebProcessProxy has a single backforwardlistitemmap
...
}

//the crux of the issue is that only the inspector pageID is prevented from 
getting '/' read access, but the inspected page isn't

Original issue reported on code.google.com by [email protected] on 11 Mar 2014 at 6:35

launchd heap corruption due to unchecked strcpy in init_session MIG ipc

MIG ipc routine:

routine
init_session(
                j           : job_t;
                session     : name_t;
                asport      : mach_port_t
);

Implementation:

core.c:

kern_return_t
job_mig_init_session(job_t j, name_t session_type, mach_port_t asport)
{
...
    if (j->mgr->session_initialized) {
        job_log(j, LOG_ERR, "Tried to initialize an already setup session!");
        kr = BOOTSTRAP_NOT_PRIVILEGED;              <-- even if the session is already initialized there's no early return here, it will still hit the strcpy
    }
...

    jobmgr_log(j->mgr, LOG_DEBUG, "Initializing as %s", session_type);
    strcpy(j->mgr->name_init, session_type);           <-- unchecked strcpy



The buffer which is being copied into is allocated here:

jobmgr_t 
jobmgr_new(jobmgr_t jm, mach_port_t requestorport, mach_port_t transfer_port, 
bool sflag, const char *name, bool skip_init, mach_port_t asport)
{
    job_t bootstrapper = NULL;
    jobmgr_t jmr;
...
    jmr = calloc(1, sizeof(struct jobmgr_s) + (name ? (strlen(name) + 1) : NAME_MAX + 1)); <-- here



jobmgr_t is typedef'd as a pointer to a struct jobmgr_s, which has a zero-sized 
array at the end:

struct jobmgr_s {
... 
    union {
        const char name[0];
        char name_init[0];
    };
};


It's possible to force the allocation of a new jobmgr_t with a partially 
controlled name field using the subset MIG ipc - this will allocate only enough 
space for this string in the jobmgr_s struct:

kern_return_t
job_mig_subset(job_t j, mach_port_t requestorport, mach_port_t *subsetportp)
{
    int bsdepth = 0;
    jobmgr_t jmr;
...
    jmr = j->mgr;
...
    char name[NAME_MAX];
    snprintf(name, sizeof(name), "%s[%i].subset.%i", j->anonymous ? j->prog : j->label, j->p, MACH_PORT_INDEX(requestorport));

    if (!job_assumes(j, (jmr = jobmgr_new(j->mgr, requestorport, MACH_PORT_NULL, false, name, true, j->asport)) != NULL)) {


This subset bootstrap port can then be passed to init_session with a longer 
session_type.

Original issue reported on code.google.com by [email protected] on 4 Apr 2014 at 2:45

OS X IOKit kernel memory disclosure due to lack of bounds checking in IOUSBControllerUserClient::ReadRegister

This one requires local root so isn't maybe so interesting on OS X since root 
is still equivalent to kernel code execution anyway. It's a different story on 
iOS, but I don't have any iOS devices to test on 
(http://theiphonewiki.com/wiki/Kernel has the output of kextstat on iOS 6 and 
the same driver (IOUSBFamily) is listed.) I'll let Apple figure out if this is 
something to worry about on iOS.

The IOUSBController userclient external method 8 is 
IOUSBControllerUserClient::ReadRegister. This method fails to bounds check its 
first argument which is used directly as an offset into kernel memory:

mov     ecx, r15d <-- r15d controlled
mov     eax, [rax+rcx]
mov     [r14], eax <-- will get returned to userspace

Severity Low because of the root requirement.

Original issue reported on code.google.com by [email protected] on 12 May 2014 at 11:50

Attachments:

Flash leak of uninitialized memory when rendering valid(?) 1bpp image

A SWF to reproduce is attached, along with source. To reproduce, host the 
additional resource SWF "imglossless1bpp.swf" on the same web server / 
directory as Lossless1bppLeak.swf

This bug is a strange one. I think the 1bpp image is reasonably well-formed and 
valid: it has a 2-color color table (black and white), and enough image data to 
fill the entire 64x64 1bpp canvas. Despite this, a multi-color image is 
rendered, which clearly contains some uninitialized data.

Maybe 1bpp image support is broken? I'm not really sure what's going on other 
than the definite observation of uninitialized memory content leaking to script.

A screenshot is attached for convenience.

Original issue reported on code.google.com by [email protected] on 14 Jul 2014 at 7:02

Attachments:

Flash leak of uninitialized data when image zlib stream ends prematurely

A SWF to reproduce is attached, along with source. To reproduce, host the 
additional resource SWF "imglossless8bpp.swf" on the same web server / 
directory as Lossless8bppLeak.swf

I'm fairly sure this is a very different bug to the "Lossless1bppLeak.swf" bug. 
To manifest this bug, we pull a fun little trick: we terminate the image data 
zlib stream early, before emitting any pixel data for the image. This leaves 
uninitialized data in the canvas which we can read out to script. The demo SWF 
file grabs a pointer value and displays it (64-bit Linux) to illustrate the 
point.

A screenshot is attached for convenience.

Since it's very easy to use this vulnerability to read uninitialized memory 
content, a 90-day disclosure deadline applies.

Original issue reported on code.google.com by [email protected] on 14 Jul 2014 at 6:45

Attachments:

launchd heap overflow in log_forward

The log_forward MIG method is used for per-user launchd processes to send log 
messages to the root launchd (pid 1 running as root.)

job.defs:

routine
log_forward(
        j     : job_t;
        inval   : pointer_t
);


The log_forward implementation checks that the sender was a per-user launchd 
process before calling launchd_log_forward:

core.c:

kern_return_t
job_mig_log_forward(job_t j, vm_offset_t inval, mach_msg_type_number_t invalCnt)
{
  ...
  if (!job_assumes(j, j->per_user)) {
    return BOOTSTRAP_NOT_PRIVILEGED;
  }

  return launchd_log_forward(ldc->euid, ldc->egid, inval, invalCnt);
  ...


This means that this vulnerability can only be exploited by a per-user launchd 
process. This still represents a privilege escalation 
however as per-user launchds processes run with the user's uid. Exploitation is 
therefore predicated on being able to inject code into a process
running as the same user.

The vulnerability is in the code which deserializes the array of log messages 
(inval points to the controlled buffer:)

log.c:

launchd_log_forward(uid_t forward_uid, gid_t forward_gid, vm_offset_t inval, 
mach_msg_type_number_t invalCnt)
{
  struct logmsg_s *lm, *lm_walk;
  mach_msg_type_number_t data_left = invalCnt;
  ...
  for (lm_walk = (struct logmsg_s *)inval; (data_left > 0) && (lm_walk->obj_sz <= data_left); lm_walk = ((void *)lm_walk + lm_walk->obj_sz)) {
    ...
    if (lm_walk->obj_sz == 0) {
      ...
      break;
    }

    if (!(lm = malloc(lm_walk->obj_sz))) {         <-- (a)
      ...
      break;
    }

    memcpy(lm, lm_walk, lm_walk->obj_sz);
    lm->sender_uid = forward_uid;                  <-- (b)
    lm->sender_gid = forward_gid;

    lm->from_name += (size_t)lm;
    lm->about_name += (size_t)lm;
    lm->msg += (size_t)lm;
    lm->session_name += (size_t)lm;
  ...


There is no check on the obj_sz field at (a). By passing in a crafted data 
structure with an obj_sz field smaller than sizeof(struct logmsg_s)
lm will point to an undersized allocation and the assignments at (b) will point 
outside the allocated memory.

Original issue reported on code.google.com by [email protected] on 3 Apr 2014 at 7:05

OS X IOKit kernel multiple exploitable memory safety issues in token parsing in IGAccelVideoContextMedia (x5)

 IGAccelVideoContextMedia is the userclient responsible for gpu accelerated video decoding - it's userclient type 0x101 of the IntelAccelerator IOService.

Clients of IGAccelVideoContextMedia call IOConnectMapMemory with type=0 to map 
a shared buffer which is used to pass tokens to the kernel.

The IGAccelVideoContextMedia::process_token_* methods parse these tokens 
(offset +0x10 of the IOAccelCommandStreamInfo& which is passed to the 
process_token_* methods is a pointer into the shared buffer.)

There are multiple cases of insufficient bounds checking allowing an attacker 
to get controlled writes to kernel memory - please see each of the attached 
PoCs for more details of each bug.

This userclient can be reached from the chrome GPU process sandbox and the 
safari renderer sandbox.

These PoCs are interpose libraries which have been tested by loading them into 
Quicktime, the bugs are still reachable from chrome and safari, but Quicktime 
hits more of the kernel video code (so required less effort to write the PoCs 
for.)

The attached Makefile will build all the libraries, load them like this:
DYLD_INSERT_LIBRARIES=<path/to/this/lib.dylib> /Applications/QuickTime\ 
Player.app/Contents/MacOS/QuickTime\ Player
and open an mp4 file

Original issue reported on code.google.com by [email protected] on 12 Jun 2014 at 4:29

Attachments:

OS X kASLR defeat using sgdt

sgdt is an unprivileged instructions, a process at any privilege level
can execute sgdt to get the address of that processor's Global Descriptor Table
pmap.h has the following comment:

/*
 * For KASLR, we alias the master processor's IDT and GDT at fixed
 * virtual addresses to defeat SIDT/SGDT address leakage.
 */
#define MASTER_IDT_ALIAS  (VM_MIN_KERNEL_ADDRESS + 0x0000)
#define MASTER_GDT_ALIAS  (VM_MIN_KERNEL_ADDRESS + 0x1000)

executing sgdt on cpu0 returns this fixed address
the GDTs for all other cpus are allocated very early on the heap
in cpu_data_alloc()

Since the heap begins at a fixed offset from the kernel text segment
vm_init.c contains the following comment:

/*
 * Eat a random amount of kernel_map to fuzz subsequent heap, zone and
 * stack addresses. (With a 4K page and 9 bits of randomness, this
 * eats at most 2M of VA from the map.)
 */

The code then uses early_random() to make a randomly-sized allocation.
However, since the granularity of kASLR is 2MB, if a suitably early
kernel heap allocation address can be leaked then it's enough to guess
the kASLR offset reasonably reliably (in testing at least 50% correct)

The GDT allocations are suitably early and the state of the heap sufficiently
deterministic that by executing sgdt on all cpus and filtering out the result
for cpu0 you can with at least 50% reliability determine the kASLR slide

PoC attached.

Original issue reported on code.google.com by [email protected] on 21 May 2014 at 6:27

Attachments:

OS X IOKit kernel memory disclosure due to lack of bounds checking in AGPMClient::getPstatesOccupancy

The AGPM (AppleGraphicsPowerManagement) user client is reachable from the 
chrome gpu process sandbox and the safari renderer sandbox.

The getPStatesOccupancy method fails to bounds check the index it's passed. The 
oob value which is read is then returned to the userspace caller allowing a 
sandboxed program to programmatically dump large amounts of kernel memory.

Attached PoC leak_kmem.c will try to dump 256 kB of kernel memory to the file 
dump.bin.

This is of course a nice kASLR defeat since you can almost certainly find all 
the pointers you need.

On OS X another interesting attack scenario with a bug like this would be to 
try to read a sandbox extension - since these are just HMAC'ed strings if you 
could force another process to request an extension and then read it from 
kernel memory you could just consume it since extensions aren't tied to a 
particular process. I don't know how feasible it would be to read the HMAC key, 
if I have time I'll experiment a bit with this. (Chrome doesn't use sandbox 
extensions, safari does.)

Original issue reported on code.google.com by [email protected] on 2 May 2014 at 3:23

Attachments:

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.