Azure’un en yaygın, ancak en az bilinen yazılım aracılarından biri olan ve Azure’daki Linux VM’lerinin büyük bir bölümünde dağıtılan OMI’de dört kritik güvenlik açığı bulundu. Güvenlik açıklarından yararlanmak çok kolay olduğu kanıtlandı. Saldırganların ağ içinde tek bir istekle uzaktan rastgele kod yürütmesine ve kök ayrıcalıklarına yükselmesine olanak tanır.

  • CVE-2021-38647 – Unauthenticated RCE as root
  • CVE-2021-38648 – Privilege Escalation vulnerability
  • CVE-2021-38645 – Privilege Escalation vulnerability
  • CVE-2021-38649 – Privilege Escalation vulnerabilityTüm teknik ayrıntılarını aşağıdaki bölümlerle anlatmaya gayret edeceğiz.

Öncelikle bu zafiyetlere sebep olan OMI nedir?

OMI, Windows’un WMI’sinin UNIX/Linux eşdeğeridir. Kullanıcıların uzak ve local ortamlardaki yapılandırmaları yönetmesine ve istatistik toplamasına olanak tanır. OMI’nin sağladığı kullanım kolaylığı ve soyutlama nedeniyle Azure’da, özellikle Open Management Suite (OMS), Azure Insights , Azure Automation ve daha pek çok yerde yaygın olarak kullanılır .

OMI saldırı yüzeyi saldırganlar için neden ilgi çekicidir?

OMI aracısı, yüksek ayrıcalıklarla kök olarak çalışır. Herhangi bir kullanıcı, bir UNIX soketi kullanarak veya bazen harici kullanıma izin verecek şekilde yapılandırıldığında bir HTTP API kullanarak iletişim kurabilir. Sonuç olarak, OMI bir saldırgan için harici kullanıcıların veya düşük ayrıcalıklı kullanıcıların hedef makinelerde uzaktan kod yürütmesine veya ayrıcalıkları yükseltmesine izin verdiği olası bir saldırı yüzeyini temsil eder.

OMI bağlantı noktalarının (5986/5985/1270) uzaktan yönetime izin vermek için internet üzerinden erişilebilir olduğu senaryolarda, bu güvenlik açığının saldırganlar tarafından hedef Azure ortamına ilk erişim elde etmek ve ardından bunun içinde yanal olarak hareket etmek için de kullanılabileceğini unutmayın. Bu nedenle, açıkta kalan bir HTTPS bağlantı noktası kötü niyetli saldırganlar için kutsal bir kâsedir. Aşağıdaki şemada gösterildiği gibi basit bir istismarla yeni hedeflere erişebilir, komutları en yüksek ayrıcalıklarda yürütebilir ve muhtemelen yeni hedef makinelere yayılabilirler.

CVE-2021-38647 Uzaktan Kod YürütmeKimlik Doğrulama Üstbilgisini Kaldırın ve Kök Olun!

Bu, doğrudan 90’lardan kalma bir zafiyet exploit etme şekli gibi. Ancak 2021’de gerçekleşen ve milyonlarca uç noktayı etkileyen bir ders niteliği taşıyan RCE güvenlik açığıdır.

Tek bir paketle, bir saldırgan yalnızca kimlik doğrulama başlığını kaldırarak uzak bir makinede kök kullanıcı olabilir.

Nasıl Bu Kadar Basit Olabilir?

Bu güvenlik açığı, OMI, HTTPS yönetim bağlantı noktasını harici olarak kullanıma sunduğunda (5986/5985/1270) uzaktan devralmaya izin verir. Bu aslında tek başına ve Azure yapılandırma yönetimi veya System Center Operations Manager (SCOM) içinde yüklendiğinde varsayılan yapılandırmadır.

Neyse ki diğer Azure hizmetleri (Log Analytics gibi) bu bağlantı noktasını açığa çıkarmaz ve bu nedenle kapsam, yerel ayrıcalık yükseltme ile sınırlıdır.

