Apple recently patched a newly discovered vulnerability in May with iOS 13.5, which could potentially allow an attacker to gain unfettered remote access to Apple iPhones and other iOS devices. The exploit, which targeted Apple Wireless Direct Link (AWDL) technology, is noteworthy not just because of its severity, but also because of the lessons to be drawn from the Apple vulnerability for any organization releasing a mobile operating system (OS) or Internet of Things (IoT)-based product, as well as for those organizations relying on regular updates and mobile device management to keep data on mobile devices secure.
For those unfamiliar, AWDL is a proprietary wireless mesh networking protocol, the same one used by AirDrop and AirPlay. The protocol is designed to allow Apple devices like iPhones, iPads, Macs, and Apple Watches to form ad-hoc peer-to-peer mesh networks.
The AWDL vulnerability was first reported by Ian Beer of Google’s Project Zero. The entire exploitation process is detailed in a 30,000-word post which you can find at https://googleprojectzero.blogspot.com/2020/12/an-ios-zero-click-radio-proximity.html.
Discovery: Deep-dive into the AWDL codebase
Interestingly, there were several clues which pointed towards the possibility that AWDL was susceptible to buffer overflow attacks, suggesting the initial entry point for the attack.
In the case of iOS 13, the full remote control AWDL vulnerability was first identified by leveraging buffer overflow using the memmove function without adequate buffer checks in place. It was the lack of these checks that allowed overflow of the kernel cache.
Many security professionals will wonder how it was possible to comb through millions of lines of proprietary AWDL source code with known reverse engineering tools. We found that the beta release of iOS 12.0b1 in 2018 had the integration of AWDL and did not strip the function name, class names, symbols and most importantly the function calls. This lack of obfuscation provides security researchers more context of the code base.
From this, we also observed that information coming from untrusted sources was not properly validated when raw AWDL frame data is parsed. This lack of validation can enable an adversary to send large bytes of data, resulting in an out-of-bounds memmove.
As an example, there are two C++ classes within the AWDL codebase:
- IO80211AWDLPeer and IO80211AWDLPeerManager. There is one IO80211AWDLPeer object for each AWDL peer which a device has recently received a frame from. There is also a single instance of the IO80211AWDLPeerManager which is responsible for orchestrating interactions between this device and other peers.
- IO80211AWDLPeerManager::actionFrameInput is the point where untrusted raw AWDL frame data starts being parsed. Each received frame ends up at this function, wrapped in an mbuf structure, which stores network packets. IO80211AWDLPeerManager::actionFrameInput first updates some timestamps, then reads various fields from type-length-values (TLVs) in the frame using the IO80211AWDLPeerManager::getTlvPtrForType method to extract them directly from the mbuf.
After this initial parsing comes the main loop which takes each TLV in turn and parses it. IO80211AWDLPeerManager::getTlvPtrForType works by passing each TLV to IO80211AWDLPeer::tlvCheckBounds.
This method checks against a hardcoded list of specific minimum and maximum TLV lengths for some of the supported TLV types. Only 60 bytes are allocated for the destination buffer in the IO80211AWDLPeer structure. However, types not listed in IO80211AWDLPeer::tlvCheckBounds get a maximum length of 1024 bytes.
Using a kernel debugger, it is possible to modify the type parameter for TLV. For the remote control AWDL vulnerability’s proof of concept, the researcher sent 0x14, which is not explicitly defined in the hardcoded list, and results in the default upper length limit of 1024. This is significantly larger than the allocated 60-byte buffer. Based on the given type 0x14, a switch statement then results in the system parsing the TLV using IO80211AWDLPeer::parseAwdlSyncTreeTLV.

sync_tree_macs is a 60-byte inline array in the IO80211AWDLPeer structure, which can store up to 10 MAC addresses. By using type 0x14, tlvCheckBounds will enforce a maximum value of 1024 for the length of the SyncTree TLV.
The TLV parser will round that value down to the nearest multiple of 6 and copy that number of bytes into the sync_tree_macs array. This became the researchers’ memory corruption primitive: a linear heap buffer overflow in 6-byte chunks which can corrupt all the fields in the IO80211AWDLPeer object. This allows IO80211AWDLPeer objects to be allocated next to each other by sending AWDL frames from a large number of different spoofed source MAC addresses in quick succession. This foundational knowledge of the AWDL codebase forged the path to the final exploit.
A proof of concept: To start getting data in the air and to reach this entry point, Beer used off-the-shelf Wi-Fi adaptors, along with a Raspberry Pi 4B that was capable of using monitor mode and performing frame injection. This supports the monitoring of network packets and the sending of raw network packets, respectively. Libpcap was used to inject raw 802.11 frames, as detailed at https://gist.github.com/jonhoo/7780260.
Watch the end-to-end demo of the ‘Zero-Click Exploit’ at https://www.youtube.com/watch?v=_sTw7GGoJ6g.
Key Takeaways
This technical walkthrough highlights a methodical approach from a single, dedicated individual that goes beyond just fuzzing. In summary, any organization releasing IoT or mobile OS-based product should take the following things into consideration:
- Always have an obfuscated binary which makes it difficult for an adversary to reverse engineer the product.
- In the production build, prevent debuggers from being attached to either an application or device.
- Always perform regression testing for application reverse engineering to better gauge the overall efficacies of the solution used.
- For IoT devices, always ensure that the device is performing integrity checks of the application before running it.
- Adopt a long-term strategy to modernize critical legacy code and focus on improving code quality.
- And finally, organizations considering their mobile security posture should ensure that a has a strict mobile patch management policy is enforced to help protect against newly discovered vulnerabilities.
About the Authors
Maxwell Zhou, Senior Security Engineer, Swapnil Deshmukh, CTO & co-founder, and Ryan McKamie, CEO & co-founder contributed to this publication.