CrushFTP Critical Vulnerability CVE-2023-43177 Unauthenticated Remote Code Execution

CrushFTP Critical Vulnerability CVE-2023-43177 Unauthenticated Remote Code Execution
Converge Technology Solutions
November 16, 2023
Blogs | Cybersecurity

Converge security researchers responsibly disclosed a critical unauthenticated zero-day vulnerability affecting the CrushFTP enterprise suite in August of 2023. With approximately 10,000 public instances and many more behind corporate firewalls, the vulnerability attack surface is broad. The exploit permits an unauthenticated attacker to access all CrushFTP files, run arbitrary programs on the host server, and acquire plain-text passwords. The vulnerability is fixed in CrushFTP version 10.5.2 and it affects software in the default configuration on all operating systems.

Indicators point to future exploitation

Anyone using CrushFTP should take immediate action.

Converge’s threat intelligence indicates that the security patch has been reverse-engineered, and adversaries have developed proofs of concept. Because of that, upcoming exploitation is likely. This advisory outlines necessary remediation steps and hardening methods to secure your CrushFTP instance. We also provide a technical deep dive into the exploit and share indicators of attack and compromise.

Remediating CVE-2023-43177

The CrushFTP development team responded rapidly to our security researchers and issued a patch overnight at the beginning of August. Their team deserves a huge shoutout for such a fast turnaround.

To secure your CrushFTP servers, apply these recommended steps, listed in order of priority:

  1. Login to the web portal, browse the administrator dashboard, and update to the latest version of CrushFTP.
  2. To ensure automatic future patching, administrators should set CrushFTP to the non-standard configuration of Auto-update for new security patches when idle.
  3. Configure the default password algorithm to Argon.
  4. Audit for any unauthorized new user accounts via the user management dashboard and recent application logs. Attackers also establish persistence by modifying passwords for existing accounts, so searching for recent password changes is necessary.
  5. The new hardened Limited Server mode, introduced by CrushFTP in response to Converge researcher feedback, should be enabled. This opt-in mode implements important security restrictions to harden your installation against any future exploitation attempts. The most restrictive configuration possible should be enabled after setting up Limited Server.

 
Additional measures to improve security posture:

  1. A limited operating system service account should be set up with the least privileges necessary to run the software effectively. CrushFTP should be configured to start in the context of this lower-privileged user.
  2. If CrushFTP is public-facing, a host/container running Nginx or Apache should be deployed to reverse proxy between CrushFTP and the public Internet. These reverse proxies are battle-hardened and will ensure incoming traffic fully adheres to RFC standards.
  3. Where possible, firewall rules should be configured to limit incoming CrushFTP traffic to known good IP ranges and hosts.

More Resources
CVE Record | NIST Database

Securing your software

If you would like assistance locating and remediating application weaknesses in your systems, Converge is happy to assist. Our team of experts is highly skilled, and we thrive on identifying flaws before attackers do.

The details behind our discovery of this exploit are below.

Anatomy of the CrushFTP exploit

The attack chain primarily hinges on an unauthenticated mass-assignment vulnerability. This vulnerability is tied to the way that CrushFTP parses request headers for a data transfer protocol called AS2. By exploiting the AS2 header parsing logic, the attacker gains partial control over user information Java Properties. This Properties object can be leveraged to establish an arbitrary file read-and-delete primitive on the host system. Using that capability, the attacker can escalate to full system compromise, including root-level remote code execution.

Payload delivery

Payloads for the attack chain are delivered via web headers to the CrushFTP web interface service that listens on ports 80, 443, 8080, and 9090. The primary header that must be present to exploit the vulnerability is AS2-To. Since AS2 is a niche feature, AS2 activity in the logs of servers that haven’t set up AS2 should be considered an indicator of enumeration or attack.

Exploit chain

Prior to providing authentication, no valid session cookies are set. However, certain CrushFTP behavior blurs the line between authenticated and unauthenticated. Performing a web request to a nonexistent /WebInterface/ endpoint returns a 404 response with a valid session cookie for a pseudo-user called anonymous. Although this user role doesn’t have any privileges, areas of the CrushFTP code base perform checks for whether any username is set at all.

Converge researchers frequently observe this kind of behavior, where applications implement a pseudo-user session with an anonymous level of access. This sort of implementation isn’t necessarily a vulnerability, but it makes it easier for developers to accidentally confuse the line between authenticated and unauthenticated. In this case, an important attack surface becomes reachable past code that checks for whether a username is set.

