( Original text )
This post is about a heap buffer overflow vulnerability which I found in Apple’s XNU operating system kernel. I have written a proof-of-concept exploit which can reboot any Mac or iOS device on the same network, without any user interaction. Apple have classified this vulnerability as a remote code execution vulnerability in the kernel, because it may be possible to exploit the buffer overflow to execute arbitrary code in the kernel.
The following operating system versions and devices are vulnerable:
- Apple iOS 11 and earlier: all devices (upgrade to iOS 12)
- Apple macOS High Sierra, up to and including 10.13.6: all devices (patched in security update 2018-001)
- Apple macOS Sierra, up to and including 10.12.6: all devices (patched in security update 2018-005)
- Apple OS X El Capitan and earlier: all devices
I reported the vulnerability in time for Apple to patch the vulnerability for iOS 12 (released on September 17) and macOS Mojave (released on September 24). Both patches were announced retrospectively on October 30.
Severity and Mitigation
The vulnerability is a heap buffer overflow in the networking code in the XNU operating system kernel. XNU is used by both iOS and macOS, which is why iPhones, iPads, and Macbooks are all affected. To trigger the vulnerability, an attacker merely needs to send a malicious IP packet to the IP address of the target device. No user interaction is required. The attacker only needs to be connected to the same network as the target device. For example, if you are using the free WiFi in a coffee shop then an attacker can join the same WiFi network and send a malicious packet to your device. (If an attacker is on the same network as you, it is easy for them to discover your device’s IP address using nmap.) To make matters worse, the vulnerability is in such a fundamental part of the networking code that anti-virus software will not protect you: I tested the vulnerability on a Mac running McAfee® Endpoint Security for Mac and it made no difference. It also doesn’t matter what software you are running on the device — the malicious packet will still trigger the vulnerability even if you don’t have any ports open.
Since an attacker can control the size and content of the heap buffer overflow, it may be possible for them to exploit this vulnerability to gain remote code execution on your device. I have not attempted to write an exploit which is capable of doing this. My exploit PoC just overwrites the heap with garbage, which causes an immediate kernel crash and device reboot.
I am only aware of two mitigations against this vulnerability:
- Enabling stealth mode in the macOS firewall prevents the attack from working. Kudos to my colleague Henti Smith for discovering this, because this is an obscure system setting which is not enabled by default. As far as I’m aware, stealth mode does not exist on iOS devices.
- Do not use public WiFi networks. The attacker needs to be on the same network as the target device. It is not usually possible to send the malicious packet across the internet. For example, I wrote a fake web server which sends back a malicious reply when the target device tries to load a webpage. In my experiments, the malicious packet never arrived, except when the web server was on the same network as the target device.
Proof-of-concept exploit
I have written a proof-of-concept exploit which triggers the vulnerability. To give Apple’s users time to upgrade, I will not publish the source code for the exploit PoC immediately. However, I have made a short video which shows the PoC in action, crashing all the Apple devices on the local network.
The vulnerability
The bug is a buffer overflow in this line of code (bsd/netinet/ip_icmp.c:339):
m_copydata(n, <span class="dv">0</span>, icmplen, (caddr_t)&icp->icmp_ip);
. According to the comment, the purpose of this function is to «Generate an error packet of type error in response to bad packet ip». It uses the ICMP protocol to send out the error message. The header of the packet that caused the error is included in the ICMP message, so the purpose of the call to
on line 339 is to copy the header of the bad packet into the ICMP message. The problem is that the header might be too big for the destination buffer. The destination buffer is an
.
is a datatype which is used to store both incoming and outgoing network packets. In this code,
is an incoming packet (containing untrusted data) and
is an outgoing ICMP packet. As we will see shortly,
is a pointer into
.
is allocated on line 294 or line 296:
<span class="cf">if</span> (MHLEN > (<span class="kw">sizeof</span>(<span class="kw">struct</span> ip) + ICMP_MINLEN + icmplen))
m = m_gethdr(M_DONTWAIT, MT_HEADER); <span class="co">/* MAC-OK */</span>
<span class="cf">else</span>
m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
Slightly further down, on line 314,
is used to get
‘s data pointer:
icp = mtod(m, <span class="kw">struct</span> icmp *);
is just macro, so this line of code does not check that the
is large enough to hold an
struct. Furthermore, the data is not copied to
, but to
, which is at an offset of +8 bytes from
.
I do not have the necessary tools to be able to step through the XNU kernel in a debugger, so I am actually a little unsure about the exact allocation size of the
. Based on what I see in the source code, I think that
creates an
that can hold 88 bytes, but I am less sure about
. Based on practical experiments, I have found that a buffer overflow is triggered when
.
At this time, I will not say any more about how the exploit works. I want to give Apple users a chance to upgrade their devices first. However, in the relatively near future I will publish the source code for the exploit PoC in our SecurityExploits repository.
Finding the vulnerability with QL
I found this vulnerability by doing variant analysis on the bug that caused the buffer overflow vulnerability in the packet-mangler. That vulnerability was caused by a call to
with a user-controlled size argument. So I wrote a simple query to look for similar bugs:
**
* @name mbuf copydata with tainted size
* @description Calling m_copydata with an untrusted size argument
* could cause a buffer overflow.
* @kind path-problem
* @problem.severity warning
* @id apple-xnu/cpp/mbuf-copydata-with-tainted-size
*/
import cpp
import semmle.code.cpp.dataflow.TaintTracking
import DataFlow::PathGraph
class Config extends TaintTracking::Configuration {
Config() { this = "tcphdr_flow" }
override predicate isSource(DataFlow::Node source) {
source.asExpr().(FunctionCall).getTarget().getName() = "m_mtod"
}
override predicate isSink(DataFlow::Node sink) {
exists (FunctionCall call
| call.getArgument(2) = sink.asExpr() and
call.getTarget().getName().matches("%copydata"))
}
}
from Config cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink, source, sink, "m_copydata with tainted size."
This is a simple taint-tracking query which looks for dataflow from
to the size of argument of a «copydata» function. The function named
returns the data pointer of an mbuf, so it is quite likely that it will return untrusted data. It is what the
macro expands to. Obviously
is just one of many sources of untrusted data in the XNU kernel, but I have not included any other sources to keep the query as simple as possible. This query returns 9 results, the first of which is the vulnerability in
. I believe the other 8 results are false positives, but the code is sufficiently complicated that I do consider them to be bad query results.
Try QL on XNU
Unlike most other open source projects, XNU is not available to query on LGTM. This is because LGTM uses Linux workers to build projects, but XNU can only be built on a Mac. Even on a Mac, XNU is highly non-trivial to build. I would not have been able to do it if I had not found this incredibly useful blog post by Jeremy Andrus. Using Jeremy Andrus’s instructions and scripts, I have manually built snapshots for the three most recent published versions of XNU. You can download the snapshots from these links: 10.13.4, 10.13.5, 10.13.6. Unfortunately, Apple have not yet released the source code for 10.14 (Mojave / iOS 12), so I cannot create a QL snapshot for running queries against it yet. To run queries on these QL snapshots, you will need to download QL for Eclipse. Instructions on how to use QL for Eclipse can be found here.
Timeline
- 2018-08-09: Privately disclosed to product-security@apple.com. Proof-of-concept exploit included.
- 2018-08-09: Report acknowledged by product-security@apple.com.
- 2018-08-20: product-security@apple.com asked me to send them the exact macOS version number and a panic log.
- 2018-08-20: Returned the requested information to product-security@apple.com. Also sent them a slightly improved version of the exploit PoC.
- 2018-08-22: product-security@apple.com confirmed that the issue is fixed in the betas of macOS Mojave and iOS 12. However, they also said that they are «investigating addressing this issue on additional platforms» and that they will not disclose the issue until November 2018.
- 2018-09-17: iOS 12 released by Apple. The vulnerability was fixed.
- 2018-09-24: macOS Mojave released by Apple. The vulnerability was fixed.
- 2018-10-30: Vulnerabilities disclosed.
Credits
- «I am Error». Screenshot from Zelda II: The Adventure of Link. The screenshot copyright is believed to belong to Nintendo. Image downloaded from wikipedia.
- «Send it back». By Edward Backhouse.