Summary
In February, Fortinet released a security advisory for CVE-2024-21762. Bishop Fox analyzed the patch and developed a scanner to quickly determine if an appliance is affected by this vulnerability.
Overview
CVE-2024-21762 is an out-of-bounds write in the SSL VPN component of FortiOS. Bishop Fox analyzed the patch and discovered multiple changes to handling of HTTP requests that are using chunked transfer encoding. In addition to developing a crash proof-of-concept for the vulnerability, we identified a method to safely test for behavioral changes that indicate if a system is using patched firmware.
What is Chunked Transfer Encoding?
HTTP/1.1 introduced the Transfer-Encoding header, which allows specifying transport-level encoding schemes for HTTP requests. Chunked transfers allow sending or forwarding HTTP payloads of unknown sizes by using a sequence of chunks with known lengths. The chunk size is hex-encoded and delimited by a CRLF. The final chunk is indicated by a length of 0 bytes. For example, we can send the string “Hello, World!” as:
5
Hello
5
, Wor
3
ld!
0
Patch Diffing
We observed that part of the patch introduces validation to ensure some value is less than 0x10. When this condition is not met, the string “invalid chunk length string” is logged.
The BinDiff control flow block diagram shows two new basic blocks. When a register is greater than 0x10, the string ”%s: %d invalid chunk length string” is loaded into another register.
As you may be able to guess, further analysis confirmed this code validates the length of the chunk size field is no more than 16 characters.
Triggering the Check
Identifying how to trigger this function through static analysis turned out to be more challenging than expected. Fortinet embeds an entire Apache2 server inside the monolithic init
binary, which means there is a complex layer of request routing and callbacks between the point where a request arrives and where it is handled by a given piece of code. As a result, we decided to set a breakpoint at the start of the target function and send chunked POST requests to all paths we were aware of. This very quickly resulted in many hits under /remote/
. In fact, even a request to an invalid page under /remote/
will trigger this function. We determined that the target function is most easily accessed through certain error related functions such as ap_die
.
Unfortunately, the fact that we are triggering the function after the server has already decided to return an error means we are unable to directly observe an error by sending a long size field. This prompts us to search for an alternative way to verify if the validation was present.
Introducing an Observable Effect
Even though we can’t use the straightforward test we initially attempted, it is still possible to observe different behaviors when the function fails early. Since the server must rely on the chunk length to determine how many bytes to read, we can cause the server to block indefinitely by simply not sending enough data. For example, we can send the following request body:
FF abcd
and observe that the connection hangs until it eventually times out.
By sending an overly long size field, the timeout becomes conditional on whether FortiOS detects that the size field is too long. That gives us a final payload which looks something like:
0000000000000000FF abcd
On vulnerable devices, this size field decodes to 255 bytes and the connection will hang until a timeout occurs. On patched devices, the request parsing will abort early and return an HTTP response without waiting for the rest of the message.
A script to automate this technique along with usage instructions is available on GitHub.
Subscribe to Bishop Fox's Security Blog
Be first to learn about latest tools, advisories, and findings.
Thank You! You have been subscribed.