The first real vulnerability occurs past those checks, in ServerSessionAJAX.java’s buildPostItem() method. Here the as2Info Properties object [A] has every request header parsed and added to it [B]. Further down, if the AS2-To header is present in a request [C], the method then incorrectly trusts the tainted data from as2Info. The application uses Java’s putAll() to add every key-value pair from as2Info into the current session’s user_info Properties [D].

Figure 1.1 Parsing headers into as2Info.

Figure 1.2. Mass-assignment overwrite of user_info with as2Info.

The user_info Properties is one of several objects used by CrushFTP to store hundreds of pieces of information about a user’s session. This includes very security-relevant data, such as username, IP address, and various other session details.

Figure 2.1. The user_info Properties holds hundreds of values about the current request’s session.

Figure 2.2. Various Properties string key values within user_info.

Since putAll() clobbers existing values, this security mistake effectively allows an attacker to specify their own username and arbitrary session details within CrushFTP. For example, it’s possible to clobber the default anonymous user_name value and set it to an administrator username like crushadmin. The application uses the uiSG()function (short for user_info String Get) hundreds of times in the request handling logic to source various pieces of data. Since the attacker controls all data returned by this function [E], it’s a strong exploit primitive.

Figure 3.1. The uiSG() function sources string data from the controlled user_info Properties.

Figure 3.2. This function is used hundreds of times within the CrushFTP web API code.

Figure 3.3. Triggering the vulnerable code path to clobber the user_name value, requesting the getUsername API endpoint to confirm the attack was successful.

Figure 3.3. Triggering the vulnerable code path to clobber the user_name value, requesting the getUsername API endpoint to confirm the attack was successful.

It initially seems like this vector should facilitate impersonating arbitrary users to reach exploitable code. Unfortunately, in addition to checking username, virtually all CrushFTP privilege checks are also performed against a different session variable called site. That variable contains a list of all privileges affiliated with a specific account. Since it’s populated during session instantiation, prior to AS2 header parsing, it’s set to the privileges of the anonymous user and can’t be changed.

Figure 4.1. Screenshot depicting the site list of privileges for an admin session.

Figure 4.1. Screenshot depicting the site list of privileges for an admin session.

However, another interesting attack vector emerges for the user_info primitive. Thousands of lines later, when the web request is winding down, ServerSessionAJAX.java’s writeResponse() function kicks in to return an HTTP response. After that takes place, the drain_log() function ends the request [F].

Figure 5.1. At the end of a request, the drain_log() function is called on the this.thisSessionHTTP.thisSession object, which contains the user_info Properties.

Figure 5.2. The controlled Properties contain a handful of logging-related string values by default.

Figure 5.2. The controlled Properties contain a handful of logging-related string values by default.

This final drain_log() function holds the key to a strong exploit primitive; it uses the controlled uiSG() function to identify the locations where user activity logs should be written as XML. If the non-default user_log_path_custom key is set in user_info, some specific file operations are performed.

Those operations are distilled to the following:

  1. The value specified in user_log_path_custom is designated as the new logging file location [G].
  2. The value specified in user_log_path is designated as the old logging file location [H].
  3. The old file is copied to the new location specified. Any non-existing directories in the new path will be created as needed automatically [I][J].
  4. The original file is deleted [K].
  5. The new file has a few lines of XML data appended that are not controlled by the attacker [L].

Figure 5.3. These attacker-controlled values are used in file operations during the final session activity logging function.

A few important aspects allow for the exploitation of this primitive:

  • CrushFTP serves static assets from a known subdirectory within the install folder.
  • Discovering the full path of the webroot is unnecessary; the ./ path value is resolved to the install folder, and traversal sequences are permitted in log path values for relative directory changes.
  • The CrushFTP web interface is intended to be run as root or NT AUTHORITY\SYSTEM.

As a result, exploiting drain_log() allows the attacker to copy any file on the filesystem to any directory, including the webroot, where it can be fetched. No operating system-agnostic method of uploading files to the server was identified, only an OS-agnostic method to download files from the server. Since the original file is deleted, and because appended XML data “dirties” the moved file by default, the primitive is also inherently destructive. 

However, the second destructive behavior can be avoided through some clever logging tricks. When the XML data is appended to the new file, the add_log() function is called. In this function, the attacker-controlled Properties is checked for an existing dont_log key [M]. If that key exists, the function returns before appending XML data. Now, any existing file can be moved as root to any directory.

