... Loading ...

SSD Secure Disclosure

Disclosing vulnerabilities responsibly since 2007
Dark Theme

SSD Advisory – Linux BlueZ Information Leak and Heap Overflow

(This advisory follows up on a presentation provided during our offensive security event in 2018 in Hong Kong – come join us at TyphoonCon –  June 2019 in Seoul for more offensive security lectures and training)

Vulnerabilities Summary
The following advisory discuss about two vulnerabilities found in Linux BlueZ bluetooth module.

One of the core ideas behind Bluetooth is allowing interoperability
between a wide range of devices from different manufacturers. This is one
of the reasons that the Bluetooth specification is extremely long and complex.

Detailed descriptions of a wide range of protocols that support all common use-cases ensure that different Bluetooth implementations can work together. However, from an attackers point of view this also means that there is a lot of unneeded complexity in the Bluetooth stack which provides a large attack surface. Due to the modular nature of Bluetooth, some critical features such as packet fragmentation are found redundantly in multiple protocols that are part of the Bluetooth core specification. This makes correct implementation very complicated and increases the likelihood of security issues.

Vendor Response
We have contacted the Bluez maintainer on 23/8/2018 and sent a report describing the two vulnerabilities. The vendor responded “I got the message and was able to decrypt it, but frankly I don’t know when I get to look at it at confirm the issue.”. We have sent few more emails to the vendor since the first report and also proposed patches for the vulnerabilities but no fix has been issued until the day of writing this post. Proposed patches have been provided by, Luiz Augusto von Dentz, at the bottom of this advisory.

CVE
CVE-2019-8921
CVE-2019-8922

Credit
An independent security researcher, Julian Rauchberger, has reported this vulnerability to SSD Secure Disclosure program.

Affected systems
Linux systems with BlueZ module with versions 5.17-5.48 (latest at the time of writing this advisory)

Vulnerability Details
To support the huge range of potential use cases for Bluetooth, the specification describes many different protocols. For the vulnerabilities detailed in this advisory, we will focus on two core protocols: L2CAP and SDP.

L2CAP
Simply speaking, L2CAP can be seen as the TCP layer of Bluetooth. It is
responsible for implementing low-level features such as multiplexing and
flow control. What would be called a “port” in TCP is the “Protocol/Service
Multiplexer” (PSM) value in L2CAP. Authentication and Authorization is
generally handled on higher layers, meaning that an attacker can open a
L2CAP connection to any PSM they want and send whatever crafted packets
they wish. From a technical point of view, BlueZ implements L2CAP inside
the kernel as a module.

SDP
SDP is the Service Discovery Protocol. It is implemented above L2CAP as a
“service” running on PSM 0x0001. Since the PSM is only a 16-bit number, it is not possible to assign a unique PSM to every Bluetooth service imaginable. SDP can translate globally unique UUIDs to a dynamic PSM used on a specific device.

For instance, a vendor specific service has the same UUID on all devices but
might run on PSM 0x0123 on device A and PSM 0x0456 on device B. It is the job of SDP to provide this information to devices that wish to connect to the service.

Example
* Device A opens a L2CAP connection to PSM 0x0001 (SDP) on device B
* Device A asks “what is the PSM for the service with UUID 0x12345678?”
* Device B responds with “PSM 0x1337”
* Device A opens an L2CAP connection to PSM 0x1337

SDP is also used to advertise all the Bluetooth Profiles (services/features) a
device supports. It can be queried to send a list of all services running
on the device as well as their attributes (mostly simple key/value pairs).
The SDP protocol is implemented in a userspace daemon by BlueZ. Since it
requires high privileges, this daemon normally runs as root, meaning
vulnerabilities should result in full system compromise in most cases.

PoC’s and Testing Environment
The PoC’s attached at the end of this advisory have been tested against
BlueZ 5.48 (the newest version at the time of writing), BlueZ 5.17 (a very old version from 2014), as well as a few in between.

The PoC’s have been written for Python 2.7 and have two dependencies, please install them first:
* pybluez (to send Bluetooth packets)
* pwntools (for easier crafting of packets and hexdump())

run them with:
python sdp_infoleak_poc.py TARGET=XX:XX:XX:XX:XX:XX
python sdp_heapoverflow_poc.py TARGET=XX:XX:XX:XX:XX:XX

(where XX:XX:XX:XX:XX:XX is the Bluetooth MAC address of the victim device)

Please ensure that the Bluetooth is activated and the device is discoverable (called “visible” in most of the GUIs)

