CVE-2019-11580 Atlassian Crowd RCE漏洞復現

  • 2019 年 10 月 6 日
  • 筆記

0X1 漏洞概述

Atlassian Crowd和Atlassian Crowd Data Center都是澳大利亞Atlassian公司的產品。Atlassian Crowd是一套基於Web的單點登錄系統。該系統為多用戶、網路應用程式和目錄伺服器提供驗證、授權等功能。Atlassian Crowd Data Center是Crowd的集群部署版。

近日,研究人員發現Atlassian Crowd和Atlassian Crowd Data Center中存在輸入驗證錯誤漏洞。該漏洞源於網路系統或產品未對輸入的數據進行正確的驗證。受影響的產品及版本包括:Atlassian Crowd 2.1.x版本,3.0.5之前的3.0.x版本,3.1.6之前的3.1.x版本,3.2.8之前的3.2.x版本,3.3.5之前的3.3.x版本,3.4.4之前的3.4.版本;Atlassian Crowd Data Center 2.1.x版本,3.0.5之前的3.0.x版本,3.1.6之前的3.1.x版本,3.2.8之前的3.2.x版本,3.3.5之前的3.3.x版本,3.4.4之前的3.4.版本。

0X2 環境搭建

下載漏洞影響版本程式碼,這裡我們選擇3.4.3版本

https://product-downloads.atlassian.com/software/crowd/downloads/atlassian-crowd-3.4.3.zip

在Ubuntu中安裝好JDK環境,病配置好JAVA_HOME和JRE_HOME等環境變數,如下所示。

JAVA_HOME=/usr/share/jdk1.8.0_221/  JRE_HOME=/usr/share/jdk1.8.0_221/jre  CLASS_PATH=.:$JAVA_HOME/lib:$JRE_HOME/lib  PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin  export JAVA_HOME JRE_HOME PATH 

重啟系統使用java和javac命令測試即可。

將漏洞程式碼文件解壓之後移動到/var目錄下

修改crowd-init.properties 配置文件,設置主目錄

vi /var/crowd-3.4.3/crowd-webapp/WEB-INF/classes/crowd-init.properties

然後回到環境主目錄中啟動環境 ,執行啟動命令即可

./start_crowd.sh 

瀏覽器訪問環境鏈接

http://ip:8095/

點擊安裝,在中間需要輸入license,去官網註冊個帳號獲取一個月的試用期即可,安裝完成之後即可登錄

環境搭建成功。

0X3 漏洞分析

安裝完成之後在插件目錄會有一些插件

漏洞發生在插件pdkinstall-plugin-0.4.jar中。查看其文件描述文件

<atlassian-plugin name="${project.name}" key="com.atlassian.pdkinstall" pluginsVersion="2">  <plugin-info>      <version>${project.version}</version>      <vendor name="Atlassian Software Systems Pty Ltd" url="http://www.atlassian.com"/>  </plugin-info>    <servlet-filter name="pdk install" key="pdk-install" class="com.atlassian.pdkinstall.PdkInstallFilter" location="before-decoration">      <url-pattern>/admin/uploadplugin.action</url-pattern>  </servlet-filter>    <servlet-filter name="pdk manage" key="pdk-manage" class="com.atlassian.pdkinstall.PdkPluginsFilter"      location="before-decoration">      <url-pattern>/admin/plugins.action</url-pattern>  </servlet-filter>    <servlet-context-listener key="fileCleanup" class="org.apache.commons.fileupload.servlet.FileCleanerCleanup" />  <component key="pluginInstaller" class="com.atlassian.pdkinstall.PluginInstaller" />  </atlassian-plugin>

從中可以看出Java servlet類com.atlassian.pdkinstall.PdkInstallFilter是通過訪問/admin/uploadplugin.action調用的。因為該漏洞是通過任意插件安裝的RCE漏洞,所以研究人員決定分析下PdkInstallFilterservlet的源碼。

