Bluetooth Keyboard - Protocol security issues

Bluetooth HID Protocol: How Keyboards Work Over Bluetooth (And Why That's a Problem)

Table of Contents

What Is HID?

HID stands for Human Interface Device. It's a USB device class introduced in the late 1990s to standardize how input devices communicate with computers. Before HID, every keyboard and mouse needed its own driver. HID replaced that with a universal protocol: describe your device in a structured way, report input in a structured format, and the OS handles the rest.

The original HID spec was built for USB. Later it was extended to Bluetooth through the Bluetooth HID profile (also called Bluetooth HID, or BT HID), and eventually to Bluetooth Low Energy through HOGP (HID Over GATT Protocol). The core concepts are the same across all three transports: a descriptor tells the host what the device is, and reports carry the actual input data.

Keyboards, mice, gamepads, touchpads, drawing tablets, barcode scanners - they all use HID. The protocol is everywhere, and the OS trusts it completely.

The HID Report Descriptor

When a HID device connects, the first thing it sends is a report descriptor. This is a structured byte array that defines exactly what kind of device this is and what data it will send.

The descriptor uses a tag-value encoding to describe things like: "I am a keyboard. I will send reports with a modifier byte, a reserved byte, and six key codes." The OS parses this descriptor, builds an internal model of the device, and uses it to interpret every subsequent report.

The descriptor is also where a device declares itself to be a keyboard instead of a mouse. There's no external verification. The OS reads the descriptor, believes it, and starts treating the device accordingly. A microcontroller that sends the right descriptor bytes will be registered as a keyboard regardless of what it actually is.

For a standard keyboard, the relevant portion of the descriptor declares usage page 0x01 (Generic Desktop), usage 0x06 (Keyboard), and a collection of inputs matching the 8-byte report format described below.

graph TD
    subgraph APP["Application Layer"]
        KEYBOARD["Keyboard App"]
    end
    subgraph HID_LAYER["HID over GATT"]
        HID_SVC["HID Service
UUID: 0x1812"] REPORT_MAP["Report Map
Characteristic"] REPORT["HID Report
Characteristic"] HID_INFO["HID Information"] end subgraph BLE_STACK["BLE Stack"] GATT["GATT
Service discovery + read/write"] ATT["ATT
Attribute protocol"] L2CAP["L2CAP
Logical link"] LL["Link Layer
Advertising + connections"] PHY["Physical Layer
2.4 GHz radio"] end KEYBOARD --> HID_SVC HID_SVC --> REPORT_MAP HID_SVC --> REPORT HID_SVC --> HID_INFO REPORT --> GATT GATT --> ATT ATT --> L2CAP L2CAP --> LL LL --> PHY

BLE HID protocol stack - keyboard reports are delivered through the HID Service (0x1812) over GATT, with the Report Map defining the data format the host OS uses to interpret keystrokes

sequenceDiagram
    participant KB as BLE Keyboard
    participant OS as Host OS
    KB->>OS: ADV_IND (HID appearance)
    OS->>KB: Connection Request
    KB->>OS: Pairing: I/O capabilities
    OS->>KB: Pairing: Confirm / PIN
    Note over KB,OS: Keys exchanged, bonding stored
    OS->>KB: GATT: Discover Services
    KB->>OS: HID Service (0x1812) found
    OS->>KB: Read Report Map descriptor
    KB->>OS: Report Map (key layout definition)
    OS->>KB: Enable HID Report notifications
    loop Every keypress
        KB->>OS: HID Report: modifier + keycode
        OS->>OS: Translates to character input
        KB->>OS: HID Report: key release (all zeros)
    end

BLE HID keyboard connection and keystroke flow - after pairing and service discovery, each keypress sends an 8-byte HID report containing modifier flags and up to 6 simultaneous keycodes

Keyboard HID Report Structure

A standard keyboard HID report is 8 bytes. Every keypress is encoded into these 8 bytes and sent to the host.

  • Byte 0: Modifier keys (bitmap). Each bit represents one modifier key: bit 0 = Left Ctrl, bit 1 = Left Shift, bit 2 = Left Alt, bit 3 = Left Meta (Windows/Command), bit 4 = Right Ctrl, bit 5 = Right Shift, bit 6 = Right Alt, bit 7 = Right Meta.
  • Byte 1: Reserved. Always 0x00. The spec reserves this byte for OEM use, but in practice it's always zero.
  • Bytes 2-7: Key codes. Up to 6 simultaneous keys can be reported. Each byte holds one USB HID usage code from the keyboard usage table. Unused slots are 0x00.

This 8-byte format is defined by the USB HID specification and has been stable for decades. The same format works over USB, Classic Bluetooth, and BLE.

Example: How Ctrl+C Becomes Bytes

Take the simple key combination Ctrl+C. On a real keyboard, when you press and hold Ctrl then press C, the keyboard sends this report:

Byte 0: 0x01  (Left Ctrl bit set)
Byte 1: 0x00  (reserved)
Byte 2: 0x06  (HID usage code for 'c')
Byte 3: 0x00
Byte 4: 0x00
Byte 5: 0x00
Byte 6: 0x00
Byte 7: 0x00

The modifier byte 0x01 has bit 0 set, meaning Left Ctrl is held. Key code 0x06 is the usage code for the letter C in the USB HID keyboard/keypad usage page. The OS receives this 8-byte report and interprets it as Ctrl+C - which in a terminal sends SIGINT to the foreground process.

When the keys are released, the device sends an empty report with all zeros, signaling that no keys are pressed.

The key code table is standardized in the USB HID Usage Tables document. Every key on a standard keyboard has an assigned usage code: 0x04 for A, 0x05 for B, 0x06 for C, 0x28 for Enter, 0x2C for Space, 0x29 for Escape, and so on. These codes are independent of the physical keyboard layout - they identify keys by position/function, not by the character printed on them.

How the OS Processes HID Reports

When the OS receives a HID report, it processes it through the input subsystem and delivers it to the focused application. There is no authentication step. There is no sandbox. There is no rate limit enforced by the HID layer. The OS does not distinguish between a physical keyboard manufactured by Logitech and a custom microcontroller that sends the same bytes.

On Windows, the HID class driver (hidclass.sys) parses reports and dispatches them through the raw input system and the keyboard/mouse device stack. On Linux, the kernel's HID subsystem (drivers/hid/) handles parsing and delivers events through the evdev interface. On macOS, IOKit handles HID devices at the kernel level and delivers events through the event system.

None of these systems check the source of the input. A report is a report. If a device says it's a keyboard and the OS accepts the connection, that device gets full, unsandboxed input access - the same input access as a keyboard you bought from a store.

The input arrives before any user-space software sees it. Antivirus products, application firewalls, and security software generally cannot intercept or analyze HID input at this layer. By the time software sees the keystrokes, they look identical to legitimate user input.

HID Over USB vs. HID Over Bluetooth

The HID report format is the same whether you're using USB or Bluetooth. The transport layer differs, but the payload format doesn't.

Over USB, the trust model is simple: the moment a USB HID device is plugged in, the OS enumerates it, loads the class driver, and the device gets input access. No pairing, no authentication, no user interaction required. This is why USB HID injection tools like the USB Rubber Ducky work immediately on any unlocked computer.

Over Classic Bluetooth, devices must pair before they can communicate. But Bluetooth has multiple pairing modes, and one of them is "Just Works" - a mode that completes pairing with no PIN, no passkey, no user confirmation on the target device. Just Works was designed for scenarios where at least one device has no display or input capability. The result is that a Bluetooth HID device can pair to a target with minimal or zero user interaction, depending on the target's pairing policy.

The authentication gap in Just Works pairing is the core of every Bluetooth HID injection attack. Pairing happens, the OS accepts the HID device, and the device gets full keyboard access.

HID Over BLE (HOGP)

BLE (Bluetooth Low Energy) handles HID differently from Classic Bluetooth. Instead of the dedicated HID profile, BLE uses GATT (Generic Attribute Profile) to expose HID data as a set of characteristics. This is called HOGP - HID Over GATT Protocol.

Under HOGP, a BLE keyboard exposes specific GATT services. The HID Service (UUID 0x1812) contains characteristics for the HID report (UUID 0x2A4D), the HID report descriptor (UUID 0x2A4B), HID information (UUID 0x2A4A), and the HID control point (UUID 0x2A4C).

The host connects, reads the report descriptor characteristic, parses it the same way it would for a USB HID descriptor, and then subscribes to notifications on the HID report characteristic. When a key is pressed, the BLE device sends a notification containing the same 8-byte keyboard report format.

From the OS perspective, a BLE keyboard using HOGP looks identical to a USB HID keyboard once connected. The same class driver handles the reports. The same input pipeline processes them. The same zero-authentication trust model applies.

BLE's connection-oriented nature means the device must be bonded to the host before it can send input. But bonding uses the same Bluetooth security modes as Classic Bluetooth, including Just Works, which means the authentication problem carries over.

Why This Is a Security Problem

The HID trust model was designed for a world where physical access to a device was the implicit authentication. If something is plugged into your USB port, you presumably put it there. If something paired to your laptop over Bluetooth, you presumably allowed it.

Neither assumption holds up in adversarial conditions.

USB: someone can plug a device into an unattended machine in seconds. Conference rooms, coffee shops, hotel lobbies, offices with unlocked workstations - physical access opportunities are common.

Bluetooth: range extends to 10-30 meters. An attacker doesn't need to touch your machine. They need your computer to be discoverable or connectable, and they need you (or an automated policy) to accept a pairing request. With Just Works, acceptance may require no user action at all on some configurations.

Once paired and connected, the device has exactly the same access as a physical keyboard. It can type commands, open applications, interact with the OS at full speed. The OS provides no signal to the user that input is coming from an unexpected source.

How Bad-BT Exploits This

Bad-BT is the BLE equivalent of USB HID injection. Instead of a physical USB device, an attacker uses a BLE-capable device that advertises as a keyboard, completes pairing via Just Works, and then injects HID reports containing scripted payloads.

The BLEShark Nano implements Bad-BT as one of its core features. It advertises over BLE as a standard keyboard using HOGP, pairs with the target, and then executes payloads written in DuckyScript - the same scripting language used by USB HID injection tools, now running wirelessly.

The wireless nature changes the threat model significantly. The attacker can be across a room, on the other side of a wall, or down a hallway. No physical access to the target machine is required. No device needs to be left behind. The BLEShark Nano can send a payload and disconnect before anyone notices anything happened.

The on-device editor means payloads can be written, modified, and tested directly on the BLEShark Nano without connecting it to a laptop. This matters for operational scenarios where pulling out a computer to edit a payload would be conspicuous.

The On-Device DuckyScript Editor

DuckyScript is a scripting language originally designed for the USB Rubber Ducky. It provides human-readable commands that map to HID report sequences. Commands like STRING hello world (type a string), ENTER (press Enter), DELAY 500 (wait 500ms), and GUI r (Windows key + R) translate into sequences of 8-byte HID reports.

The BLEShark Nano includes an on-device DuckyScript editor. You can write and edit scripts directly on the device, store multiple payloads, and execute them on demand - no laptop required. When a command like STRING is executed, the firmware converts each character to the appropriate HID usage code (accounting for modifiers required for uppercase and symbols), builds the report, and sends it over BLE.

Timing matters for HID injection. Some payloads need delays to wait for applications to open or for the OS to process previous commands. DuckyScript's DELAY command handles this. Variable timing also helps evade naive rate-based detection - a payload that types at irregular intervals looks less obviously scripted than one that sends reports at machine-constant speed.

Defense

Defending against HID injection - USB or Bluetooth - requires addressing the trust model at the source rather than at the endpoint.

Bluetooth pairing policies: Configure systems to require authentication for Bluetooth pairing. Most MDM platforms (Jamf, Microsoft Intune, etc.) allow administrators to set Bluetooth pairing requirements that reject Just Works pairing or require user confirmation for all pairings. On Windows, Group Policy can restrict Bluetooth device installation.

MDM device restrictions: Managed devices can be configured to disallow pairing with unrecognized Bluetooth devices, require PIN-based pairing, or disable Bluetooth entirely. These policies are enforced at the OS level and can't be bypassed by the connecting device.

Endpoint detection: Some EDR (Endpoint Detection and Response) products now include behavioral detection for anomalous keystroke patterns - unusually fast typing, keyboard shortcuts that open terminals or PowerShell, suspicious command sequences. This isn't foolproof, but it provides a detection layer that antivirus cannot.

Auto-lock timers: HID injection only works on unlocked machines. Aggressive auto-lock policies (30-60 seconds) reduce the window of opportunity significantly. A locked machine requires a password to unlock - something an HID injector can only provide if the payload knows the password, which is an additional barrier.

Physical controls: In high-security environments, USB port blockers (hardware devices that physically block USB ports) and policies prohibiting Bluetooth use on sensitive machines provide physical-layer enforcement.

Understanding the HID protocol at the byte level is what makes these defenses make sense. The vulnerability isn't a bug - it's a design decision made before adversarial wireless input was a real threat. The fix requires adding authentication back into a system that was designed without it.

BLEShark Nano is designed for authorized security research, penetration testing, and educational use. Always obtain proper authorization before testing on systems or networks you do not own.

Get the BLEShark Nano

Back to blog

Leave a comment