L2CAP

HEADER (4 Bytes)                PAYLOAD
+-----------+-----------+-----------------------------------+
|           |           |                                   |
|  LENGTH   |    CID    |          INFORMATION              |
| (2 Bytes) | (2 Bytes) |           PAYLOAD                 |
|           |           |                                   |
+-----------+-----------+-----------------------------------+
      ^           ^                     ^
      |           |                     |
 Size of     Channel ID          ATT / SMP Data
 Payload     04 = ATT
             05 = Signal
             06 = SMP

Context: If the Link Layer is the traffic cop, L2CAP (Logical Link Control and Adaptation Protocol) is the packaging facility. It takes huge chunks of data from upper layers (like ATT or SMP) and chops them into small pieces that fit into the tiny radio packets. Conversely, it takes incoming radio packets and glues them back together.

For security researchers, L2CAP is the most dangerous layer for memory corruption. Why? because it handles Segmentation and Reassembly (SAR). It allocates buffers, parses length fields, and trusts incoming data sizes. This is where famous exploits like BlueBorne and BleedingBit lived—exploiting heap overflows when the stack tried to reassemble a malicious packet.


1. Channels (CIDs): The "Ports" of BLE

L2CAP works like TCP/UDP ports. It uses Channel IDs (CIDs) to route traffic.

  • Fixed CIDs:

    • 0x0004: ATT (Attribute Protocol) - Where your data lives.

    • 0x0005: L2CAP Signaling - Control commands.

    • 0x0006: SMP (Security Manager) - Pairing logic.

  • Dynamic CIDs: Used for high-throughput custom data (like audio or Object Transfer).

Security Perspective

The stack assumes that data arriving on 0x0006 is valid SMP traffic. If you can inject raw packets into this CID without proper state checks, you can crash the security manager or bypass pairing checks. Additionally, many stacks have "hidden" dynamic CIDs open for testing that shouldn't be there.

🛡️ Security Checks & Testing

  • CID Scanning: Use l2ping (standard Linux tool) or Python scripts with Scapy.

    • Test: Iterate through CIDs from 0x0001 to 0xFFFF sending an echo request.

    • Goal: Identify open dynamic channels that are not documented.

  • Cross-Talk Injection:

    • Test: Try sending an ATT packet (data) to the SMP CID (0x0006).

    • Goal: Does the device crash? Does it try to parse your data as a key?


2. Segmentation & Reassembly (SAR): The Exploit Vector

BLE radio packets have a limit (historically 27 bytes, now up to 251 bytes). But an application might want to send 500 bytes.

  1. Sender: Breaks 500 bytes into smaller chunks (Segments).

  2. Receiver: Reads the L2CAP Header ("I am sending 500 bytes") -> Allocates a 500-byte buffer -> Waits for chunks to fill it.

Security Perspective (The "BlueBorne" logic)

What if I send a header saying "I am sending 50 bytes," but then I send 100 bytes of fragments?

  • Buffer Overflow: The stack allocated 50 bytes. The extra 50 bytes overwrite adjacent memory (heap overflow).

  • Memory Exhaustion (DoS): Send a header saying "I am sending 10,000 bytes." The stack allocates a huge buffer. Then... stop sending. Do this 100 times. The device runs out of RAM and crashes (Remote DoS).

🛡️ Security Checks & Testing

  • Oversized Packets: Use Scapy or Mirage.

    • Test: Craft a packet where L2CAP_Length < Actual_Payload_Length.

    • Goal: Trigger a heap overflow.

  • Incomplete Fragments (DoS):

    • Test: Send the "Start Fragment" for 50 different large packets but never send the "Continuation Fragments."

    • Goal: Check if the device freezes (memory leak) or smartly drops the stale buffers.


3. L2CAP Signaling (CID 0x0005): The Control Plane

This channel handles connection updates. It uses opcodes like:

  • COMMAND_REJECT_RSP

  • CONNECTION_PARAMETER_UPDATE_REQ (Change speed/latency)

  • LE_CREDIT_BASED_CONNECTION_REQ

Security Perspective

This channel is mandatory. Every BLE device must listen to it. If the parser for these commands has a bug, every device using that chip is vulnerable. Fuzzing Target: This is the #1 place to fuzz. If you send a CONNECTION_PARAMETER_UPDATE_REQ with impossible values (e.g., Min Interval > Max Interval, or Interval = 0), poorly written stacks will divide by zero and panic.

🛡️ Security Checks & Testing

  • Signaling Fuzzing: Use Braktooth or Sweyntooth.

    • Test: Fuzz the fields of CONNECTION_PARAMETER_UPDATE_REQ.

    • Goal: Crash the device.

  • Echo Request:

    • Test: Send ECHO_REQ on the signaling channel.

    • Goal: While harmless, if the device responds, it confirms the stack is processing your raw L2CAP frames.


4. MTU Exchange: The Size Negotiation

When connecting, devices negotiate the Maximum Transmission Unit (MTU). "I can handle 512 bytes." "I can only handle 23."

Security Perspective

If you (the attacker) claim to support a massive MTU (e.g., 65,000 bytes), and the victim device tries to match it, it might trigger an Integer Overflow or Malloc Failure in the firmware.

🛡️ Security Checks & Testing

  • MTU Fuzzing:

    • Test: During connection init, send an Exchange MTU Request with values like 0, 1, 65535, or 0xFFFFFFFF.

    • Goal: Verify if the device sanitizes this input or crashes.

Last updated