研究人員將pdkinstall-plugin導入到IntelliJ中,並開始分析doFilter()方法。

如果請求方法不是POST,就退出會返回錯誤:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {  HttpServletRequest req = (HttpServletRequest) servletRequest;  HttpServletResponse res = (HttpServletResponse) servletResponse;    if (!req.getMethod().equalsIgnoreCase("post"))  {      res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Requires post");      return;  }

然後確定請求中是否含有multipart內容。Multipart內容是指含有一個或多個不同集合的數據。如果其中含有multipart內容就調用extractJar()方法來提取請求中發送的jar,否則調用buildJarFromFiles()方法並嘗試從請求中的數據中構建插件jar文件。

/ Check that we have a file upload request  File tmp = null;  boolean isMultipart = ServletFileUpload.isMultipartContent(req);  if (isMultipart)  {      tmp = extractJar(req, res, tmp);  }  else  {      tmp = buildJarFromFiles(req);  }

下面再看一下extractJar()方法。

private File extractJar(HttpServletRequest req, HttpServletResponse res, File tmp) throws IOException  {      // Create a new file upload handler      ServletFileUpload upload = new ServletFileUpload(factory);        // Parse the request      try {          List<FileItem> items = upload.parseRequest(req);          for (FileItem item : items)          {              if (item.getFieldName().startsWith("file_") && !item.isFormField())              {                  tmp = File.createTempFile("plugindev-", item.getName());                  tmp.renameTo(new File(tmp.getParentFile(), item.getName()));                  item.write(tmp);              }          }      } catch (FileUploadException e) {          log.warn(e, e);          res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unable to process file upload");      } catch (Exception e) {          log.warn(e, e);          res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to process file upload");      }      return tmp;  }

下面再看一下extractJar()方法。

首先,用ServletFileUpload的新對象來示例,然後調用parseRequest()方法並分析HTTP請求。該方法會處理HTTP請求的multipart/form數據流,並設置FileItems的列表為變數items。

對每個FileItems中的每個item,如果field名是以file_開始的,並且不是form field,就會創建和寫如上傳到磁碟空文件的文件。如果失敗,變數tmp就為空,如果成功,變數tmp中就含有寫入文件的路徑。然後返回doFilter()主方法。

if (tmp != null)  {      List<String> errors = new ArrayList<String>();      try      {          errors.addAll(pluginInstaller.install(tmp));      }      catch (Exception ex)      {          log.error(ex);          errors.add(ex.getMessage());      }        tmp.delete();        if (errors.isEmpty())      {          res.setStatus(HttpServletResponse.SC_OK);          servletResponse.setContentType("text/plain");          servletResponse.getWriter().println("Installed plugin " + tmp.getPath());      }      else      {          res.setStatus(HttpServletResponse.SC_BAD_REQUEST);          servletResponse.setContentType("text/plain");          servletResponse.getWriter().println("Unable to install plugin:");          for (String err : errors)          {              servletResponse.getWriter().println("t - " + err);          }      }      servletResponse.getWriter().close();      return;  }  res.sendError(HttpServletResponse.SC_BAD_REQUEST, "Missing plugin file");

如果extractJar()成功,tmp變數就會被設置,並且不等於null。應用會嘗試用pluginInstaller.install()方法來安裝插件,並找到進程中的錯誤。如果沒有錯誤,伺服器就會響應200 OK和插件成功安裝的消息。否則,伺服器會響應400 Bad Request和Unable to install plugin的消息,以及引發安裝失敗的錯誤。

如果extractJar()方法失敗了,tmp變數就會被設置為null,伺服器會響應400 Bad Request和Missing plugin file消息。

0X4 漏洞利用

下載漏洞利用腳本

https://github.com/jas502n/CVE-2019-11580

使用腳本測試

通關瀏覽器訪問鏈接測試其他命令:

利用成功。

0X5 漏洞修復

升級到最新版

漏洞環境源碼和利用程式碼下載鏈接