Bruce Firmware · Volume 9

Bruce Firmware Volume 9 — RFID/NFC & the Scripting Interpreter


9.1 About this Volume

This volume covers two subsystems that are unrelated in function but share a volume because each is, in its own way, where Bruce’s breadth becomes depth. The first is the RFID/NFC subsystem — the firmware’s ability to read, clone, and write contactless cards and tags at both 125 kHz (low frequency, LF) and 13.56 MHz (high frequency, HF) through a choice of reader front-ends. The second is the scripting interpreter — a JavaScript engine running on the device that turns Bruce from a menu of fixed features into a programmable platform, and which is arguably the single capability that most distinguishes it from the narrower firmwares it is compared against.

The treatment is category-level, consistent with the rest of this deep dive and with the ESP32 Marauder Firmware and WiFi Pineapple dives elsewhere in the hub. The volume describes what class of operation each RFID/NFC feature performs and what the interpreter’s module surface exposes; it does not reproduce the on-device menu tree and does not give a procedure for cloning a specific access credential or scripting a specific attack. Access-control cloning is among the more legally consequential things in the whole firmware, and the volume is deliberate about framing it as authorized-only work.

A theme that recurs here, as it does in Vols 7 and 8, is the honest gap between a menu entry and a working feature. Bruce’s RFID menu lists an emulate tag entry; the brief for this deep dive — verified against the official project on 2026-10-16 — records tag emulation as not yet implemented, and §5 treats that gap head-on rather than describing emulation as a capability the firmware has.

Figure 1 — The Bruce NFC feature demonstrated on-device — the contactless read/clone workflow is the practical heart of the RFID/NFC subsystem. Image: BruceDevices/Bruce (pr3y).
Figure 1 — The Bruce NFC feature demonstrated on-device — the contactless read/clone workflow is the practical heart of the RFID/NFC subsystem. Image: BruceDevices/Bruce (pr3y).

9.2 The RFID/NFC front-ends — PN532, PN532Killer, ST25R3916

RFID/NFC is, like the CC1101 and nRF24 radios of Vols 7 and 8, an add-on capability on most Bruce boards: it requires a dedicated reader IC, and the firmware supports a choice of three. Which one a board carries determines both the protocol reach and the configuration an operator sets. Vol 2’s capability matrix has a PN532 (NFC) column for exactly this reason — most boards have no NFC hardware at all, and the feature exists only where a reader is wired in.

 READER FRONT-END ROLE IN BRUCE
 ──────────────── ─────────────
 PN532 (NXP) the common, cheap HF reader module — the
 default "NFC" front-end wired to most boards
 that have NFC at all; I2C/SPI/UART attach
 PN532Killer an enhanced PN532-class device/mode the
 firmware supports as a selectable reader
 ST25R3916 (ST) a higher-performance HF front-end integrated
 on Bruce's own RF REAPER PCB (see Vol 3)

The PN532 is the ubiquitous NXP HF reader — the same chip in countless hobbyist NFC projects — and it is the default “NFC” front-end that Bruce drives on boards wired for it, attached over I2C, SPI, or UART. The PN532Killer is an enhanced PN532-class reader that the firmware exposes as a selectable front-end, giving broader capability than a plain PN532 where one is present. The ST25R3916 is a more capable STMicroelectronics HF reader that is integrated directly onto Bruce’s own RF REAPER PCB (Vol 3), which is why the RF REAPER is the strongest single-board RFID platform in the Bruce hardware line — it carries a proper HF front-end on the board rather than relying on an external module.

The firmware’s RFID config lets the operator select which reader is present (PN532 vs PN532Killer, with the RF REAPER using its on-board ST25R3916), mirroring the pin/module configuration pattern seen for the CC1101 and nRF24. As always in Bruce, the capability is compiled in where the board declares it and the specific hardware is selected at runtime — the HAL model of Vol 4 applied to the contactless reader.

