⚠️ *免责声明:仅用于研究和教育目的
本项目是一个概念验证(PoC),演示了蓝牙键盘注入攻击,并用 Rust 重新实现。它严格用于教育和安全研究目的。
下载、克隆或使用此代码即表示您同意负责任的用法,并遵守所有适用的法律法规。
欢迎使用 Rusty Injector —— 一个受 Marc Newlin 的 蓝牙键盘注入 PoC 启发,用 Rust 实现的版本,涉及 CVE-2023-45866、CVE-2024-21306 和 CVE-2024-0230。
目前,本仓库仅实现了 CVE-2023-45866,该漏洞利用 Linux 操作系统下 BlueZ 中的键盘注入漏洞。
以下是 NIST 的描述截图,包含 CVSS 评分:
截图 1:CVE-2023-45866 的 NIST 描述。
其他 CVE(CVE-2024-21306 和 CVE-2024-0230)本人暂无实现计划,但热烈欢迎贡献。
在深入细节之前,建议您观看 Marc Newlin 在 NullCon 2024 大会上的演讲,其中对这些漏洞进行了清晰而详尽的解释:Hi, My Name Is keyboard - Marc Newlin 演讲。
我也制作了一个科普视频,您可以在以下链接观看:如何用简单的蓝牙黑客入侵你的设备 - Hi, my name is keyboard。
📌 如果您发现下面的解释中有任何遗漏、可简化之处或潜在错误,欢迎修改并提交合并请求。我将很高兴审阅您的贡献并合并到仓库中。
由于我们只涉及针对 Linux 操作系统的 CVE-2023-45866 漏洞,因此仅解释针对 BlueZ 库进行特定利用的过程。
首先,您必须了解此漏洞仅在蓝牙 BR/EDR 上可利用,因为它针对的是基于该技术的 HID 配置文件。您可能知道,蓝牙架构的实现类似于以太网协议的 OSI 模型,分为多个层,如下示意图所示。
图 1:简化的蓝牙 BR/EDR(基本速率/增强速率)堆栈。堆栈最底层代表带有专用天线的物理层,最上层代表应用层,有时也指操作系统。当两个设备要相互通信时,它们会经过这些不同层:向外发送包时从上到下,接收蓝牙包时从下到上。
在查询过程之后,当设备确定要建立连接时,它们会进行配对过程。该过程允许设备之间进行相互认证,并建立一个加密密钥,用于后续通信的安全。
蓝牙规范提供了不同级别的认证和安全性。根据用于认证的机制,通信的安全级别会有所不同。设备可以根据其拥有的输入/输出外设进行认证,这被称为关联模型。您在配对两个设备时可能遇到过这种情况——例如被要求输入另一个设备上显示的 PIN 码。
有四种配对关联模型,由设备的 I/O(输入/输出)能力决定:
下表显示了根据 IoT 设备的能力使用哪种关联模型。
图 2:蓝牙 BR/EDR 关联模型表,灵感来源于蓝牙核心规范 5.3 - 2.3.5.1 选择密钥生成方法 表 2.8:IO 能力到密钥生成方法的映射(第 1573 页)。有关安全模式和关联模型的更多信息,请查看 Thyrasec 发表的这篇有趣的博文:蓝牙安全:经典和 BLE!
我相信您对“Just Works”方法很感兴趣,这正是漏洞所在。问题在于:该方法在建立配对时无需用户确认或交互,因此无法验证配对设备的真实性。在 Linux 系统上,BlueZ 堆栈默认接受来自归类为 NoInputNoOutput 设备的传入配对请求(以实现向后兼容)。这真是一个“绝妙”的设计选择,您同意吗?
截图 2:BlueZ 默认配置的更新,旨在启用蓝牙安全性并修补 CVE-2023-45866。
与目标设备配对后,我们的系统通过 L2CAP 层的端口 1 与服务发现协议(SDP)建立连接。如图 1 所示,L2CAP 层充当底层和上层服务层之间的中间层,提供数据包的分段、复用和重组。通过 SDP 连接,我们识别目标设备上的所有可用服务,并连接到人机交互配置文件(HID)服务。HID 配置文件用于操作系统处理蓝牙键盘和鼠标的输入,它通过 L2CAP 层的端口 17(HID 控制)和端口 19(HID 中断)运行。访问 HID 配置文件不需要认证,任何连接到 L2CAP 端口 17 和 19 的设备都被识别为 HID 设备。
攻击者可以冒充蓝牙无线键盘的服务和设备类别,通过指定“NoInputNoOutput”能力来利用“Just Works”关联模型,并将未经授权的按键注入到目标设备。
坦率地说,唯一目标是将概念验证用 Rust 重新实现,以更深入地理解该漏洞的细节。因此,在首次迭代中,整体架构受到了 Marc Newlin 的 Python PoC 的启发:Github "hi_my_name_is_keyboard"。在未来的重构中,可能会以更 Rust 风格的方式实现。
本次实现中使用的 Crate(= Rust 库):
[dependencies]
bluer = { version = "0.17.3", features = ["l2cap", "bluetoothd", "id", "rfcomm"] }
tokio = "1.42.0"
regex = "1.11.1"
clap = { version = "4.5.23", features = ["derive"] }
我项目中关键的依赖是'BlueR',一个构建在原始 BlueZ C 库之上的 Rust API。您可以在这里查看他们的工作:GitHub BlueR。我还集成了 Tokio 以使用 BlueR 库提供的 async 函数。此外,我还使用了 Clap(命令行参数解析器)和 Regex(Rust 中实现正则表达式的 crate)来验证用户输入的 'bt_addr'(蓝牙地址)。
在观察 Marc Newlin 的实现后,我将架构分解为几个步骤以复现相同的功能:
--iface 或 -i:要使用的蓝牙接口/适配器。此参数可选,如果未指定,将尝试使用默认接口。--target 或 -t:必须 指定目标设备的蓝牙地址。NoInputNoOutput 能力的蓝牙代理,以便在与目标设备连接时能够使用“Just Works”配对方法。0x002540 以模拟键盘。我尽力对我的代码进行了注释。如果您通读代码,很容易认出所有这些步骤。
为了更优雅,下一步将是使其更 Rust 风格,实现优雅终止,并添加若干其他功能,例如解析键盘脚本(预制的负载)或提供图形用户界面。再次强调,这只是出于教育目的,我不确定是否会继续开发此程序的另一个迭代。但再次欢迎贡献。如果您有任何问题,请随时提出。
首先要注意:此工具是在 Ubuntu 24.04 上开发和测试的。
要使用此工具,您需要禁用 BlueZ 注册的默认 HID 服务,以便在启动利用时可以重新注册。请按照以下步骤操作:
/etc/systemd/system/bluetooth.target.wants/bluetooth.service 配置文件ExecStart=/usr/libexec/bluetooth/bluetoothd
修改为:
ExecStart=/usr/libexec/bluetooth/bluetoothd --noplugin=inputsudo systemctl daemon-reload
sudo systemctl restart bluetooth现在您只需拉取项目,并通过 cargo build 构建或直接指定参数运行:cargo run -- -i [BT_INTERFACE] -t [BT_TARGET]。
截图 3:Rusty Injector 的命令行界面。
示例如下:
cargo run -- -i hci0 -t AA:BB:CC:DD:EE:FF
另外需要注意,如果您想修改代码并通过 Configuration trait 使用“set_address”函数,则必须安装 bdaddr 工具才能使用它:
# 从 bluez 源码构建 bdaddr
cd ~/
git clone --depth=1 https://github.com/bluez/bluez.git
gcc -o bdaddr ~/bluez/tools/bdaddr.c ~/bluez/src/oui.c -I ~/bluez -lbluetooth
sudo cp bdaddr /usr/local/bin/
请注意,如果您不指定任何蓝牙接口,它将尝试使用默认接口。显然,别忘了将蓝牙接口插入您的 Ubuntu 操作系统。🙃
我相信我已经涵盖了所有内容——您现在可以充分利用 Rusty Injector 了!如果您遇到任何问题,或有意见或反馈,请随时开启 issue。祝您体验愉快,开心破解!🚀