XSS toolchain for the Switch 1 edition of Civilization 7. Only the 1.0.0 version of the game in physical cartridge is supported.
Tested at Switch 1 fw 22.5.0, Switch 2 fw 20.4.0 and 23.0.1 using pi pico 2 (RP2350) pi pico 1 (RP2040) is untested.
PicoSwitch — clean, compilable Pico firmware (RP2040 + RP2350) that types a chat-XSS payload into the Switch (USB HID macro). Default payload = the Remote JS Loader bootstrap (exploit core + TCP loader). Ships with compress.js (chat transport: terser -> deflateRaw -> base-25 chunked img/onerror lines with censor armor).
PicoSwitch — clean, compilable Pico firmware (RP2040 + RP2350) that types a chat-XSS payload into the Switch (USB HID macro).
Default payload = the Remote JS Loader bootstrap (exploit core + TCP loader).
Ships with compress.js (chat transport: terser -> deflateRaw -> base-25 chunked img/onerror lines with censor armor).
installer-creator — given any JavaScript payload, produces the boot-persistent installer ( XSTRIG.Civ7Save trigger written to savedata) to send through the Remote JS Loader.
installer-creator — given any JavaScript payload, produces the boot-persistent installer ( XSTRIG.Civ7Save trigger written to savedata) to send through the Remote JS Loader.
core.js at the root is the shared payload both components build from, and send_js.py at the root sends any payload to a running loader.
Build + flash the loader UF2 (see PicoSwitch/README.md). On the Switch, change system language to English(US). Then launch game and navigate main -> MULTIPLAYER -> WIRELESS -> CREATE GAME -> HOST LOBBY -> CHAT then plug the Pico in (in docked mode, focus the chat box first to receive Pico input). The Pico types the chat XSS (~87 lines, ~15 min); the Remote JS Loader comes up on the console (TCP :9026) for that session.
Build + flash the loader UF2 (see PicoSwitch/README.md).
On the Switch, change system language to English(US). Then launch game and navigate main -> MULTIPLAYER -> WIRELESS -> CREATE GAME -> HOST LOBBY -> CHAT then plug the Pico in (in docked mode, focus the chat box first to receive Pico input).
The Pico types the chat XSS (~87 lines, ~15 min); the Remote JS Loader comes up on the console (TCP :9026) for that session.
Install the persistent boot trigger from the PC: python installer-creator/install.py — uploads the save template, splices the compressed payload into it on-console, wipes save:/saves/Single/ , and writes the trigger.
Install the persistent boot trigger from the PC: python installer-creator/install.py — uploads the save template, splices the compressed payload into it on-console, wipes save:/saves/Single/ , and writes the trigger.
From then on, every boot: main -> Load Game -> the payload runs from savedata (and if the payload is the loader, :9026 is up at boot — and survives console sleep/wake).
From then on, every boot: main -> Load Game -> the payload runs from savedata (and if the payload is the loader, :9026 is up at boot — and survives console sleep/wake).
You can revert system language after installer finished.
You can revert system language after installer finished.
Sending payloads ( send_js.py )
Sends a JS file as-is to the Remote JS Loader (default port 9026), then prints the log output the console streams back (read until close or timeout).
Build (Pico firmware)
The JS exploit core in core.js referenced inject.js from Netflix-N-Hack and its fork by ufm42
The PicoSwitch firmware is based on Pico-Switch-Controller by Element2424
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