It might be necessary to update the SERVICE_REC_HANDLE and/or SERVICE_ATTR_ID to get the PoC’s to work. These values can differ between devices. They are advertised by SDP so it could be automated to find them but we didn’t implemented that. Detailed information is inside the comments of the PoC’s.

Vulnerability 1: SDP infoleak
Note: All line numbers and filenames referenced here were taken from BlueZ 5.48 which is the newest version at the time of writing.

The vulnerability lies in the handling of a SVC_ATTR_REQ by the SDP implementation of BlueZ. By crafting a malicious CSTATE, it is possible to trick the server into returning more bytes than the buffer actually holds, resulting in leaking arbitrary heap data.

Background
This vulnerability demonstrates very well issues arising due to the
aforementioned complexity caused by the redundant implementation of some features in multiple protocols.

Even though L2CAP already provides sufficient fragmentation features, SDP
defines its own. However, incorrect implementation in BlueZ leads to a
significant information leak.

One of the features of SDP is to provide the values of custom attributes a
service might have. The client sends the ID of an attribute and SDP responds with the corresponding value.

If the response to an attribute request is too large to fit within a single
SDP packet, a “Continuation State” (cstate) is created.

Here is how it should work in theory:

  • client sends an attribute request
  • server sees that the response is too large to fit in the reply
  • server appends arbitrary continuation state data to the response
  • client recognizes this means the response is not complete yet
  • client sends the same request again, this time including the continuation state data sent by the server
  • server responds with the rest of the data

According to the specification, the cstate data can be arbitrary data,
basically whatever the specific implementation wants and the client is
required to send the same request again, including the cstate data sent by
the server.

The implementation of this mechanism in BlueZ is flawed. A malicious client can manipulate the cstate data it sends in the second request. The server does not check this and simply trusts that the data is the same. This leads to an infloleak described in the next section.

Root cause analysis
The root cause can be found in the function service_attr_req on line 633 of
src/sdpd-request.c

The main issue here is in line 727 where BlueZ calculates how many bytes should be sent to the client.

The value of max_rsp_size can be controlled by the attacker but normally the MIN function should ensure that it cannot be larger than than the actual bytes available. The vulnerability is that we can cause an underflow when calculating (pCache->data_size – cstate->cStateValue.maxBytesSent) which causes that value to be extremely high when interpreted as an unsigned integer. MIN will then return whatever we sent as max_rsp_size since it is smaller than the result of the underflow.

pCache->data_size is how large the initially generated response has been.
cstate->cStateValue.maxBytesSent is directly read from the cstate we sent to
the server. So we can set it to any value we want.

If we set maxBytesSent to a value higher than data_size, we trigger an
underflow that allows us to cause MIN() to return our max_rsp_size which lets us set “sent” to any value we want.

The memcpy in line 729 will then copy all that data to the response buffer
which later gets sent to us.

Since “sent” is a signed short, we have two possible ways to exploit this:

  • If we set sent to value <= 0x7FFF it is treated as a positive integer and we will get sent this amount of bytes back.
  • If we set it to 0x8000 or larger it will be treated as a negative value, meaning zero expansion will fill all the most significant bits with 1, resulting in a extremely large copy operation in line 729 that is guaranteed to crash the program.

So this vulnerability can be either used as a infoleak to leak up 0x7FFF bytes or as a Denial of Service that crashes the bluetooth application.

Triggering the vulnerability
To trigger this vulnerability, we first send a legitimate attribute request
to the server.

In our request, we can specify how many bytes we are willing to accept within a single response packet. Since we already know how large the
response will be, we set this so the response will be one byte too large.

This results in the server storing that there is one byte left it hadn’t sent us
yet. The server also sends us a cstate that contains how many bytes it has
already sent us. For simplicity, we call this value the “offset”.

Then we send the same request again, but we increase the “offset” contained in the cstate to create the underflow described above.

For detailed documentation about how the packets we send look exactly, please refer to the comments in the Python PoC file.

Vulnerability 2: SDP Heap Overflow
Like the information leak, this vulnerability lies in the SDP protocol handling of attribute requests as well. By requesting a huge number of attributes at the same time, an attacker can overflow the static buffer provided to hold the response. Normally, it would not be possible to request so many attributes but we will demonstrate a trick that allows us to do so.

Root cause analysis

In the same function service_attr_req of src/sdpd-request.c, in line 745 the
function extract_attrs is called.

This function is used to find the actual values for all the attributes requested by the client. Inside it, we find the following code:

The important part here is that after getting the values of the attributes
with sdp_data_get, they are simply appended to the buffer with sdp_append_to_pdu.
The code of this function can be found in lib/sdp.c

What happens here is that an appropriately sized sdp_buf_t is created and the new data is copied into it. After that, sdp_append_to_buf is called to append this data to the buffer originally passed by extract_attrs.
sdp_append_to_buf can be found in the same file, the relevant part is here:

As we can see, there isn’t any check if there is enough space in the destination buffer. The function simply appends all data passed to it.

To sum everything up, the values of all attributes that are requested will simply be appended to the output buffer. There are no size checks whatsoever, resulting in a simple heap overflow if one can craft a request where the response is large enough to overflow the preallocated buffer.

service_attr_req gets called by process_request (also in src/sdpd-request.c) which also allocates the response buffer.

On line 973, the response buffer gets allocated with size USHRT_MAX meaning it will be 2^16 bytes large.

So in order to overflow this buffer we need to generate a response that is larger than 2^16 bytes. While SDP does not restrict how many attributes we can request within a single packet, we are limited by the outgoing maximum transmission unit L2CAP forces us to use. For SDP, this seems to be hardcoded as 672 bytes.

So the problem in exploiting this vulnerability is that we can only send a very small request but need to generate a large response.

Some attributes are rather long strings, but even by requesting the longest string we found we could not even get close to generating a response large
enough. SDP also has a feature where we can not only request one attribute at a time but also a range of attributes. This requires us to only send the starting and ending IDs. SDP will then return all attributes within that range. Unfortunately, the response generated by this also wasn’t large enough.

Since the limiting factor seemed to be the MTU imposed by L2CAP, after investigating further how this MTU gets set and if we can do anything about it. Normally, we can only specifiy the maximum size of incoming packets (IMTU) but not the size of packets the other side is willing to accept (OMTU).
After looking at the way L2CAP handles the negotiation of these values we found that it is also possible to reject the configuration supplied by the other side. If we reject a configuration parameter, we can supply a suggestion of a better value that we would accept.

If this happens for the OMTU, the BlueZ will simply accept whatever
suggestion it gets sent. This allows us to force the other side to use whatever
OMTU we want. Then we can send much larger SDP attribute requests, containing enough attributes to overflow the heap.

In a simplified way,  this is how the MTU negotiation looks like:

  • attacker: I want to open a L2CAP connection, my MTU is 65536
  • victim: ok, I will send you packets up to 65536 bytes, my MTU is 672, please do not send larger packets (normally, we would be done here)
  • attacker: that MTU is not acceptable for me, I will only open the connection if I can send you packets up to 65536
  • victim: ok, I will allow you to send packets up to 65536 bytes

Unfortunately, Linux does not allow us to reject any MTU values so we modified the kernel on the attacker machine to implement the behavior described above.

Please note that this behavior is not really a security vulnerability in itself.
It does follow the specification which describes that it should be possible to
reject configuration parameters and suggest acceptable ones. Normally it would not be a problem to increase MTU size, it is simply due to the heap overflow that this causes trouble.

Modifying the kernel
Important: only the ATTACKER has to modify their kernel. The victim kernel does not need to be modified otherwise there wasn’t a vulnerability at all.

In our case, we used a Linux 4.13 kernel. Here are the required modifications:
Before compiling the kernel, you need to modify l2cap_parse_conf_req in
net/bluetooth/l2cap_core.c

We added the “else if” that ensures we do not accept the configuration as long as the OMTU isn’t 65535. Additionally we added a printk so we can check that the branch has been triggered correctly by viewing kernel.
Once you compile your modified kernel, you can run the PoC attached to this writeup.

Conclusion
Implementing a complete Bluetooth stack correctly is extremely challenging. There are dozens of different protocols involved which often implement the same features.

This can for instance be seen with the fragmentation in SDP. All this complexity creates a huge attack surface. We have demonstrated that only within a single, commonly used protocol multiple critical issues can be found. It seems highly likely that other parts of BlueZ contain similar vulnerabilities, more research is definitely required to ensure the Linux Bluetooth stack is secure from attacks.

Exploits
Information leak PoC:

If everything happens as expected, we shall get a similar output to this:

Heap overflow poc:

If everything happens as expected, we shall get a similar output to this:

Patches suggested by Luiz Augusto von Dentz

SDP Info leak patch:

SDP Heap Overflow patch:

Print Friendly, PDF & Email

Leave a Reply