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 漏洞修复
升级到最新版
漏洞环境源码和利用代码下载链接