WebSocket security testing framework for IoT, automotive, and connected devices.
⚠️ Authorized testing only. This tool is built for IoT security research, red-team engagements, and stress-testing your own embedded devices. Running it against systems you do not own or have explicit written permission to test is illegal in most jurisdictions and unethical everywhere. The author accepts no responsibility for misuse.
wsdos started as a 35-line WebSocket connection-flood PoC in 2019. v3.x is a real framework: passive vulnerability scanning, known-CVE fingerprinting, frame-level fuzzing, vendor presets for the most common IoT / robotics WebSocket endpoints, and the original DoS modes (now async). v3.1 hardened the detection logic so every finding is falsifiable; see CHANGELOG.md.
| Check | Detects |
|---|---|
cswsh |
Missing or weak Origin enforcement → Cross-Site WebSocket Hijacking |
auth |
Endpoints accepting no auth at all, JWT alg:none, tokens in query strings |
compression |
permessage-deflate amplification (CVE-2020-7662 family) |
frames |
RFC 6455 violations: unmasked client frames, bad RSV bits, reserved opcodes, oversize length, dangling continuations |
smuggling |
HTTP request smuggling at the Upgrade boundary |
tls |
Plaintext ws://, obsolete TLS versions, weak ciphers, untrusted certs |
cves |
Fingerprint matching against 4 verified WebSocket CVEs in the ws (npm), websocket-extensions, and socket.io-parser stacks |
| Mode | Pressure pattern |
|---|---|
flood |
Maximize concurrent open connections (fd / accept-loop exhaustion) |
slowloris |
Hold many sockets open with periodic 1-byte frames (timeout-budget) |
frame-flood |
Blast rapid small frames over few connections (parser CPU) |
payload |
Send oversized frames (memory pressure) |
compression |
Ship highly-compressible payloads (decoder DoS) |
| Category | Presets |
|---|---|
robotics |
ros1-rosbridge, ros2-rosbridge, foxglove-bridge |
iot-smart-home |
home-assistant, esphome-dashboard, shelly-gen2-rpc, tasmota-console |
iot-3dprinter |
octoprint |
Every preset cites its primary source URL in wsdoscore/presets.py. Earlier
versions of this README listed ~18 presets across 7 categories; that table
contained guesses for vendors whose protocols I had not verified. v3.1.0
removed the unverified entries. PRs adding new verified presets (with a
primary source cite) are welcome.
git clone https://github.com/V33RU/wsdos-iotdevice.git
cd wsdos-iotdevice
pip install -r requirements.txtPython 3.8+.
python3 wsdos.py probe ws://192.168.1.42:8080/python3 wsdos.py scan ws://192.168.1.42:8080/ --format prettyOutput (pretty, with confidence annotation per finding):
=== wsdos scan report ===
Target: wss://192.168.1.42:8080/
Findings: 8
Summary: [HIGH] 2 [MEDIUM] 1 [INFO] 5
[HIGH] (high) cswsh.origin-enforcement No Origin enforcement: every Origin (including attacker.example, null, file://) is accepted
baseline_no_origin_accepted=True (ok). 6/6 forged Origins accepted: ...
-> https://owasp.org/www-community/attacks/Cross_Site_WebSocket_Hijacking_(CSWSH)
[HIGH] (medium) auth.handshake-open WebSocket handshake accepts unauthenticated clients
Server completed the handshake with no Authorization / Cookie / X-API-Key header.
This is acceptable only if the application enforces auth in the first WS message.
...
python3 wsdos.py vuln cswsh ws://192.168.1.42:8080/
python3 wsdos.py vuln cves ws://target/
python3 wsdos.py vuln tls wss://device.lan/ws -k# list everything we know
python3 wsdos.py preset list
# show details
python3 wsdos.py preset show ros2-rosbridge
# scan with the preset's defaults (right port, path, scheme)
python3 wsdos.py preset scan shelly-gen2-rpc 192.168.1.50
python3 wsdos.py preset scan ros2-rosbridge 10.0.0.7
python3 wsdos.py preset scan home-assistant 192.168.1.100 --format markdown -o ha.md# pretty (default)
python3 wsdos.py scan <target>
# JSON for piping
python3 wsdos.py scan <target> --format json > report.json
# Markdown for tickets / engagement reports
python3 wsdos.py scan <target> --format markdown -o report.mdpython3 wsdos.py scan <target> --only cswsh,auth,tls
python3 wsdos.py scan <target> --skip frames,smugglingpython3 wsdos.py flood ws://192.168.1.42:8080/ -n 5000 -c 500 -d 60 \
--i-have-authorization
python3 wsdos.py slowloris ws://192.168.1.42:8080/ -n 800 -i 20 \
--i-have-authorization
python3 wsdos.py frame-flood ws://192.168.1.42:8080/api -w 20 --payload-size 256 -d 30 \
--i-have-authorization
python3 wsdos.py payload ws://192.168.1.42:8080/ -w 5 --payload-size 8388608 --frames 50 \
--i-have-authorization
python3 wsdos.py compression wss://device.lan/ws -k -w 4 --payload-size 10485760 -d 30 \
--i-have-authorizationEvery entry has been verified against NVD. Earlier versions of this README listed 10 CVEs; on verification, six of them either pointed to unrelated products (Apache UIMA, Traefik, Taiwanese-language packages) or were real CVEs but not WebSocket-specific. v3.1.0 trimmed the registry to the four that are both real and WebSocket-specific:
| CVE | Stack | Severity |
|---|---|---|
| CVE-2020-7662 | websocket-extensions (Node.js) < 0.1.4 , ReDoS via Sec-WebSocket-Extensions header |
HIGH (7.5) |
| CVE-2021-32640 | ws (npm) 5.0.0-6.2.1, 7.0.0-7.4.5 , ReDoS via Sec-WebSocket-Protocol header |
MEDIUM (5.3) |
| CVE-2024-37890 | ws (npm) < 8.17.1 , DoS via requests exceeding server.maxHeadersCount |
HIGH (7.5) |
| CVE-2023-32695 | socket.io-parser 3.4.0-3.4.2, 4.0.4-4.2.2 , DoS via crafted packet (uncaught exception) |
HIGH (7.5) |
The check is a fingerprint match against Server/X-Powered-By response
headers, not active exploitation. A match is a hint, not proof , always
verify the installed version against the affected range before reporting.
PRs adding more verified WebSocket CVEs (with NVD link + primary advisory)
are welcome.
Verified preset coverage (everything below has a primary-source citation
in wsdoscore/presets.py):
| Device class | Typical paths / ports | What to test |
|---|---|---|
| Shelly Gen2/Gen3 relays | /rpc on :80 |
auth, cswsh (often no auth at all on LAN) |
| Tasmota firmware | /ws on :80 |
auth, cswsh |
| ESPHome dashboard | / on :6052 |
auth, cswsh |
| Home Assistant | /api/websocket on :8123 |
auth (token bypass), cves |
| OctoPrint (3D printer) | /sockjs/websocket on :5000 |
cswsh, auth |
| ROS 1/2 rosbridge | / on :9090 |
auth (default = NONE), frames, see iotsrg/awesome-ros-security |
| Foxglove bridge | / on :8765 |
auth, compression |
Not yet covered (would welcome PRs with vendor-doc citations): industrial fieldbus-over-WS (Modbus / BACnet / OPC UA), automotive (OBD WiFi, Tesla firehose, OVMS), IP cameras with proprietary WS bridges, building management (Niagara N4). Earlier versions of this README claimed coverage of those; on verification the entries were guesses, so v3.1.0 removed them.
wsdos.py # CLI dispatcher
wsdoscore/
├── __init__.py
├── common.py # Metrics, URL helpers, Finding dataclass
├── recon.py # probe command
├── stress.py # 5 attack modes
├── presets.py # 8 vendor presets (each with source citation)
├── report.py # JSON / pretty / Markdown emitters
└── vuln/
├── __init__.py # CHECK_REGISTRY
├── cswsh.py # Cross-Site WebSocket Hijacking
├── auth.py # no-auth, JWT alg:none, query-string tokens
├── compression.py # permessage-deflate amplification
├── frames.py # raw-socket RFC 6455 fuzzing
├── smuggling.py # HTTP/WS upgrade-boundary smuggling
├── tls.py # plaintext, weak versions, weak ciphers, untrusted certs
└── cves.py # known-CVE fingerprint registry
Adding a new vuln check:
- Create
wsdoscore/vuln/<name>.pyexportingasync def check(args) -> list[Finding] - Register in
wsdoscore/vuln/__init__.py wsdos vuln <name>andwsdos scanpick it up automatically.
| Symptom | Likely root cause |
|---|---|
| Connections cap out at ~1020 | ulimit -n on a per-process fd limit |
| Server stops accepting after N seconds | Per-IP connection cap or accept-queue exhaustion |
| CPU pegged but accept still works | Single-threaded WebSocket handler |
handshake_rejects jumps to non-zero |
Server-side rate-limiting kicked in (good!) |
| Web UI unreachable during flood | No separation between control-plane and data-plane |
| Device reboots / freezes | Watchdog or OOM kill |
cswsh reports HIGH |
Browser-based attack chain becomes available |
auth.no-auth-accepted HIGH |
Anyone on the LAN can drive the device |
frames.rfc6455-compliance HIGH |
Custom parser; consider deeper fuzzing |
tls.plaintext HIGH |
All traffic visible to on-path attackers |
Please follow coordinated disclosure with the vendor.
- For robots / ROS: also consider submitting to the Robot Vulnerability Database.
- For ICS / OT: ICS-CERT / CISA via cisa.gov/report.
- For consumer IoT: vendor PSIRT, then CVE Numbering Authority if no response within 90 days.
The original 2019 PoC video is still in the repo:
Original demo: https://www.youtube.com/watch?v=GhhDNFVsQBc
MIT, see LICENSE.
See also:
- iotsrg/awesome-ros-security , full robotics/ROS security curation
- V33RU/awesome-connected-things-sec , IoT / embedded / automotive / ICS resources
