CVE-2024-27815 A Buffer Overflow in the XNU Kernel What if I'm not like the others? A broken mbuf, an overwrite-- What if my Mac won't recover? I'll clean my code with TURPENTINE!
CVE-2024-27815 is a buffer overflow in the XNU kernel I reported in sbconcat_mbufs . It was publicly fixed in xnu-10063.121.3 , released with macOS 14.5, iOS 17.5, and visionOS 1.2.
This bug was introduced in xnu-10002.1.13 (macOS 14.0/ iOS 17.0) and was fixed in xnu-10063.121.3 (macOS 14.5/ iOS 17.5). The bug affects kernels compiled with CONFIG_MBUF_MCACHE . I have verified the existence of this bug on X86_64 builds of macOS 14.2, 14.3, and 14.4.
TURPENTINE.c contains a PoC, and an example crash log is shown below. You can find the proof-of-concept code here.
I would like to thank the Apple Product Security Team for both their swift response to this bug and their overall support for security research.
Timeline
September 26, 2023 : macOS 14.0 Sonoma was released with xnu-10002.1.13 , the first public kernel release with this bug.
: macOS 14.0 Sonoma was released with , the first public kernel release with this bug. Early 2024 : I disclosed the details of this bug along with TURPENTINE.c (the proof-of-concept attached to this report) to Apple in early 2024.
: I disclosed the details of this bug along with (the proof-of-concept attached to this report) to Apple in early 2024. February 17, 2024 : I posted the hash of TURPENTINE.c to X on Feb 17, 2024.
: I posted the hash of to X on Feb 17, 2024. April 2, 2024 : macOS Sonoma 14.5 beta 1 ( 23F5049f ) shipped with xnu-10063.120.88.501.3 , the first kernel version with a fix for this bug.
: macOS Sonoma 14.5 beta 1 ( ) shipped with , the first kernel version with a fix for this bug. May 13, 2024 : macOS Sonoma 14.5 ( 23F79 ) shipped with xnu-10063.121.3 , the first public release containing a fix.
: macOS Sonoma 14.5 ( ) shipped with , the first public release containing a fix. June 10, 2024: visionOS 1.2 is released (the last OS on a kernel prior to xnu-10063.121.3 ), and Apple's website is updated to include CVE-2024-27815 for visionOS 1.2, macOS 14.5, iOS 17.5/ iPadOS 17.5, watchOS 10.5, and tvOS 17.5.
Security Advisories
https://support.apple.com/HT214108
https://support.apple.com/HT214106
https://support.apple.com/HT214104
https://support.apple.com/HT214102
https://support.apple.com/HT214101
$ sha256sum TURPENTINE.c f7160a6ad7d52f32d64b86cf3006c98a217954d80c3fc71a8f27595e227d0fa0 TURPENTINE.c
Root Cause
Message buffers ( struct mbuf 's) are objects used in various networking / BSD portions of the kernel. mbuf 's consist of a header and data portion, both of fixed size. _MSIZE is the total size of a message buffer, and MLEN is the length of the data part of the message buffer (eg. not counting the header). asa->sa_len can be up to SOCK_MAXADDRLEN (255) as it's just a single unsigned byte.
When CONFIG_MBUF_MCACHE is on:
This bcopy in sbconcat_mbufs copies a socket address into a message buffer. It allows the attacker to write up to sa_len (up to 255) bytes of data into a message buffer's data field, which is only MLEN (224) bytes long.
uipc_socket2.c:1249:
MGET(m, M_DONTWAIT, MT_SONAME); ... m->m_len = asa->sa_len; bcopy((caddr_t)asa, mtod(m, caddr_t), asa->sa_len);
Note: Recall that bcopy 's arguments are (source, dest, len)- the opposite of memcpy . mtod just grabs the data field from an mbuf . So, this bcopy copies the socket address into mbuf.M_databuf , potentially overflowing it.
xnu-10002.1.13 introduced the bug when this macro was added that only emits the bounds check when _MSIZE is smaller than a byte (after checking sa_len is only a byte). The mistake was using _MSIZE instead of MLEN , the actual length of available space in which to copy data.
Change that Introduced the Bug
@@ -1233,9 +1233,13 @@ sbconcat_mbufs(struct sockbuf *sb, struct sockaddr *asa, struct mbuf *m0, struct } if (asa != NULL) { + _CASSERT(sizeof(asa->sa_len) == sizeof(__uint8_t)); +#if _MSIZE <= UINT8_MAX if (asa->sa_len > MLEN) { return NULL; } +#endif + _CASSERT(sizeof(asa->sa_len) == sizeof(__uint8_t)); space += asa->sa_len; }
This macro was presumably added to increase performance by removing a redundant check, but this was done incorrectly. _MSIZE is the total size of a message buffer, including its header. There are only MLEN bytes available to copy into, not _MSIZE . The original check (that sa_len > MLEN ) is correct, but the added macro is not.
Apple's Fix
@@ -1226,12 +1226,9 @@ sbconcat_mbufs(struct sockbuf *sb, struct sockaddr *asa, struct mbuf *m0, struct if (asa != NULL) { _CASSERT(sizeof(asa->sa_len) == sizeof(__uint8_t)); -#if _MSIZE <= UINT8_MAX - if (asa->sa_len > MLEN) { + if (MLEN <= UINT8_MAX && asa->sa_len > MLEN) { return NULL; } -#endif - _CASSERT(sizeof(asa->sa_len) == sizeof(__uint8_t)); space += asa->sa_len; }
Apple released a fix in xnu-10063.121.3 , which is the kernel that ships with macOS 14.5. Now, sbconcat_mbufs correctly compares MLEN , not _MSIZE , to UINT8_MAX . When the constant MLEN is always larger than UINT8_MAX , the compiler can optimize this check out, and this is safe because the CASSERT guarantees sa_len is at most 255.
PoC
All you need are 3 syscalls- socketpair , bind , and write . No extra privileges are needed. The kernel needs to have been compiled with CONFIG_MBUF_MCACHE as well. I have tested and verified the presence of this bug on X86_64 builds of macOS 14.2, 14.3, and 14.4.
To run: gcc turpentine.c -o turpentine and ./turpentine .
Here's an example crash- a general protection fault in kernel_task .
Don't let the kexts in the backtrace fool you- this bug is localized to just the main kernel. The PoC allocates an mbuf and corrupts the mbuf in memory after it, which in my environment usually is owned by a kext. It's not an mbuf created by the PoC that is corrupted, but some random mbuf owned by something else (usually a kext). That's why this backtrace shows kext methods.
Debugger: Unexpected kernel trap number: 0xd, RIP: 0xffffff8003901082, CR2: 0x0 CPU 0 panic trap number 0xd, rip 0xffffff8003901082 cr0 0x000000008001003b cr2 0x00007ff7bac54ad0 cr3 0x000000000843e000 cr4 0x00000000001406e0 Debugger called: panic(cpu 0 caller 0xffffff8003d851b3): Kernel trap at 0xffffff8003901082, type 13=general protection, registers: CR0: 0x000000008001003b, CR2: 0x00007ff7bac54ad0, CR3: 0x000000000843e000, CR4: 0x00000000001406e0 RAX: 0xbdbdbd48aab09a6e, RBX: 0xffffff8aecf2dcb0, RCX: 0x000000000000003c, RDX: 0x000000000000003c RSP: 0xffffffb55ab0bcf8, RBP: 0xffffffb55ab0bd10, RSI: 0x4242424242424242, RDI: 0xffffff8aecf2dcb0 R8: 0xffffff8aecf2dd00, R9: 0xffffff8aecf2dd00, R10: 0xffffff8aecf2dc08, R11: 0x0000000066316350 R12: 0x000000000000003c, R13: 0x000000000000003c, R14: 0xffffff8aecf2dcb0, R15: 0x000000000000003c RFL: 0x0000000000010282, RIP: 0xffffff8003901082, CS: 0x0000000000000008, SS: 0x0000000000000010 Fault CR2: 0x0000000000000000, Error code: 0x0000000000000000, Fault CPU: 0x0 VMM, PL: 0, VF: 0 Panicked task 0xffffff9fc2224be8: 165 threads: pid 0: kernel_task Backtrace (CPU 0), panicked thread: 0xffffff915cabbb30, Frame : Return Address 0xffffff800390c140 : 0xffffff8003c36c41 mach_kernel : _handle_debugger_trap + 0x4b1 0xffffff800390c190 : 0xffffff8003d955c0 mach_kernel : _kdp_i386_trap + 0x110 0xffffff800390c1d0 : 0xffffff8003d84d0c mach_kernel : _kernel_trap + 0x55c 0xffffff800390c250 : 0xffffff8003bd3971 mach_kernel : _return_from_trap + 0xc1 0xffffff800390c270 : 0xffffff8003c36f2d mach_kernel : _DebuggerTrapWithState + 0x5d 0xffffff800390c360 : 0xffffff8003c365d3 mach_kernel : _panic_trap_to_debugger + 0x1e3 0xffffff800390c3c0 : 0xffffff80043d8d0b mach_kernel : _panic + 0x84 0xffffff800390c4b0 : 0xffffff8003d851b3 mach_kernel : _sync_iss_to_iks + 0x2c3 0xffffff800390c630 : 0xffffff8003d84e97 mach_kernel : _kernel_trap + 0x6e7 0xffffff800390c6b0 : 0xffffff8003bd3971 mach_kernel : _return_from_trap + 0xc1 0xffffff800390c6d0 : 0xffffff8003901082 0xffffffb55ab0bd10 : 0xffffff8005cfeb8f com.apple.iokit.IONetworkingFamily : __ZL12IO_COPY_MBUFP6__mbufS0_i + 0xb3 0xffffffb55ab0bd60 : 0xffffff8005cfec9c com.apple.iokit.IONetworkingFamily : __ZN19IONetworkController19replaceOrCopyPacketEPP6__mbufjPb + 0x9e 0xffffffb55ab0bda0 : 0xffffff8005f46b93 com.apple.driver.AppleVmxnet3Ethernet : __ZN21AppleVMXNETController18replace_dma_bufferEP10mbuf_dma_sii + 0x2f 0xffffffb55ab0bdf0 : 0xffffff8005f45ffe com.apple.driver.AppleVmxnet3Ethernet : __ZN21AppleVMXNETController12rx_pkt_queueEiP11IOMbufQueuej + 0x128 0xffffffb55ab0bea0 : 0xffffff8005f45d1d com.apple.driver.AppleVmxnet3Ethernet : __ZN21AppleVMXNETController21interrupt_msi_handlerEP22IOInterruptEventSourcei + 0x5f 0xffffffb55ab0bed0 : 0xffffff800430e90a mach_kernel : __ZN22IOInterruptEventSource12checkForWorkEv + 0x12a 0xffffffb55ab0bf20 : 0xffffff800430d12e mach_kernel : __ZN10IOWorkLoop15runEventSourcesEv + 0x13e 0xffffffb55ab0bf60 : 0xffffff800430c756 mach_kernel : __ZN10IOWorkLoop10threadMainEv + 0x36 0xffffffb55ab0bfa0 : 0xffffff8003bd319e mach_kernel : _call_continuation + 0x2e Kernel Extensions in backtrace: com.apple.iokit.IONetworkingFamily(3.4)[5D912FD8-C4CD-3CF6-B214-DF5BF7AB4FA0]@0xffffff8005cf4000->0xffffff8005d0bfff com.apple.driver.AppleVmxnet3Ethernet(1.0.10)[5FAEBB98-3551-319C-8075-EA4855C07B97]@0xffffff8005f42000->0xffffff8005f46fff dependency: com.apple.iokit.IONetworkingFamily(3.4)[5D912FD8-C4CD-3CF6-B214-DF5BF7AB4FA0]@0xffffff8005cf4000->0xffffff8005d0bfff dependency: com.apple.iokit.IOPCIFamily(2.9)[1B194276-D13F-32DD-8B6D-4751C1C73603]@0xffffff8005f55000->0xffffff8005f86fff Process name corresponding to current thread (0xffffff915cabbb30): kernel_task Boot args: kcsuffix=release wdt=-1 serial=5 debug=0x10012a kasan.checks=4294967295 -v keepsyms=1 amfi_get_out_of_my_way=1 tlbto_us=0 vti=9 Mac OS version: 23D56 Kernel version: Darwin Kernel Version 23.3.0: Wed Dec 20 21:28:58 PST 2023; root:xnu-10002.81.5~7/RELEASE_X86_64 Kernel UUID: 8C96896D-43A3-3BF0-8F4C-4118DA6AC9AA roots installed: 0 KernelCache slide: 0x0000000003800000 KernelCache base: 0xffffff8003a00000 Kernel slide: 0x00000000038e0000 Kernel text base: 0xffffff8003ae0000 __HIB text base: 0xffffff8003900000
-ravi
June 19, 2024