参考链接
漏洞描述
CVE-2021-21972:未经认证的文件上传导致的RCE
影响范围
VMware vCenter Server 7.0系列 < 7.0.U1c
VMware vCenter Server 6.7系列 < 6.7.U3l
VMware vCenter Server 6.5系列 < 6.5 U3n
分析
其实成因很简单,就是其中一个接口没有进行认证即可被访问到,而该接口中的其中一个方法又存在目录穿越漏洞,最终导致了任意文件上传到服务器任意路径的漏洞
存在漏洞的jar包为vropsplugin-service.jar
,物理路径:
C:\ProgramData\VMware\vCenterServer\cfg\vsphere-client\vc-packages\vsphere-client-serenity\com.vmware.vrops.install-6.7.0.10000\plugins\vropsplugin-service.jar
直接解压缩并反编译其中的下面这个class文件:
vropsplugin-service\com\vmware\vropspluginui\mvc\ServicesController.class
漏洞代码片段:
@RequestMapping(value = {"/uploadova"}, method = {RequestMethod.POST})
public void uploadOvaFile(@RequestParam(value = "uploadFile", required = true) CommonsMultipartFile uploadFile, HttpServletResponse response) throws Exception {
logger.info("Entering uploadOvaFile api");
int code = uploadFile.isEmpty() ? 400 : 200;
PrintWriter wr = null;
try {
if (code != 200) {
response.sendError(code, "Arguments Missing");
return;
}
wr = response.getWriter();
} catch (IOException e) {
e.printStackTrace();
logger.info("upload Ova Controller Ended With Error");
}
response.setStatus(code);
String returnStatus = "SUCCESS";
if (!uploadFile.isEmpty())
try {
logger.info("Downloading OVA file has been started");
logger.info("Size of the file received : " + uploadFile.getSize());
InputStream inputStream = uploadFile.getInputStream();
File dir = new File("/tmp/unicorn_ova_dir");
if (!dir.exists()) {
dir.mkdirs();
} else {
String[] entries = dir.list();
for (String str : entries) {
File currentFile = new File(dir.getPath(), str);
currentFile.delete();
}
logger.info("Successfully cleaned : /tmp/unicorn_ova_dir");
}
TarArchiveInputStream in = new TarArchiveInputStream(inputStream);
TarArchiveEntry entry = in.getNextTarEntry();
List<String> result = new ArrayList<String>();
while (entry != null) {
if (entry.isDirectory()) {
entry = in.getNextTarEntry();
continue;
}
File curfile = new File("/tmp/unicorn_ova_dir", entry.getName());
File parent = curfile.getParentFile();
if (!parent.exists())
parent.mkdirs();
OutputStream out = new FileOutputStream(curfile);
IOUtils.copy((InputStream)in, out);
out.close();
result.add(entry.getName());
entry = in.getNextTarEntry();
}
in.close();
logger.info("Successfully deployed File at Location :/tmp/unicorn_ova_dir");
} catch (Exception e) {
logger.error("Unable to upload OVA file :" + e);
returnStatus = "FAILED";
}
wr.write(returnStatus);
wr.flush();
wr.close();
}
在遍历tar文件并逐个释放到/tmp/unicorn_ova_dir
目录的过程中,有这么一行代码:
File curfile = new File("/tmp/unicorn_ova_dir", entry.getName());
这个操作没有任何对entry.getName()
的检查与过滤,直接将/tmp/unicorn_ova_dir
和entry.getName()
拼接了起来,如果我们构造出包含..\
的tar文件,那么就能达到目录穿越的目的
使用evilarc.py
制作目录层级为2的tar文件,就会在目录结构中包含两个目录穿越符号,正好够我们达到服务器文件系统的根路径
结语
漏洞很简单,难的是找到无需认证即可访问的接口以及存在漏洞的方法!!!