CVE-2019-8717 : Detail

CVE-2019-8717

7.8
/
High
Overflow
0.75%V4
Local
2019-12-18
16h33 +00:00
2020-02-12
00h06 +00:00
Notifications for a CVE
Stay informed of any changes for a specific CVE.
Notifications manage

CVE Descriptions

A memory corruption issue was addressed with improved memory handling. This issue is fixed in macOS Catalina 10.15, tvOS 13. An application may be able to execute arbitrary code with kernel privileges.

CVE Informations

Related Weaknesses

CWE-ID Weakness Name Source
CWE-787 Out-of-bounds Write
The product writes data past the end, or before the beginning, of the intended buffer.

Metrics

Metrics Score Severity CVSS Vector Source
V3.1 7.8 HIGH CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Base: Exploitabilty Metrics

The Exploitability metrics reflect the characteristics of the thing that is vulnerable, which we refer to formally as the vulnerable component.

Attack Vector

This metric reflects the context by which vulnerability exploitation is possible.

Local

The vulnerable component is not bound to the network stack and the attacker’s path is via read/write/execute capabilities.

Attack Complexity

This metric describes the conditions beyond the attacker’s control that must exist in order to exploit the vulnerability.

Low

Specialized access conditions or extenuating circumstances do not exist. An attacker can expect repeatable success when attacking the vulnerable component.

Privileges Required

This metric describes the level of privileges an attacker must possess before successfully exploiting the vulnerability.

Low

The attacker requires privileges that provide basic user capabilities that could normally affect only settings and files owned by a user. Alternatively, an attacker with Low privileges has the ability to access only non-sensitive resources.

User Interaction

This metric captures the requirement for a human user, other than the attacker, to participate in the successful compromise of the vulnerable component.

None

The vulnerable system can be exploited without interaction from any user.

Base: Scope Metrics

The Scope metric captures whether a vulnerability in one vulnerable component impacts resources in components beyond its security scope.

Scope

Formally, a security authority is a mechanism (e.g., an application, an operating system, firmware, a sandbox environment) that defines and enforces access control in terms of how certain subjects/actors (e.g., human users, processes) can access certain restricted objects/resources (e.g., files, CPU, memory) in a controlled manner. All the subjects and objects under the jurisdiction of a single security authority are considered to be under one security scope. If a vulnerability in a vulnerable component can affect a component which is in a different security scope than the vulnerable component, a Scope change occurs. Intuitively, whenever the impact of a vulnerability breaches a security/trust boundary and impacts components outside the security scope in which vulnerable component resides, a Scope change occurs.

Unchanged

An exploited vulnerability can only affect resources managed by the same security authority. In this case, the vulnerable component and the impacted component are either the same, or both are managed by the same security authority.

Base: Impact Metrics

The Impact metrics capture the effects of a successfully exploited vulnerability on the component that suffers the worst outcome that is most directly and predictably associated with the attack. Analysts should constrain impacts to a reasonable, final outcome which they are confident an attacker is able to achieve.

Confidentiality Impact

This metric measures the impact to the confidentiality of the information resources managed by a software component due to a successfully exploited vulnerability.

High

There is a total loss of confidentiality, resulting in all resources within the impacted component being divulged to the attacker. Alternatively, access to only some restricted information is obtained, but the disclosed information presents a direct, serious impact. For example, an attacker steals the administrator's password, or private encryption keys of a web server.

Integrity Impact

This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information.

High

There is a total loss of integrity, or a complete loss of protection. For example, the attacker is able to modify any/all files protected by the impacted component. Alternatively, only some files can be modified, but malicious modification would present a direct, serious consequence to the impacted component.

Availability Impact

This metric measures the impact to the availability of the impacted component resulting from a successfully exploited vulnerability.

High

There is a total loss of availability, resulting in the attacker being able to fully deny access to resources in the impacted component; this loss is either sustained (while the attacker continues to deliver the attack) or persistent (the condition persists even after the attack has completed). Alternatively, the attacker has the ability to deny some availability, but the loss of availability presents a direct, serious consequence to the impacted component (e.g., the attacker cannot disrupt existing connections, but can prevent new connections; the attacker can repeatedly exploit a vulnerability that, in each instance of a successful attack, leaks a only small amount of memory, but after repeated exploitation causes a service to become completely unavailable).

Temporal Metrics

The Temporal metrics measure the current state of exploit techniques or code availability, the existence of any patches or workarounds, or the confidence in the description of a vulnerability.

Environmental Metrics

These metrics enable the analyst to customize the CVSS score depending on the importance of the affected IT asset to a user’s organization, measured in terms of Confidentiality, Integrity, and Availability.

