Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

gif

[ Have a look at the demo: qwqoro.github.io/walletprint ]

Overview | Usage | More on the topic


A demo application, Proof-of-Concept – showing you what information can a specially crafted webpage extract from the Hardware Crypto Wallet you've connected via USB.

preview preview


📦 Overview

[⚠️] Disclaimer

  1. Yes, the demo itself is vibecoded, sorry about that!

  2. Prerequisites & key features:

    • Silent = 0-clicks / approvals / notifications on the wallet;
    • Basic data leak (model, OS version, battery %): CONNECTED VIA USB, LOCKED / UNLOCKED
    • App-specific data leak (version, settings, wallet address): CONNECTED VIA USB, UNLOCKED, TARGET APP IS ACTIVE
  3. It is important to understand that this demo is based on a legitimate functionality, which works as intended – both WebHID & WebUSB expect you to trust the "host" device you connect your "peripheral" device to.

  4. No truly sensitive data (PIN, private keys) gets leaked through the method utilized here – your assets remain safe.

  5. This demo should not be used to make assumptions about companies or their products; No part of this project should be interpreted as one's (mine, yours, ours, employer's, ...) opinion.

  6. This demo must not be used for any purpose, other than research – run against your own / explicitly authorized devices only.


A single-page live demo app that, the moment it gains WebHID / WebUSB access, harvests everything the transport reveals about a connected hardware wallet with no on-device prompts.

Client-Side & Read-only: no sensitive data is ever requested + nothing leaves the browser (it is a single html page hosted on Github pages, so..).

📊 Techniques & Results

Target Technique Silent? Impact / Leaked data
Ledger HID descriptor (vendorId / productId) âś… Exact model
Ledger getDevices() on return visit âś… Re-identifies the device with no chooser
Ledger GET_APP_AND_VERSION (b0 01 00 00 00) âś… Running app + its version
Ledger getDeviceInfo (e0 01 00 00 00) ✅ SE firmware, MCU, targetId → model
Ledger e0 10 âś… Battery %
Ledger listApps (e0 de / e0 df) ❌ List of installed applications
Ledger ETH getAppConfiguration (e0 06 00 00 00) âś… ETH app settings: blind signing, ERC-20, Starknet
Ledger ETH getAddress e0 02 00 00 (P1=0) âś… ETH addresses (= USDC/ERC-20, Hyperliquid)
Ledger BTC [legacy] getWalletPublicKey (e0 40, P1=0) ✅ Account xpub / zpub → every address + full balance / history offline + first receive addresses
Ledger BTC [new] GET_MASTER_FINGERPRINT + GET_EXTENDED_PUBKEY (e1 00, display=0) ✅ Account xpub / zpub → every address + full balance / history offline + first receive addresses
Ledger Solana e0 05 / Tron e0 02 / XRP e0 02 / Hyperliquid e0 02, ... (P1=0 / display=0) âś… SOL, TRX, XRP, Hyperliquid, ... addresses
Ledger TON / GRAM e0 05 ✅ ed25519 public key (address needs wallet-contract — experimental)
Trezor GetFeatures (WebUSB, protobuf) âś… Wallet labels, device_id, language, model, fw, PIN/passphrase settings
Trezor / other WebUSB device descriptor âś… serialNumber (stable cross-site tracker), version

The app auto-detects which protocol the active app uses.

  • USDC has no address of its own; it rides the ETH (ERC-20), Solana (SPL), Tron (TRC-20) address
  • Non-EVM / Bitcoin addresses that aren't returned by the device (e.g: XRP, the new BTC app's receive addresses, ...) are derived Client-side (hash160 + bech32 / base58check), thus flagged [verify] in the UI

App Ledger Live address path prefix
EVM (ETH / Hyperliquid) 44'/60'/N'/0/0
Solana 44'/501'/N'/0/0
Tron 44'/195'/N'/0/0
XRP 44'/144'/N'/0/0
TON / GRAM 44'/607'/N'/0/0
BTC (Legacy) 44'/0'/N'
BTC (Nested SegWit) 49'/0'/N'
BTC (Native SegWit) 84'/0'/N'
BTC (Taproot) 86'/0'/N'

 

🪫 Limitations

  • PIN / button presses — handled securely, never streamed to the "host" device;
  • Account names — Ledger / Trezor store these names in Ledger Live / Trezor Suite metadata off-device;
  • Pending operations / Statuses of inactive apps — requests / responses are handled within a "1-host" session, so this app cannot keep "watching" while another operation takes place ( // this is the exact behaviour the Spam mode relies on);

 

⚙️ Usage

  1. Go to qwqoro.github.io/walletprint
  2. Turn on your hardware wallet + Connect it via USB
  3. Click Connect WebHID (Ledger) / Connect WebUSB (Trezor), select your hardware wallet / [Enable Passive mode] → Watch Passive fingerprint
    • [For "DoS"] Enable Spam mode → Watch errors in other apps that try to connect to the wallet
    • [For app data leak] Unlock + Open the target app → Watch Active fingerprint & Eavesdropped account data

🩺 Passive mode

â € â €
Default value ✅ Enabled
Behaviour Re-fingerprints every already-granted device
• Every 1 sec: checks which app is active;
• Every app change: performs full harvest

🚿 Spam mode

â € â €
Default value ⭕️ Disabled
Behaviour Every 1 sec: sends "NOP" requests to block other possible requests to the device

When the Spam mode is on, the page fires a lot of "NOPs" at the Ledger via HIDDevice.sendReport.

// WebHID has no NOP primitive, so this application uses an OUT report with a harmless payload. The device serialises USB work → a steady stream contends with other hosts (e.g: Ledger Live). The status bar shows the rate (packets/s) and running total — the rate is controlled with the setInterval(spamTick, …).


đź“‘ More on the topic

About

🩺 [Research] Demo PoC: silent Ledger / Trezor fingerprinting <> WebHID / WebUSB

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages