Skip to content

Security review: CarWatch dashboard (:8088) surface #14

Description

@ThinkOffApp

Scope

Security review of CarWatch itself — the Python in carwatch/ and the
in-car dashboard it serves on :8088. That port listens on all interfaces
and carries the sensitive controls (OBD commands, self-update, the
manufacturer-cloud provider), so it is the real attack surface.

Note for reviewers: findings about su2 / xforbot / /api/sync /
Supabase belong to other codebases, not this repo. CarWatch is pure
Python standard library — no Supabase, no JS, no service-role keys
(verified). Please review against carwatch/*.py only.

Auth model (context)

webchat.py gates by network origin: a request that did not arrive
through the dial-out tunnel (i.e. it is on the same LAN/hotspot as the Pi) is
trusted and needs no token; a request that came through the tunnel must carry
the dashboard token (Authorization: Bearer or ?t=). do_GET and
do_POST both enforce this.

Findings

1. HA token can be exfiltrated by a same-LAN device (real, fix in progress)

POST /api/cloudcar/ha-url repoints the Mercedes provider at an arbitrary
URL, and the provider then sends the Home Assistant Bearer token to that
URL on its next poll. Because same-LAN requests are unauthenticated, any
device sharing the Pi's LAN/hotspot can point the provider at an attacker
host and capture the HA token — which grants persistent remote control of the
victim's whole Home Assistant, even after leaving the LAN. This is an
escalation beyond the existing LAN-trust boundary (LAN access → permanent
remote HA access).

Fix: only ever send the HA token to a private / loopback / Tailscale /
.local target; refuse public hosts. This also matches the
Tailscale remote-access direction (HA
should be reached over a private mesh, never a public URL). Landing with the
Tailscale switch so cloud access has no downtime.

To review (open)

  • webchat.py: the tunnel-origin auth (header-based _came_through_tunnel)
    and whether any sensitive action should require the token even on-LAN.
  • mercedesme.py: the command allowlist (lock / close-windows only — unlock/
    open/engine are intentionally not expressible) and VIN handling (kept out
    of serialized state).
  • elm327.py / obdwatch.py: input handling on the serial/CAN parse path.
  • /api/update reachable from anywhere with the token — confirm that is the
    intended trade-off.

Not affected

No secrets in the repo or its history (independent scans clean). Zero
third-party dependencies. The manufacturer-cloud module is read-only by
construction except the two make-safe, PIN-free commands.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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