CVE-2018-4087 PoC: Escaping the sandbox by misleading bluetoothd
Following my previous blog post titled “New Crucial Vulnerabilities in Apple’s bluetoothd daemon”, I am releasing the vulnerability PoC.
The PoC is released for educational purposes and evaluation by IT Administrators and Pentesters alike, and should not be used in any unintended way. Furthermore, this PoC and any other related material, is published only after responsibly disclosing it to Apple and Apple has fixed the issues.
As part of my work in the zLabs platform research team at Zimperium, I investigated the iOS mach message IPC from within the default app sandbox with the goal of ultimately gaining escalation to escape the sandbox.
To start the project, I mapped which mach ports are accessible from within the sandbox. I used sbtool by Jonathan Levin (@Morpheus______) from his *OS Internals Volume III book.
With this blog post, I intend to reveal the full PoC of the vulnerability for your own study.
Both of the vulnerabilities were addressed in the latest OS releases by Apple: iOS – 11.2.5, watchOS – 4.2.2, tvOS – 11.2.5. Apple assigned 2 CVEs for each of the vulnerabilities:
- CVE-2018-4087: Rani Idan (@raniXCH) of Zimperium zLabs Team
- CVE-2018-4095: Rani Idan (@raniXCH) of Zimperium zLabs Team
- https://support.apple.com/en-il/HT208462 – tvOS 11.2.5
- https://support.apple.com/en-il/HT208463 – iOS 11.2.5
- https://support.apple.com/en-il/HT208464 – watchOS 4.2.2
The PoC source code is available here
Different sandboxed processes can communicate with different daemons such as mediaserverd, bluetoothd, and others using IPC to use the daemon features. In our case, we will focus on mach message communication with bluetoothd.
bluetoothd is initiating “com.apple.server.bluetooth” port and queueing received mach messages on that port.
Mach message is a form of IPC in *OS; its usage is not well encouraged or documented by Apple in order to make space for higher IPC frameworks.
Now, the function apple_bluetoothd_mig_server will receive every mach message being sent to com.apple.server.bluetooth and handling it by the mach message id.
In our case and to simplify the process, a sandboxed process is asking launchd for a mach port to a service that registered its port with bootstrap_check_in. Afterwards, the process can communicate with the service using the mach port it retrieved from launchd.
Let’s have a look at apple_bluetoothd_mig_server:
You can see the function handling the mach message is subtracting a value of 0xFA300 from the msgh_id of the message sent to bluetoothd and then gets the matching callback to finally call it with the input message.
Moreover, you can see that the function checks if the message id is below or equal 0x83 – that means we have 0x84 available callbacks.
Because the binary is without symbols, I developed a small tool to parse that struct using more information that I am gaining from different library images. This made it possible to create the full list of the available callbacks.
* The “machUnderfined_handler” functions are undefined because the image I used here is iPod touch and some of the features do not exist.
In our case, we will focus on mach__BTLocalDeviceAddCallbacks; this callback is the handler for message with the mach message id 3.
The function mach__BTLocalDeviceAddCallbacks_3 is checking if the mach message is the size of 0x48 and it is not a complex mach message.
Afterwards, it will try to add the callback to the matching session using the session_token.
When a legitimate client is creating a session with bluetoothd, it will create a session token to bluetoothd, and using that token the client is identified by bluetoothd.
Here is where the plot thickens – what is this session token? Wrongfully, Apple used that session_token as the port name between the client and bluetoothd. It is exactly the same port (literally the port name) used for the communication.
That is a huge problem because mach port has specific struct that makes it really easy to bruteforce. The session_token is of type mach_port_t.
In my PoC, I received a port from launchd to bluetoothd in order to communicate directly with bluetoothd. Using that port, I bruteforced the session_token (mach port struct) and could finally register new callbacks to bluetoothd clients by hijacking the session between bluetoothd and its clients.
The attack process:
- bluetoothd’s clients are attaching to it and getting a session token that the client will need to use on the mach communication in order to identify itself to bluetoothd.
- The malicious app (sandboxed app) can bruteforce that session token because the session token consists of the communication mach port and is made of mach_port_t struct.
- After successfully bruteforcing the token, the malicious app can register a new callback on the client process that will be invoked when messages are sent to the client.
It is important to keep in mind that it means a malicious app running from a sandboxed context managed to add callback on clients of bluetoothd which have different sandbox contexts.
List of all bluetoothd clients whom I managed to hijack (bluetoothd is also registered as client):
What’s next, How can it be exploited?
This vulnerability can be used to leak mach ports of every client and it will reveal enormous attack surface on each of the clients!
Leaking the mach ports of the clients can be done by jumping to a suitable gadget and sending the port back to the sandboxed app.
I reported the issue to Apple, Indeed, they fixed the issue however I think the solution could be designed better.
The fix still does not make sure that the session will not be hijacked. Apple changed the session token from being the actual mach port to a randomized token.
10/11/2017 – First bug discovered
14/11/2017 – Shared the bugs with Apple
05/12/2017 – Apple confirmed the bugs
25/01/2018 – Apple distributed patches
I would like to thank Apple for their professional response, Nikias Bassen (@pimskeks) and the rest of Zimperium team