CVE-2015-7112 : Detail

CVE-2015-7112

Overflow
0.41%V3
Network
2015-12-11
10h00 +00:00
2017-09-12
07h57 +00:00
Notifications for a CVE
Stay informed of any changes for a specific CVE.
Notifications manage

CVE Descriptions

The IOHIDFamily API in Apple iOS before 9.2, OS X before 10.11.2, tvOS before 9.1, and watchOS before 2.1 allows attackers to execute arbitrary code in a privileged context or cause a denial of service (memory corruption) via a crafted app, a different vulnerability than CVE-2015-7111.

CVE Informations

Related Weaknesses

CWE-ID Weakness Name Source
CWE-119 Improper Restriction of Operations within the Bounds of a Memory Buffer
The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.

Metrics

Metrics Score Severity CVSS Vector Source
V2 9.3 AV:N/AC:M/Au:N/C:C/I:C/A:C [email protected]

EPSS

EPSS is a scoring model that predicts the likelihood of a vulnerability being exploited.

EPSS Score

The EPSS model produces a probability score between 0 and 1 (0 and 100%). The higher the score, the greater the probability that a vulnerability will be exploited.

EPSS Percentile

The percentile is used to rank CVE according to their EPSS score. For example, a CVE in the 95th percentile according to its EPSS score is more likely to be exploited than 95% of other CVE. Thus, the percentile is used to compare the EPSS score of a CVE with that of other CVE.

Exploit information

Exploit Database EDB-ID : 39379

Publication date : 2016-01-27 23h00 +00:00
Author : Google Security Research
EDB Verified : Yes

