Author: Jianjun Dai(@jioundai) of 360 Alpha Lab
Zero-click vulnerabilities have become more and more popular in recent years, and the bounty for full exploit chains has also surged. In 2019, Apple Security Bounty even raised the reward up to one million US dollars for zero-click kernel code execution with persistence and kernel PAC bypass.
In the meanwhile, high prices also mean greater threat the vulnerability may pose and more difficulty to discover. Anyone who has done researches on remote zero-click vulnerabilities knows that it is very tough to get a stable zero-click exploitation as it lacks flexible interface for remote calls and has many unstable factors.
In December 2019, I submitted 5 macOS Bluetooth vulnerabilities to Apple, together with a complete report of zero-click bugs that can remotely take down macOS Bluetooth.
The odd thing is that in the March security update, the vulnerability is numbered CVE-2019-8853 (why it’s 2019?), and macOS Catalina 10.15.3 was left out of the affected versions.
In this article, I will detail two vulnerabilities used in the exploit chain, CVE-2020-3847 and CVE-2020-3848, and how did I get the code execution. However, I will not release the exploit code itself. If you are interested, you can try to reproduce the exploit yourself.
In macOS, the data on the layer L2CAP (Logical Link Control and Adaptation Protocol) is processed by the kernel driver IOBluetoothFamily (the vulnerability I found in the IOBluetoothFamily was in Apple's January acknowledgement). The data on L2CAP, such as SDP, BNEP, and so on, are handled by the user mode process bluetoothd, and the bluetoothd process runs with root privilege.
The two vulnerabilities involved in this exploit are both in the processing code of SDP (Service Discovery Protocol) data frames. This section briefly introduces the SDP frame, as follows:
The first byte PDU field indicates the SDP request or response message. PDU = 2/4/6 indicates SDP Request, and PDU = 0/1/3/5 indicates SDP Response.
The Parameter Length field indicates the length of the payload. You can use wireshark to capture and analyze the packets as follows:
CVE-2020-3847 can cause remote out-of-bounds read, and it exits in the (PDU=4) of function [SDPServerConnection handleServiceAttributeRequest:length:transactionID:]. To trigger the vulnerability, two SDP requets in different states should be sent.
The 1st request:
Line 136 cont_state reads a byte from the transmitted data packet (pdata). The first time we make cont_state = 0, so line 143 is_cont_pkt = false.
Then move onto the following lines:
Line 270, rem_len is an indirectly controllable variable. According to the data in the request packet, query the attributes in the SDP database, Rem_len indicates the length of the query, let’s assumed it to be 0x16. max_list_len is 2-byte data read from the data packet, and it is also a directly controllable variable. We can make rem_len> max_list_len so that the code goes to the else branch.
Line 280, because is_cont_pkt = false, the final code is executed to line 284. rem_len will assign service_attr_result_len to a member variable of the object pSDPServerConn, and pSDPServerConn-> ServiceAttributeResults = malloc (0x16). The pSDPServerConn object is an object generated when an SDP socket connection is established. It is destroyed only when the connection is disconnected.
The 2nd SDP Request:
Send a second SDP request message. Make cont_state = 4, so that is_cont_pkt = true.
Line 153, cont_offset is an unsigned int variable that reads 4 bytes of data from the data packet and is also directly controllable.
Line 154, we make cont_offset> 0x16. Assuming cont_offset = 0x18, an integer overflow occurs and rem_len = uint32_t (-2).
Line 270, rem_len> max_list_len, to enter the else branch.
Line 278, v74 = max_list_len, is also a directly controllable variable, of course, the value must be smaller than MTU (672).
280 lines, because is_cont_pkt = true, the following code will not be executed.
The code runs to the following lines:
Line 324, because cont_offset is controllable, it can cause an out-of-bounds read on pSDPServerConn-> ServiceAttributeResults, and the length v74 is also controllable. And p_rsp_buf will eventually be sent back to the attacker, resulting in information leakage.
CVE-2020-3848 can cause remote memory corruption. It exists in the function [SDPClientConnection handleServiceSearchAttributeResponse:length:transactionID:]. The code is as below:
Line 105, v44 is a byte of data read from the data packet, assuming v44 = 0xff.
Line 116, then v43 = true.
Then look at the following code snippet:
Line 149, * ((unsigned __int16 *) pSDPClientConn + 35)-17 is pSDPClientConn-> req_buf, and req_buf = malloc (0x20) points to a fixed length of memory. Because v44 = 0xff, memcpy caused a heap overflow, and the overflowed data was completely controllable.
When I discovered the above memory corruption vulnerability, I was actually very frustrated because of the code in the function [SDPClientConnection handleServiceSearchAttributeResponse:length:transactionID:
This code is to check whether pSDPClientConn has sent the corresponding Request message. If no request has been sent before, it is considered that an abnormal response message is received and the function is exited, so as not to trigger the above memory corruption vulnerability.
If you have researched the Bluetooth SDP protocol, you should know how great this check is. Many Bluetooth protocols don't do this.
Because of this code, I once considered giving up on this vulnerability. Because to trigger the vulnerability, a Bluetooth pairing connection needs to be established so that macOS can actively send SDP requests. On the surface, it is one-click, but its influence is greatly reduced. This is not the result I wanted.
The experience I have accumulated while researching Android Bluetooth vulnerabilities has helped me. I know that many manufacturers will design unique features on the Bluetooth connection of their own products to achieve some special functions.
So I decided to analyze it again in depth. Finally, hard work pays off. I found a very interesting feature in the [SDPServerConnection handleServiceSearchAttributeRequest: length: transactionID:] function:
Line 173, max_list_len is 2-byte data read from the SDP request message;
Line 212, if max_list_len == 0xFD2D, the performSDPQuery function will be called. We can guess from the function name that it will send an SDP request. My analysis confirmed my previous guess.
In this way, I found a way to trigger a memory corruption vulnerability with zero click. I was thrilled to find this feature!
A more conventional idea to a heap overflow exploitation is to heap feng shui. But when I wrote the exploitation, I encountered the following two difficulties:
The SDP channel could not find a suitable interface to generate a large number of new objects and reside in memory.
Other conventional channels cannot achieve zero-click, such as BNEP, GATT and other protocols. When the connection is established, a popup prompts
Translation of the popup window:
The connection is from: 4
If you want to permit the connection, please press “connect”.
Refuse please press “cancel”.
So I can only complete all the exploits through the SDP channel.
After testing and research, it is found that the same client can establish 30 SDP socket connections with macOS Bluetooth at the same time, so that 30 SDPServerConnection objects can be created.
And other objects will be created in the SDPServerConnection object. In the end, I found that I can get the following relationship diagram:
Thus, 90 available objects can be laid out in memory, so that a simple heap feng shui can be completed. As for the macOS heap management mechanism, it will not be introduced here.
The complete exploit idea is as follows:
Create 30 SDP socket connections to complete the simple heap feng shui.
Use 30 SDPServerConn for information leakage, leak the object SDPClientConn, and obtain the address of SDPClientConn-> req_buf, and the address of SDPClientConn-> result_buf (for the memory layout later, such as fake_obj etc.)
Leak object SDPServerConn, find SDPServerConn objects that meet the following conditions: addr (SDPServerConn-> ServiceAttributeResults) <addr (SDPClientConn-> req_buf); keep a record of: offset = addr (SDPClientConn-> req_buf) --addr (SDPServerConn-> ServiceAttributeResults); sock [i];
Use sock [i] and offset to directly leak the data (<255) after SDPClientCon-> req_buf, and verify whether it is one of the following three objects: SDPServerConn L2CAPChannel L2CAPChannelExpansion
If a known object is successfully laid out after req_buf, a memory corruption vulnerability is triggered, covering obj-> isa, and use Objective-c's exploit techniques to complete code execution.
Otherwise, exit and return to step 1.
During the entire process of triggering and exploiting the vulnerability, the attacker's device must act as both a client and a server, as shown in the following figure:
After researching the Bluetooth protocol of Apple devices, it is known that most protocols cannot complete zero-click Bluetooth socket connection. This article mainly introduces how to find the SDP protocol vulnerabilities, explore the possibility of zero-click, and finally complete the exploitation in such a narrow gap. The article analyzes the vulnerability in detail, introduces some interesting features in the design of the macOS Bluetooth, and take advantage of them to complete the interaction-less vulnerability exploitation, and also shares ideas behind it.
December 1, 2019, submitted 5 vulnerabilities and an exploit report to Apple
December 4, 2019, Apple officially confirmed the vulnerabilities
January 29, 2020, Apple Security Update released 4 patches
March 25, 2020, Apple Security Update released the fifth patch