CVE-2026-64824: Home Assistant Backup-Restore Symlink Traversal — How to Find and Patch Affected Instances
A path traversal vulnerability in Home Assistant Core's backup-restore function lets a crafted backup archive write files to an arbitrary path on the host filesystem. On the official Docker image — which runs Home Assistant as root — that arbitrary write becomes remote code execution by overwriting a Python file the interpreter loads at startup. CVE-2026-64824 is scored 9.3 Critical on CVSS 4.0 and 8.4 High on CVSS 3.1 (both by VulnCheck, the assigning CNA), classified CWE-22, and fixed in Home Assistant Core 2026.7.0. What makes it worth a close read is the mechanism: this is not the classic filename form of "Zip Slip," and validating archive filenames — the usual defense — does nothing to stop it. One important caveat up front: exploitation requires a malicious backup to actually be restored, so this is an administrator-trust problem, not an anonymous internet-facing one.
The Vulnerability
Home Assistant backups are tar archives. Restoring one extracts its members back onto disk. The flaw lives in how that extraction handled symbolic links: the code validated each member's name against the destination directory, but never validated where a symlink member's target (its linkname) pointed. A backup could therefore contain a symlink whose name is perfectly innocent and stays inside the extraction directory, while its linkname is an absolute path pointing somewhere else entirely — and a later regular-file member, written through that symlink, lands wherever the link resolves.
- CVSS: 9.3 Critical (CVSS 4.0) · 8.4 High (CVSS 3.1) — both are the CNA's (VulnCheck's) scores; NVD had not published an independent assessment as of July 22, 2026
- CWE: CWE-22 (Path Traversal) per the CNA record; CWE-59 (Improper Link Resolution Before File Access) describes the symlink mechanism more specifically
- AFFECTED: Home Assistant Core before 2026.7.0 (the CNA's stated range)
- FIXED: Home Assistant Core 2026.7.0 — PR #172252 ("Harden backup tar extraction with Python tar_filter")
- VECTOR: requires an administrator-authorized backup restore — not pre-authentication or drive-by (see preconditions below)
- EXPLOITED: no public in-the-wild exploitation found as of July 22, 2026; not CISA KEV-listed
- ADVISORY: NVD: CVE-2026-64824
Not Classic Zip Slip: A Stateful Symlink Pivot
The best-known archive-extraction bug — often called "Zip Slip" — is a member with a name like ../../etc/cron.d/x, and the familiar defense is to reject or normalize any member name that escapes the destination directory. Home Assistant did exactly that. This is a different variant of the same broad family: not the classic ../ member-name form, but a stateful symlink pivot. Name-checking treats an archive as a flat list of files, whereas extraction actually mutates the filesystem as it goes — and a symlink member is an earlier write that redirects where a later write resolves. Python's own tarfile documentation calls out exactly this hazard: "symlinks that affect later members."
Concretely, the exploit is a two-member sequence:
- • Member 1 (SYMTYPE): a symlink whose name is benign and stays inside the extraction directory — say ./link — but whose linkname is an absolute path outside it, e.g. /usr/local/lib/python3.13/site-packages. A name-only check sees ./link, judges it safe, and creates the symlink.
- • Member 2 (regular file): a file named ./link/sitecustomize.py. Its name also looks contained, but the path now traverses the symlink planted a moment earlier, so the write follows the link and lands at the absolute location the symlink points to — anywhere the Home Assistant process can write.
Neither member's name escapes the destination on its own. The escape is an emergent property of the two members together — which is precisely why validation that inspects only each member's name cannot catch it. (A validator that also examined link targets, or a state-aware filter that re-checks each write against the real on-disk path, can.) The full chain reads:
safe-looking symlink member → absolute linkname escapes the sandbox → later file write follows the symlink → root-owned write outside the extraction directory → Python loads the planted file at startup → code execution
From Arbitrary Write to RCE
An arbitrary file write is not automatically remote code execution — the gap between them is what the deployment provides. Two conditions turn the write into execution on the official container image:
- • The process runs as root. The official homeassistant/home-assistant Docker image runs the Home Assistant process as root inside the container, so the write is unconstrained by file ownership across the container filesystem. Container root is not automatically host root, but it is more than enough to plant an executable Python file the process itself will read.
- • Python runs a startup hook. On normal CPython startup the site module attempts to import sitecustomize (and usercustomize) from directories on sys.path; Home Assistant's standard launcher does not disable this behavior with -S. Writing sitecustomize.py into the active site-packages directory means attacker-controlled Python executes the next time the interpreter starts — and a restore typically ends in a restart — with no further interaction.
The cleanest traversal-to-RCE chain is the sitecustomize.py write above. Worth stating plainly for honesty: a full backup legitimately contains the entire config directory, including any configured custom components — so a malicious full backup can already carry executable Python without exploiting this bug at all. The traversal's real added power is writing outside the config directory, to a path like site-packages that the operator never intended a backup to touch.
Deployment matters for impact. The RCE narrative is strongest on the official Docker/container image because of the root process and the matching site-packages path. A Home Assistant Core install inside a Python virtual environment typically runs as an unprivileged user — there, the same bug is still an arbitrary write within that user's reach (which can include the venv's site-packages, so code execution as that user is often still on the table), but it is not root on the host. Home Assistant OS and Supervised installs restore through the Supervisor, whose analogous tar-handling issue was fixed separately (Supervisor PR #6559), so do not assume this specific Core code path executes on every HAOS/Supervised restore. Assess your own deployment rather than assuming the worst case or dismissing it.
Preconditions: This Needs a Restore
This is the part that separates a realistic risk assessment from a headline. CVE-2026-64824 is not a pre-authentication or drive-by exploit. The CNA's AV:N (network) vector reflects that a restore is reachable through the authenticated UI/API — not that an anonymous internet scan can trigger it. Triggering the bug requires a malicious backup archive to actually be restored, which in practice means one of:
- • Authenticated access to upload and restore a backup through the Home Assistant UI or API — a user who can already reach the restore function.
- • Social engineering — convincing an administrator to restore an attacker-supplied backup file, e.g. one shared as a "config migration" or "recovery" archive, or sourced from an untrusted community/marketplace channel.
- • A compromised or attacker-controlled backup source — a remote storage location or add-on that supplies backups the instance will restore.
The practical lesson is that restoring a backup is a privileged, trust-laden operation: a backup is not just data, it is a set of filesystem instructions. Treat backup files with the same suspicion as any other executable-adjacent artifact, and only restore archives whose provenance you trust.
The Fix: filter="tar" Instead of Trust
The vulnerable code combined Python's filter="fully_trusted" extraction mode with a custom securetar.secure_path() check. As the patch author notes, that combination did not protect against a symlink whose linkname points outside the destination — because secure_path() inspected member names, not link targets. The fully_trusted filter, by design, applies no safety constraints at all.
PR #172252 replaces that with Python's built-in filter="tar", added in the standard library's tarfile extraction filters (PEP 706). The key behavior: the tar filter catches the attack at the second member — when it computes the later regular file's real destination after following the already-created symlink and finds that it escapes the extraction directory. With the default errorlevel=1 it then aborts the extraction rather than silently skipping the member. (Extraction is not transactional, so earlier members can already be on disk; Home Assistant restores into a temporary directory that is cleaned up, which contains that.) The lesson generalizes well beyond Home Assistant — if your code extracts untrusted archives and honors links, name validation alone is insufficient; use a link-aware extraction filter and treat every write as escaping until its resolved path proves otherwise.
Investigation Workflow: Inventory Home Assistant Instances
Because the exploit path is a restore operation rather than a network request, network scanning does not tell you whether an instance is exploitable — only that it exists and needs patching. That still matters: the point of discovery here is inventory and version management, so you can be sure every Home Assistant deployment you run is on 2026.7.0 or later. Home Assistant is self-hosted software often stood up on home and lab networks, cloud VMs, and inside corporate segments by enthusiasts and integrators, so shadow deployments are common.
Passive discovery — Shodan, certificate-transparency logs, passive DNS — surfaces candidates without touching them. Active checks — connecting to a port, requesting a page — are direct interactions and should be run only against systems you own or are authorized to assess.
1. Port Scan: Find Home Assistant Servers
Home Assistant serves its web UI and API over HTTP on a well-known default port. Instances are frequently reachable on the LAN and, when misconfigured or deliberately published, from the internet:
- • 8123 — default Home Assistant HTTP/API port (the frontend and /api)
- • 80 / 443 — where Home Assistant sits behind a reverse proxy (nginx, Traefik, Nabu Casa Cloud, Caddy)
2. HTTP Headers & Endpoints: Fingerprint Home Assistant
On systems you control or are authorized to test, Home Assistant exposes distinctive signals. None is a perfect fingerprint on its own — combine several:
- • The login page HTML title and app shell reference Home Assistant
- • /manifest.json — the web-app manifest names the application
- • /api/ — returns a JSON API message when reachable (often requires a token)
- • Home Assistant does not advertise its exact version in HTTP headers, so version confirmation generally requires the authenticated UI (Settings → About) or your own deployment records
3. TLS Inspect: Examine Certificates
Where Home Assistant is fronted by TLS, pull the certificate and look for tell-tale hostnames — homeassistant.*, hass.*, ha.*, the *.ui.nabu.casa remote-access domain, or self-signed / Let's Encrypt certs on non-standard ports. Certificate-transparency logs are a passive source for the same signal and often reveal deployments inventory tools miss.
4. DNS: Discover Home Assistant Infrastructure
Query DNS for the naming patterns operators use for home-automation tooling: homeassistant.*, hass.*, ha.*, home.*, iot.*. Self-hosted tools frequently get descriptive subdomains that give away their function.
5. CVE Lookup: Track the Advisory
Track CVE-2026-64824 and confirm every instance is on Home Assistant Core 2026.7.0 or later. Note the version discrepancy in circulation: some early aggregated feeds listed the fix as 2026.6.0, but the patch shipped in 2026.7.0 — verify against the official changelog.
Cross-Reference with External Data
- SHODAN: Search http.title:"Home Assistant" or port:8123 to find internet-exposed instances (passive)
- CVE LOOKUP: Track CVE-2026-64824; confirm the fixed version is 2026.7.0, not 2026.6.0
- NVD: VulnCheck is the assigning CNA (9.3 CVSS 4.0 / 8.4 CVSS 3.1); NVD had not published an independent assessment as of July 22, 2026. Treat these as CNA scores, not an uncontested measure of practical risk given the restore precondition
- CHANGELOG: The fix appears in the Home Assistant 2026.7 changelog as "Harden backup tar extraction with Python tar_filter"
Remediation
- Upgrade to Home Assistant Core 2026.7.0 or later. This is the authoritative fix — it replaces the trust-based extraction with a link-aware tarfile filter. Verify the running version under Settings → About; do not assume 2026.6.x is patched.
- Only restore backups you trust. Until you have upgraded — and as durable practice afterward — treat a backup archive as privileged input. Do not restore backups from untrusted sources, shared "recovery" files, or community/marketplace channels you cannot vouch for.
- Restrict who can reach the restore function. The exploit requires an actor able to trigger a restore. Limit administrative access, enforce strong authentication and MFA on Home Assistant accounts, and avoid exposing the UI directly to the internet.
- Prefer least-privilege deployment where practical. The root-in-container default is what elevates arbitrary write to root-level RCE on the official image. Where your setup allows it, run behind network isolation and review whether the process needs the privileges it has.
- If you restored an untrusted backup on a vulnerable version, treat the host as suspect. Look for unexpected files at Python startup-hook paths (sitecustomize.py, usercustomize.py in site-packages), unfamiliar custom components, new processes, and unexpected outbound connections. When in doubt, rebuild from a known-good state.
Every tool used in this investigation — port scan, HTTP headers, TLS inspect, DNS, CVE lookup — runs from your phone in RECON. Get it on the App Store.
Follow @hellorecon for new CVE investigations.
Sources
- → NVD: CVE-2026-64824 (backup-restore path traversal)
- → Home Assistant PR #172252 (Harden backup tar extraction with Python tar_filter)
- → Home Assistant Supervisor PR #6559 (analogous tar-handling fix)
- → Home Assistant 2026.7 changelog
- → Python site module documentation (sitecustomize)
- → Python tarfile extraction filters (PEP 706)
- → Home Assistant Core security advisories