Figure 2 — Bruce running on an M5StickC-class device; the compact M5Stick form factor is a common host for the RFID/NFC and companion-device workflows when paired with a reader module. Image: BruceDevices/Bru…
Figure 2 — Bruce running on an M5StickC-class device; the compact M5Stick form factor is a common host for the RFID/NFC and companion-device workflows when paired with a reader module. Image: BruceDevices/Bruce (pr3y).

A scope note an engineer should hold: these front-ends are HF-centric. The PN532 and ST25R3916 are 13.56 MHz readers. The 125 kHz LF capability (§4) is a separate matter, and Bruce’s handling of LF read is lighter than its HF handling — which is consistent with the broader RFID tool landscape, where HF (ISO 14443, MIFARE, NFC Forum) is where the research depth lives and LF (EM410x, HID Prox, T5577) is a simpler, older world.

9.3 HF (13.56 MHz) — read, clone, NDEF

At 13.56 MHz, Bruce performs the standard contactless-card workflow, and describing it at the class level tells an engineer the reach without reproducing a procedure.

Read. The reader interrogates a presented HF card or tag and reports its type and contents — UID, the card technology (MIFARE Classic, MIFARE Ultralight, NTAG, and the NFC-Forum tag types the PN532/ST25R3916 can address), and, where the sectors are readable, the data. For a card with default or known keys this is a complete read; for a MIFARE Classic card with unknown sector keys, the depth of what can be read depends on the key-recovery capability available, which on a cheap PN532 is limited compared to a dedicated attack platform. This is the reconnaissance step that everything else builds on.

The MIFARE Classic key-recovery point deserves a sentence of its own because it is where the gap between a multi-tool and a dedicated instrument is widest. MIFARE Classic protects each sector with keys, and reading a non-default card requires recovering those keys through the well-known Crypto1 weaknesses — the dictionary/default-key sweep, and the nested and hardnested attacks for cards that resist it. A PN532-based platform can perform the simplest of these (trying known and default keys), but the deeper statistical attacks that recover keys from a hardened card with no known sector are the province of the Proxmark3 and the Chameleon Ultra (both with their own deep dives in this hub), not of Bruce’s PN532 path. The honest statement is that Bruce reads MIFARE Classic well when keys are default or known, and hands off to a dedicated tool when they are not — which is the same division of labour the Chameleon Ultra and Proxmark3 dives describe from their side.

Clone. Having read a card, Bruce can write its contents to a compatible writable card — the “clone tag” capability. The critical nuance, familiar from the iCopy-X and Chameleon Ultra deep dives in this hub, is that cloning a MIFARE Classic card to an ordinary blank fails at the UID, because a normal MIFARE card’s UID is read-only from the factory; cloning requires a Magic card (Gen1a/Gen2 and relatives) whose UID block is writable. Bruce’s clone capability is therefore, in practice, “read a card, write it to a Magic blank,” and its success is bounded by the same key-recovery and Magic-card-stock realities that bound every tool in this class. It is potent against low-security cards and credentials that use default keys; it is bounded against cards with recovered-key requirements and against the diversified, keyed credentials used in serious access control.

NDEF write. Bruce writes NDEF records — the NFC Data Exchange Format used by NFC Forum tags for structured data like URLs, text, Wi-Fi credentials, and contact records. Writing a URL or a Wi-Fi-join record to an NTAG is the benign, everyday NFC-tag-programming use (the same thing a phone’s NFC-writer app does), and it sits at the low-risk end of the HF capability — it is writing data to a blank tag you hold, not interacting with someone’s credential.

Companion-device support. Bruce explicitly supports Amiibolink and Chameleon devices in its RFID menu — the Amiibolink being an Amiibo-emulation companion, and “Chameleon” referring to the Chameleon-family emulators (covered in this hub’s Chameleon Ultra deep dive). This is Bruce acting as a controller/companion for purpose-built emulation hardware rather than performing the emulation itself — a meaningful distinction given the emulation gap discussed in §5.

Figure 3 — A Bruce UI screen; the RFID subsystem presents read, clone, write and companion-device options. Image: BruceDevices/Bruce (pr3y).
Figure 3 — A Bruce UI screen; the RFID subsystem presents read, clone, write and companion-device options. Image: BruceDevices/Bruce (pr3y).

Callout — HF cloning is the legally heaviest RFID operation. Reading, cloning, and emulating an access credential you are not authorized to possess is unauthorized-access of the system that credential opens, and in many jurisdictions possession of a cloned access credential is itself an offence. NDEF-writing a URL to your own NTAG is harmless; cloning the badge that opens your employer’s door without written authorization is not. The RFID/NFC subsystem spans that entire range, and the operator — not the firmware — is responsible for staying on the authorized side of it. See ../_shared/legal_ethics.md.

9.4 LF (125 kHz) — read, and the companion devices

At 125 kHz Bruce offers a read-125 kHz capability — reading the low-frequency tag families that dominate legacy access control and asset tracking: EM410x/EM4100, HID Prox, Indala, and the T5577 writable multi-emulation chip. LF is a simpler world than HF: the tags are typically fixed-ID, unencrypted, and short, and reading one yields its ID directly. This is the same LF landscape the iCopy-X, Proxmark3, and Flipper Zero deep dives cover, and an engineer who has read those will recognize the families immediately.

The honest characterization is that Bruce’s LF support is lighter than its HF support — the headline RFID/NFC work in the firmware is at 13.56 MHz through the PN532/ST25R3916, and 125 kHz read is a supporting capability rather than a deep one. Bruce does not position itself as an LF powerhouse the way a Proxmark3 does; for serious LF cloning to T5577 blanks, the dedicated tools in the hub (iCopy-X, Proxmark3) are the stronger instruments, and Bruce’s value on LF is “read and identify a tag with hardware you may already have flashed for other reasons.” Where LF read fits is reconnaissance and the convenience of a single multi-tool handling a quick LF lookup alongside everything else it does.

The reason LF and HF are so different in treatment is physical as much as a project priority. 125 kHz LF uses inductive coupling over a short range with simple, often-unencrypted protocols, and reading an EM410x or HID Prox ID is a modest technical task; the writing side (cloning to a T5577, which must be coerced into emulating the right LF protocol) is where the real work lives, and that is precisely the side Bruce does not emphasize. 13.56 MHz HF, by contrast, carries the entire MIFARE/NFC-Forum/ISO-14443 world with its keys, sectors, and standards, which is where both the interesting security research and the richer tooling naturally concentrate. An engineer choosing Bruce for RFID work should map the task onto this split: HF read/clone/NDEF on gear you already own is a genuine Bruce strength; LF cloning and HF emulation are where a dedicated device earns its place in the bag.

Table 1 — 4. LF (125 kHz) — read, and the companion devices

BandWhat Bruce doesDepth vs. dedicated tools
HF 13.56 MHzRead, clone (to Magic), NDEF-write; PN532/PN532Killer/ST25R3916Solid for a multi-tool; bounded on keyed MIFARE vs Proxmark3
LF 125 kHzRead / identifyLighter; iCopy-X and Proxmark3 are the LF depth tools

9.5 The emulation gap — what “emulate tag” does not yet do

Bruce’s RFID menu includes an emulate tag entry, and an operator reading the menu would reasonably expect the firmware to present a captured card back to a reader as if it were the original — the defining capability of a Chameleon-class device. The brief for this deep dive, verified against the official project on 2026-10-16, records tag emulation as not yet implemented: the menu entry exists, the firmware does not yet deliver working, reliable card emulation.This is the RFID analogue of the Mousejack gap in Vol 7 §7 and the FM-spectrum gap in Vol 8 §7, and it matters for the same reason: Bruce’s menu is a map of ambition, and the release notes are the map of reality. An engineer evaluating Bruce for an RFID task must not assume that because the device reads and clones, it also emulates — those are different firmware capabilities with different hardware demands, and Bruce has the first pair and not yet the third.

