Moderate
CVE-2024-21762
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2024-21762
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
A out-of-bounds write in Fortinet FortiOS versions 7.4.0 through 7.4.2, 7.2.0 through 7.2.6, 7.0.0 through 7.0.13, 6.4.0 through 6.4.14, 6.2.0 through 6.2.15, 6.0.0 through 6.0.17, FortiProxy versions 7.4.0 through 7.4.2, 7.2.0 through 7.2.8, 7.0.0 through 7.0.14, 2.0.0 through 2.0.13, 1.2.0 through 1.2.13, 1.1.0 through 1.1.6, 1.0.0 through 1.0.7 allows attacker to execute unauthorized code or commands via specifically crafted requests
Add Assessment
Ratings
-
Attacker ValueMedium
-
ExploitabilityVery Low
Technical Analysis
Attacker Value & Exploitability
CVE-2024-21762 is a memory corruption vulnerability that affects a very wide range of FortiNet Firewalls. However it is difficult to exploit and is likely that every affected version will require its own unique offsets, ROP chain etc. in order to be exploited successfully. Patching should be prioritized as these devices sit on the edge of the network however widespread exploitation across all versions is quite unlikely. Due to these factors I’d say the attacker value is a 3/5 as financially motivated sophisticated threat actors could still benefit from this exploit although the exploitability I would say is quite low, 1/5, given the complexities that come along with a memory corruption vulnerability such as this one.
Technical Analysis
FortiOS and FortiProxy with the SSL VPN feature enabled (which is not enabled by default) are vulnerable to an out-of-bound write vulnerability which can allow an unauthenticated attacker to execute arbitrary code by sending specially crafted HTTP requests. The affected versions are:
Version | Affected | Solution |
---|---|---|
FortiOS 7.4 | 7.4.0 through 7.4.2 | Upgrade to 7.4.3 or above |
FortiOS 7.2 | 7.2.0 through 7.2.6 | Upgrade to 7.2.7 or above |
FortiOS 7.0 | 7.0.0 through 7.0.13 | Upgrade to 7.0.14 or above |
FortiOS 6.4 | 6.4.0 through 6.4.14 | Upgrade to 6.4.15 or above |
FortiOS 6.2 | 6.2.0 through 6.2.15 | Upgrade to 6.2.16 or above |
FortiOS 6.0 | 6.0.0 through 6.0.17 | Upgrade to 6.0.18 or above |
FortiProxy 7.4 | 7.4.0 through 7.4.2 | Upgrade to 7.4.3 or above |
FortiProxy 7.2 | 7.2.0 through 7.2.8 | Upgrade to 7.2.9 or above |
FortiProxy 7.0 | 7.0.0 through 7.0.14 | Upgrade to 7.0.15 or above |
FortiProxy 2.0 | 2.0.0 through 2.0.13 | Upgrade to 2.0.14 or above |
FortiProxy 1.2 | 1.2 all versions | Migrate to a fixed release |
FortiProxy 1.1 | 1.1 all versions | Migrate to a fixed release |
FortiProxy 1.0 | 1.0 all versions | Migrate to a fixed release |
The vulnerability stems from how the request body is parsed when the header Transfer-Encoding: Chunked
is set. The vulnerability in itself is quite limited – all it allows for is the ability to write two bytes (\r\n) out of bounds onto the stack. Since all you have to work with is two bytes which have to be 0a0d
achieving RCE by directly hijacking rip is not an option and so one must focus on the memory pointers saved on the stack.
Below is a simple PoC which can be used to trigger the vulnerability and cause a segmentation fault:
import socket import ssl def create_ssock(hostname, port): try: context = ssl.create_default_context() context.check_hostname = False context.verify_mode = ssl.CERT_NONE ssock = context.wrap_socket(socket.socket(socket.AF_INET), server_hostname=hostname) ssock.connect((hostname, port)) return ssock except Exception as e: print(f"Error creating secure socket: {e}") return None hostname = '192.168.1.50' # Replace with your target hostname port = 443 # Replace with your target port if different pkt = b"""\ GET / HTTP/1.1 Host: %s Transfer-Encoding: chunked %s\r\n%s\r\n\r\n""" % (hostname.encode(), b"0"*((0x202e//2)-2), b"a") ssock = create_ssock(hostname, port) if ssock: ssock.send(pkt) response = ssock.recv(4096) print(response) else: print("Failed to create a secure socket.")
The above crash PoC will result in the following segmentation fault:
We can see that the stack pointer has been overwritten by our two bytes 0x0a0d
. Turning this seemingly harmless out of bounds write into RCE is no easy process.
By decompiling the binaries of the SSL VPN you can see that a certain function of interest saves the values of r13
and a few other registers on the stack and restores them when the function returns. r13
is of particular interest because it holds a structure pointer to a structure called a1
which happens to hold an address on the heap.
The goal here is to spray the heap with our desired payload. Then trigger the out of bounds write vulnerability when the heap address is stored in r13
such that the low byte of the a1
pointer is overwritten and if all goes to plan, will point to the pre-arranged memory as the shown below:
With this we could reliably redirect the r13
pointer to a buffer we control. Now we just have to fill the buffer with our payload and we should have remote code execution.
Once the above is successfully set up and the memory pointed to is controllable the hard part is done. Normally at this point you would use the system function to execute commands. However in FortiGate this is not an option – the system function cannot be used as the system function runs /bin/sh
and the /bin/sh
binary on the FortiGate device is a custom application which only runs a few commands.
So if we look back to a previous FortiGate vulnerability from last year CVE-2023-27997 (a similar memory corruption vulnerability in the SSL VPN component) and a number of others FortiGate exploits, they often overwrite a function pointer in an SSL struct which can be triggered by a call to SSL_do_handshake
.
Since SSL_do_handshake
is dynamically linked we can call it ourselves. We control the first argument and just have to create an SSL struct with the function pointer where we need it.
Once the struct is set up we can load an address from our buffer and now we have direct execution to an arbitrary address. Now all that’s left to do is to construct a ROP chain that will set up and call execl
with the same Node.js reverse shell as the last FortiGate exploit. Lucky for exploit devs the FortiGate main program is an all in one binary with a size of over 70MB which provides a plethora of gadgets which can be used.
There is a lot more that goes into ensuring that this process is successful and if you’re interested in exactly how it works I would suggest reading the following wonderfully written blog post.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- fortinet
Products
- fortios,
- fortiproxy
Exploited in the Wild
- Vendor Advisory (https://www.fortiguard.com/psirt/FG-IR-24-015)
- Government or Industry Alert (https://advisories.ncsc.nl/advisory?id=NCSC-2024-0058)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this report- Vendor Advisory (https://fortiguard.fortinet.com/psirt/FG-IR-24-015)
- Government or Industry Alert (https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
- Other: CISA Gov Alert (https://www.cisa.gov/news-events/alerts/2024/02/09/cisa-adds-one-known-exploited-vulnerability-catalog)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Exploit
A PoC added here by the AKB Worker must have at least 2 GitHub stars.
Miscellaneous
Additional Info
Technical Analysis
Report as Emergent Threat Response
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: