Module: Msf::Exploit::Remote::HTTP::NagiosXi::Login
- Includes:
- URIs
- Included in:
- Msf::Exploit::Remote::HTTP::NagiosXi, Install
- Defined in:
- lib/msf/core/exploit/remote/http/nagios_xi/login.rb
Constant Summary collapse
- AUTH_RESULTS =
{ :connection_failed => 1, :unexpected_error => 2, :not_nagios_application => 3, :not_fully_installed => 4, :failed_to_handle_license_agreement => 5, :failed_to_extract_tokens => 6, :unable_to_obtain_version => 7 }
Instance Method Summary collapse
- #authenticate(username, password, finish_install, handle_full_install = true, handle_license = true, handle_nsp = false) ⇒ Object
-
#clean_cookies(pre_auth_cookies, auth_cookies) ⇒ String?
Compares cookies obtained before and after authentication and modifies the latter to remove cookies that may cause session timeouts.
-
#extract_auth_cookies(res_array) ⇒ String?
Grabs the auth_cookies value from an HTTP response and validate using regex.
-
#get_nsp(res) ⇒ String?
Grabs the nsp_str value from an HTTP response using regex.
-
#handle_unsigned_license(res_array, username, password, finish_install) ⇒ Object
Returns a status code an a error message on failure.
-
#install_full_nagios(username, password, finish_install) ⇒ Object
Returns a status code an a error message on failure.
-
#login_after_install_or_license(username, password, finish_install) ⇒ Array
Performs an authentication attempt.
-
#nagios_xi_login(user, pass, finish_install) ⇒ Array
performs a Nagios XI login.
-
#visit_nagios_dashboard(auth_cookies, finish_install) ⇒ Array
Performs an HTTP GET request to the Nagios XI backend to verify if authentication succeeded.
Methods included from URIs
#nagios_xi_backend_url, #nagios_xi_install_url, #nagios_xi_login_url
Instance Method Details
#authenticate(username, password, finish_install, handle_full_install = true, handle_license = true, handle_nsp = false) ⇒ Object
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 |
# File 'lib/msf/core/exploit/remote/http/nagios_xi/login.rb', line 60 def authenticate(username, password, finish_install, handle_full_install = true, handle_license = true, handle_nsp = false) login_result, res_array = nagios_xi_login(username, password, finish_install) case login_result when 1..3 return login_result, res_array[0] when 4 # Nagios XI is not fully installed return login_result, 'Nagios is not fully installed.' unless handle_full_install login_result, res_array = install_full_nagios(username, password, finish_install) return login_result, res_array unless (login_result == 0) when 5 return login_result, 'The Nagios license has not been signed.' unless handle_license login_result, res_array = handle_unsigned_license(res_array, username, password, finish_install) return login_result, res_array unless (login_result == 0) end print_good('Successfully authenticated to Nagios XI.') return 6, "Failed to extract auth cookies and nsp string" unless res_array.length == 2 = (res_array) # if we are here, this cannot be nil since the mixin checks for that already return 6, 'Failed to extract authentication cookies' unless .present? nsp = get_nsp(res_array[0]) if handle_nsp return 6, 'Failed to extract nsp string' if handle_nsp && !nsp.present? # Obtain the Nagios XI version nagios_version = nagios_xi_version(res_array[0]) if nagios_version.nil? return 7, 'Unable to obtain the Nagios XI version from the dashboard' end print_status("Target is Nagios XI with version #{nagios_version}.") # Versions of NagiosXI pre-5.2 have different formats (5r1.0, 2014r2.7, 2012r2.8b, etc.) that Rex cannot handle, # so we set pre-5.2 versions to 1.0.0 for easier Rex comparison because the module only works on post-5.2 versions. if /#{Msf::Exploit::Remote::HTTP::NagiosXi::PRE_5_2_VERSION_REGEX}/.match(nagios_version) || nagios_version == '5r1.0' nagios_version = '1.0.0' end version = Rex::Version.new(nagios_version) return 0, 'Successfully authenticated and retrieved NagiosXI Version.', , version, nsp end |
#clean_cookies(pre_auth_cookies, auth_cookies) ⇒ String?
Compares cookies obtained before and after authentication and modifies the latter to remove cookies that may cause session timeouts
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/msf/core/exploit/remote/http/nagios_xi/login.rb', line 189 def (, ) if .nil? || .nil? return nil end # Nagios XI may sometimes send the cookie `nagiosxi=deleted;` as part of the cookies after authentication. # This was observed when trying to access Nagios XI 5.3.0 when the license agreement had not been accepted yet. # The `nagiosxi=deleted;` cookie should be filtered out, since it may break authentication. if .include?('nagiosxi=deleted;') = .gsub('nagiosxi=deleted;', '').strip end # Remove duplicate cookies, necessary to make the next check work in case multiple # identical cookies were set (as observed on older Nagios versions) = .split(' ') .uniq! = .join(' ') # For newer Nagios XI versions, we need to remove the pre_auth cookies from the auth_cookies # string, otherwise the session will timeout. However, older Nagios XI versions use a single cookie # which has the same name both before and after authentication. unless == if .include?() = .gsub(, '').strip end end end |
#extract_auth_cookies(res_array) ⇒ String?
Grabs the auth_cookies value from an HTTP response and validate using regex
265 266 267 268 |
# File 'lib/msf/core/exploit/remote/http/nagios_xi/login.rb', line 265 def (res_array) = res_array[1] return if && /nagiosxi=[a-z0-9]+;/.match() end |
#get_nsp(res) ⇒ String?
Grabs the nsp_str value from an HTTP response using regex
274 275 276 277 |
# File 'lib/msf/core/exploit/remote/http/nagios_xi/login.rb', line 274 def get_nsp(res) res = res.body if res.kind_of?(Rex::Proto::Http::Response) nsp = res.scan(/nsp_str = "([a-z0-9]+)/)&.flatten&.first end |
#handle_unsigned_license(res_array, username, password, finish_install) ⇒ Object
Returns a status code an a error message on failure. On success returns the status code and an array so we can update the login_result and res_array variables appropriately.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/msf/core/exploit/remote/http/nagios_xi/login.rb', line 19 def handle_unsigned_license(res_array, username, password, finish_install) , nsp = res_array sign_license_result = sign_license_agreement(, nsp) if sign_license_result return 5, 'Failed to sign license agreement' end print_status('License agreement signed. The module will wait for 5 seconds and retry the login.') sleep 5 login_result, res_array = login_after_install_or_license(username, password, finish_install) case login_result when 1..4 # An error occurred, propagate the error message return login_result, res_array[0] when 5 # The Nagios XI license agreement still has not been signed return 5, 'Failed to sign the license agreement.' end return login_result, res_array end |
#install_full_nagios(username, password, finish_install) ⇒ Object
Returns a status code an a error message on failure. On success returns the status code and an array so we can update the login_result and res_array variables appropriately.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/msf/core/exploit/remote/http/nagios_xi/login.rb', line 42 def install_full_nagios(username, password, finish_install) install_result = install_nagios_xi(password) if install_result return install_result[0], install_result[1] end login_result, res_array = login_after_install_or_license(username, password, finish_install) case login_result when 1..4 # An error occurred, propagate the error message return login_result, res_array[0] when 5 # The license agreement still needs to be signed login_result, res_array = handle_unsigned_license(res_array, username, password, finish_install) return login_result, res_array end return login_result, res_array end |
#login_after_install_or_license(username, password, finish_install) ⇒ Array
Performs an authentication attempt. If the server does not return a response, a second attempt is made after a delay of 5 seconds
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/msf/core/exploit/remote/http/nagios_xi/login.rb', line 286 def login_after_install_or_license(username, password, finish_install) # After installing Nagios XI or signing the license agreement, we sometimes don't receive a server response. # This loop ensures that at least 2 login attempts are performed if this happens, as the second one usually works. second_attempt = false while true login_result, = nagios_xi_login(username, password, finish_install) break unless == ['Connection failed'] if second_attempt print_warning('The server is still not responding. If you wait a few seconds and rerun the module, it might still work.') break else print_warning('No response received from the server. This can happen after installing Nagios XI or signing the license agreement') print_status('The module will wait for 5 seconds and retry.') second_attempt = true sleep 5 end end return [login_result, ] end |
#nagios_xi_login(user, pass, finish_install) ⇒ Array
performs a Nagios XI login
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 |
# File 'lib/msf/core/exploit/remote/http/nagios_xi/login.rb', line 110 def nagios_xi_login(user, pass, finish_install) print_status('Attempting to authenticate to Nagios XI...') # Visit the login page in order to obtain the cookies and the `nsp_str` token required for authentication res_pre_login = send_request_cgi({ 'method' => 'GET', 'uri' => nagios_xi_login_url, }) unless res_pre_login return [1, ['Connection failed']] end unless res_pre_login.code == 200 && res_pre_login.body.include?('>Nagios XI<') # Check if we are perhaps dealing with a Nagios XI app that hasn't been fully installed yet unless res_pre_login.code == 302 && res_pre_login.body.include?('>Nagios XI<') && res_pre_login.headers['Location'].end_with?('/install.php') return [3, ['Target is not a Nagios XI application']] end print_warning('The target seems to be a Nagios XI application that has not been fully installed yet.') unless finish_install return [2, ['You can let the module complete the installation by setting `FINISH_INSTALL` to true.']] end return [4, [nil]] end # Grab the necessary tokens required for authentication nsp = get_nsp(res_pre_login) if nsp.nil? return [2, ['Unable to obtain the value of the `nsp_str` token required for authentication']] end = res_pre_login. if .blank? return [2, ['Unable to obtain the cookies required for authentication']] end # authenticate res_login = send_request_cgi({ 'method' => 'POST', 'uri' => nagios_xi_login_url, 'cookie' => , 'vars_post' => { 'nsp' => nsp, 'pageopt' => 'login', 'username' => user, 'password' => pass } }) unless res_login return [1, ['Connection failed']] end unless res_login.code == 302 && res_login.headers['Location'] == 'index.php' return [2, ['Received unexpected reply while trying to authenticate. Please check your credentials.']] end # Grab the cookies = res_login. if .blank? return [2, ['Unable to obtain the cookies required for authentication']] end # Make sure we only use the cookies we need, otherwise we may encounter a session timeout = (, ) # Try to visit the dashboard visit_nagios_dashboard(, finish_install) end |
#visit_nagios_dashboard(auth_cookies, finish_install) ⇒ Array
Performs an HTTP GET request to the Nagios XI backend to verify if authentication succeeded
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 |
# File 'lib/msf/core/exploit/remote/http/nagios_xi/login.rb', line 223 def visit_nagios_dashboard(, finish_install) # Visit the index page to verify we successfully authenticated res_index = send_request_cgi({ 'method' => 'GET', 'uri' => nagios_xi_backend_url, 'cookie' => }) unless res_index return [1, ['Connection failed']] end unless res_index.code == 200 && res_index.body.include?('>Home Dashboard<') # Check if we need to sign the license agreement unless res_index.code == 302 && res_index.headers['Location'].end_with?('login.php?showlicense') return [2, ['Received unexpected reply while trying to access the NagiosXI home dashboard after authenticating.']] end print_warning('The Nagios XI license agreement has not yet been signed on the target.') unless finish_install return [2, ['You can let the module sign the Nagios XI license agreement by setting `FINISH_INSTALL` to true.']] end nsp = get_nsp(res_index) if nsp.nil? return [2, ['Failed to obtain the nsp token required for signing the license agreement.']] end return [5, [, nsp]] end # Return the HTTP response body and the authentication cookies. # The response body can be used to obtain the version number. # The cookies can be used by exploit modules to send authenticated requests. [0, [res_index.body, ]] end |