Aşağıdaki şema, yetkilendirme başlığı olmadan bir komut yürütme isteği yayınlandığında OMI’nin beklenmeyen davranışını gösterir.

  1. Kimlik Doğrulama başlığında geçerli parola ile normal akış sağlanır, uzak OMI örneğine bir HTTP isteği gönderir ve oturum açma bilgilerini yetkilendirme başlığında iletir.
  2. Geçersiz Bir Kimlik Doğrulama başlığını geçirirken yetkilendirme hatası alınır, beklendiği gibi omicli geçersiz bir başlık geçirirse başarısız olur.
  3. Kimlik Doğrulama başlığı olmadan bir komutu geçirirken zafiyetten yararlanılabilmektedir. OMI sunucusu, bir Kimlik Doğrulama başlığı olmadan bile isteğe güvenir ve mükemmel RCE’yi etkinleştirir.

Zafiyete maruz kalan ve henüz güncelleme yapmamış yaklaşık 15,707 çevrimiçi cihaz bulunmaktadır.

Daha Çok Teknik Detay!

OMI, bir ön uç-arka uç mimarisi vardır. Omiengine istemciden kimlik doğrulama isteği alır, omicli yeni bir kimlik doğrulama isteği gönderir, omiserver kullanıcının kimlik doğrulama sonucu bilgisini kaydeder. uid ve gid’ ileri yanıt olarak kullanıcıya geri gönderir.

_ProcessReceivedMessage işlevinin içindeki kimlik doğrulama mantığına bakalım:

 

static Protocol_CallbackResult _ProcessReceivedMessage(

ProtocolSocket* handler)

{

BinProtocolNotification* binMsg = (BinProtocolNotification*) msg;

if (binMsg->type == BinNotificationConnectRequest)

{

// forward to server

uid_t uid = INVALID_ID;

gid_t gid = INVALID_ID;

Sock s = binMsg->forwardSock;

Sock forwardSock = handler->base.sock;

// Note that we are storing (socket, ProtocolSocket*) here

r = _ProtocolSocketTrackerAddElement(forwardSock, handler); <— (1)

if(MI_RESULT_OK != r)

{

trace_TrackerHashMapError();

return PRT_RETURN_FALSE;

}

DEBUG_ASSERT(s_socketFile != NULL);

DEBUG_ASSERT(s_secretString != NULL);

/* If system supports connection-based auth, use it for

implicit auth */

if (0 != GetUIDByConnection((int)handler->base.sock, &uid, &gid))

{

uid = binMsg->uid;

gid = binMsg->gid;

}

/* Create connector socket */

{

if (!handler->engineBatch)

{

handler->engineBatch = Batch_New(BATCH_MAX_PAGES);

if (!handler->engineBatch)

{

return PRT_RETURN_FALSE;

}

}

ProtocolSocketAndBase *newSocketAndBase = Batch_GetClear(handler->engineBatch, sizeof(ProtocolSocketAndBase));

if (!newSocketAndBase)

{

trace_BatchAllocFailed();

return PRT_RETURN_FALSE;

}

r = _ProtocolSocketAndBase_New_Server_Connection(newSocketAndBase, protocolBase->selector, NULL, &s); <— (2)

if( r != MI_RESULT_OK )

{

trace_FailedNewServerConnection();

return PRT_RETURN_FALSE;

}

handler->clientAuthState = PRT_AUTH_WAIT_CONNECTION_RESPONSE;

handler = &newSocketAndBase->protocolSocket;

newSocketAndBase->internalProtocolBase.forwardRequests = MI_TRUE;

// Note that we are storing (socket, ProtocolSocketAndBase*) here

r = _ProtocolSocketTrackerAddElement(s, newSocketAndBase); <— (3)

if(MI_RESULT_OK != r)

{

trace_TrackerHashMapError();

return PRT_RETURN_FALSE;

}

}

handler->clientAuthState = PRT_AUTH_WAIT_CONNECTION_RESPONSE;

if (_SendAuthRequest(handler, binMsg->user, binMsg->password, NULL, forwardSock, uid, gid) ) <— (4)

{

ret = PRT_CONTINUE;

}

}

….

}

