| Title: | Mac OS X shared_region_make_private_np() Memory Corruption |
|
| Description: | Mac OS X shared_region_make_private_np() system call fails to handle crafted user input, leading to an exploitable memory corruption condition. Unprivileged local users can abuse this issue in order to escalate privileges (via arbitrary code execution) or cause a denial of service. | |
| Author/Contributor: | LMH <lmh [at] info-pull.com> - discovery, MoKB release, debugging. | |
| References: | ||
| Proof of concept or exploit: |
The following (one-liner) proof of concept / exploit can be used to reproduce the bug
(requires Xcode/GNU GCC compiler to be installed):
MOKB-28-11-2006.c
gcc MOKB-28-11-2006.c -o MOKB-28-11-2006 && ./MOKB-28-11-2006 |
|
| Debugging information: |
It's been tested on an up-to-date (28-11-2006) Mac OS X installation, running on an Intel "shipping" Mac (x86).
---------------- beware: scary code. Oh yes, there will be blood.
---------------- bsd/vm/vm_unix.c
443 int
444 shared_region_make_private_np(
445 struct proc *p,
446 struct shared_region_make_private_np_args *uap, <--- syscall arg
447 __unused int *retvalp)
448 {
449 int error;
450 kern_return_t kr;
451 boolean_t using_shared_regions;
452 user_addr_t user_ranges;
453 unsigned int range_count;
454 struct shared_region_range_np *ranges;
455 shared_region_mapping_t shared_region;
456 struct shared_region_task_mappings task_mapping_info;
457 shared_region_mapping_t next;
458
459 ranges = NULL;
460
461 range_count = uap->rangeCount; <--- user controlled
462 user_ranges = uap->ranges; <--- user controlled
463
464 /* allocate kernel space for the "ranges" */
465 if (range_count != 0) {
466 kr = kmem_alloc(kernel_map,
467 (vm_offset_t *) &ranges,
468 (vm_size_t) (range_count * sizeof (ranges[0]))); <--- nice
469 if (kr != KERN_SUCCESS) {
470 error = ENOMEM;
471 goto done;
472 }
473
474 /* copy "ranges" from user-space */
475 error = copyin(user_ranges, <--- even better
476 ranges,
477 (range_count * sizeof (ranges[0])));
478 if (error) {
479 goto done;
480 }
481 }
482
483 if (p->p_flag & P_NOSHLIB) {
484 /* no split library has been mapped for this process so far */
485 using_shared_regions = FALSE;
486 } else {
487 /* this process has already mapped some split libraries */
488 using_shared_regions = TRUE;
489 }
490
(...)
498 error = clone_system_shared_regions(using_shared_regions,
499 FALSE, /* chain_regions */
500 ENV_DEFAULT_ROOT);
501 if (error) {
502 goto done;
503 }
504
505 /* get info on the newly allocated shared region */
506 vm_get_shared_region(current_task(), &shared_region);
507 task_mapping_info.self = (vm_offset_t) shared_region;
508 shared_region_mapping_info(shared_region,
509 &(task_mapping_info.text_region),
510 &(task_mapping_info.text_size),
511 &(task_mapping_info.data_region),
512 &(task_mapping_info.data_size),
513 &(task_mapping_info.region_mappings),
514 &(task_mapping_info.client_base),
515 &(task_mapping_info.alternate_base),
516 &(task_mapping_info.alternate_next),
517 &(task_mapping_info.fs_base),
518 &(task_mapping_info.system),
519 &(task_mapping_info.flags),
520 &next);
521
(...)
527 kr = shared_region_cleanup(range_count, ranges, &task_mapping_info);
528 switch (kr) {
529 case KERN_SUCCESS:
530 error = 0;
531 break;
532 default:
533 error = EINVAL;
534 goto done;
535 }
536
537 done:
538 if (ranges != NULL) {
539 kmem_free(kernel_map,
540 (vm_offset_t) ranges,
541 range_count * sizeof (ranges[0]));
542 ranges = NULL;
543 }
544
545 return error;
546 }
---------------- bsd/vm/vm_unix.c
|