$npx -y skills add SnailSploit/Claude-Red --skill offensive-z-waveZ-Wave attack methodology — sniffing with Z-Force / EZ-Wave / RTL-SDR + ZniffMobile, S0 (legacy) network-key derivation flaw and key reuse, S2 (modern) ECDH commissioning analysis, replay/injection on unauthenticated nodes, default-key brute-force on test deployments, and home-au
| 1 | # Z-Wave Attacks |
| 2 | |
| 3 | Z-Wave runs in the 800/900 MHz ISM band (US: 908 MHz, EU: 868 MHz). Older networks used the S0 security scheme with a fixed-derivation network key — long-known to be flawed. S2 (mandatory for Z-Wave Plus v2 since 2017) uses ECDH commissioning and is significantly stronger. |
| 4 | |
| 5 | ## Quick Workflow |
| 6 | |
| 7 | 1. Identify region (US 908 MHz / EU 868 MHz) — adapter frequency must match |
| 8 | 2. Sniff inclusion (commissioning) traffic — that's where keys are exchanged |
| 9 | 3. Determine S0 vs S2 from frame format |
| 10 | 4. For S0: derive/replay; for S2: analyze ECDH and look for implementation flaws |
| 11 | |
| 12 | --- |
| 13 | |
| 14 | ## Hardware |
| 15 | |
| 16 | | Adapter | Use | |
| 17 | |---|---| |
| 18 | | Z-Force (legacy, hard to find) | Original research tool | |
| 19 | | EZ-Wave (custom HackRF firmware) | Modern, full transceiver | |
| 20 | | Aeotec Z-Stick | Commercial controller, useful as legitimate node | |
| 21 | | HackRF + open Z-Wave firmware | Multi-band SDR approach | |
| 22 | | RTL-SDR + ZniffMobile (passive only) | Cheap sniffer | |
| 23 | |
| 24 | ## Sniffing |
| 25 | |
| 26 | ```bash |
| 27 | # EZ-Wave (HackRF firmware-based) |
| 28 | git clone https://github.com/cureHsu/EZ-Wave |
| 29 | ezwave-sniff -f 908.4MHz -o capture.pcap |
| 30 | |
| 31 | # Wireshark with the Z-Wave dissector parses captured frames |
| 32 | wireshark capture.pcap |
| 33 | ``` |
| 34 | |
| 35 | Look for the inclusion phase (controller adding new device) — that's where the network key is exchanged. |
| 36 | |
| 37 | ## S0 Security Flaw |
| 38 | |
| 39 | S0 derives the network key from a fixed all-zero PSK during the inclusion of the first device. That fixed material is well-known — any S0 network you sniff during inclusion can be decrypted offline. |
| 40 | |
| 41 | ``` |
| 42 | S0 commissioning: |
| 43 | 1. New node joins → controller sends key with zero-PSK encryption |
| 44 | 2. Attacker sniffs commissioning frame → derives session key |
| 45 | 3. All future S0 traffic on that network is decryptable |
| 46 | ``` |
| 47 | |
| 48 | If you can: |
| 49 | - Trigger inclusion (factory-reset a node, or wait for legitimate inclusion) |
| 50 | - Sniff during the ~2-second key-exchange window |
| 51 | |
| 52 | You own the network key for that mesh. |
| 53 | |
| 54 | ## S2 (Z-Wave Plus / S2 Authenticated) |
| 55 | |
| 56 | S2 fixes S0 by using ECDH for commissioning: |
| 57 | - Each device has a Curve25519 keypair |
| 58 | - Inclusion uses DSK (Device Specific Key) verified out-of-band (sticker/QR) |
| 59 | - Network key never traverses the air in plaintext |
| 60 | |
| 61 | S2 attack surface is mostly implementation: |
| 62 | - Inclusion-mode-always-open (controller misconfig) |
| 63 | - Firmware bugs in S2 verification |
| 64 | - Side-channel on ECDH on resource-constrained chips |
| 65 | - DSK printed on a sticker → physical access yields it |
| 66 | |
| 67 | ## Replay / Injection on Unauthenticated Nodes |
| 68 | |
| 69 | Many low-end Z-Wave devices (older sensors, basic switches) don't enforce S0 or S2 — they accept commands in cleartext. |
| 70 | |
| 71 | ```python |
| 72 | # scapy-zwave (community fork) for crafted frames |
| 73 | from scapy.contrib.zwave import * |
| 74 | frame = ZWave(home_id=0x12345678)/ZWaveBasic(set_value=0xff) |
| 75 | sendp(frame, iface='ezwave0') |
| 76 | ``` |
| 77 | |
| 78 | This unlocks doors / switches lights / unarms sensors when the target lacks authentication. |
| 79 | |
| 80 | ## Key Brute-Force |
| 81 | |
| 82 | For old test deployments using default home IDs / network keys: |
| 83 | |
| 84 | ```bash |
| 85 | # Try default home IDs |
| 86 | for hid in 0x00000000 0x12345678 ...; do |
| 87 | ezwave-test --home-id $hid --target-node 1 |
| 88 | done |
| 89 | ``` |
| 90 | |
| 91 | Hit rate on production is low; useful only for default-config IoT lab gear. |
| 92 | |
| 93 | ## Hub Pivots |
| 94 | |
| 95 | Z-Wave devices are typically controlled by a hub (SmartThings, Hubitat, Vera, Home Assistant, Z-Wave JS UI). The hub is a Linux device with the Z-Wave PSK in plaintext storage: |
| 96 | |
| 97 | - SmartThings Hub: previously cloud-only credentials; modern v3 stores network key locally |
| 98 | - Home Assistant: `~/.homeassistant/zwave_js.json` typically contains keys |
| 99 | - Hubitat: web UI with default password on older versions |
| 100 | |
| 101 | Compromise the hub → walk away with the Z-Wave PSK + every paired device's command authority. See `offensive-iot` for hub firmware extraction. |
| 102 | |
| 103 | ## Engagement Cheatsheet |
| 104 | |
| 105 | ```bash |
| 106 | # 1. Identify region + frequency |
| 107 | # US: 908.4 MHz; EU: 868.4 MHz; CN: 868.4 MHz |
| 108 | |
| 109 | # 2. Sniff |
| 110 | ezwave-sniff -f 908.4MHz -o cap.pcap |
| 111 | wireshark cap.pcap # filter zwave |
| 112 | |
| 113 | # 3. Identify S0 vs S2 from frame format |
| 114 | |
| 115 | # 4. For S0: capture inclusion → derive key → decrypt history + control devices |
| 116 | |
| 117 | # 5. For S2: focus on hub compromise / DSK theft / implementation bugs |
| 118 | |
| 119 | # 6. Test unauthenticated cleartext devices with crafted frames |
| 120 | ``` |
| 121 | |
| 122 | ## Detection |
| 123 | |
| 124 | - Most Z-Wave deployments have no IDS comparable to Wi-Fi/Zigbee monitoring |
| 125 | - Hub may log unexpected commands but UI rarely surfaces these to users |
| 126 | - Inclusion-mode-open is visible in hub UI but ignored by inattentive admins |
| 127 | |
| 128 | ## Reporting |
| 129 | |
| 130 | - Identify chipset / firmware revision per device (ZW0500 series, ZW7000 series) |
| 131 | - Map S |