蓝牙重放攻击记录
买了一台蓝牙设备,想要电脑写一些不同的逻辑控制它,于是有了这篇文章。
步骤一:手机抓包
参考:https://www.cnblogs.com/eezhijun/p/17629196.html
用的小米手机:
- 开发者选项中打开【蓝牙数据包日志】
- 在开发者选项中打开【蓝牙调试日志】(没有的请忽略此步骤)
- 拨号键输入##5959## 开启抓取
- 拨号键再次输入##5959## 结束抓取
步骤二:Wireshark 分析
参考:https://zhuanlan.zhihu.com/p/45717775
用 wireshark 打开后发现有各种各样的包,但我们要找 Sent Write,可以在 info 列中排序以此来快速找到。
上图中有两种 write,0x25和0x29,由于下面全是 0x29,所以 0x29 才是我们发送的命令,而数据包中的 value 就是我们需要重放的数据。
步骤三:GattTool 复现
参考:https://zhuanlan.zhihu.com/p/45717775
第三步,使用 gattTool 工具尝试重放工具,这个只有 Linux 才有。没有 Linux 的话,最简单的就是用 U盘制作一个 Ubuntu 启动盘,启动的时候选择【尝试 Ubuntu】,这样也是可以使用终端的。
1. 寻找设备的地址,这个其实在 Wireshark 里面就可以找到,参考文章使用的另一种方法。
2. 输入命令 gatttool -b 94:a9:a8:2a:89:fc -I
使用interactive方式连接设备,先 connect,然后 char-write-req 发送我们要重放的数据即可。没错很神奇,不需要配对密钥。下图是参考文章里的图,一目了然。
3. 寻找对应的 UUID,这一步对于后续第四大步骤很重要,如参考文章的图所示,根据 handle 去找到对应的 UUID,并且记录下来。
步骤四:Python+Bleak复现
经过前三个步骤,我们现在有:第二步中找到的命令的 handle、命令的 value;第三步中找到的 UUID。
但这种命令行发送肯定不是最终想要的效果。第四步就是使用 python 的 bleak 包来完成,很棒的包,比其他蓝牙包要好很多。还有一个好处是在 windows 下也能重放攻击了。使用 bleak 的 write_gatt_char 可以达到步骤三中的效果,直接给一个样例代码。
import asyncio
from bleak import BleakScanner, BleakClient
import keyboard
# 先看看能不能寻找到设备
# while True:
# devices = bluetooth.discover_devices(duration=20, lookup_names=True)
# for addr, name in devices:
# print(f'Devices: {addr}, {name}')
# time.sleep(5)
target_addr = '94:a9:a8:2a:89:fc'
# move和reset就是要重放的value
move = "010201881300000000000000000000000000"
move = bytearray([int(move[i:i + 2], 16) for i in range(0, len(move), 2)])
reset = "060000840300000000000000000000000000"
reset = bytearray([int(reset[i:i + 2], 16) for i in range(0, len(reset), 2)])
uuid = "0000ff82-0000-1000-8000-00805F9B34FB"
print('uuid', uuid)
async def main():
# device = await BleakScanner.find_device_by_address(target_addr, timeout=30)
async with BleakClient(target_addr) as client:
print('connect~')
while True:
# 根据键盘响应来进行不同的处理
now_key = keyboard.read_key()
if now_key == 'right':
move[1] = 0x01 # 向右移动,修改对应位置的值
# response==False 不等待返回数据
await client.write_gatt_char(uuid, data=move, response=False)
if now_key == 'r':
await client.write_gatt_char(uuid, data=reset, response=False)
asyncio.run(main())