highCVE-2026-55488Jun 26, 2026

CVE-2026-55488: motionEye Absolute Path Traversal in Media File Handlers

Shubham Kandhare
Security Engagement Manager, SecureLayer7

motionEye's media playback and download endpoints accept a user-supplied filename that can be an absolute path, letting anyone with access read arbitrary files off the server.

Packagemotioneye
Ecosystempip
Affected< 0.44.0
Fixed in0.44.0
CVE-2026-55488: motionEye Absolute Path Traversal in Media File Handlers

The problem

Multiple media handlers in motionEye before 0.44.0 pass a user-controlled filename directly to os.path.join(target_dir, path). When the supplied path is absolute, Python silently discards target_dir and returns the attacker's path as-is.

To make things worse, MoviePlaybackHandler overrides both get_absolute_path() and validate_absolute_path() to return the path unchecked, intentionally bypassing Tornado's built-in boundary enforcement. An attacker can therefore read any file the motionEye process has permission to open, including /etc/motioneye/motion.conf, SSH keys, and /etc/passwd.

On default installs where the normal-user password is empty, the endpoint is reachable without any credentials at all.

Proof of concept

A working proof-of-concept for CVE-2026-55488 in motioneye, with the exact payload below.

http
GET /movie/1/playback//etc/motioneye/motion.conf HTTP/1.1
Host: target:8765

# --path-as-is is required with curl to prevent the client from normalising the double slash
# curl --path-as-is -s "http://target:8765/movie/1/playback//etc/motioneye/motion.conf"

# Same primitive works on the download and preview endpoints:
# GET /movie/1/download//etc/passwd
# GET /picture/1/download//etc/passwd
# GET /picture/1/preview//etc/passwd

The root cause is a two-part failure. First, get_media_path() in mediafiles.py calls os.path.join(target_dir, path) without checking whether path is absolute. Python's documented behaviour for os.path.join is that any absolute component discards all previous components, so supplying /etc/passwd as path yields /etc/passwd, not <target_dir>/etc/passwd.

Second, MoviePlaybackHandler (and equivalent download/preview handlers) deliberately override Tornado's get_absolute_path() and validate_absolute_path() safety hooks to return the path unchanged, removing the last safety net. No check for absolute paths, .. sequences, or canonical-path boundary enforcement existed in these handlers before the patch.

PR #3324 by @MichaIng extended the backend input-path validation so that any supplied path is resolved with os.path.realpath() and verified to remain within the camera's configured target_dir before the file is served. The overridden Tornado hooks were also corrected to not bypass this check.

The fix

Upgrade to motioneye 0.44.0 or later. The fix (PR #3324) adds a canonical-path check using os.path.realpath() to every media handler and removes the unchecked overrides of Tornado's path-validation hooks. As a short-term workaround, restrict network access to port 8765 and set a non-empty normal-user password so the endpoint is not reachable unauthenticated.

Reported by pizza-power, sermikr0, C4spr0x1A, MichaIng, alanturing881.

References: [1][2]

Related research