BRIGHTIUP


  • Home

  • Tags

  • Categories

  • Archives

CVE-2018-4242: Use after Free in AppleHV.kext

Posted on 2018-06-11 | In Apple OS

When user closes a handle of AppleHVClient the kernel will call AppleHVClient::free via IOService::terminateWorker, and this free function will call hv_vmx_vm_t::free as follow.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
hv_vmx_vm_t::free():
0000000000002414 pushq %rbp
0000000000002415 movq %rsp, %rbp
0000000000002418 pushq %rbx
0000000000002419 pushq %rax
000000000000241a movq %rdi, %rbx
000000000000241d movb $0x0, 0x1c(%rbx)
0000000000002421 movq 0x80(%rbx), %rdi
0000000000002428 testq %rdi, %rdi
000000000000242b je 0x2432
000000000000242d callq _IOLockFree
0000000000002432 movq 0x38(%rbx), %rdi
0000000000002436 testq %rdi, %rdi
0000000000002439 je 0x2445
000000000000243b movl $0x100, %esi
0000000000002440 callq _IOFreeAligned
0000000000002445 movq 0x70(%rbx), %rdi
0000000000002449 testq %rdi, %rdi
000000000000244c je 0x2454
000000000000244e movq (%rdi), %rax
0000000000002451 callq *0x28(%rax)

hv_vmx_vm_t::free function will fetch the lock and free this lock. But at the same time, user can use this lock via machdep system call hv_vmx_vm_t::TRAP_hv_destroy_vm.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
hv_vmx_vm_t::TRAP_hv_destroy_vm(hv_vmx_vm_t*):
0000000000001772 pushq %rbp
0000000000001773 movq %rsp, %rbp
0000000000001776 pushq %r14
0000000000001778 pushq %rbx
0000000000001779 movq %rdi, %rbx
000000000000177c testq %rbx, %rbx
000000000000177f je 0x17a7
0000000000001781 movq 0x80(%rbx), %rdi
0000000000001788 callq _IOLockLock
000000000000178d cmpl $0x0, 0x40(%rbx)
0000000000001791 jle 0x17af
0000000000001793 movq 0x80(%rbx), %rdi
000000000000179a callq _IOLockUnlock
000000000000179f movl $0xfae94001, %r14d
00000000000017a5 jmp 0x17c5
00000000000017a7 movl $0xfae94006, %r14d
00000000000017ad jmp 0x17c5
00000000000017af xorl %r14d, %r14d
00000000000017b2 xorl %edi, %edi
00000000000017b4 callq _hv_set_task_target

So UaF happens. Details about this issue and some basic knowledges about XNU which powers iOS/macOS are discussed in the complete write-up.

And PoC of this issue is also available on Github.

Integer Overflow in Latest macOS/iOS Kernel

Posted on 2018-02-16 | In Apple OS

There exists an integer overflow in kqueue syscall which can lead to kernel deadlock(Or crash on some devices) on latest macOS and iOS. kqueue in BSD is just like epoll in Linux which powers IO multiplexing. It means users can monitor a set of actions of file descriptors, like read or write available. At the same time, an object of kqueue in kernel is also a file descriptor in user space, so users can monitor a file descriptor of kqueue object by kqueue. The kqueues which monitor other objects are called parent kqueue, so we can name the kqueue to be monitored as child kqueue, it is fine until now because they are both file descriptors. The problem is that the user can make a circular kqueue which means the two kqueues can monitor each other.

Lets look at the source. The function associated with this issue lies in bsd/kern/kern_event.c named kqueue_kqfilter. This function is called when the child kqueue is attached to the list of parent kqueue. This is a piece of comment in this function.

1
2
3
4
5
6
7
8
9
10
/*
* We have to avoid creating a cycle when nesting kqueues
* inside another. Rather than trying to walk the whole
* potential DAG of nested kqueues, we just use a simple
* ceiling protocol. When a kqueue is inserted into another,
* we check that the (future) parent is not already nested
* into another kqueue at a lower level than the potenial
* child (because it could indicate a cycle). If that test
* passes, we just mark the nesting levels accordingly.
*/

It seems fine because the level of child will always be lower than the parent. But the level field of kqueue is just a 16 bit unsigned integer which means we can overflow it easily.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* kqueue - common core definition of a kqueue
*
* No real structures are allocated of this type. They are
* either kqfile objects or kqworkq objects - each of which is
* derived from this definition.
*/
struct kqueue {
struct waitq_set kq_wqs; /* private waitq set */
lck_spin_t kq_lock; /* kqueue lock */
uint16_t kq_state; /* state of the kq */
uint16_t kq_level; /* nesting level of the kq */
uint32_t kq_count; /* number of queued events */
struct proc *kq_p; /* process containing kqueue */
struct kqtailq kq_queue[1]; /* variable array of kqtailq structs */
};

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

static int kqueue_kqfilter {
...
if (parentkq->kq_level > 0 &&
parentkq->kq_level < kq->kq_level)
{
kqunlock(parentkq);
kn->kn_flags = EV_ERROR;
kn->kn_data = EINVAL;
return 0;
} else {
/* set parent level appropriately */
if (parentkq->kq_level == 0)
parentkq->kq_level = 2;
if (parentkq->kq_level < kq->kq_level + 1)
parentkq->kq_level = kq->kq_level + 1;
kqunlock(parentkq);

kn->kn_filtid = EVFILTID_KQREAD;
kqlock(kq);
KNOTE_ATTACH(&kqf->kqf_sel.si_note, kn);
/* indicate nesting in child, if needed */
if (kq->kq_level == 0)
kq->kq_level = 1;

int count = kq->kq_count;
kqunlock(kq);
return (count > 0);
}
...
}

Line 16 is the root cause of this integer overflow. When the kq->kq_level is 65535 and parentkq->kq_level is 2(Changed to 2 when it is 0), the parentkq->kq_level will be set to 0. When we do this in the opposite way, it can bypass the condition of the if statement.

Download Proof of concept.

How to reproduce:

  1. clang circular_queue.c -o circular_queue
  2. ./circular_queue

After that you can see a kernel crash or kernel deadlock.

Information about my device(sw_vers):
ProductName: Mac OS X
ProductVersion: 10.13.3
BuildVersion: 17D47

This poc is also worked on my iOS 11.0.2 of iPhone 8 Plus, I believe it can also work on latest iOS 11.2.5.

Update on July 5, 2018: This issue still exists on latest macOS 10.13.5 and iOS 11.4.

brightiup

To Be Four Haves Youth.

2 posts
1 categories
3 tags
Twitter Weibo GitHub Instagram
© 2018 brightiup