21 Eyl, 2021

[:tr]Sayısız Azure Müşterisini Etkileyen OMI’deki Kritik Güvenlik Açıkları[:en]Critical Vulnerabilities in OMI Affecting Countless Azure Customers[:]

[:tr]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

[:en]Four critical vulnerabilities have been found in OMI, one of Azure’s most common but least-known software agents, and deployed on a large portion of Linux VMs in Azure. It has proven to be very easy to exploit vulnerabilities. It allows attackers to remotely execute arbitrary code within the network with a single request and escalate to root privileges.

• CVE-2021-38647 – Unauthenticated RCE as root

• CVE-2021-38648 – Privilege Escalation vulnerability

• CVE-2021-38645 – Privilege Escalation vulnerability

• CVE-2021-38649 – Privilege Escalation vulnerability

We will try to explain all the technical details in the following sections.

First of all, what is the OMI that causes these vulnerabilities?

OMI is the UNIX/Linux equivalent of Windows’ WMI. It allows users to manage configurations and gather statistics in remote and local environments. It is widely used in Azure, especially Open Management Suite (OMS), Azure Insights, Azure Automation, and more because of the ease of use and abstraction that OMI provides.

Why is the OMI attack surface of interest to attackers?

The OMI agent runs as root with elevated privileges. Any user can communicate using a UNIX socket or sometimes using an HTTP API when configured to allow external use. As a result, OMI represents a potential attack surface for an attacker where external users or low-privileged users allow remote code execution or elevation of privileges on target machines.

Note that in scenarios where OMI ports (5986/5985/1270) are accessible over the internet to allow remote management, this vulnerability could also be exploited by attackers to gain initial access to the target Azure environment and then move laterally within it. Therefore, an exposed HTTPS port is a holy grail for malicious attackers. With a simple exploit, as shown in the diagram below, they can access new targets, execute commands with the highest privileges, and possibly propagate to new target machines.

CVE-2021-38647 Remote Code Execution – Remove Authentication Header and Become Root!

It’s like a way of exploiting a vulnerability straight out of the 90s. However, it is the RCE vulnerability that took place in 2021 and is a lesson that affects millions of endpoints.

With a single package, an attacker can become a root user on a remote machine by simply removing the authentication header.

How Can It Be So Simple?

This vulnerability allows remote takeover when OMI exposes the HTTPS management port externally (5986/5985/1270). This is actually the default configuration when installed standalone and in Azure configuration management or System Center Operations Manager (SCOM).

Fortunately other Azure services (like Log Analytics) don’t expose this port and therefore scope is limited to local privilege escalation.

The diagram below shows the unexpected behavior of OMI when a command execution request is issued without an authorization header.

  1. 1. Normal flow with valid password in Authentication header, send an HTTP request to remote OMI instance and pass login information in authorization header.2. Getting an authorization error when passing an Invalid Authentication header, as expected it will fail if omicli passes an invalid header.3. The vulnerability can be exploited when passing a command without the Authentication header. Even without an Authentication header, the OMI server trusts the request and enables perfect RCE.There are approximately 15,707 online devices that are vulnerable and have not yet updated.

More Technical Details!

OMI has a front-end-back-end architecture. Omiengine receives an authentication request from the client, sends a new authentication request with omic, the omiserver records the user’s authentication result information. uid and gid’ forward response back to user.

Let’s look at the authentication logic inside the _ProcessReceivedMessage function:

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;

}

}

….

}

Let’s review the logic; (1) first, omiengine registers the client’s socket in a connection hash map, using the port number as the key. (2) Next, omiengine establishes a new connection with the omiserver. (3) and saves it to the same tracker hash map. (4) Then the verification request is sent to the server for verification.

Now let’s see how the same function handles the server response:

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);

….

}

}

The _ProcessReceivedMessage function forwards an incoming request to the client and server alike, without any server authentication. (1.1) The client’s socket ID is taken from the response. (1.2) is taken from the hash map. If the socket is not found in the hash-map, the authentication fails. (2) Then the authentication response is parsed and the authentication information is set accordingly. From now on, every command that leaves this client socket is executed with binMsg->uid and binMsg->gid. Then (3) the server takes the socket from the hash map. Otherwise, the authentication process will fail.

Vulnerability Exploitation

The exploit creates a Python script that sends messages directly over the omiengine UNIX socket.

Python Algorithm

First business logic;

1. Submit an authentication request with fake credentials

2. Submit another request

3. Send command id >> /tmp/win

Second business logic;

1. Submit an authentication request

2. Get authentication successful response with uid=0, gid=0 for authentication request initiated in first business logic. (You can manipulate the post body using Burp Suite.)

3. After a certain number of iterations, our code will be executed as root.

OMI PATCH?

Minimal patch required; Simply initialize the Auth-Header described in the OMI GitHub repository with an invalid value.

[:]

İçerik Hakkında:
Sosyal Medyada Paylaş:
Facebook
Twitter
LinkedIn
Telegram