motionEye Unauthenticated Admin Credential Theft via Path Traversal
A path traversal bug in motionEye's media playback handler lets a zero-credential attacker read the server's config file, steal the admin password hash, and immediately replay it for full admin access

The problem
motionEye before 0.44.0 grants unauthenticated 'normal user' access to all normal-level endpoints when the surveillance password is empty, which is the default. The movie playback handler (and picture download/preview handlers) accept a user-supplied filename and construct a filesystem path with os.path.join().
They also explicitly override Tornado's StaticFileHandler safety methods to return the attacker-supplied path unchanged, stripping all built-in protections.
Passing an absolute path like /etc/motioneye/motion.conf causes Python's os.path.join() to discard the camera's target_dir entirely and serve the file directly. That config file stores the admin password as a plain SHA-1 hash, and motionEye's auth code accepts that raw hash as a valid signing key.
No cracking is required: the stolen hash can be set as a browser cookie to gain full admin access immediately.
Proof of concept
# Step 1: Read the config file (no credentials needed; default empty surveillance password)
curl --path-as-is -s 'http://TARGET:8765/movie/1/playback//etc/motioneye/motion.conf'
# Response contains:
# @admin_username admin
# @admin_password 7b7d55439abccf4ae83047c1af2707e6eb6664db
# Step 2: Replay the hash as admin in browser console
document.cookie = "meye_username=admin; path=/";
document.cookie = "meye_password_hash=7b7d55439abccf4ae83047c1af2707e6eb6664db; path=/";
location.reload();
# Step 3 (optional): Achieve RCE via admin config API
# POST /config/1/set HTTP/1.1
# Content-Type: application/json
# {"command_notifications_enabled": true, "command_notifications_exec": "touch /tmp/pwned"}The root cause is two independent weaknesses chained together. First, MoviePlaybackHandler (and related handlers) override get_absolute_path() and validate_absolute_path() to return the caller's input verbatim, so Tornado's StaticFileHandler never runs its containment checks.
When the filename is an absolute path, os.path.join(target_dir, path) silently drops target_dir entirely (CWE-35 / CWE-22). Second, the admin password is stored as SHA-1(plaintext) in a world-readable config file, and that hash value is accepted directly as a valid HMAC signing key, enabling a pass-the-hash attack with no cracking step.
The patch removes the unsafe overrides, adds traversal-element rejection across all media handlers, restricts motion.conf to 0600 permissions so it cannot be read even if traversal were attempted, and switches to Argon2 for password storage so a stolen hash can no longer be replayed directly.
The fix
Upgrade to motionEye 0.44.0 or later. The release removes the get_absolute_path/validate_absolute_path overrides, rejects any API path containing traversal elements with a 403, creates motion.conf and camera-*.conf with 0600 permissions, enforces a non-empty password for both users, and stores passwords as Argon2 hashes instead of raw SHA-1.
Reported by FireByteApplications (and 0xLynk, dimashn04, C4spr0x1A, sighnwaive, MichaIng, Marijn0, zagrim).