Exécution de code à distance (RCE), expliqué: qu’est-ce que c’est et comment l’empêcher

L’exécution de code à distance (RCE) est une classe de failles / vulnérabilités de sécurité logicielles. Les vulnérabilités RCE permettront à un acteur malveillant d’exécuter n’importe quel code de son choix sur une machine distante via LAN, WAN ou Internet. RCE appartient à la classe plus large des vulnérabilités d’exécution de code arbitraire (ACE). Avec l’omniprésence d’Internet, cependant, l’impact des vulnérabilités RCE augmente rapidement. Ainsi, les RCE sont maintenant probablement le type de vulnérabilité ACE le plus important.

Comme c’est le cas, nous voulions examiner plus en détail les différents types de vulnérabilités RCE et les contre-mesures possibles.

Classification RCE par origine

La plupart, sinon la totalité, des vulnérabilités RCE connues ont un petit nombre de causes sous-jacentes.

Exécution de code dynamique

L’exécution de code dynamique tend à être le vecteur d’attaque le plus courant menant à RCE. La plupart des langages de programmation ont un moyen de générer du code avec du code et de l’exécuter sur place. C’est un concept très puissant qui aide à résoudre de nombreux problèmes complexes. Cependant, un tiers malveillant peut facilement en abuser pour acquérir des capacités RCE.

Souvent, le code généré à l’exécution est basé sur une entrée utilisateur. Le plus souvent, le code inclut cette entrée sous une forme ou une autre. Un acteur malveillant, réalisant que la génération de code dynamique utilisera une entrée donnée, pourrait fournir du code valide comme entrée pour attaquer votre application. Si les entrées utilisateur ne sont pas vérifiées, ce code sera exécuté sur la machine cible.

D’une manière générale, l’exécution de code dynamique provoque deux grandes classes de vulnérabilités RCE : directes et indirectes.

Direct

Dans le cas d’une exécution de code dynamique directe, l’acteur malveillant est conscient que son entrée serait utilisée dans la génération de code.

Indirect

Un cas indirect, encore une fois, se résume à la génération de code dynamique, y compris les entrées utilisateur. Cependant, l’entrée utilisateur passe par une ou plusieurs couches. Certaines couches peuvent même transformer cette entrée avant qu’elle ne se termine par une génération de code dynamique. En outre, la génération de code dynamique peut être un effet secondaire et non l’utilisation principale de l’entrée. En tant que tel, il n’est pas vraiment évident pour l’utilisateur fournissant l’entrée que l’entrée sera utilisée comme bloc de construction dans un extrait de code à exécuter sur une machine distante.

Désérialisation

La désérialisation est un très bon exemple de ce scénario. Apparemment, aucune génération de code dynamique ne devrait se produire lors de la désérialisation. C’est en fait le cas lorsque l’objet sérialisé ne contient que des champs de données de types primitifs ou d’autres objets de ce type. Les choses se compliquent cependant lorsque les méthodes / fonctions d’un objet sont sérialisées. La désérialisation comprendra alors généralement une forme de génération de code dynamique.

Vous pourriez penser que les langages dynamiques sont le seul endroit où la sérialisation des fonctions a du sens. Le problème sera alors d’une portée limitée. Mais c’est aussi un scénario utile dans les langages statiques. C’est un peu plus difficile à réaliser dans un langage statique mais de loin pas impossible.

Assez souvent, l’implémentation consiste en des objets/fonctions proxy générés par désérialisation. La génération d’objets / fonctions à l’exécution est un cas de génération de code dynamique. Ainsi, si les données à désérialiser proviennent d’une requête faite par une machine distante, un acteur malveillant pourrait les modifier. Des extraits de code sérialisés soigneusement conçus peuvent être injectés qui trompent la génération de code dynamique pour les exécuter lorsqu’ils sont invoqués dans le cadre de la désérialisation.

Sécurité de la mémoire

Une autre cause de vulnérabilités RCE est liée à la sécurité de la mémoire. La sécurité de la mémoire signifie empêcher le code d’accéder à des parties de la mémoire qu’il n’a pas initialisées ou obtenues en entrée. Intuitivement, vous pouvez vous attendre à ce qu’un manque de sécurité de la mémoire entraîne un accès non autorisé aux données. Cependant, le système d’exploitation et le matériel sous-jacent utilisent de la mémoire pour stocker le code exécutable réel. Les métadonnées relatives à l’exécution du code sont également stockées en mémoire. L’accès à ce type de mémoire pourrait entraîner ACE et éventuellement RCE. Alors, quelles sont les principales raisons des problèmes de sécurité de la mémoire?

Défauts de conception logicielle

Les défauts de conception logicielle sont un type de vulnérabilité de sécurité de la mémoire où il y a une erreur de conception dans un composant sous-jacent. Le plus souvent, il s’agirait d’un compilateur, d’un interpréteur ou d’une machine virtuelle, ou potentiellement du noyau du système d’exploitation ou des bibliothèques. Il y a un certain nombre de défauts différents qui appartiennent à cette classe. Nous allons examiner plus en détail ce qui est sans doute le plus courant.

Dépassement de tampon ou dépassement de tampon

Le dépassement de tampon (également appelé dépassement de tampon) est une technique assez simple et bien connue pour violer la sécurité de la mémoire. Il exploite un défaut de conception ou un bogue pour écrire dans les cellules de mémoire qui suivent la fin réelle d’un tampon mémoire. Le tampon lui-même est renvoyé par un appel légitime à l’API publique. Cependant, le tampon sert uniquement de point d’origine pour calculer les adresses de mémoire physique des valeurs de champ / membre privées d’un compteur d’objet ou de programme. Leur position relative par rapport au tampon est soit bien connue, soit peut-être devinée. La recherche du code si disponible ou le débogage de l’exécution du programme au moment de l’exécution peuvent aider un acteur malveillant à obtenir des positions relatives.

Ainsi, un débordement de tampon permet de modifier la mémoire qui devrait être inaccessible par conception. Ce tampon peut résider dans l’espace d’adressage d’une autre machine et être modifié en appelant une API distante. Cela permettra d’accéder à la mémoire de la machine distante. De toute évidence, il existe différentes façons d’utiliser ce type d’accès pour instrumenter un RCE. L’hypothèse générale est que si une vulnérabilité de débordement de tampon existe, un RCE est possible. Ainsi, les propriétaires de code doivent corriger les débordements de tampon DÈS que POSSIBLE, bien avant que l’attaque RCE réelle n’émerge.

Portée

Le plus souvent, le débordement de tampon cible le code C / C ++ car ces langages n’ont pas de vérifications de taille de tampon intégrées. Beaucoup d’autres frameworks et technologies populaires finissent par utiliser des bibliothèques C / C++ au fond de la surface, ce qui les rend automatiquement vulnérables à ce type d’attaque.

Nœud.js en est un bon exemple car, en plus d’être basé sur C / C++, le runtime JavaScript permet également des modules complémentaires C / C ++ natifs. Pour cette raison, un attaquant peut créer soigneusement les requêtes vers un nœud.serveur js pour provoquer un débordement de tampon et ainsi modifier la mémoire système sur la machine affectée, provoquant l’exécution de code arbitraire.

Défauts de conception matérielle

Il est intéressant de noter que des violations de la sécurité de la mémoire peuvent également survenir en raison de défauts de conception de la sécurité matérielle. Bien que moins courantes et plus difficiles à trouver, ces vulnérabilités ont généralement un impact extrêmement élevé.

Attaques RCE déviantes

Bien que le résultat de chaque attaque RCE soit le même en termes d’exécution de code par un attaquant, les vecteurs d’attaque sont de nature très différente. Bloquer tous demande des efforts importants. De plus, l’effort augmente avec la pile technologique. Tous les vecteurs d’attaque décrits dans cet article sont indépendants de la technologie. Toutes les implémentations sont spécifiques à la technologie, tout comme les mécanismes de défense.

Ainsi, une approche traditionnelle pour gagner du temps consiste à surveiller le trafic réseau à la recherche de contenu suspect au lieu de surveiller chaque point de terminaison avec sa technologie spécifique. Un pare-feu d’application Web (WAF) effectue généralement ce travail. Bien que cela fasse gagner du temps, cela a également un prix : le WAF est un goulot d’étranglement des performances du réseau et il manque toutes les informations de base disponibles sur le point de terminaison réel ou au niveau de l’application et de l’utilisateur. Par conséquent, l’analyse du trafic WAF ne pourrait jamais être parfaite. L’heuristique est inévitable sans données complètes, donc soit toutes les menaces n’apparaîtront pas, soit de faux positifs apparaîtront, ou le plus souvent les deux.

Déplacement à l’intérieur de l’application: L’approche de Sqreen

Sqreen résout ces lacunes WAF sans augmenter le coût de développement pour l’utilisateur final en déplaçant la visibilité à l’intérieur de l’application, apportant une protection plus complète avec une RÂPE spécifique à la technologie et un WAF intégré à l’application. La RÂPE et le WAF de Sqreen s’exécutent dans l’application Web, l’API ou le trafic réseau de réception de microservices. Cependant, il ne nécessite aucune modification de code. Il utilise des points d’instrumentation spécifiques à chaque technologie (par exemple, API JVM pour Java, API v8 pour Node.js, etc.) pour modifier le code avant l’exécution au moment de l’exécution. Ainsi, il est capable de surveiller et de modifier les événements système et réseau tout en ayant le contexte complet de tout ce qui se passe à l’intérieur de l’application.

Ainsi, Sqreen peut détecter que l’application utilise des composants présentant des problèmes de sécurité de la mémoire connus. Il peut également détecter les entrées utilisateur réelles qui se rendent aux événements d’exécution de code dynamique. Naturellement, il s’agit d’une approche supérieure pour détecter et prévenir les RCE par rapport à un WAF traditionnel qui n’a accès qu’au trafic réseau.

Conclusion

Clairement, le RCE est un vecteur d’attaque très puissant. Mais, heureusement, il est également possible de se défendre contre les attaques RCE. Les informations ci-dessus peuvent vraiment vous aider à élaborer votre stratégie de défense. Si vous êtes intéressé par d’autres vecteurs et détails d’attaque, consultez nos articles précédents sur l’injection SQL, XXE et LFI.

Leave a Reply