Mantığı gözden geçirelim; (1) önce omiengine , anahtar olarak bağlantı numarasını kullanarak istemcinin soketini bir bağlantı karma haritasına kaydeder. (2) Ardından, omiengine omiserver ile yeni bir bağlantı kurar. (3) ve onu aynı tracker hash haritasına kaydeder. (4) Ardından doğrulama talebi doğrulama için sunucuya gönderilir.

Şimdi aynı işlevin sunucu yanıtını nasıl ele aldığına bakalım:

static Protocol_CallbackResult _ProcessReceivedMessage(

ProtocolSocket* handler)

{

// forward to client

Sock s = binMsg->forwardSock; <— (1.1)

Sock forwardSock = INVALID_SOCK;

ProtocolSocket *newHandler = _ProtocolSocketTrackerGetElement(s); <— (1.2)

if (newHandler == NULL)

{

trace_TrackerHashMapError();

return PRT_RETURN_FALSE;

}

 

if (binMsg->result == MI_RESULT_OK || binMsg->result == MI_RESULT_ACCESS_DENIED)

{

if (binMsg->result == MI_RESULT_OK)

{

newHandler->clientAuthState = PRT_AUTH_OK; <— (2)

newHandler->authInfo.uid = binMsg->uid;

newHandler->authInfo.gid = binMsg->gid;

trace_ClientCredentialsVerfied(newHandler);

}

ProtocolSocketAndBase *socketAndBase = _ProtocolSocketTrackerGetElement(handler->base.sock); <— (3)

if (socketAndBase == NULL)

{

trace_TrackerHashMapError();

return PRT_RETURN_FALSE;

}

r = _ProtocolSocketTrackerRemoveElement(handler->base.sock);

if(MI_RESULT_OK != r)

{

trace_TrackerHashMapError();

return PRT_RETURN_FALSE;

}

r = _ProtocolSocketTrackerRemoveElement(s);

if(MI_RESULT_OK != r)

{

trace_TrackerHashMapError();

return PRT_RETURN_FALSE;

}

 

// close socket to server

trace_EngineClosingSocket(handler, handler->base.sock);

….

}

}

_ProcessReceivedMessage fonksiyonu, herhangi bir sunucu doğrulama olmadan istemci ve sunucu aynı şekilde gelen bir talep iletir. (1.1) İstemcinin soket kimliği yanıttan alınır. (1.2) karma haritadan alınır. Hash-map içinde soket bulunamazsa, kimlik doğrulama işlemi başarısız olur. (2) Ardından kimlik doğrulama yanıtı ayrıştırılır ve kimlik doğrulama bilgisi buna göre ayarlanır. Şu andan itibaren, bu istemci soketinden çıkan her komut binMsg->uid ve binMsg->gid ile yürütülür. Ardından (3)sunucu soketi karma haritadan alır. Yoksa, kimlik doğrulama işlemi başarısız olur.

Zafiyet Sömürüsü

Sömürü, mesajları doğrudan omiengine UNIX soketi üzerinden gönderen bir Python betiği oluşturulur.

Python Algoritması

Birinci iş mantığı;

  1. Sahte kimlik bilgileriyle bir kimlik doğrulama isteği gönderin
  2. Başka bir istek daha yollayın
  3. id >> /tmp/win komutunu gönderin

İkinci iş mantığı;

  1. Kimlik doğrulama isteği gönderin
  2. Birinci iş mantığında başlatılan kimlik doğrulama isteği için uid =0, gid =0 ile kimlik doğrulama başarılı yanıtı alın. (Burp Suite kullanarak post gövdesinde işlem yapabilirsiniz.)
  3. Belirli sayıda yinelemeden sonra, kodumuz kök olarak yürütülecektir.

OMI YAMASI?

Gereken en minimal yama; OMI GitHub deposunda açıklanan Auth-Header değerini geçersiz bir değerle başlatmanız yeterli olacaktır.

KVKK, ISO 27001, Bilgi ve İletişim Güvenliği Rehberi, ISO 27701, Bilgi Güvenliği, Siber Güvenlik ve Bilgi Teknolojileri konularında destek ve teklif almak için lütfen

< Önceki Sonraki >