Attacker Value
Moderate
(1 user assessed)
Exploitability
Very Low
(1 user assessed)
User Interaction
None
Privileges Required
None
Attack Vector
Network
3

CVE-2024-21762

Disclosure Date: February 09, 2024
Exploited in the Wild
Add MITRE ATT&CK tactics and techniques that apply to this CVE.

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

3
Ratings
  • Attacker Value
    Medium
  • Exploitability
    Very 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.

CVSS V3 Severity and Metrics
Base Score:
9.8 Critical
Impact Score:
5.9
Exploitability Score:
3.9
Vector:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Attack Vector (AV):
Network
Attack Complexity (AC):
Low
Privileges Required (PR):
None
User Interaction (UI):
None
Scope (S):
Unchanged
Confidentiality (C):
High
Integrity (I):
High
Availability (A):
High

General Information

Vendors

  • fortinet

Products

  • fortios,
  • fortiproxy

Exploited in the Wild

Reported by:

References

Exploit
PoCs that have not been added by contributors directly have been sourced from: nomi-sec/PoC-in-GitHub.
A PoC added here by the AKB Worker must have at least 2 GitHub stars.

Additional Info

Technical Analysis