The engineering reason emulation is harder than reading is instructive. A reader-side front-end like the PN532 is built to interrogate a card — to be the active side of the HF link. Presenting as a card to another reader (card emulation / tag emulation) is a different mode that requires the front-end to respond with precise timing to the reader’s anti-collision and authentication sequence, impersonating the card’s low-level link behaviour, and getting that timing right across the range of readers in the field is exactly the hard part that dedicated emulators (the Chameleon family) are purpose-built for. This is also why Bruce supports Amiibolink and Chameleon devices as companions (§3): the firmware delegates the emulation it cannot yet do natively to hardware that does it well.

Callout — read/clone ≠ emulate on Bruce, today. If the task is “present this captured credential to a reader,” Bruce’s native emulation is not the tool; a Chameleon-family device (see the Chameleon Ultra deep dive) is, and Bruce can serve as its companion controller. If the task is “read this card and write it to a physical Magic blank,” Bruce’s clone capability does that. Keeping these straight prevents the most likely disappointment with the RFID subsystem.

9.6 The scripting interpreter — JavaScript, and the Doolittle lineage

The scripting interpreter is where Bruce stops being a fixed menu of features and becomes a programmable platform, and it is one of the firmware’s genuinely distinguishing strengths against the narrower Wi-Fi/BLE firmwares it is compared with. The first fact to get right, because the fork landscape invites confusion, is the language: Bruce’s interpreter runs JavaScript. There is no separate “BruceScript” dialect — scripts are JavaScript, executed by an on-device JS engine. The implementation is derived from justinknight93’s Doolittle project, which Bruce credits (alongside ESP32Marauder, M5Stick-Nemo, Launcher, and Evil-M5Project) as upstream work it borrows from. An engineer who knows JavaScript can write Bruce scripts; there is no new grammar to learn, only a device-specific module/API surface (§7).

 justinknight93 / Doolittle ──(derived from)──► Bruce JS interpreter
 │
 a JavaScript engine on the ESP32, exposing device capabilities
 as callable modules; scripts are.js files on the SD card
 │
 ┌─────────────────────────────┴─────────────────────────────┐
 │ CORE modules (device-agnostic) HARDWARE modules (radios │
 │ Console, Dialog, Display,... + buses: WiFi, Sub-GHz, │
 │ RFID, BadUSB, GPIO,...) │
 └─────────────────────────────────────────────────────────────┘

The significance of a JS interpreter on this class of device is worth drawing out. It means a capability Bruce does not expose as a menu item can often be scripted from the primitives the modules provide — a custom scan-and-react loop, a sequenced multi-radio action, a bespoke BadUSB payload generated at runtime. It means the firmware’s feature surface is extensible by users without recompiling, which is a different and larger thing than a fixed menu. And it means Bruce can be automated: a script on the SD card can drive the device through a sequence of operations unattended. This is the capability that most justifies the “micro-OS” framing some apply to Bruce, as against the “tool with a menu” framing that fits the narrower firmwares. (Note that the interpreter is one of the features the LITE build drops to fit the M5Launcher size ceiling — see Vol 5 — so a LITE-build board has the menu features but not the scripting platform.)

It is worth being precise about what “a JavaScript engine on an ESP32” does and does not mean, because an engineer used to Node.js or a browser will carry expectations that do not transfer. This is an embedded JS interpreter running in a few hundred kilobytes of RAM on a microcontroller: it supports the core language and the device-specific modules, not the sprawling standard library, package ecosystem, or asynchronous runtime of a server-side JS environment. There is no npm, no DOM, no multi-gigabyte heap; a script is a single file exercising the Core and Hardware modules (§7) with plain procedural JavaScript. The value is not that it is a full JS platform — it is not — but that it is a familiar, readable, forgiving scripting surface over the device’s radios and buses, which lowers the barrier to writing device automation from “learn a bespoke embedded DSL and recompile” to “write a short .js file and drop it on the SD card.” For the broad community of users who know some JavaScript and no embedded C, that is a large and deliberate accessibility win, and it is a meaningful part of why Bruce accrued the contributor base and feature breadth it has.

9.7 The module surface — Core and Hardware