Source: https://code.google.com/p/google-security-research/issues/detail?id=542 The IOHIDLibUserClient allows us to create and manage IOHIDEventQueues corresponding to available HID devices. Here is the ::start method, which can be reached via the IOHIDLibUserClient::_startQueue external method: ************ SNIP ************** void IOHIDEventQueue::start() { if ( _lock ) IOLockLock(_lock); if ( _state & kHIDQueueStarted ) goto START_END; if ( _currentEntrySize != _maxEntrySize ) <--- (a) { mach_port_t port = notifyMsg ? ((mach_msg_header_t *)notifyMsg)->msgh_remote_port : MACH_PORT_NULL; // Free the existing queue data if (dataQueue) { <-- (b) IOFreeAligned(dataQueue, round_page_32(getQueueSize() + DATA_QUEUE_MEMORY_HEADER_SIZE)); } if (_descriptor) { _descriptor->release(); _descriptor = 0; } // init the queue again. This will allocate the appropriate data. if ( !initWithEntries(_numEntries, _maxEntrySize) ) { (c) <---- goto START_END; } _currentEntrySize = _maxEntrySize; // RY: since we are initing the queue, we should reset the port as well if ( port ) setNotificationPort(port); } else if ( dataQueue ) { dataQueue->head = 0; dataQueue->tail = 0; } _state |= kHIDQueueStarted; START_END: if ( _lock ) IOLockUnlock(_lock); } ************ SNIP ************** If _currentEntrySize is not equal to _maxEntrySize then the start method will attempt to reallocate a better-sized queue; if dataQueue (a member of IODataQueue) is non-zero its free'd then initWithEntries is called with the new _maxEntrySize. Note that the error path on failure here jumps straight to the end of the function, so it's up to initWithEntries to clear dataQueue if it fails: ************ SNIP ************** Boolean IOHIDEventQueue::initWithEntries(UInt32 numEntries, UInt32 entrySize) { UInt32 size = numEntries*entrySize; if ( size < MIN_HID_QUEUE_CAPACITY ) size = MIN_HID_QUEUE_CAPACITY; return super::initWithCapacity(size); } ************ SNIP ************** There's a possible overflow here; but there will be *many* possible overflows coming up and we need to overflow at the right one... This calls through to IOSharedDataQueue::initWithCapacity ************ SNIP ************** Boolean IOSharedDataQueue::initWithCapacity(UInt32 size) { IODataQueueAppendix * appendix; vm_size_t allocSize; if (!super::init()) { return false; } _reserved = (ExpansionData *)IOMalloc(sizeof(struct ExpansionData)); if (!_reserved) { return false; } if (size > UINT32_MAX - DATA_QUEUE_MEMORY_HEADER_SIZE - DATA_QUEUE_MEMORY_APPENDIX_SIZE) { return false; } allocSize = round_page(size + DATA_QUEUE_MEMORY_HEADER_SIZE + DATA_QUEUE_MEMORY_APPENDIX_SIZE); if (allocSize < size) { return false; } dataQueue = (IODataQueueMemory *)IOMallocAligned(allocSize, PAGE_SIZE); ************ SNIP ************** We need this function to fail on any of the first four conditions; if we reach the IOMallocAligned call then dataQueue will either be set to a valid allocation (which is uninteresting) or set to NULL (also uninteresting.) We probably can't fail the ::init() call nor the small IOMalloc. There are then two integer overflow checks; the first will only fail if size (a UInt32 is greater than 0xfffffff4), and the second will be impossible to trigger on 64-bit since round_pages will be checking for 64-bit overflow, and we want a cross-platform exploit! Therefore, we have to reach the call to initWithCapacity with a size >= 0xfffffff4 (ie 12 possible values?) Where do _maxEntrySize and _currentEntrySize come from? When the queue is created they are both set to 0x20, and we can partially control _maxEntrySize by adding an new HIDElement to the queue. _numEntries is a completely controlled dword. So in order to reach the exploitable conditions we need to: 1) create a queue, specifying a value for _numEntries. This will allocate a queue (via initWithCapacity) of _numEntries*0x20; this allocation must succeed. 2) add an element to that queue with a *larger* size, such that _maxEntrySize is increased to NEW_MAX_SIZE. 3) stop the queue. 4) start the queue; at which point we will call IOHIDEventQueue::start. since _maxEntrySize is now larger this will free dataQueue then call initWithEntries(_num_entries, NEW_MAX_SIZE). This has to fail in exactly the manner described above such that dataQueue is a dangling pointer. 5) start the queue again, since _maxEntrySize is still != _currentEntrySize, this will call free dataQueue again! The really tricky part here is coming up with the values for _numEntries and NEW_MAX_SIZE; the constraints are: _numEntries is a dword (_numEntries*0x20)%2^32 must be an allocatable size (ideally <0x10000000) (_numEntries*NEW_MAX_SIZE)%2^32 must be >= 0xfffffff4 presumable NEW_MAX_SIZE is also reasonably limited by the HID descriptor parsing code, but I didn't look. This really doesn't give you much leaway, but it is quite satisfiable :) In this case I've chosen to create a "fake" hid device so that I can completely control NEW_MAX_SIZE, thus the PoC requires root (as did the TAIG jailbreak which also messed with report descriptors.) However, this isn't actually a requirement to hit the bug; you'd just need to look through every single HID report descriptor on your system to find one with a suitable report size. In this case, _numEntries of 0x3851eb85 leads to an initial queue size of (0x3851eb85*0x20)%2^32 = 0xa3d70a0 which is easily allocatable, and NEW_MAX_SIZE = 0x64 leads to: (0x3851eb85*0x64)%2^32 = 0xfffffff4 To run the PoC: 1) unzip and build the fake_hid code and run 'test -k' as root; this will create an IOHIDUserDevice whose cookie=2 IOHIDElementPrivate report size is 0x64. 2) build and run this file as a regular user. 3) see double free crash. There's actually nothing limiting this to a double free, you could go on indefinitely free'ing the same pointer. As I said before, this bug doesn't actually require root but it's just *much* easier to repro with it! Testing on: MacBookAir5,2 10.10.5 14F27 Guessing that this affects iOS too but haven't tested. Proof of Concept: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/39379.zip

Products Mentioned

Configuraton 0

Apple>>Iphone_os >> Version To (including) 9.1

Configuraton 0

Apple>>Tvos >> Version To (including) 9.0

Configuraton 0

Apple>>Watchos >> Version To (including) 2.0

Configuraton 0

Apple>>Mac_os_x >> Version To (including) 10.11.1

References

https://support.apple.com/HT205635
Tags : x_refsource_CONFIRM
https://support.apple.com/HT205637
Tags : x_refsource_CONFIRM
http://www.securitytracker.com/id/1034344
Tags : vdb-entry, x_refsource_SECTRACK
http://www.securityfocus.com/bid/78719
Tags : vdb-entry, x_refsource_BID
https://support.apple.com/HT205641
Tags : x_refsource_CONFIRM
https://support.apple.com/HT205640
Tags : x_refsource_CONFIRM