Sec Hotspot 首页  友情链接  收藏本站  技术博客  RSS
统计信息
已收录文章数量:13935 篇
已收录公众号数量:68 个
本站文章为爬虫采集,如有侵权请告知
本周热门文章
分享图片   2021.04.14 16:51:17  312
Chrome V8 RCE 0day之WeChatWeb   2021.04.17 16:40:17  130
面试官常考的 21 条 Linux 命令   2021.04.12 08:00:59  109
Windows手工入侵排查思路   2021.04.13 08:00:49  93
近期CNVD重大漏洞汇总   2021.04.15 18:20:29  69
Vmware vcenter未授权任意文件/RCE漏洞的复现与分析
本文来自公众号:HACK学习呀   2021.02.28 13:13:56

0x01 背景

vSphere VMware 推出的虚拟化平台套件,包含 ESXi vCenter Server 等一系列的软件。其中 vCenter Server ESXi 的控制中心,可从单一控制点统一管理数据中心的所有 vSphere 主机和虚拟机,使得 IT 管理员能够提高控制能力,简化入场任务,并降低 IT 环境的管理复杂性与成本。

vSphere Client(HTML5) vCenter Server 插件中存在一个远程执行代码漏洞。未授权的攻击者可以通过开放 443 端口的服务器向 vCenter Server 发送精心构造的请求,从而在服务器上写入 webshell ,最终造成远程任意代码执行。

受影响的版本:

7.0 U1c 之前的 7.0 版本6.7 U3l 之前的 6.7 版本6.5 U3n 之前的 6.5 版本

0x02 代码分析

vCenter Server vROPS 插件的 API 未经过鉴权,存在一些敏感接口。其中 uploadova 接口存在一个上传 OVA 文件的功能:

   @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 var14) {         var14.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();               String[] var9 = entries;               int var10 = entries.length;               for(int var11 = 0; var11 < var10; ++var11) {                  String entry = var9[var11];                  File currentFile = new File(dir.getPath(), entry);                  currentFile.delete();               }               logger.info("Successfully cleaned : /tmp/unicorn_ova_dir");            }            TarArchiveInputStream in = new TarArchiveInputStream(inputStream);            TarArchiveEntry entry = in.getNextTarEntry();            ArrayList result = new ArrayList();            while(entry != null) {               if (entry.isDirectory()) {                  entry = in.getNextTarEntry();               } else {                  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(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 var15) {            logger.error("Unable to upload OVA file :" + var15);            returnStatus = "FAILED";         }      }      wr.write(returnStatus);      wr.flush();      wr.close();   }