The interpreter exposes device capability to scripts through two families of modules, documented in the official wiki. The Core modules are device-agnostic — they handle the script’s interaction with the UI, input, storage, timing, and the language runtime. The Hardware modules are the interesting half for an offensive-security firmware: they expose the radios and buses, which is what lets a script do something in the physical world rather than merely compute.

Table 2 — 7. The module surface — Core and Hardware

FamilyModules (per official wiki)What they expose to a script
CoreConsole, Device, Dialog, Display, Global, Keyboard, Math, Notification, Runtime, StorageTerminal/log output, device info, user dialogs, screen drawing, globals, key input, math helpers, notifications, runtime control, and SD/LittleFS storage access
HardwareAudio, BadUSB, GPIO, I2C, Infrared, Microphone, RFID, Serial, Sub-GHz, WiFiSound; USB-HID keystroke injection; raw GPIO; the I2C bus; IR TX/RX; the microphone; the RFID reader; the serial port; the CC1101 sub-GHz radio; and the Wi-Fi stack

The Hardware module list is effectively a programmatic mirror of the firmware’s own feature categories: a script can reach the Wi-Fi stack, the sub-GHz CC1101, the IR subsystem, the RFID reader, the GPIO and I2C buses, the microphone, the serial port, and — the one that carries the most weight — the BadUSB HID-injection path (§8). This is the mechanism by which the interpreter inherits the full legal envelope of the features it can reach: a script that drives the Wi-Fi module into an attack is subject to exactly the authorization rules of Vol 6, a script that drives the Sub-GHz module to transmit is subject to the rules of Vol 8, and so on. The interpreter does not create new capabilities so much as it makes the existing ones composable and automatable.

Because the module surface is the API an engineer actually programs against, a short illustrative sketch is worth more than prose. The following is a schematic example of the shape of a Bruce script — showing the module-call style, not a working offensive payload:

// Schematic Bruce JS — illustrates module-call shape only.
// (Not a working payload; API names per the wiki module list.)
dialog.message("Starting demo on " + device.getBoardName());

// read a value and show it
var hit = wifi.scan(); // Hardware: WiFi module
console.log("networks seen: " + hit.length);

// write a log to SD
storage.write("/demo.log", "ran at boot\n"); // Core: Storage

// benign on-device feedback
notification.blink(2); // Core: Notification
dialog.message("Done.");

The point of the sketch is the programming model: core modules for UI/storage/timing, hardware modules for the radios and buses, plain JavaScript gluing them together, and the whole thing loaded as a file rather than compiled in. An engineer can read the wiki’s per-module function lists and write to this surface directly.

9.8 Script storage, execution and BadUSB-via-interpreter

Scripts live on the SD card (the same removable storage the file managers in Vol 4 manage), are selected and run from the device’s Scripts menu, and execute on the on-device JS engine. This is the same SD-centric model Bruce uses for everything else — captured RF signals, card dumps, Evil Portal templates, and bruce.conf all live on the card — and it means building a library of scripts is a matter of dropping .js files into the card’s script directory. The SD + LittleFS storage model of Vol 4 is the substrate; the Scripts menu is the launcher.

The SD-centric model has two practical consequences an engineer will appreciate. First, scripts are portable and version-controllable off-device: a library of .js files can be developed and diffed on a workstation, kept under version control, and copied to any Bruce board’s card — the scripts are not tied to a particular device’s internal flash, which makes a tested script trivially reusable across the whole fleet of supported boards (the ones with the interpreter — recall the LITE build omits it). Second, the same WebUI and file-manager control surfaces of Vol 4 can be used to manage the script library over the board’s AP or over serial, so an operator is not forced to pull the card to add or edit a script. The card is the single source of truth for both the device’s configuration and its script library, which keeps the mental model simple: what the device can do out of the menu is fixed by the build, and what it can do beyond the menu is whatever .js files are on the card.

 microSD
 └── (script directory)
 ├── demo.js ← selected and run from the Scripts menu
 ├── payload-a.js ← JS; may call hardware modules incl. BadUSB
 └──... ← a user-built library, no recompile needed

