T09 · Insecure Skill Coding Practices
- Location
assets/campaign-mode.html:2144- Finding
Unauthenticated relay peers can inject executable HTML into network-game clients
- Content
View full analysis
Vulnerability Details
File Location:
net-server.js:92-103;assets/campaign-mode.html:2144, 2179-2192;assets/skirmish-mode.html:2610, 2647-2649, 2814
Vulnerability Type: Unauthenticated message injection leading to DOM-based cross-site scripting
Risk Level: HighTechnical Analysis
The WebSocket relay permits any connected peer to join an arbitrary room by sending a
HELLOmessage. It does not authenticate the peer, validate the claimed role, limit a room to an authenticated host and client, or enforce which message types each role may send.js // net-server.js:92-103 if (msg.t === 'HELLO') { peer.room = String(msg.room || 'qiguo'); if (!rooms[peer.room]) rooms[peer.room] = new Set(); rooms[peer.room].add(peer); const n = rooms[peer.room].size; peer.socket.write(encodeFrame(0x1, Buffer.from(JSON.stringify({ t: 'ROOM', room: peer.room, size: n })))); if (n >= 2) { broadcast(peer.room, { t: 'READY' }, null); } return; } if (peer.room) broadcast(peer.room, msg, peer);Consequently, any peer in the room can send a forged
STATEmessage, and the relay broadcasts it without verifying that it originated from the legitimate host.The campaign client dynamically dispatches incoming messages based solely on the attacker-controlled
tproperty:js // assets/campaign-mode.html:2144 ws.onmessage=e=>{ let m; try { m=JSON.parse(e.data); } catch(e) { return; } if(m&&m.t&&h[m.t]) h[m.t](m); };The client registers
onClientStateas the handler forSTATEand writes the suppliedlogHtmldirectly into the DOM:js // assets/campaign-mode.html:2179-2192 Net.on('STATE', onClientState); Net.send({t:'JOIN'}); function onClientState(m){ units = m.units.map(u=>Object.assign({},u)); terrain = m.terrain; scenario = m.scenario; turn=m.turn; weather=m.weather; combo=m.combo; turnCount=m.turnCount; battleResolved=m.over; lastWinner=m.winner; myCountry=m.enemyCountry; enemyCountry=m.myCou ...[truncated 2888 chars]- Remediation
View remediation
Remediation Suggestions
- Generate an unguessable room secret when the host creates a room, and require clients to prove possession of it.
- Assign roles server-side rather than trusting the
rolevalue supplied by a client. - Track the authenticated host connection for each room and accept
STATEmessages only from that connection. - Restrict clients to an explicit allowlist of permitted action messages and validate every message against a strict schema.
- Enforce a maximum of one host and the intended number of clients per room.
- Replace remote log HTML with structured, plain-text log records and render them using
textContent. - If limited formatting is required, construct known elements through DOM APIs or apply a strict allowlist sanitizer that removes event handlers, scripts, dangerous URLs, and unsupported elements.
- Add a restrictive Content Security Policy that disallows inline scripts and inline event handlers as defense in depth.
- Reject malformed state objects before accessing or rendering their fields.