代码逻辑是将 TAR 文件解压后上传到 /tmp/unicorn_ova_dir 目录。注意到如下代码:

                while(entry != null) {                    if (entry.isDirectory()) {                        entry = in.getNextTarEntry();                    } else {                        File curfile = new File("/tmp/unicorn_ova_dir", entry.getName());                        File parent = curfile.getParentFile();                        if (!parent.exists()) {                            parent.mkdirs();

直接将 TAR 的文件名与 /tmp/unicorn_ova_dir 拼接并写入文件。如果文件名内存在 ../ 即可实现目录遍历。

0x03 影响版本

vcenter_server 7.0 U1c 之前的 7.0 版本 vcenter_server 6.7 U3l 之前的 6.7 版本 vcenter_server 6.5 U3n 之前的 6.5 版本

0x04 复现

VMware-VCSA-all-6.7.0-8217866 为例, web 的资源目录为 /usr/lib/vmware-vsphere-ui/server/work/deployer/s/global/{REPLACE_RANDOM_ID_HERE}/0/h5ngc.war/resources/

https://VCIP/ui/vropspluginui/rest/services/uploadova 发送POST请求, tar 包内容为带有目录穿越的 shell

上传成功会返回 SUCCESS

访问上传的shell

0x05 VMware的骚操作

vSphere vCenter 6.7 U2的某个分支里,VM官方搞了个骚操作导致6.7U2+无法通过上传 webshell 的形式去获取 vCenter 权限,以下为启动前端UI的命令

/usr/java/jre-vmware/bin/vsphere-ui.launcher-Xmx597m-XX:CompressedClassSpaceSize=256m-Xss320k-XX:ParallelGCThreads=1-Djava.io.tmpdir=/usr/lib/vmware-vsphere-ui/server/work/tmp-Dorg.eclipse.virgo.kernel.home=/usr/lib/vmware-vsphere-ui/server-DPS_BASEDIR=/storage/vsphere-ui/-Declipse.ignoreApp=true-Dcatalina.base=/usr/lib/vmware-vsphere-ui/server-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/var/log/vmware/vsphere-ui/-XX:ErrorFile=/var/log/vmware/vsphere-ui/java_error%p.log-XX:+PrintGCDetails-XX:+PrintGCDateStamps-XX:+PrintReferenceGC-XX:+UseGCLogFileRotation-XX:NumberOfGCLogFiles=10-XX:GCLogFileSize=1024K-XX:-OmitStackTraceInFastThrow-Xloggc:/var/log/vmware/vsphere-ui/vsphere-ui-gc.log-Djava.security.properties=/etc/vmware/java/vmware-override-java.security-Djava.ext.dirs=/usr/java/jre-vmware/lib/ext:/usr/java/packages/lib/ext:/opt/vmware/jre_ext/-Dorg.osgi.framework.system.packages.extra=sun.misc-Dsun.zip.disableMemoryMapping=true-Dui.component.name=vsphere-ui-Dvlsi.client.vecs.certstore=false-DisFling=false-Dorg.apache.tomcat.websocket.DISABLE_BUILTIN_EXTENSIONS=true-Dlogback.configurationFile=/usr/lib/vmware-vsphere-ui/server/conf/serviceability.xml-Dlogs.dir=/var/log/vmware/vsphere-ui/logs/-Dhttps.port=5443-Dhttp.port=5090-Dshutdown.port=-1-Dcom.sun.org.apache.xml.internal.security.ignoreLineBreaks=true-Dorg.apache.xml.security.ignoreLineBreaks=true-classpath/usr/lib/vmware-vsphere-ui/server/bootstrap/server-launcher.jar:/usr/lib/vmware-vsphere-ui/server/bin/bootstrap.jar:/usr/lib/vmware-vsphere-ui/server/bin/tomcat-juli.jarcom.vmware.vise.launcher.tomcat.TomcatLauncherstart

注意 server-launcher.jar 这个包调用了 com.vmware.vise.launcher.tomcat.TomcatLauncher 类,反编译后部分代码如下

public final class TomcatLauncher{    private static final String VECS_KEYSTORE_TYPE = "VKS";    private static final String MACHINE_SSL_CERT = "MACHINE_SSL_CERT";    private static final String VECS_LOAD_STORE_PARAM_TYPE = "com.vmware.provider.VecsLoadStoreParameter";    private static final String FILE_SEPARATOR;    private static final String SYSPROP_COMPONENT_NAME = "ui.component.name";    private static final String DEFAULT_COMPONENT_NAME = "vsphere-ui";    private static final String CLIENT_APP_DATA_FOLDER;    private static final String DEST_KEYSTORE_NAME = "keystore.jks";    private static final String PASSWORD;    private static final String TOMCAT_DIR = "catalina.base";    private static final String CONFIG_DIR;        public static void main(final String[] args) {        loadKeystore();        deleteWorkDirectory();        createJavaTempDir();        Bootstrap.main(args);    }

其中有个函数 deleteWorkDirectory

    private static void deleteWorkDirectory() {        final File workDir = new File(System.getProperty("catalina.base") + "/work");        if (!workDir.exists()) {            return;        }        try {            deleteDirectoryRecur(workDir);        }        catch (IOException e) {            throw new RuntimeException("Cannot clean work dir", e);        }    }

这个函数主要作用就是删除 web 目录,先从命令行中的 catalina.base 获取设置的 Tomcat 工作目录 /usr/lib/vmware-vsphere-ui/server ,然后拼接 /work 路径并判断是否存在,不存在则返回,存在的调用 deleteDirectoryRecur 函数进行删除

    private static void deleteDirectoryRecur(final File directory) throws IOException {        final File[] contents = directory.listFiles();        if (contents != null) {            for (final File f : contents) {                if (isSymlink(f)) {                    f.delete();                }                else {                    deleteDirectoryRecur(f);                }            }        }        directory.delete();    }        private static boolean isSymlink(final File file) throws IOException {        if (file == null) {            throw new NullPointerException("File must not be null");        }        File canon;        if (file.getParent() == null) {            canon = file;        }        else {            final File canonDir = file.getParentFile().getCanonicalFile();            canon = new File(canonDir, file.getName());        }        return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());    }

这也是为什么6.7U2+、7.0+的版本无法进行常规写shell的操作原因。

但是问题来了,实际发现 tomcat 并没有启动

0x06 柳暗花明

经过分析启动进程 rhttpproxy ,发现该进程将443请求转发到5090端口

这和启动进程的参数吻合

经过后续的分析,发现了一种有条件写 shell 的操作, vCenter 在重启时会启动服务,但是在启动服务的过程中会调用 war 包重新生成 web 目录,这就可以给我们一个可乘之机

查看 war 包权限

770 权限,那么我们可以先把该文件下载回本地进行加料,再利用 CVE-2021-21792 漏洞回传回 vCener 服务器进行复写操作,等待 vCenter 重启时就会在内存中驻留我们的webshell

成功如图

0x07 EXP下载地址

https://github.com/NS-Sp4ce/CVE-2021-21972




点赞,转发,在看


原创投稿作者: sp4ce