Paint it Blue: Attacking the Bluetooth stack
Mehdi Talbi (Security Researcher · Synacktiv), Etienne Helluy-Lafont (Synacktiv)
Hexacon 2025 · Day 2 · Main Stage
Overview
This talk, "Paint it Blue: Attacking the Bluetooth stack," presented by Mehdi Talbi and Etienne Helluy-Lafont from Synacktiv, details a sophisticated exploitation chain targeting a critical heap overflow vulnerability within Android's Bluetooth stack, specifically the Fluoride implementation. The researchers demonstrate how a seemingly minor integer overflow in the Generic Attribute Profile (GATT) server could be leveraged to achieve remote code execution (RCE) on Android devices running both Gmal (Google's standard malloc) and Scudo (a hardened allocator).

Key moments
- 0:00 Introduction: Why Bluetooth is an attacker's target
- 1:30 Overview of the Android Bluetooth stack architecture
- 3:00 Custom framework for Bluetooth research and exploitation
- 6:00 Bypassing authentication with 'Just Work' method
- 8:00 Introduction to the GATT server integer overflow bug
- 8:40 How GATT server handles Read Multiple Variable Request
- 9:40 Root cause: 16-bit unsigned integer overflow
Paint it Blue: Attacking the Bluetooth stack
Speakers: Mehdi Talbi, Security Researcher, Synacktiv; Etienne Helluy-Lafont, Security Researcher, Synacktiv
Conference: Hexacon
YouTube: https://www.youtube.com/watch?v=wYulofbUDqY
Overview
This talk, "Paint it Blue: Attacking the Bluetooth stack," presented by Mehdi Talbi and Etienne Helluy-Lafont from Synacktiv, details a sophisticated exploitation chain targeting a critical heap overflow vulnerability within Android's Bluetooth stack, specifically the Fluoride implementation. The researchers demonstrate how a seemingly minor integer overflow in the Generic Attribute Profile (GATT) server could be leveraged to achieve remote code execution (RCE) on Android devices running both Gmal (Google's standard malloc) and Scudo (a hardened allocator).
The significance of this research lies in its demonstration of a proximity zero-click attack surface. Bluetooth, often left enabled on mobile devices, presents an attractive target due to its inherent "always-on" nature and the potential for attacks requiring no user interaction. The speakers not only dissect the vulnerability but also meticulously outline novel heap shaping and exploitation primitives developed specifically for the challenging environment of the Android Bluetooth stack, culminating in a remote shell.
Background
▶ Watch: Introduction: Why Bluetooth is an attacker's target (0:00)
Bluetooth has long been a fertile ground for security researchers and attackers alike, primarily due to its ubiquitous presence on mobile devices, its complex protocol stack, and its "always-on" operational mode. This combination creates a persistent, proximity-based attack surface that is particularly appealing for zero-click exploits. Historically, Bluetooth stacks have been a source of significant vulnerabilities, with notable examples like BlueBorne demonstrating the potential for widespread compromise.
The speakers acknowledge prior work, specifically an "excellent presentation by Android red teams earlier this year" at Offensive Con, which highlighted multiple memory corruptions in the Fluoride Bluetooth stack. Fluoride is Android's user-space implementation of the Bluetooth host stack, distinct from kernel-level implementations found in Linux (BlueZ) or proprietary stacks in Windows and iOS. Running as a user service, Fluoride's vulnerabilities can still lead to severe compromise due to its privileged access and the sensitive data it handles. The challenge, however, lies in transforming memory corruption bugs into reliable code execution, especially given the ephemeral nature of many Bluetooth memory allocations. The Synacktiv team's research builds upon these prior findings, showcasing a full exploit development process.
A crucial aspect of Bluetooth security is authentication. While many Bluetooth services require authentication or pairing, the specification includes various methods with differing security assurances. The talk highlights the "just work" authentication method, which, despite its name, offers minimal security and requires no user interaction or PIN verification. This method, often used for convenience with devices like Bluetooth headsets, significantly expands the attack surface by making many services, typically protected by more robust authentication, accessible to an attacker. This bypass is critical for reaching the deeper code paths necessary for exploitation.
To conduct their research, the Synacktiv team developed a custom framework based on the BlueBorne exploit code, leveraging Scapy to construct and parse Python HCI frames. They reimplemented significant portions of the Bluetooth host stack, including L2CAP and ERTM channels, to enable precise control over Bluetooth interactions, which is essential for crafting the specific packets needed to trigger and exploit vulnerabilities.
Key Findings
▶ Watch: Custom framework for Bluetooth research and exploitation (3:00)
The core of this research revolves around a critical integer overflow vulnerability, fixed in 2023, within the GATT server of Android's Fluoride Bluetooth stack. This vulnerability, accessible without authentication or user notification via the "just work" method, leads to a massive heap overflow.
The main discoveries and contributions are:
- Authentication Bypass via "Just Work": The ability to authenticate to most Bluetooth services using the "just work" method, bypassing user interaction and expanding the attack surface to include services typically requiring stronger authentication. This allowed access to code paths that would otherwise be unreachable.
- Integer Overflow in GATT Server's
read_multi_var: A specific integer overflow in theread_multi_varcommand's reply construction logic. This bug occurs because the calculation for cropping an attribute value to fit the reply buffer (length = attr_val_len - (total_len - reply_buf_cap)) fails to account for the two bytes added for encoding the length field. This oversight allowslengthto become a negative value (e.g., -1 or -2), which, when cast to an unsigned 16-bit integer, results in a huge value (approximately 64 kilobytes). - Massive Heap Overflow Primitive: The integer overflow translates into an almost arbitrary
memcpyof roughly 64 KB. While the source buffer (attribute value buffer) is small and not fully controlled (around 600 bytes), the sheer size of the copy allows for overwriting a significant portion of the heap in both directions relative to the destination buffer (the reply buffer), whose size is largely controllable via the Maximum Transmission Unit (MTU). - Novel Heap Shaping Primitives:
- ACL Congestion for Heap Spraying: The researchers discovered a method to force persistent packet allocations by abusing ACL congestion. By using a vendor-specific HCI command (on Cypress Bluetooth controllers) to advertise congestion, Fluoride accumulates replies in an internal queue rather than sending them, effectively allowing an attacker to spray arbitrary data onto the heap using crafted L2CAP configuration rejection packets. This technique provides an "infinite amount" of spray on the signaling channel.
- ERTM Mode for Precise Allocation Control: The Enhanced Retransmission Mode (ERTM) of L2CAP was leveraged for more precise, persistent allocations. By sending ERTM fragments with unexpected sequence numbers, fragments are kept in memory awaiting missing pieces, providing controlled-size and controlled-data allocations with precise lifetime management. Similarly, by not acknowledging fragments sent by Fluoride, they remain in memory.
- Relative Read/Write Primitives: Building upon the heap overflow and shaping techniques, the team developed robust relative read and write primitives using manipulated ERTM fragments and the
bth_hdr_tpacket structure. - Code Execution on GMAL and Scudo: The successful development of a full exploit chain leading to remote code execution on Android devices using both the standard Gmal allocator and the hardened Scudo allocator. This involved bypassing Scudo's memory shuffling and checksum verification mechanisms.
- Persistent Bluetooth Shell: The ultimate achievement was a persistent remote shell over Bluetooth, patching key functions in the Bluetooth stack to maintain connectivity and execute commands.
These findings collectively demonstrate a sophisticated understanding of the Android Bluetooth stack's internals and the practical challenges of exploiting memory corruption vulnerabilities on modern, hardened Android systems.
Technical Deep Dive
▶ Watch: Bypassing authentication with 'Just Work' method (6:00)
The exploitation chain begins with the integer overflow in the GATT server's read_multi_var command. This command allows a client to request multiple attribute values by providing a list of 16-bit handles. The server then constructs a reply containing the values, each prefixed by its 16-bit length.
The Integer Overflow (CVE-2023-XXXX):
The vulnerability lies in the function responsible for building this reply. Variables for indices and lengths are declared as unsigned 16-bit integers. The reply buffer's size is primarily determined by the MTU configured by the client. Within a loop that appends attribute values, the code calculates the total_space required, adding two bytes for the length prefix of the current attribute. It then attempts to crop the attribute value if total_space exceeds the reply_buffer_capacity. The crucial flawed calculation is:
length = attr_val_len - (total_len - reply_buf_cap)
The speakers explain that this calculation "does not account for the two bytes that were added for encoding the lens." By carefully crafting the query and MTU, an attacker can manipulate total_len - reply_buf_cap such that attr_val_len - (computed_value) results in a negative number (e.g., -1 or -2). When this negative value is implicitly cast to an unsigned 16-bit integer, it wraps around to a very large positive number, approximately 64 KB (0xFFFE or 0xFFFF). This leads to a memcpy operation attempting to copy 64 KB of data.
The source buffer for this memcpy is the attribute value buffer, which is allocated on the heap and typically around 600 bytes. While its contents are not fully controlled, the destination buffer is the reply buffer, whose size is controlled by the MTU. The massive 64 KB copy results in a heap overflow that can overwrite large regions of memory in both directions from the destination buffer.
Heap Shaping Primitives:
- ACL Congestion for Heap Spraying:
- The
bth_hdr_tstructure is fundamental, used for all Bluetooth packets in Fluoride. It contains a fixed-size header, alenfield for the data array size, and anoffsetfield. Crucially, it has no pointers, making it easy to forge valid structures even without an initial information leak. - The challenge: Most interesting packet allocations in Fluoride are short-lived.
- The solution: Exploit ACL congestion, a flow control feature at the ACL layer. Normally, the Bluetooth controller toggles this state to prevent buffer exhaustion. However, on Cypress Bluetooth controllers (used in many Android devices), a vendor-specific HCI command allows the host to toggle this state.
- Attack flow:
- Attacker advertises that it cannot receive packets (congestion state).
- Attacker sends many Bluetooth commands that trigger Fluoride to allocate replies.
- Fluoride, unable to send replies, accumulates them in an internal queue.
- This creates persistent allocations on the heap, allowing for arbitrary data spraying.
- The
L2CAP Configuration Requestwith invalid options is used to triggerConfig Rejectionreplies. These replies contain the rejected option, allowing for controlled data and size in the sprayed allocations. The signaling channel has no queue limit, enabling "infinite" spraying.
- ERTM Mode for Precise Allocation Control:
- Enhanced Retransmission Mode (ERTM), built on top of L2CAP, adds reliability features like sequence numbering and retransmissions.
- Persistent allocation (attacker-to-target): Sending an ERTM fragment with an unexpected sequence number (e.g., sequence 1 without sequence 0) causes Fluoride to hold it in memory until the missing fragment arrives. This provides persistent allocation of controlled size and data.
- Persistent allocation (target-to-attacker): Forcing Fluoride to send an ERTM fragment and then not acknowledging it causes the fragment to remain in Fluoride's memory queue indefinitely. This fragment can be re-requested later.
- Limitations: Only up to 10 messages can be allocated per L2CAP channel, and it requires authenticated channels (made possible by the "just work" bypass).
Relative Read and Write Primitives:
- Relative Read:
- Force Fluoride to send an ERTM fragment (e.g., by querying
AVCTP browsing channel's get_folder_itemsfor song metadata, which can be crafted to control fragment size) and do not acknowledge it. This fragment remains in memory. - Trigger the 64 KB heap overflow to corrupt this ERTM fragment's
bth_hdr_tstructure, specifically itslenandoffsetfields. - Set
lento a large value (up to 64 KB) andoffsetto point further into the heap. - Request retransmission of the corrupted fragment. Fluoride reads from the manipulated
offsetandlen, effectively leaking heap data.
- Relative Write:
- Send an incomplete ERTM fragment from the attacker to Fluoride. This fragment remains in memory awaiting the next part.
- Trigger the 64 KB heap overflow to corrupt this stored fragment's
bth_hdr_tstructure, manipulating itslenandoffsetfields to point to the desired write location and size. - Send the following fragment. Fluoride will append this data to the location specified by the corrupted
offsetandlen, achieving an arbitrary write.
Target Object for RCE:
The chosen target for RCE is the SDP Discovery Callback object.
- Why: It's a
libchromecallback object, used extensively in Fluoride, containing multiple function pointers. Its lifecycle is controllable (allocated by establishing an AVCTP connection, executed by closing the SDP connection). It also contains heap pointers as arguments, useful for ASLR bypass. - Goal: Leak its contents, rewrite its function pointers, and trigger its execution.
Code Execution on GMAL:
- Heap Layout:
- Source Shaping: Enable ACL congestion, spray
Config Rejectionmessages (attacker-controlled data). Interleave ERTM allocations to create holes. Disable congestion (frees spray, but data remains). Free ERTM allocations (holes for GATT attributes). - Destination Shaping: Enable ACL congestion, spray
Config Rejectionmessages. Create ERTM placeholders for reader/writer objects and the vulnerable object. Close channels to free placeholders, then immediately allocate reader/writer objects (using ERTM) and then the callback object.
- Exploitation Steps:
- Trigger the overflow to corrupt the reader/writer objects.
- Allocate the
SDP Discovery Callback. - Use the corrupted read primitive to leak the callback's contents, obtaining heap addresses for ASLR bypass.
- Use the corrupted write primitive to overwrite the callback's function pointer.
- Gadget Function: The
SDP Discovery Callbackhas limited argument control (only the third argument). A "gadget function" was found that allows calling an arbitrary function with up to five controlled arguments, enabling calls likemprotect. - List Clear Function: To execute multiple functions (e.g.,
mprotectthen jump to shellcode), alist_clearfunction was used. This function takes alist objectas input, which contains two function pointers called when releasing nodes. By crafting a fakelist objectand injecting it into memory (using a large 10 KB object reference in theSDP Callbackas a known heap address), multiple functions can be chained. - The final payload: Overwrite callback function pointer with
gadget_function->gadget_functioncallslist_clearwith fake list object ->list_clearcallsmprotectand then jumps to shellcode.
Code Execution on Scudo:
Scudo, a hardened allocator, presents additional challenges:
- Memory Regions: Scudo organizes memory into regions, each for a specific class size, sandwiched between guard pages.
- Metadata Checksums: Each memory block is prefixed with 16 bytes of metadata, including a checksum. Corrupting this checksum aborts the program. Solution: Overflow only free chunks or persistent, controlled allocations.
- Memory Shuffling: Scudo shuffles memory blocks within transfer batches (groups of pre-allocated blocks) to randomize allocation addresses. This prevents reliable fixed-offset allocations, breaking the relative write primitive that expects the callback at a fixed offset.
- Scudo Bypass:
- Separate target and vulnerable object allocations by inserting an allocation in between, hoping to land them in different shuffled batches.
- Trigger the overflow twice:
- First overflow: Overwrite a reader object to get a memory leak.
- Second overflow: Overwrite a callback object to get code execution, hoping to survive the 64 KB heap damage.
- Heap Spray (Scudo): Similar to GMAL, but the source of the overflow (the GATT attribute) must be followed by many
Config Rejectionmessages to reach the target object with controlled data, bypassing shuffling. - Leak Callback: The
SDP Discovery Callbackwas not consistently present in leaks on Scudo. Instead, theCapture Callbackwas found to be always present, providing a heap reference and a function pointer, enabling ASLR bypass. - Execution Callback: The
SDP Discovery Callbackis still used for execution because its lifecycle is precisely controllable. Due to shuffling, not all fields can be reliably overwritten, but thanks to the "gadget function," only two pointers need to be overwritten to divert control flow. The rest of the payload is identical to GMAL.
Post-Exploitation:
Once code execution is achieved, the goal is a persistent shell over Bluetooth. The researchers implemented a basic protocol over Bluetooth to run shell commands and send files. This involved patching functions in the Bluetooth stack that handle receiving frames and registering a signal handler to keep the Bluetooth process alive after its initial crash from the exploit.
Demo / Proof of Concept
▶ Watch: How GATT server handles Read Multiple Variable Request (8:40)
The speakers presented a demonstration of their exploit. The setup involved two Bluetooth controllers. One controller was used to enable ACL congestion, while the second controller maintained interaction with the target device. This dual-controller setup was necessary to overcome limitations on the number of ERTM channels that could be connected simultaneously.
While the live demonstration of the actual exploit (the second video) experienced a technical glitch where it did not start on screen during the presentation, the researchers confirmed that the exploit worked during their rehearsals. The intention was to show the successful execution of their remote shell on both a Xiaomi device running Gmal and a Samsung device running Scudo. The demo aimed to illustrate the full exploitation chain, from initial connection to code execution and interaction with the target device via the custom Bluetooth shell.
Defensive Implications
▶ Watch: Root cause: 16-bit unsigned integer overflow (9:40)
The detailed exploitation presented in "Paint it Blue" highlights several crucial defensive implications for Android device manufacturers, Bluetooth stack developers, and security practitioners:
- Memory Safety is Paramount: The root cause – an integer overflow leading to a massive
memcpy– underscores that even subtle integer arithmetic errors in C/C++ code can have catastrophic security consequences, especially in critical, privileged components like the Bluetooth stack. Adopting memory-safe languages (like Rust, as seen with Gabaldor) for new development or re-writing of existing components is a strong mitigation strategy. - Rethink "Just Work" Authentication: The abuse of the "just work" authentication method to expand the attack surface is a significant finding. While convenient for users, this method effectively bypasses security measures for many services. Device manufacturers should re-evaluate which services are truly safe to expose via "just work" and consider stricter default authentication policies, or at least provide clear user warnings and options to disable less secure pairing methods.
- Allocator Hardening is Not a Panacea: While Scudo provides significant hardening against common heap exploitation techniques (like deterministic offsets and simple overflows), this research demonstrates that sophisticated attackers can still bypass its protections (e.g., memory shuffling, checksums) with enough effort and novel primitives. This means security relies on a defense-in-depth approach, not just allocator-level mitigations. Continuous research into allocator bypasses and further hardening is essential.
- Proximity Attack Surface Requires Vigilance: Bluetooth remains an attractive vector for zero-click proximity attacks. Users are often unaware of the risks of keeping Bluetooth enabled in public spaces. Device manufacturers should consider implementing more robust sandboxing for the Bluetooth daemon, stricter SELinux policies, and potentially runtime application self-protection (RASP) mechanisms to detect and prevent exploitation attempts. Regular security audits specifically targeting Bluetooth stack implementations are critical.
- Robust Error Handling and Input Validation: The integer overflow stemmed from insufficient error handling and input validation in the GATT server's reply construction logic. Developers must rigorously validate all inputs, especially lengths and offsets, and ensure that arithmetic operations involving sizes and capacities are carefully checked for potential overflows or underflows across different integer types.
- Continuous Monitoring and Patching: The vulnerability was fixed in 2023, emphasizing the need for prompt patching and updates. Users should keep their devices updated, and manufacturers must prioritize rapid deployment of security patches for critical components like Bluetooth.
- Transition to Memory-Safe Languages: The mention of Gabaldor, Android's new Bluetooth stack being rewritten in Rust, is a positive development. This transition aims to eliminate entire classes of memory safety vulnerabilities. However, as the talk notes, the transition is gradual, and legacy C components will remain a target for some time. Developers should accelerate this transition where possible.
Key Takeaways
- The Android Fluoride Bluetooth stack contained a critical integer overflow in its GATT server (
read_multi_varcommand), fixed in 2023. - This bug allowed for a 64 KB heap overflow accessible via a zero-click, proximity attack without user interaction, leveraging the "just work" Bluetooth authentication method.
- Novel heap shaping primitives, including abusing ACL congestion for heap spraying and ERTM mode for precise allocations, were developed to achieve reliable exploitation.
- The researchers built relative read and write primitives by manipulating the
bth_hdr_tpacket structure and ERTM fragments. - Full remote code execution was achieved on Android devices running both Gmal and Scudo, demonstrating bypasses for Scudo's memory shuffling and checksum protections.
- The exploit culminates in a persistent Bluetooth shell on the target device, taking approximately 2 minutes on Gmal and up to 5 minutes on Scudo.
- Despite the ongoing rewrite of the Bluetooth stack in Rust (Gabaldor), the vulnerability initially remained relevant as the affected GATT server components were still in C at the time of the talk.
About the Speaker(s)
Mehdi Talbi and Etienne Helluy-Lafont are both security researchers at Synacktiv. Their work focuses on in-depth security analysis and vulnerability research, as demonstrated by their detailed investigation into the Android Bluetooth stack. Their presentation at Hexacon showcases their expertise in identifying complex memory corruption vulnerabilities, developing sophisticated exploitation primitives, and achieving remote code execution on hardened mobile platforms.
Reviews
Dr. Zero (Offensive Security Researcher) — MUST SEE
Talbi and Helluy-Lafont deliver the real thing: a complete, novel exploitation chain against Android's Fluoride Bluetooth stack that goes from integer overflow to authenticated zero-click RCE with a persistent shell, all without user interaction, on both standard and hardened allocator targets. This isn't a bug report dressed up as a talk — it's a masterclass in heap exploitation tradecraft on a constrained, privileged attack surface that most researchers treat as a black box. The dual allocator bypass (Gmal and Scudo), the ACL congestion spray primitive, and the ERTM-based read/write gadgets are all original contributions that will feed the field for years. This is exactly what a premier…
Heather Calloway (CISO) — WEAK
Technically impressive exploit research demonstrating a zero-click proximity RCE on Android's Bluetooth stack — a patched but instructive vulnerability. The researchers built novel heap shaping primitives and bypassed hardened allocators, which is genuinely difficult work. But as a contribution to defender operations, governance, or security leadership, it delivers almost nothing. The defensive implications section reads like it was appended to justify the talk's relevance and never earns that relevance. This is expert-level offensive research aimed at an audience of exploit developers, not the people responsible for protecting organizations or making platform security decisions.