Affected Versions
| Affected | not applicable (first-party code) |
| Fixed in | commit-based fix (see PR) |
| Ecosystem | N/A |
| CVE / GHSA | not assigned |
| CWE | CWE-287 (Improper Authentication) |
Introduction
The Tung Tung Tracker's administrative API endpoints—responsible for data synchronization, backfill operations, and overlay visibility—sat exposed behind only two weak controls: a localhost origin check and a static X-Tracker: 1 HTTP header. The guard() function in the request handler performed no user identity validation, meaning any process running on the local machine could trigger privileged operations by simply knowing the header value.
This is a textbook CWE-287 vulnerability: authentication that verifies something other than the claimed identity. Origin validation proves where a request came from, not who made it. The static header added no meaningful barrier—it's a secret shared with every local process by default.
The Vulnerability Explained
The vulnerable code relied on exception-based handling for expired tokens in JWT decoding, but more critically, the administrative endpoints bypassed meaningful authentication entirely:
# Before: guard() only checked localhost and static header
def test_post_requires_custom_header(client):
assert client.post("/api/sync").status_code == 403
assert client.post("/api/sync", headers={"X-Tracker": "1"}).status_code == 200
The test itself documented the vulnerability: presenting X-Tracker: 1 was sufficient to access /api/sync, /api/backfill, /api/show, and /api/overlay/hide. No session, no user identity, no per-instance secret.
Attack scenario: A malicious local process—perhaps a compromised npm package or a rogue browser extension with local access—iterates through ports 8000-9000, sends POST /api/sync with X-Tracker: 1, and triggers data synchronization against the user's intent. The same applies to backfill operations (potentially expensive) or overlay manipulation (UI disruption).
The make_server("127.0.0.1", port, app, threaded=True) binding meant the API was intentionally local-only, but "local" in modern systems includes sandboxed apps, containerized services, and browser extensions—all of which share the localhost interface.
The Fix
The fix introduces two complementary authentication mechanisms:
1. Session-based authentication for web clients
The X-Tracker header check is removed. Instead, loading pages like / or /overlay establishes a Flask session cookie, which subsequent mutating requests must present:
# After: session established by page load, no static header needed
def test_post_requires_custom_header(client):
assert client.post("/api/sync").status_code == 403
client.get("/") # loading our own page grants the session
assert client.post("/api/sync").status_code == 200
2. IPC secret for desktop-to-server communication
The desktop application cannot use session cookies (it calls from a native process), so it receives a random secret passed through app.config["IPC_SECRET"]:
# app.py change
- instance.publish(port)
+ instance.publish(port, app.config["IPC_SECRET"])
The desktop client presents this token on self-wake calls, proven by a dedicated test:
def test_show_requires_ipc_token(tmp_path):
"""The desktop app's self-wake call has no session, so it must present the random IPC token."""
This bifurcated design elegantly solves both use cases: web clients get standard session cookies, while the native desktop client gets a cryptographically random, per-instance secret that expires with the process.
Key Takeaways
- Static headers are not authentication: The
X-Tracker: 1header provided only security theater—any local process could discover and replay it. - Origin validation ≠ identity validation:
127.0.0.1checks prevent remote attacks but not local privilege escalation between processes. - Different clients need different authentication: The fix recognizes that browser-based and native-desktop clients have different capabilities, providing session cookies for the former and IPC tokens for the latter.
- Tests document vulnerabilities: The original
test_post_requires_custom_headerexplicitly encoded the vulnerable behavior; security review of test suites can reveal assumed trust boundaries.
How Orbis AppSec Detected This
- Source: HTTP request parameters and headers reaching the administrative endpoint handlers
- Sink: The
guard()function's authorization check, which validated onlyrequest.remote_addrand the staticX-Trackerheader - Missing control: No session validation, token binding to user identity, or per-instance secret verification before executing privileged operations
- CWE: CWE-287 — Improper Authentication
- Fix: Replaced static header with session-based authentication for web clients and random IPC secret for desktop process communication
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
The Tung Tung Tracker fix demonstrates that local-only APIs require the same rigor as remote-facing ones. The X-Tracker header created a false sense of security while offering no meaningful protection against local threats. By replacing it with proper session management and per-instance IPC secrets, the fix closes the authentication gap without breaking the desktop integration that makes the application useful.