DEV Community

Cover image for A BadUSB that isn't so bad: Making a keystroke injector in Arduino that automates GatherTown movements
Lena
Lena

Posted on • Updated on

A BadUSB that isn't so bad: Making a keystroke injector in Arduino that automates GatherTown movements

Whenever we make something, we should consider whether it can be used for malicious purposes.

What about the other way around? Can we use something meant for malicious purposes be used for good (or not so bad) purposes?

Back when I participated in IPA's セキュリティ・キャンプ全国大会 2020, I learnt about BadUSBs and how they can be made to accept infrared inputs for remote keystroke control. This was a really interesting and fun course, and it got me wondering if we can use BadUSBs for non-malicious purposes.

A BadUSB is a malicious USB that acts as a Human Interface Device. This can be used to automate and inject keystrokes at lightning speed, which can allow the attacker to do many bad things (think of all the things you can do if you can control someone's keyboard!).

Since the keystroke injector can be used to type anything, I figured it might be interesting to use it to automate GatherTown game movements.

On my other blog post, I talked about using a bash script and xdotool to automate GatherTown movements. Unfortunately, that only works on Linux environments.

This time, we will be using Arduino to build a BadUSB. This will be independent of the OS, so this keystroke injector will have more use cases than using xdotool on a bash script.

Now let's get started on building a (not so)BadUSB!

Table Of Contents

Getting started with the Arduino Pro Micro

The materials used here are:

  • Arduino Pro Micro
  • Bread Board
  • Male-Male Jumper Wires
  • 3 pin slide switch
  • Micro USB cable
  • [Optional] Epoxy Resin

For the Arduino Pro Micro, I soldered them and prepared it as shown in the following video. The micro USB port is prone to breaking off, so it's recommended to use epoxy resin to secure the micro USB port onto the Pro Micro. However, make sure the epoxy resin doesn't get inside the micro USB port. Also, don't plug the micro USB cable into the Pro Micro's port before the epoxy cures, as this can cause the cable to get stuck to the port and removing it will be a hassle.

Next, we will use the Arduino IDE, which can be downloaded from here.

Create a new file, and we will use the following code to automatically type "Hello!" every 5 seconds. Try not to put anything under 5 seconds, as we will see what will happen in the next section.


#include "Keyboard.h"

void setup() {
  // put your setup code here, to run once:
  Keyboard.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  Keyboard.print("Hello!");
  delay(5000);
  //Hello! is typed in every 5 seconds
  //Try not to put anything under 5 seconds
}

Enter fullscreen mode Exit fullscreen mode

This .ino file can be found here on my Github.

By default, Arduino Pro Micro will not be shown in Tools > Board, so in order to select Arduino Pro Micro, follow the steps shown in Shell Hack's Arduino Pro Micro: Board Selection.
Plug in your Arduino Pro Micro into your computer, and make sure SparkFun Pro Micro is selected for the devices, ATmega32U4 (5V) is selected for the processor, and AVRISP mkII is selected for the programmer. The port settings will vary across different computers.

The following video shows the upload process and the demonstration.

Image description

It is possible to use Arduino Micro/Leonardo as the device, but that will likely crash the bootloader and cause your Arduino Pro micro to become bricked. If it becomes bricked, the Arduino Pro Micro will not show up in the port selection, and upload will fail. In this case, the following error might be shown when you plug in your Arduino Pro Micro into a Windows Machine.
Image description

To resolve this issue, follow the instructions shown in ShellHack's Arduino Pro Micro: Reset & Restore Bootloader to restore the bootloader and unbrick your Arduino Pro Micro.

What to do when the keystrokes are too fast and cannot update the Arduino program

Suppose you accidentally type in a small number (under 1000) into delay(). This is a bit tricky to resolve, since the keystrokes will overwrite the program whenever you try to update the program, which will cause errors in the verification and upload process.

To resolve this issue, you will need to unplug the Arduino Pro Micro, and type "//" at the very top of the program like the following, so the keystrokes will get commented out when you plug the Arduino Pro Micro back in.

//
#include "Keyboard.h"

void setup() {
  // put your setup code here, to run once:
  Keyboard.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  Keyboard.print("Chaos!");
  delay(10);
}
Enter fullscreen mode Exit fullscreen mode

If you used Keyboard.println() that types a new line every time, type this into the top of the program so every injected line will be commented out.

/*
 * 
 */
#include "Keyboard.h"

void setup() {
  // put your setup code here, to run once:
  Keyboard.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  Keyboard.print("Chaos!");
  delay(10);
}
Enter fullscreen mode Exit fullscreen mode

The following video demonstrates this situation.

Creating a circuitry that allows you to turn the keystrokes on/off

If we had a switch that allowed us to control whether the keystrokes are on/off, the keystroke injector would be much easier to handle as situations shown in the previous section will not happen.

We will be using a slide switch with three terminals. The following video shows how you should configure the circuit to use the slide switch.

In the end, your circuit should look something like this.
Image description

The simplified circuit diagram is shown in the following,
Image description

As we configured the switch to Arduino Pro Micro's pin 9, we will need to declare a variable called switchPin as ,

int switchPin = 9;

and setup this as an input pin using,

pinMode(switchPin, INPUT);

The state of pin 9 is read using,

switchstate = digitalRead(switchPin);

To test this, we will use the following program.

#include "Keyboard.h"

//we are using pin 9 for the switch input
int switchPin = 9;
int switchstate = 0;

void setup() {
  // put your setup code here, to run once:
  Keyboard.begin();

  //setup pin 9 as input
  pinMode(switchPin, INPUT);
}

void loop() {
  //check the state of pin 9, whether it is HIGH (on) or LOW (off)
  switchstate = digitalRead(switchPin);

  if (switchstate == HIGH){
    //Output to serial monitor
    Serial.println("Its on");
  } else if (switchstate == LOW){
    //Output to serial monitor
    Serial.println("Its off");
  }
}
Enter fullscreen mode Exit fullscreen mode

This .ino file can be found here on my Github. Here, we are using Serial.println() instead of Keyboard.println(), which will output the results onto the Serial Monitor instead of the keyboard. The demo is shown in the following video,

Making your GatherTown avatar move around randomly automatically

Now, we would like to make a program that can make the GatherTown avatar move around randomly, like the one from the last blog post.

In the loop(), we will first check if the switch is on/off. If it is on, we will generate the random numbers as shown below,

void loop() {
  //check the state of the input pin
  switchstate = digitalRead(switchPin);
  //If HIGH, then its on
  if (switchstate == HIGH){ 
    int i = 0;
    //generate random number between 5-20
    int stepcount = random(20) + 5;

    //generate random number between 0-3
    int randdirec = random(4);

    //generate random number between 1-200
    int dance = random(200) + 1;


    //generate random number between 1-100
    int g = random(100) + 1;
    ...
  }
  ...
}
Enter fullscreen mode Exit fullscreen mode

In GatherTown, the avatar will dance when you press the "z" key. As it doesn't require you to hold the key down to dance, we use Keyboard.print() to dance for 2 seconds with a likelihood of 1/200 as shown in the following.

    //1/200 chance of dancing
    if(dance >= 200){
      Keyboard.print("z");
      delay(2000);
    }
Enter fullscreen mode Exit fullscreen mode

To walk through people, one must hold down the "g" key while moving, thus we will use Keyboard.press() to hold down the "g" key.

    //3/100 chance of g
    if(g >= 98){
      Keyboard.press('g');
      flag = 1;
    }
Enter fullscreen mode Exit fullscreen mode

Now, we will either go up ("w" key), down ("s" key), left ("a" key), or right ("d" key) by using Keyboard.print(). The direction will be determined by the previously generated randdirec, and the number of steps in that direction will be the previously generated stepcount.

    while(i <= stepcount){
      //break the loop once the switch turns off
      if (switchstate == LOW){
        break;
      }
      switch(randdirec){
        case 0:
          Keyboard.print("w");
          break;

        case 1:
          Keyboard.print("s");
          break;

        case 2:
          Keyboard.print("a");
          break;

        case 3:
          Keyboard.print("d");
          break;
      }

      delay(10);
      i = i + 1;
    }

Enter fullscreen mode Exit fullscreen mode

At the end of the loop, we will release the "g" key if it was pressed using Keyboard.release(),

    if (flag == 1){
      Keyboard.release('g');
      flag = 0;
    }
Enter fullscreen mode Exit fullscreen mode

The full code can be found here on my Github.

The following demo shows what will happen when you upload this onto the Arduino, open up a text editor, and turn on the switch.

Finally, you can open up GatherTown and turn on the switch, and the demo is shown in the following video.

There you go! A (not so)BadUSB which can be used to automate your GatherTown Avatar!

Arduino IDE is only required to program the Arduino Pro Micro. Thus, the Keystroke injector should work on a different computer as well. Also, the keystroke injector is independent of the OS, so it can be used before the OS finishes booting as long as it allows USB input devices. So as long as your USB keyboard can be used, this keystroke injector can also be used.

Here's an example of the keystroke injector being used in Ubuntu's recovery mode CMD.

And here's an example of the keystroke injector being used in Window's recovery mode CMD.

There are many things you can do with this keystroke injector, so you can get very creative when making your own! You can attach other external I/O devices such as infrared sensors, motion sensors, etc. to control this BadUSB!

Have fun making your own (not so)BadUSB, and try not to break anything while experimenting!

Top comments (0)