VMware View Planner 远程代码执行漏洞复现(CVE-2021-21978)
1.漏洞原理:
VMware View Planner 的 web 上传接口中itrLogPath参数未进行严格的校验,允许攻击者实施目录穿越,将文件上传至任意目录。
在攻击者上传恶意脚本文件覆盖特定的 web 程序执行脚本时,可造成远程代码执行。
2.影响版本:
vmware:view_planner: 4.6
3.漏洞环境:
下载地址:
链接:https://pan.baidu.com/s/1DtCBw6zW1IuEzBQbKLQeuQ
提取码:rc1s
下载之后直接拖到虚拟机:
设置完密码后界面如下:
访问:
exp工具地址:
https://github.com/skytina/CVE-2021-21978
利用成功如下:
访问链接查看log_upload_wsgi.py
4.漏洞分析:
对应目录:/etc/httpd/html/wsgi_log_upload/log_upload_wsgi.py
#! /usr/bin/env python3 import cgi import os,sys import logging import json WORKLOAD_LOG_ZIP_ARCHIVE_FILE_NAME = "workload_log_{}.zip" class LogFileJson: """ Defines format to upload log file in harness Arguments: itrLogPath : log path provided by harness to store log data logFileType : Type of log file defined in api.agentlogFileType workloadID [OPTIONAL] : workload id, if log file is workload specific """ def __init__(self, itrLogPath, logFileType, workloadID = None): self.itrLogPath = itrLogPath self.logFileType = logFileType self.workloadID = workloadID def to_json(self): return json.dumps(self.__dict__) @classmethod def from_json(cls, json_str): json_dict = json.loads(json_str) return cls(**json_dict) class agentlogFileType(): """ Defines various log file types to be uploaded by agent """ WORKLOAD_ZIP_LOG = "workloadLogsZipFile" try: # TO DO: Puth path in some config logging.basicConfig(filename="/etc/httpd/html/logs/uploader.log",filemode='a', level=logging.ERROR) except: # In case write permission is not available in log folder. pass logger = logging.getLogger('log_upload_wsgi.py') def application(environ, start_response): logger.debug("application called") if environ['REQUEST_METHOD'] == 'POST': post = cgi.FieldStorage( fp=environ['wsgi.input'], environ=environ, keep_blank_values=True ) # TO DO: Puth path in some config or read from config is already available resultBasePath = "/etc/httpd/html/vpresults" try:#从cgi中获取logfile和logMetaData两个参数数据 filedata = post["logfile"] metaData = post["logMetaData"] if metaData.value:#如果logMetaData参数传入了json数据,则从json数据中获取itrLogPath和logFileType两个键值,随之将itrLogPath和logFileType与resultBasePath 进行路径拼接 logFileJson = LogFileJson.from_json(metaData.value) if not os.path.exists(os.path.join(resultBasePath, logFileJson.itrLogPath)):#判断路径是否存在,不存在则创建, os.makedirs(os.path.join(resultBasePath, logFileJson.itrLogPath)) if filedata.file:#之后判断logfile是否为上传文件 if (logFileJson.logFileType == agentlogFileType.WORKLOAD_ZIP_LOG): filePath = os.path.join(resultBasePath, logFileJson.itrLogPath, WORKLOAD_LOG_ZIP_ARCHIVE_FILE_NAME.format(str(logFileJson.workloadID))) else:#上传了文件,如果logFileType参数的值不等于workloadLogsZipFile 则直接将resultBasePath,itrLogPath,logFileType三个参数的值进行路径拼接,并进行文件写入。 filePath = os.path.join(resultBasePath, logFileJson.itrLogPath, logFileJson.logFileType) #由于缺少路径规范过滤,只需要稍微构造一下数据包即可上传恶意文件到任意路径 with open(filePath, 'wb') as output_file: while True: data = filedata.file.read(1024) # End of file if not data: break output_file.write(data) body = u" File uploaded successfully." start_response( '200 OK', [ ('Content-type', 'text/html; charset=utf8'), ('Content-Length', str(len(body))), ] ) return [body.encode('utf8')] except Exception as e: logger.error("Exception {}".format(str(e))) body = u"Exception {}".format(str(e)) else: logger.error("Invalid request") body = u"Invalid request" start_response( '400 fail', [ ('Content-type', 'text/html; charset=utf8'), ('Content-Length', str(len(body))), ] ) return [body.encode('utf8')]
在源文件的基础上增加的命令执行的代码:
其中上传的文件内容是log_upload_wsgi.py的源文件,但添加了执行命令的代码,在上传成功之后,再请求一次logupload端点,收到curl请求,命令执行成功。
5.修复建议:
下载补丁:https://my.vmware.com/web/vmware/downloads/details?downloadGroup=VIEW-PLAN-460&productId=1067&rPId=53394
升级到最新版本
参考链接:
https://paper.seebug.org/1495/