nvd@nist.gov
V2 7.2 AV:L/AC:L/Au:N/C:C/I:C/A:C nvd@nist.gov

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 : 47479

Publication date : 2019-10-08 22h00 +00:00
Author : Google Security Research
EDB Verified : Yes

=== Summary === This report describes a bug in the XNU implementation of the IPComp protocol (https://tools.ietf.org/html/rfc3173). This bug can be remotely triggered by an attacker who is able to send traffic to a macOS system (iOS AFAIK isn't affected) *over two network interfaces at the same time*. === Some basics to provide context === IPComp is a protocol for compressing the payload of IP packets. The XNU implementation of IPComp is (going by the last public XNU release) enabled only on X86-64; ARM64 doesn't seem to have the feature enabled at all (look for ipcomp_zlib in config/MASTER.x86_64 and config/MASTER.arm64). In other words, it's enabled on macOS and disabled on iOS. While IPComp is related to IPsec, the IPComp input path processes input even when the user has not configured any IPsec stuff on the system. zlib requires fairly large buffers for decompression and especially for compression. In order to avoid allocating such buffers for each packet, IPComp uses two global z_stream instances "deflate_stream" and "inflate_stream". If IPComp isn't used, the buffer pointers in these z_stream instances remain NULL; only when IPComp is actually used, the kernel will attempt to initialize the buffer pointers. As far as I can tell, the IPComp implementation of XNU has been completely broken for years, which makes it impossible to actually reach the decompression code. ipcomp_algorithm_lookup() is responsible for allocating global buffers for the compression and decompression code; however, all of these allocations go through deflate_alloc(), which (since xnu-1228, which corresponds to macOS 10.5 from 2007) calls _MALLOC() with M_NOWAIT. _MALLOC() leads to kalloc_canblock(), which, if the M_NOWAIT flag was set and the allocation is too big for a kalloc zone (size >= kalloc_max_prerounded), immediately returns NULL. On X86-64, kalloc_max_prerounded is 0x2001; both deflateInit2() and inflateInit2() attempt allocations bigger than that, causing them to fail with Z_MEM_ERROR, as is visible with dtrace when observing the system's reaction to a single incoming IPComp packet [empty lines removed]: ``` bash-3.2# ./inflate_test.dtrace dtrace: script './inflate_test.dtrace' matched 11 probes CPU ID FUNCTION:NAME 0 243037 deflateInit2_:entry deflate init (thread=ffffff802db84a40) 0 224285 kalloc_canblock:entry kalloc_canblock(size=0x1738, canblock=0, site=ffffff8018e787e8) 0 224286 kalloc_canblock:return kalloc_canblock()=0xffffff80496b9800 0 224285 kalloc_canblock:entry kalloc_canblock(size=0x2000, canblock=0, site=ffffff8018e787e8) 0 224286 kalloc_canblock:return kalloc_canblock()=0xffffff802f42f000 0 224285 kalloc_canblock:entry kalloc_canblock(size=0x2000, canblock=0, site=ffffff8018e787e8) 0 224286 kalloc_canblock:return kalloc_canblock()=0x0 0 224285 kalloc_canblock:entry kalloc_canblock(size=0x20000, canblock=0, site=ffffff8018e787e8) 0 224286 kalloc_canblock:return kalloc_canblock()=0x0 0 224285 kalloc_canblock:entry kalloc_canblock(size=0x20000, canblock=0, site=ffffff8018e787e8) 0 224286 kalloc_canblock:return kalloc_canblock()=0x0 0 243038 deflateInit2_:return rval=0xfffffffc 0 243073 inflateInit2_:entry inflate init (thread=ffffff802db84a40) 0 224285 kalloc_canblock:entry kalloc_canblock(size=0x2550, canblock=0, site=ffffff8018e787e8) 0 224286 kalloc_canblock:return kalloc_canblock()=0x0 0 243074 inflateInit2_:return rval=0xfffffffc ``` (On iOS, the kalloc() limit seems to be higher, so if IPComp was built there, the input path might actually work?) === main bug description === IPComp uses a single global `static z_stream inflate_stream` for decompressing all incoming packets. This global is used without any locking. While processing of packets from a single interface seems to be single-threaded, packets arriving on multiple ethernet interfaces at the same time (or on an ethernet interface and a non-ethernet interface) can be processed in parallel (see dlil_create_input_thread() and its caller for the precise threading rules). Since zlib isn't designed for concurrent use of a z_stream, this leads to memory corruption. If IPComp actually worked, I believe that this bug would lead to things like out-of-bounds reads, out-of-bounds writes and use-after-frees. However, since IPComp never actually manages to set up the compression and decompression state, the bug instead manifests in the code that, for every incoming IPComp packet, attempts to set up the deflate buffers and tears down the successfully allocated buffers because some of the allocations failed: ``` int ZEXPORT deflateInit2_(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size) { [...] if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { return Z_STREAM_ERROR; } if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); if (s == Z_NULL) return Z_MEM_ERROR; strm->state = (struct internal_state FAR *)s; [...] s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); s->pending_buf = (uchf *) overlay; [...] if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || s->pending_buf == Z_NULL) { [...] deflateEnd (strm); return Z_MEM_ERROR; } [...] } [...] int ZEXPORT deflateEnd(z_streamp strm) { [...] /* Deallocate in reverse order of allocations: */ TRY_FREE(strm, strm->state->pending_buf); TRY_FREE(strm, strm->state->head); TRY_FREE(strm, strm->state->prev); TRY_FREE(strm, strm->state->window); ZFREE(strm, strm->state); strm->state = Z_NULL; return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; } ``` When multiple executions of this code race, it is possible for two threads to free the same buffer, causing a double-free: ``` *** Panic Report *** panic(cpu 2 caller 0xffffff8012802df5): "zfree: double free of 0xffffff80285d9000 to zone kalloc.8192\n"@/BuildRoot/Library/Caches/com.apple.xbs/Sources/xnu/xnu-4903.261.4/osfmk/kern/zalloc.c:1304 Backtrace (CPU 2), Frame : Return Address 0xffffff912141b420 : 0xffffff80127aea2d mach_kernel : _handle_debugger_trap + 0x47d 0xffffff912141b470 : 0xffffff80128e9e95 mach_kernel : _kdp_i386_trap + 0x155 0xffffff912141b4b0 : 0xffffff80128db70a mach_kernel : _kernel_trap + 0x50a 0xffffff912141b520 : 0xffffff801275bb40 mach_kernel : _return_from_trap + 0xe0 0xffffff912141b540 : 0xffffff80127ae447 mach_kernel : _panic_trap_to_debugger + 0x197 0xffffff912141b660 : 0xffffff80127ae293 mach_kernel : _panic + 0x63 0xffffff912141b6d0 : 0xffffff8012802df5 mach_kernel : _zcram + 0xa15 0xffffff912141b710 : 0xffffff8012804d4a mach_kernel : _zfree + 0x67a 0xffffff912141b7f0 : 0xffffff80127bac58 mach_kernel : _kfree_addr + 0x68 0xffffff912141b850 : 0xffffff8012dfc837 mach_kernel : _deflateEnd + 0x87 0xffffff912141b870 : 0xffffff8012dfc793 mach_kernel : _deflateInit2_ + 0x253 0xffffff912141b8c0 : 0xffffff8012c164a3 mach_kernel : _ipcomp_algorithm_lookup + 0x63 0xffffff912141b8f0 : 0xffffff8012c16fb2 mach_kernel : _ipcomp4_input + 0x112 0xffffff912141b990 : 0xffffff8012b89907 mach_kernel : _ip_proto_dispatch_in_wrapper + 0x1a7 0xffffff912141b9e0 : 0xffffff8012b8bfa6 mach_kernel : _ip_input + 0x18b6 0xffffff912141ba40 : 0xffffff8012b8a5a9 mach_kernel : _ip_input_process_list + 0xc69 0xffffff912141bcb0 : 0xffffff8012aac3ed mach_kernel : _proto_input + 0x9d 0xffffff912141bce0 : 0xffffff8012a76c41 mach_kernel : _ether_attach_inet + 0x471 0xffffff912141bd70 : 0xffffff8012a6b036 mach_kernel : _dlil_rxpoll_set_params + 0x1b36 0xffffff912141bda0 : 0xffffff8012a6aedc mach_kernel : _dlil_rxpoll_set_params + 0x19dc 0xffffff912141bf10 : 0xffffff8012a692e9 mach_kernel : _ifp_if_ioctl + 0x10d9 0xffffff912141bfa0 : 0xffffff801275b0ce mach_kernel : _call_continuation + 0x2e BSD process name corresponding to current thread: kernel_task Boot args: -zp -v keepsyms=1 Mac OS version: 18F132 Kernel version: Darwin Kernel Version 18.6.0: Thu Apr 25 23:16:27 PDT 2019; root:xnu-4903.261.4~2/RELEASE_X86_64 Kernel UUID: 7C8BB636-E593-3CE4-8528-9BD24A688851 Kernel slide: 0x0000000012400000 Kernel text base: 0xffffff8012600000 __HIB text base: 0xffffff8012500000 System model name: Macmini7,1 (Mac-XXXXXXXXXXXXXXXX) ``` === Repro steps === You'll need a Mac (I used a Mac mini) and a Linux workstation. Stick two USB ethernet adapters into the Mac. Make sure that your Linux workstation has two free ethernet ports; if it doesn't, also stick USB ethernet adapters into your workstation. Take two ethernet cables; for both of them, stick one end into the Linux workstation and the other end into the Mac. Set up static IP addresses for both interfaces on the Linux box and the Mac. I'm using: - Linux, first connection: 192.168.250.1/24 - Mac, first connection: 192.168.250.2/24 - Linux, second connection: 192.168.251.1/24 - Mac, second connection: 192.168.251.2/24 On the Linux workstation, ping both IP addresses of the Mac, then dump the relevant ARP table entries: ``` $ ping -c1 192.168.250.2 PING 192.168.250.2 (192.168.250.2) 56(84) bytes of data. 64 bytes from 192.168.250.2: icmp_seq=1 ttl=64 time=0.794 ms [...] $ ping -c1 192.168.251.2 PING 192.168.251.2 (192.168.251.2) 56(84) bytes of data. 64 bytes from 192.168.251.2: icmp_seq=1 ttl=64 time=0.762 ms [...] $ arp -n | egrep '192\.168\.25[01]' 192.168.250.2 ether aa:aa:aa:aa:aa:aa C eth0 192.168.251.2 ether bb:bb:bb:bb:bb:bb C eth1 $ ``` On the Linux workstation, build the attached ipcomp_uaf.c and run it: ``` $ gcc -o ipcomp_recursion ipcomp_recursion.c -Wall $ sudo bash # ./ipcomp_uaf usage: ./ipcomp_uaf <if1> <target_mac1> <src_ip1> <dst_ip1> <if2> <target_mac2> <src_ip2> <dst_ip2> # ./ipcomp_uaf eth0 aa:aa:aa:aa:aa:aa 192.168.250.1 192.168.250.2 eth1 bb:bb:bb:bb:bb:bb 192.168.251.1 192.168.251.2 ``` After something like a second, you should be able to observe that the Mac panics. I have observed panics via double-free and via null deref triggered by the PoC. (Stop the PoC afterwards, otherwise it'll panic again as soon as the network interfaces are up.) (The PoC also works if you use broadcast addresses as follows: ``` # ./ipcomp_uaf eth0 ff:ff:ff:ff:ff:ff 0.0.0.0 255.255.255.255 eth1 ff:ff:ff:ff:ff:ff 0.0.0.0 255.255.255.255 ```) === Fixing the bug === I believe that by far the best way to fix this issue is to rip out the entire feature. Unless I'm missing some way for the initialization to succeed, it looks like nobody can have successfully used this feature in the last few years; and apparently nobody felt strongly enough about that to get the feature fixed. At the same time, this thing is remote attack surface in the IP stack, and it looks like it has already led to a remote DoS bug in the past - the first search result on bing.com for both "ipcomp macos" and "ipcomp xnu" is <https://www.exploit-db.com/exploits/5191>. In case you decide to fix the bug in a different way, please note: - I believe that this can *NOT* be fixed by removing the PR_PROTOLOCK flag from the entries in `inetsw` and `inet6sw`. While removal of that flag would cause the input code to take the domain mutex before invoking the protocol handler, IPv4 and IPv6 are different domains, and so concurrent processing of IPv4+IPComp and IPv6+IPComp packets would probably still trigger the bug. - If you decide to fix the memory allocation of IPComp so that the input path works again (please don't - you'll never again have such a great way to prove that nobody is using that code), I think another bug will become reachable: I don't see anything that prevents unbounded recursion between ip_proto_dispatch_in() and ipcomp4_input() using an IP packet with a series of IPComp headers, which would be usable to cause a kernel panic via stack overflow with a single IP packet. In case you want to play with that, I wrote a PoC that generates packets with 100 such headers and attached it as ipcomp_recursion.c. (The other IPv6 handlers for pseudo-protocols like IPPROTO_FRAGMENT seem to avoid this problem by having the ) Proof of Concept: https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47479.zip

Products Mentioned

Configuraton 0

Apple>>Mac_os_x >> Version To (excluding) 10.15

Apple>>Tvos >> Version To (excluding) 13

References

https://support.apple.com/kb/HT210722
Tags : x_refsource_CONFIRM