Figure 6.1. By setting dont_log to true in user_info, the attacker can prevent log data from being appended when using drain_log() for file operations. This is critical for exploiting the next steps of the vulnerability.

The resulting refined attack permits attackers to read any files on the system as root via the webroot, but the file is deleted from its original location during the operation. As a result, traditional interesting local file disclosure targets like /etc/shadow and application configuration files can’t be read without causing a denial of service.

When exploiting the vulnerability, it’s necessary to identify a file that’s important enough to permit meaningful, new exploitation when read. Crucially, the file must also be unimportant enough that it can be deleted from its original location without consequences. The CrushFTP install folder includes numerous dire-sounding README_DO_NOT_TOUCH_FILES files. However, in the primary install folder, a serialized Java object called sessions.obj contains cached information about current logins, including all live user session tokens. Vitally, since the file acts as a cache between restarts, it can be deleted without consequences.

Figure 6.2. DO_NOT_TOUCH warnings are present in many configuration and database-related directories of the install.

Figure 6.2. DO_NOT_TOUCH warnings are present in many configuration and database-related directories of the install.

Figure 6.3. A Java serialized object, sessions.obj, is routinely written to the install folder.

Figure 6.3. A Java serialized object, sessions.obj, is routinely written to the install folder.

Figure 6.4. Included in that file are cached session cookies for every live user session.

Figure 6.4. Included in that file are cached session cookies for every live user session.

The attacker can repeatedly leak the sessions.obj file to check for live sessions. As soon as any user logs in, their session is immediately compromised, and the attacker hijacks their account. If a leaked token is for an administrator account, the attacker can move directly to remote code execution and post-exploitation. In the most likely scenario, the user is a low-privilege user and privilege escalation can commence.

CrushFTP supports filesystem-based accounts that are defined in folders containing a user.XML file. As a low-privilege user, the attacker can upload a malicious administrator user.XML definition file to the FTP server and leak the full path of the uploaded file. Though the full filesystem path of this uploaded file is not easily leaked via legitimate methods, sessions.obj persists full filesystem paths of recently uploaded files. To complete the privilege escalation, the attacker can use the unauthenticated file move primitive to copy the XML file from the known upload location to the /MainUsers/{username} directory where it will be processed.

Figure 7.1. Screenshot depicting a malicious ‘user.XML’ file with the administrator “(CONNECT)” privilege and an attacker-controlled password.

Figure 7.2. Screenshot depicting the full filesystem path of a recent file upload, leaked via the serialized Java object file.

Figure 7.2. Screenshot depicting the full filesystem path of a recent file upload, leaked via the serialized Java object file.

With admin access, the attacker can exploit an arbitrary class instantiation vulnerability on the admin panel for remote code execution. CrushFTP supports dynamic SQL driver loading and configuration testing in the administrator settings. A malicious SQL driver JAR file is uploaded, the administrator sets the driver to that filename with a traversal sequence [N]. Lastly, the testDB method within AdminControls.java is triggered for arbitrary Java code execution via the Class.forName() method [O].

Figure 8.1. The administrator dashboard permits administrators to specify an arbitrary Java class that exists on the classpath to use as a database driver.

Figure 8.1. The administrator dashboard permits administrators to specify an arbitrary Java class that exists on the classpath to use as a database driver.

Figure 8.2. The database driver can be manually specified with a traversal sequence to point to any JAR file on the filesystem.

Figure 8.2. The database driver can be manually specified with a traversal sequence to point to any JAR file on the filesystem.

Figure 8.3. Clicking Test DB attempts to load the specified driver class, if it exists on the classpath.

Figure 8.3. Clicking Test DB attempts to load the specified driver class, if it exists on the classpath.

Figure 8.4. Depiction of the vulnerable code that permits administrator-level RCE.

Demonstrated proof of concept

The video below demonstrates exploiting the vulnerability for unauthenticated remote code execution as the root user.

Talk to experts

Human ingenuity and skill found this zero day. That’s why the skill of our pentesters is the backbone of our trusted, proven penetration testing methodology. If you’re ready to find deep issues in your environment beyond what automated or crowd-sourced pentesting can discover alone, reach out to us today for a free consultation.

Follow Us

Recent Posts

Want To Read More?

Categories

You May Also Like…

Let’s Talk