The capability within the interpreter that most deserves an engineer’s attention is BadUSB via the interpreter. The Hardware module set includes a BadUSB module, which means a script can perform USB-HID keystroke injection programmatically — generating or sequencing keystrokes at runtime rather than replaying a fixed Ducky Script file. This is strictly more flexible than a static payload: a script can branch, time, and construct injection sequences conditionally. It is also, correspondingly, subject to the full keystroke-injection legal envelope — identical to the Bad BLE discussion in Vol 7 §4 and the whole of this hub’s Ducky Script deep dive. Driving a keyboard on a computer you are not authorized to control is unauthorized access whether the keystrokes come from a fixed .txt payload or a JavaScript program that generates them; the transport and the generator do not change the law.

Callout — the interpreter inherits every envelope it can reach. A script is only as lawful as the hardware modules it drives. A script that reads sensors and writes logs is benign; a script that transmits on the CC1101, floods BLE, or injects USB HID carries the exact legal weight of doing those things by hand. The scripting platform’s power is precisely that it can reach every offensive capability in the firmware, which means authorization discipline applies to scripts no less than to menu features. ../_shared/legal_ethics.md governs scripted actions identically to interactive ones.

A security consideration of the scripting platform deserves a dedicated note, because it is the kind of thing an engineer should think about before running community scripts. A Bruce script is arbitrary code with access to every hardware module — it can transmit, inject USB HID, touch the network stack, and read and write the SD card. Running a script someone else wrote is therefore exactly as much a trust decision as running any downloaded executable: a malicious or careless .js file could transmit where it should not, inject keystrokes unexpectedly, or exfiltrate captured data. The SD-card-and-plain-text model (§8) is a mitigation in that scripts are human-readable and can be reviewed before running — there is no compiled opacity — but the discipline of reading a script before running it is the operator’s responsibility, and it matters more on a device whose scripts can drive radios and a USB-HID keyboard than it would on a sandboxed platform. The interpreter’s power and its risk are the same property viewed from two sides.

A closing engineering observation: the interpreter is the reason Bruce resists easy comparison to the narrower firmwares. A Wi-Fi/BLE specialist with a fixed feature set can be fully characterized by its menu; Bruce cannot, because a scriptable JS surface over the full radio and bus complement is an open-ended capability. That open-endedness is a strength (extensibility without recompiling) and a caution (the firmware can do things its menu never names), and both sides of it are what Vol 10’s comparison has to weigh.

9.9 Summary and cross-references

Bruce’s RFID/NFC subsystem reads, clones, and NDEF-writes contactless cards at 13.56 MHz through a choice of PN532, PN532Killer, or (on the RF REAPER) ST25R3916 front-ends, reads 125 kHz LF tags more lightly, and supports Amiibolink and Chameleon companion devices — but its emulate tag entry is a not-yet-implemented menu stub, which is why it delegates emulation to companion hardware. The scripting interpreter runs JavaScript (derived from justinknight93’s Doolittle, not a bespoke dialect), exposes the device through Core and Hardware module families, loads scripts from the SD card, and reaches every offensive capability in the firmware including BadUSB HID injection — making Bruce a programmable platform rather than a fixed menu, at the cost of inheriting every legal envelope its modules can reach.

For the hardware that carries the ST25R3916 and the broadest RFID complement, see Vol 3 (RF REAPER); for the HAL, SD/LittleFS storage, and bruce.conf model the interpreter and reader configuration both rest on, see Vol 4; for the LITE build that drops the interpreter, see Vol 5. The dedicated RFID/NFC depth tools this subsystem complements rather than replaces are covered in the iCopy-X, Proxmark3, Flipper Zero, and especially the Chameleon Ultra deep dives in this hub — the Chameleon Ultra being the emulation platform Bruce’s companion-device support points at. The Ducky Script deep dive covers the keystroke-injection payload language that the BadUSB module and Bad BLE (Vol 7 §4) both replay. And the binding authorization and RF-law framing for every active capability named here is ../_shared/legal_ethics.md.

Comments (0)

  1. Loading…

Comments are held for moderation — nothing appears until approved.