Simon Willison's Battledrones v14 release — 'Compass Rose,' a four-player map — is, on its face, a hobby game update. But buried in the release notes is a detail every defender should pay attention to: the WebSocket multiplayer network code supporting up to four concurrent human players was written by an AI coding assistant (Claude Opus 4.5) from a single natural-language prompt. No CVE, no exploit, no breach — but a perfect, real-world case study of a trend we're seeing across every client environment we assess in 2026: AI-generated network-facing code shipping to production with minimal human security review.
This post is not about the game. It's about the defensive posture your organization needs as AI-assisted development pushes more WebSocket servers, real-time APIs, and custom protocol handlers into your attack surface — often written by developers (or non-developers) who have never had to think about connection flood protection, authentication, or protocol abuse.
Why This Matters to Defenders
The prompt quoted in the release is instructive:
Add a new map for a four player battle — four way rotational symmetry, up to four AI players, works with the WebSocket non-audio based multiplayer mode so up to four human players can join (any spare slots use AI)
That's a complete specification for a multi-client, stateful, internet-facing WebSocket service — with zero mention of authentication, rate limiting, input validation, session management, or connection limits. And the AI delivered exactly what was asked: functional multiplayer networking code, with security controls absent because they were never requested.
This is the systemic risk pattern we now encounter in penetration tests and IR engagements:
- AI coding assistants optimize for the prompt, not the threat model. Unless the developer explicitly asks for authentication, input validation, and resource limits, generated network code typically ships without them.
- WebSocket services bypass traditional HTTP security controls. WAFs, reverse proxy rules, and API gateways frequently inspect the initial HTTP Upgrade request and then ignore the persistent, bidirectional frame traffic that follows.
- Shadow deployments are exploding. Hobby projects, internal tools, and 'quick prototypes' — exactly like this one — end up exposed on cloud VMs and developer workstations with listeners the SOC doesn't know exist.
- Protocol abuse is cheap. An unauthenticated WebSocket endpoint is trivially enumerable (
nmap,zgrab2, Shodan) and abusable for resource exhaustion, application-layer fuzzing, and as a foothold into whatever the backend process can reach.
Technical Analysis: The Exposure Profile of AI-Generated WebSocket Services
What's Actually Running
Services like the Battledrones multiplayer server typically share this profile:
- Runtime: Node.js (
wslibrary), Python (websockets,aiohttp), or Go — spawned by a script interpreter rather than a hardened service binary - Transport: Raw
ws://(unencrypted) by default unless TLS was explicitly requested in the prompt - Authentication: None, or a trivially guessable room code
- Input handling: JSON messages parsed directly into game state — a classic injection/deserialization surface
- Resource controls: No per-IP connection limits, no frame size caps, no message rate throttling
Attack Surface From a Defender's Perspective
| Risk | Mechanism | Impact |
|---|---|---|
| Unauthenticated access | No handshake validation after HTTP Upgrade | Unauthorized clients join sessions, scrape state, inject messages |
| Connection exhaustion | Unlimited concurrent connections per source IP | Process memory exhaustion, denial of service |
| Message flooding | No per-frame rate limiting | CPU exhaustion in JSON parse/dispatch loops |
| Malformed frame handling | Unvalidated message schemas | Crashes, unexpected state transitions, potential memory-safety issues in native bindings |
| Cleartext transport | ws:// instead of wss:// | Session hijacking, message tampering on-path |
| Interpreter-spawned listeners | node server.js / python server.py on workstations | Shadow IT, unmanaged listeners invisible to EDR policy |
Exploitation Status
There is no known vulnerability in Battledrones itself, and this is not a CVE-driven advisory. The threat here is architectural and procedural: the growing population of AI-generated, internet-facing WebSocket services deployed without security review. Internet-wide scan data throughout 2025 and 2026 consistently shows exposed WebSocket endpoints being enumerated within hours of deployment, and we've responded to multiple incidents where an unauthenticated real-time service was the initial reconnaissance target that led attackers to adjacent, higher-value services on the same host.
Detection & Response
The highest-fidelity detection opportunity isn't the WebSocket protocol itself — it's the operational footprint of these services: script interpreters opening listening sockets, unexpected new listeners on server fleets, and anomalous connection fan-in to non-standard ports. The rules below target those observables, which are reliable indicators regardless of the application running on top.
Sigma Rules
---
title: Script Interpreter Spawning Network Listener
tid: 3f8a2c41-9b6e-4d5a-a1c7-8e2f4b9d0c3a
status: experimental
description: Detects script runtimes (Node.js, Python) spawning processes or binding listeners, a common footprint of ad-hoc and AI-generated WebSocket services deployed outside change control.
references:
- https://attack.mitre.org/techniques/T1059/
- https://attack.mitre.org/techniques/T1571/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.execution
- attack.t1059.007
- attack.t1059.006
logsource:
category: process_creation
product: windows
detection:
selection_runtime:
Image|endswith:
- '\node.exe'
- '\python.exe'
- '\python3.exe'
- '\deno.exe'
- '\bun.exe'
selection_script:
CommandLine|contains:
- 'server.js'
- 'websocket'
- 'ws.listen'
- 'websockets.serve'
- 'aiohttp'
- 'socket.io'
condition: selection_runtime and selection_script
falsepositives:
- Approved development workstations with known Node.js/Python service projects
- Build and CI agents running local test servers
level: medium
---
title: Outbound WebSocket Connection From Server Workload
tid: 91c4d7e2-5a3b-4f8c-b6d9-2e7a1c5f8b3d
status: experimental
description: Detects processes on server-class systems initiating outbound WebSocket connections to non-standard ports, which may indicate command-and-control over WebSocket channels or unauthorized real-time service enrollment.
references:
- https://attack.mitre.org/techniques/T1071.001/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.command_and_control
- attack.t1071.001
logsource:
category: network_connection
product: windows
detection:
selection:
DestinationPort:
- 8080
- 8443
- 3000
- 3001
- 8000
- 9000
filter_browsers:
Image|endswith:
- '\chrome.exe'
- '\msedge.exe'
- '\firefox.exe'
- '\brave.exe'
condition: selection and not filter_browsers
falsepositives:
- Legitimate application backends using WebSocket APIs on alternate ports
- Development tooling and hot-reload services
level: low
---
title: New Listening Socket Established by Non-Service Binary
tid: b2e6f1a8-7c4d-4e9f-a3b5-6d8c2f1e9a4b
status: experimental
description: Detects user-space script interpreters binding network listeners on Linux hosts, indicating potential shadow WebSocket or ad-hoc service deployment outside configuration management.
references:
- https://attack.mitre.org/techniques/T1571/
author: Security Arsenal
date: 2026/04/06
tags:
- attack.command_and_control
- attack.t1571
logsource:
category: process_creation
product: linux
detection:
selection:
Image|endswith:
- '/node'
- '/python3'
- '/python'
- '/deno'
CommandLine|contains:
- 'websocket'
- 'server.js'
- 'ws.Server'
- 'websockets.serve'
- 'socket.io'
- '--port'
condition: selection
falsepositives:
- Managed application deployments via systemd or container orchestration
- Developer environments with authorized local services
level: medium
KQL — Microsoft Sentinel / Defender Hunt
This query hunts for script interpreters establishing network listeners or handling high fan-in connection volumes — the operational signature of an unmanaged WebSocket service. It works against DeviceNetworkEvents in Defender-integrated environments and against Syslog/CommonSecurityLog where Linux telemetry is forwarded to Sentinel.
// Hunt: script interpreters with listening sockets or high connection fan-in
// Identifies shadow WebSocket / ad-hoc real-time services
let ScriptRuntimes = dynamic(["node", "node.exe", "python", "python3", "python.exe", "deno", "bun"]);
DeviceNetworkEvents
| where TimeGenerated > ago(7d)
| where InitiatingProcessFileName has_any (ScriptRuntimes)
| where ActionType == "ConnectionSuccess" or ActionType == "InboundConnectionAccepted"
| extend IsNonStandardPort = RemotePort !in (80, 443, 53, 25, 587, 993, 995)
| summarize Connections = count(),
UniqueRemoteIPs = dcount(RemoteIP),
Ports = make_set(RemotePort),
SampleCommandLine = any(InitiatingProcessCommandLine)
by DeviceName, InitiatingProcessFileName, LocalPort
| where UniqueRemoteIPs > 10 or IsNonStandardPort
| project DeviceName, InitiatingProcessFileName, LocalPort, Connections, UniqueRemoteIPs, Ports, SampleCommandLine
| sort by UniqueRemoteIPs desc;
// Companion hunt: Syslog-ingested Linux hosts showing new ws/wss listeners
Syslog
| where TimeGenerated > ago(7d)
| where SyslogMessage has_any ("websocket", "ws://", "wss://", "socket.io", "listening on")
| where ProcessName in~ ("node", "python3", "python", "deno")
| summarize Hits = count(), Samples = make_set(SyslogMessage, 5)
by Computer, ProcessName
| sort by Hits desc;
Tune the UniqueRemoteIPs > 10 threshold to your environment. In our managed SOC we suppress known CI/CD subnets and developer VLANs via a watchlist join rather than hardcoding exclusions.
Velociraptor VQL — Endpoint Listener Audit
Use this artifact to sweep a fleet for script interpreters holding open listening sockets — the fastest way to inventory shadow WebSocket services during an assessment or IR scoping exercise.
-- Artifact: Inventory script-interpreter processes holding listening sockets
-- Use: hunt across server and workstation fleets to find unmanaged WebSocket/real-time services
LET listeners = SELECT Pid, Name, CommandLine, Exe, Username
FROM pslist()
WHERE Name =~ '(?i)node|python|deno|bun'
SELECT l.Pid, l.Name, l.CommandLine, l.Username,
n.Family, n.Type, n.Laddr, n.Lport, n.Status
FROM listeners AS l
JOIN (
SELECT Pid, Family, Type, Laddr, Lport, Status
FROM netstat()
WHERE Status = 'LISTEN'
) AS n
ON l.Pid = n.Pid
Hardening & Verification Script
Run this on hosts where Node.js/Python services are expected, to enumerate actual listeners, flag unencrypted or unauthenticated WebSocket endpoints, and verify firewall posture.
#!/bin/bash
# audit-websocket-exposure.sh — Inventory and flag risky WebSocket-style listeners
# Run on Linux hosts; requires ss, and root for full PID attribution
echo "=== [1] Script interpreters holding LISTEN sockets ==="
ss -tlnp 2>/dev/null | grep -Ei 'node|python|deno|bun' || echo "None found."
echo ""
echo "=== [2] All listeners on non-standard service ports ==="
ss -tlnp 2>/dev/null | awk 'NR>1 {split($4,a,":"); port=a[length(a)]; \
if (port != 22 && port != 80 && port != 443 && port != 53) print}'
echo ""
echo "=== [3] Probe local endpoints for WebSocket Upgrade acceptance ==="
for port in $(ss -tln | awk 'NR>1 {split($4,a,":"); print a[length(a)]}' | sort -u); do
resp=$(curl -s -o /dev/null -w '%{http_code}' --max-time 2 \
-H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
-H "Sec-WebSocket-Version: 13" \
"http://127.0.0.1:${port}/" 2>/dev/null)
if [ "$resp" = "101" ]; then
echo "[!] Port ${port}: WebSocket Upgrade ACCEPTED (101) — verify authn/TLS controls"
fi
done
echo ""
echo "=== [4] Firewall posture check ==="
if command -v nft >/dev/null 2>&1; then
nft list ruleset 2>/dev/null | grep -c 'drop' | xargs -I{} echo "nftables drop rules present: {}"
elif command -v iptables >/dev/null 2>&1; then
iptables -L INPUT -n 2>/dev/null | head -5
fi
echo ""
echo "=== [5] systemd unit check for unmanaged services ==="
systemctl list-units --type=service --state=running 2>/dev/null | grep -Ei 'node|python|websocket' || echo "No matching systemd services."
For Windows endpoints hosting Node.js services, the PowerShell equivalent:
# Audit script-interpreter listeners on Windows
Get-NetTCPConnection -State Listen | ForEach-Object {
$proc = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
if ($proc.ProcessName -match 'node|python|deno|bun') {
[PSCustomObject]@{
Port = $_.LocalPort
Address = $_.LocalAddress
Process = $proc.ProcessName
Path = $proc.Path
CommandLine = (Get-CimInstance Win32_Process -Filter "ProcessId=$($_.OwningProcess)").CommandLine
}
}
} | Format-Table -AutoSize
Remediation & Hardening Guidance
Since there is no vendor patch here — the remediation is architectural — apply these controls to any internally developed or AI-generated WebSocket service before it reaches production, and retroactively to anything the inventory scripts above uncover:
- Mandate security requirements in AI-assisted development. Update your secure SDLC so that prompts and generated code reviews explicitly require: authentication on the Upgrade handshake, per-IP connection limits, maximum frame size, message schema validation, and rate limiting. Treat AI-generated network code as untrusted third-party code — review it line by line.
- Terminate TLS at a reverse proxy; never expose raw
ws://. Put nginx, Caddy, or Envoy in front, enforcewss://, and let the proxy absorb connection-flood pressure withlimit_connandlimit_reqdirectives. - Enforce message schema validation. Every frame should be parsed against a strict schema (e.g., JSON Schema, zod for Node.js, pydantic for Python) before touching application state. Reject and log anything else.
- Bound resources explicitly. Cap concurrent connections per source IP, cap total connections, set idle timeouts (30–60s), and enforce maximum message sizes (8–16 KB is generous for most real-time apps).
- Authenticate before the Upgrade completes. Validate a signed token (JWT or HMAC session ticket) during the HTTP Upgrade request — not after the socket is established. Reject with HTTP 401 before protocol switch.
- Inventory and govern. Add the VQL listener audit above to your recurring hunt schedule. Any interpreter-spawned listener that isn't tied to a managed deployment (systemd unit, container, or registered service) is shadow IT — remediate or decommission.
- Segment. Real-time prototype services belong in a segmented VLAN or VPC subnet with no lateral reach to production data stores or identity infrastructure.
Executive Takeaways
- AI coding assistants now routinely produce functional, internet-facing network services from a single prompt — with security controls absent unless explicitly requested. Your secure development policy must account for this.
- WebSocket traffic evades most HTTP-layer inspection. Detection must focus on the operational footprint: interpreter-spawned listeners, non-standard ports, and connection fan-in anomalies.
- Shadow real-time services are enumerable by internet scanners within hours. Assume anything exposed is already indexed.
- Inventory before you harden: you cannot secure WebSocket endpoints you don't know exist. Fleet-wide listener audits should run weekly, not annually.
Related Resources
Security Arsenal Managed SOC Services AlertMonitor Platform Book a SOC Assessment soc-mdr Intel Hub
Is your security operations ready?
Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.