Connexion
Abonnez-vous

Une faille critique dans le langage Rust, Windows trinque

De la rouille, des fenêtres, une rustine

Une faille critique dans le langage Rust, Windows trinque

Une faille critique de sécurité a été trouvée dans la bibliothèque standard Rust. Elle ne peut être exploitée que sous Windows, à cause de la manière particulière dont le système de Microsoft découpe et interprète les arguments d’une commande.

Le 12 avril à 17h02

La découverte a été faite par le chercheur en sécurité RyotaK, qui l’a signalée à l’équipe de développement de Rust. Elle est désormais référencée comme CVE-2024-24576 et a reçu la note maximale de sévérité, soit 10 sur 10. Elle a bien sûr reçu un petit nom pour l’occasion : BatBadBud.

Cette vulnérabilité est présente dans la bibliothèque standard de Rust, cette dernière n’échappant pas correctement les paramètres quand des commandes (batch ou cmd) sont invoquées.

En cas d’exploitation, le risque est que des commandes arbitraires soient transmises. Bien que la faille soit bien celle de Rust, elle remet sur le devant de la scène la manière dont Windows exécute les commandes et lit les arguments qui les accompagnent. En effet, comme expliqué par d’autres, Rust n’est pas le seul langage avec lequel il faut faire attention dans ce contexte.

Ce qui se passe sous Windows

Comme indiqué le 9 avril dans un billet de blog, le groupe de travail Rust Security Response a été mis au courant du problème. Quand des fichiers bat ou cmd sont invoqués depuis une application Rust via l’API Command de Windows, il existe une possibilité qu’un acteur malveillant puisse glisser des arguments spécialement conçus pour exploiter la faille.

« Un attaquant capable de contrôler les arguments transmis au processus créé pourrait exécuter des commandes shell arbitraires en contournant l'échappement », explique ainsi Pietro Albini, l’un des membres du groupe de travail.

Pourquoi un tel problème ? Parce que l’invite de commande de Windows a sa propre logique de séparation des arguments. Une logique différente de celle trouvée dans les API Command::arg et Command::args de la bibliothèque standard de Rust.

Ces dernières permettent normalement de faire passer des arguments en toute sécurité. Mais, « la mise en œuvre est plus complexe que sur les autres plateformes, car l'API Windows ne fournit qu'une seule chaîne de caractères contenant tous les arguments du processus créé », explique Albini. Or, c’est à ce processus créé qu’incombe alors la tâche de diviser les arguments.

Dans ce contexte, l’équipe de sécurité considère que la logique d’échappement de Rust n’est pas « suffisamment rigoureuse ». En conséquence, Chris Denton, l’un des contributeurs de Rust, a développé un correctif pour atténuer – et non supprimer – le problème. Dans la version 1.77.2, des améliorations sont portées au code d’échappement, afin notamment que l’API Command renvoie une erreur (InvalidInput) quand les arguments ne sont pas échappés de manière sécurisée.

Toutes les versions antérieures sont considérées comme vulnérables.

Le problème est présent dans d'autres langages

RyotaK, le découvreur de la faille, a expliqué que d’autres langages peuvent rencontrer le même problème, toujours à cause de la manière dont Windows divise les arguments d’une commande.

Haskell a ainsi déjà son correctif, tandis que Node.js et PHP travaillent sur les leurs. Erlang, Go, Python et Ruby ont mis à jour leur documentation pour sensibiliser les développeurs sur ce sujet. Il est probable que la couverture médiatique de la faille aidera d’ailleurs à mieux les avertir.

Pour le chercheur, le problème de fond réside bien dans Windows, même si les failles de sécurité se trouvent bien dans les langages.

Il donne plusieurs conseils généraux, comme toujours spécifier l’extension de fichier de la commande dans les arguments. Il recommande également de toujours échapper les données contrôlées par l’utilisateur avant de les utiliser comme arguments.

« Pour éviter l'exécution inopinée de fichiers batch, vous devriez envisager de déplacer les fichiers batch dans un répertoire qui n'est pas inclus dans la variable d'environnement PATH. Dans ce cas, les fichiers batch ne seront pas exécutés si le chemin complet n'est pas spécifié, ce qui permet d'éviter l'exécution inopinée de fichiers batch », ajoute le chercheur.

Pourquoi axer la communication sur Rust ?

On peut se poser la question, de multiples langages étant concernés. D’abord, c’est dans Rust que la faille a été découverte. Son fonctionnement a incité d’autres chercheurs et développeurs à analyser le comportement dans d’autres langages.

Ensuite, Rust attire énormément l’attention depuis quelques années, particulièrement depuis qu’il est pris au sérieux par des entreprises comme Google et Microsoft. La première a ainsi investi un million de dollars dans la fondation Rust, tandis que la seconde embauche des développeurs Rust et compte réécrire tout ou partie du noyau Windows dans ce langage. La Maison-Blanche a même exhorté les entreprises technologiques à se pencher sur Rust.

Pourquoi un tel enthousiasme ? Parce que le langage, créé initialement par Mozilla (plus précisément par Graydon Hoare comme projet personnel), a fait ses preuves. C’est essentiellement parce qu’il est memory-safe tout en présentant de bonnes performances, proches de ce que l’on trouve en C et C++. Il est en tête du classement des langages les plus appréciés depuis plusieurs années sur StackOverflow.

Commentaires (34)

Vous devez être abonné pour pouvoir commenter.

Abonnez-vous
votre avatar
Avoir axé la communication quasi seulement sur Rust et moins sur le fait que c'est Windows le problème je trouve ça malhonnête. Ainsi que la note de 10/10, il faut déjà être sur la machine et lancer la commande d'une certaine façon, aucun rapport avec par exemple le backdoor xz ou l'exécution de RCE via le réseau.
votre avatar
J'ai regardé l'article de blog cité pour plus de détails techniques.

Alors non, le problème ne vient pas de Windows. Le problème vient de la différence de comportement entre le monde Unix et le monde Windows (plus particulièrement, les caractères d'échappement à utiliser lors de la création d'un processus, où, sous Linux les spawn, popen, etc. utilise le backslash tandis que CreateProcess (l'API WIndows donc) utilise le caret (^).

Le fait que la même erreur ait été faite par plusieurs équipes distinctes dans plusieurs langages n'en fait pas un problème de Windows. Quand on appelle mal une API, il ne faut pas rejeter la faute sur l'API.

Des comportements Unix ont été calqués sur Windows, et après on vient dire que c'est de la faute de Windows. Le problème aurait tout aussi bien pu être dans l'autre sens, et là, personne n'aurait remis en cause Linux.

Comme expliqué dans l'article, on peut regretter que la communication soit axée sur Rust alors que la faille touche de nombreux langages. C'est juste que la faille a été découverte via Rust en premier.
votre avatar
Pour avoir eu un souci similaire en Java (plus exactement une issue reportée par un outil de sécurité), c'est surtout qu'il y a une différence fondamentale entre Windows et Linux : en C, tu as des commandes comme execvp dont la signature passe par un tableau de paramètres.

Mais CreateProcess fonctionne différemment : learn.microsoft.com Microsoft
[in, out, optional] lpCommandLine
The command line to be executed.
The maximum length of this string is 32,767 characters, including the Unicode terminating null character. If lpApplicationName is NULL, the module name portion of lpCommandLine is limited to MAX_PATH characters.
On passe une ligne de commande - pas un exécutable et ses paramètres - , ce qui introduit tout un tas de problèmes :

- celui des espaces
- celui des caractères spéciaux, qui t'en qu'à faire sont différents de Windows à Linux
- ... une limite de 32767 caractères

Pour reprendre Java, et je suppose que Rust fait pareil, on utilise bien des tableaux dans le code du langage : github.com GitHub

Côté Windows, on voit que Java recalcule une ligne de commande : github.com GitHub

Côté Linux c'est (bien) différent : on passe des tableaux, que des tableaux, rien que des tableaux avec la seule contrainte du caractère NUL en fin de chaîne.

- github.com GitHub
- github.com GitHub

Et pour le coup, si Windows ne fournit pas l'API pour éviter (car il s'agit de ça au final) de prendre les paramètres et de recréer une ligne de commande, alors oui Windows est responsable de la situation.

(si les liens ci-dessus référencent Java 21, ça existe depuis au moins Java 8, donc ce problème Windows qui concerne Rust, Java, etc.... est là depuis au moins 2013 :))
votre avatar
Et pour le coup, si Windows ne fournit pas l'API pour éviter (car il s'agit de ça au final) de prendre les paramètres et de recréer une ligne de commande, alors oui Windows est responsable de la situation.
Ben non, pas d'accord. En quoi c'est une situation à éviter ? Ce sont des choix, différents (ce que je souligne depuis le début), et c'est de cette différence que vient le problème.

Et les deux ont aussi des différences en dehors du caractère d'échappement. Il est beaucoup plus simple avec l'approche de Windows de vérifier la taille max des arguments de la ligne de commande (alors que sous linux, il faut la reconstituer, sans oublier de compter les espaces !). Car oui, il y a une taille max !

De même, l'approche utilisée par Windows préserve les espaces. S'il y a besoin de 3 espaces entre deux arguments, c'est possible de le faire sous Windows, pas sous Linux.

Encore une fois, il n'y a pas une approche qui soit meilleure qu'une autre. Ce sont juste 2 approches différentes.
votre avatar
Ben non, pas d'accord. En quoi c'est une situation à éviter ? Ce sont des choix, différents (ce que je souligne depuis le début), et c'est de cette différence que vient le problème.
Parce que tu fournis une ligne de commande à analyser (parser) plutôt que de fournir le résultat de cette analyse ? Résultat que tu connais car au final, d'un point de vue programme tu connais les paramètres du programme que tu appelles.
Cela passe pour un shell, où l'utilisateur entre des commandes, mais pas pour des APIs bas niveau.

Et je ne vois pas le souci d'espaces dans la façon Linux : tu passes juste un tableau de chaîne de caractères (terminées par le caractère NUL). Le fait de vérifier la taille max de l'ensemble n'est pas plus coûteux : vu la différence de temps de lancement entre Linux et Windows (plus lent), ce n'est certainement pas ça le plus coûteux !
Encore une fois, il n'y a pas une approche qui soit meilleure qu'une autre. Ce sont juste 2 approches différentes.
Si justement : dans le cas présent, il s'agit d’exécuter un programme depuis un autre programme avec les paramètres connus.

D'une part, la façon de faire de WIndows introduit des problèmes d'analyses vu que les règles sont difficiles à utiliser (je dis ça d'expérience de batch), pas documentée sur la doc de CreateProcess (pas trouvé sur la page de lien ou d’explication), d'autre part ça introduit des failles de sécurité du fait de cette complexité et de cas limites (notamment sur les caractères ").

Or, il s'agit bien de ça : Rust a une faille critique à cause de cette façon de faire qui nécessite un travail compliqué, mal compris et pour rien
votre avatar
Parce que tu fournis une ligne de commande à analyser (parser) plutôt que de fournir le résultat de cette analyse ? Résultat que tu connais car au final, d'un point de vue programme tu connais les paramètres du programme que tu appelles.
Cela passe pour un shell, où l'utilisateur entre des commandes, mais pas pour des APIs bas niveau.

La question est de savoir si les paramètres du ligne de commande sont nécessaires au système d'exploitation ou pas. La réponse est non. L'OS à juste besoin de savoir le programme à lancer, et les paramètres à passer, mais n'a pas à connaitre la structure des paramètres (tableau ou ligne).

Sous Linux, le choix a été fait d'organiser les paramètres sous forme de tableau. Sous Windows, c'est une chaine de caractère. Les deux sont tout à fait viable et ont de subtiles différences.
Et je ne vois pas le souci d'espaces dans la façon Linux : tu passes juste un tableau de chaîne de caractères (terminées par le caractère NUL). Le fait de vérifier la taille max de l'ensemble n'est pas plus coûteux : vu la différence de temps de lancement entre Linux et Windows (plus lent), ce n'est certainement pas ça le plus coûteux !
Je ne parlais de la complexité en terme de temps de calcul, mais de la complexité d'avoir un algorithme correcte. Et j'avoue ne pas trop voir en quoi le temps de lancement vient jouer un rôle ici. On parle des paramètres des programmes. Cela n'a strictement rien à voir.
D'une part, la façon de faire de WIndows introduit des problèmes d'analyses vu que les règles sont difficiles à utiliser (je dis ça d'expérience de batch),
Jusqu'au jour où on va se rendre compte que linux a en fait potentiellement le même problème, car quand un script est exécuté, l'interpréteur est déterminé via le shell bang. Chaque interpréteur peut avoir des règles d'échappements différents. Du coup, là aussi, on accusera Windows ?
Or, il s'agit bien de ça : Rust a une faille critique à cause de cette façon de faire qui nécessite un travail compliqué, mal compris et pour rien
Pas pour rien non. Quand les systèmes sont différents, il faut bien palier les différences. C'est le rôle du pattern adapteur ou d'un wrapper en programmation. Mais parfois c'est compliqué oui. Comme ici.
votre avatar
La question est de savoir si les paramètres du ligne de commande sont nécessaires au système d'exploitation ou pas. La réponse est non. L'OS à juste besoin de savoir le programme à lancer, et les paramètres à passer, mais n'a pas à connaitre la structure des paramètres (tableau ou ligne).
Sous Linux, le choix a été fait d'organiser les paramètres sous forme de tableau. Sous Windows, c'est une chaine de caractère. Les deux sont tout à fait viable et ont de subtiles différences.
Analyser une ligne de commande pour la traduire en paramètres, n'est pas le souci en soit : le souci c'est que cette analyse ne sert à rien dans le cas d'une API surtout quand tous les programmes C/C++ passent par une méthode main qui prends le nombre de paramètres (argc) et ces paramètres (argv).

C'est le cas autant sous Windows que sous Linux.

D'ailleurs, j'ai aussi trouvé la doc qui explique ce que fait Windows pour transformer une chaîne en paramètres :
learn.microsoft.com Microsoft
Je ne parlais de la complexité en terme de temps de calcul, mais de la complexité d'avoir un algorithme correcte. Et j'avoue ne pas trop voir en quoi le temps de lancement vient jouer un rôle ici. On parle des paramètres des programmes. Cela n'a strictement rien à voir.
Justement : l'algorithme est simple.
Calculer la somme des tailles de chaîne d'un tableau c'est très facile.
Rien ne justifie la méthode de Windows, certainement pas la complexité algorithme ou le temps d’exécution de l'algorithme.
Jusqu'au jour où on va se rendre compte que linux a en fait potentiellement le même problème, car quand un script est exécuté, l'interpréteur est déterminé via le shell bang. Chaque interpréteur peut avoir des règles d'échappements différents. Du coup, là aussi, on accusera Windows ?
Sauf que les règles du shebang sont (probablement) entièrement liées au système : j'imagine que sous Linux, il commence déjà par voir à quoi il a affaire, programme ELF ou simple fichier avec shebang, puis il fait son travail : ce n'est pas l'application qui doit faire ce travail.

Si par exemple le caractères 0x36 doit être interdit, c'est le système que tu patches : pas chaque programme souhaitant lancer un programme.

Or, c'est justement ça le cœur du problème ici : Rust doit gérer ça, Java doit gérer ça, ...
Pas pour rien non. Quand les systèmes sont différents, il faut bien palier les différences. C'est le rôle du pattern adapteur ou d'un wrapper en programmation. Mais parfois c'est compliqué oui. Comme ici.
Certes, mais tout le débat ici c'est justement que cette différence n'a aucun sens dans le cas présent et ne fait qu'introduire des failles de sécurité pour rien...

Si on veut parler d'un adapter/wrapper, probablement que tous les langages utilisant CreateProcess devraient passer par une API commune, cela réduirait les surfaces d'attaque.
votre avatar
Et pour le shebang, c'est au final assez bien décrit : https://www.man7.org/linux/man-pages/man2/execve.2.html

Et ça me donne l'impression qu'au final, il refait un exeve en ayant juste changer le programme pour celui du shebang et ajouter le paramètre optionnel en début du tableau = pas de ligne de commandes.
votre avatar
Il n'y a pas que les programmes C sous Windows... Le "point d'entrée" d'une application Win32 est WinMain...

learn.microsoft.com Microsoft

Pas d'argc/argv...
votre avatar
Le problème c'est que CreateProcess à deux comportement différent suivant que le fichier qu'il exécute est un exécutable ou un fichier de commande et que le second cas n'est pas documenté.

Dans le premier cas les paramètre sont simplement passés comme arguments à l’exécutable, un simple échappement suffit, alors que dans le second cas les paramètre sont interprétés comme une ligne de commande ce qui n'est clairement pas ce qu'attendent les utilisateurs, et qui est difficilement échappable.

On pourrait au moins excuser ce comportement étrange s'il était documenté.
votre avatar
On est d'accord sur un manque de documentation. Ce n'est pas pour rien que plusieurs équipes ont fait la même erreur.

Cela n'en constitue pas pour autant une faille dans l'API Windows.
votre avatar
Si tu parts du principe que l'utilisateur est responsable de la sécurité de ses paramètres mais qu'en même temps c'est l'implémentation qui fait foi et pas la documentation, en effet rien ne sera jamais une faille dans l'API.

Il n’empêche que, le fait que CreateProcess traite les arguments comme on traite une ligne de commande n'est ni le comportement auquel on s'attend, ni ce qui est documenté, et il en résulte une possibilité d'exploitation.

Dire si c'est une faille ou pas, c'est jouer sur les mots. En tout cas, ça en a les symptômes et ça devrait vraiment être corrigé, au minimum par une mise à jour de la documentation.
votre avatar
Pour ma part je ne savais même pas que CreateProcess prenait les .bat en charge. "instinctivement" si besoin je me tournerais vers ShellExecute.
votre avatar
Le fait que la même erreur ait été faite par plusieurs équipes distinctes dans plusieurs langages n'en fait pas un problème de Windows. Quand on appelle mal une API, il ne faut pas rejeter la faute sur l'API.
Vraiment ? L'argument serait valide pour un petit nombre de personnes, ou sur un langage unique.
Ce n'est pas le cas ici.

Le standard sur lequel s'appuie Rust est celui de GNU C, pas "Linux" (qui est un noyau). Rien de bien nouveau, et qui à défaut d'être une norme, est un standard d'interopérabilité.
Qui plus, un vieux standard : on ne parle pas de dernière fraîcheur.

La vieillerie cmd.exe contient une manière très particulière (comme personne d'autre) de fonctionner, et ce depuis donc bien trop longtemps.
Une n-ème démonstration que l'habitude de M$ de produire du non-standard crée un/des risque(s) pour les utilisateurs de leurs produits.

Les langages doivent donc adopter un comportement particulier pour ce système d'exploitation particulier. D'aucuns dirait que c'est bien le système d'exploitation le problème.

Cf. https://blog.rust-lang.org/2024/04/09/cve-2024-24576.html
votre avatar
Le standard sur lequel s'appuie Rust est celui de GNU C, pas "Linux" (qui est un noyau). Rien de bien nouveau, et qui à défaut d'être une norme, est un standard d'interopérabilité.
Qui plus, un vieux standard : on ne parle pas de dernière fraîcheur.

Pour être plus précis, le standard derrière s'appelle Posix (et pas GNU C). Sauf que l'API Windows existe 1985, POSIX depuis 1988. Windows ne respecte donc pas un standard qui n'existait tout simplement pas l'époque des premières versions du système d'exploitation.
Les langages doivent donc adopter un comportement particulier pour ce système d'exploitation particulier. D'aucuns dirait que c'est bien le système d'exploitation le problème.
C'est le principe de la bibliothèque standard de chaque langage. Quand tu as un langage dont l'API est proche de Posix, c'est plus simple à développer sur un système Posix que sur un système non Posix.

Le fait que plusieurs personnes se sont cassées les dents et qu'il y ait une faille à ce sujet aujourd'hui montre 2 choses :
- écrire une implémentation d'une API standard, ce n'est pas quelque chose de trivial
- un manque de documentation de l'API Windows.

Mais cela ne signifie pas que l'API présent une faille comme beaucoup le pense.

Le problème aujourd'hui touche uniquement Windows car :
- les systèmes à base de Linux (distribution classique, Android, etc.) respectent plus ou moins le standard POSIX
- les BSD respectent plus ou moins POSIX
- MacOS (basé sur BSD) respect plus ou moins POSIX

En fait, Windows est le seul système d'exploitation "grand publique" qui ne soit pas Posix de nos jours.
Une n-ème démonstration que l'habitude de M$ de produire du non-standard crée un/des risque(s) pour les utilisateurs de leurs produits
Comme dit plus haut, le standard n'existait pas à l'époque des premières versions de Windows.
votre avatar
Le problème est que l'API Windows est fondamentalement limitée et ne permet pas de passer une liste d'arguments à un programme, uniquement une chaîne de caractères qui sera "la commande", ce qui implique tout un tas de problèmes.

Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Encore une fois, il n'y a pas une approche qui soit meilleure qu'une autre.
Si, il y a une approche correcte, qui maintient la sémantique de la liste d'arguments sur toute la ligne, de la commande ou du script jusqu'à la fonction main du programme, et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.

Le fait que les langages de programmation aient repris cette sémantique indique bien que tout le monde considère ça comme la manière correcte de faire. Je ne connais aucun langage qui ait choisi de prendre ses arguments comme une seule grande chaîne de caractères au lieu d'une liste.
votre avatar
Oui Rust, Java & co doivent trouver une manière correcte pour contourner cette limitation de Windows. Mais pas seulement, c'est a peu près certain qu'on trouve dans le monde des dizaines de milliers de failles à cause de cette limitation problématique et difficilement compatible avec la manière dont la fonction main est spécifiée dans la plupart des langages (les argument sont une liste de string, pas une string)
Quitte à me répéter, les premières versions de l'API Windows sont sorties avant pas mal de standardisation, dont celle du C (dont le premier standard remonte à 89 rappelons le). Si les points d'entrées ont été mis à jour depuis et que Windows accepte aussi le point d'entrée actuellement répandue (autrement dit, la fonction main), beaucoup des choix techniques de l'époque ont perduré pour des raisons de compatibilité (car oui, Windows possède une API extrêmement stable, contrairement à ce que l'on trouve dans les environnements Linux). Mais pareil, il n'y a pas de solution quoi meilleure que l'autre Les deux ont des avantages et des inconvénients.

Et une fois encore, ce n'est pas une limitation de Windows. C'est une problématique entre un environnement POSIX et un non POSIX. C'est radicalement différent.
Si, il y a une approche correcte, qui maintient la sémantique de la liste d'arguments sur toute la ligne, de la commande ou du script jusqu'à la fonction main du programme, et une approche fondamentalement problématique et qui encourage les failles de sécurité, mais qui marchotte à peu près avec des hacks et des contournements.
Sérieusement ? Appeler ce qui relève d'un adapteur ou d'un wrapper un hack ? Sinon, certains langage n'avaient pas la notion de tableau d'arguments, mais bien d'une ligne : BASIC, Fortran à partir de 2003 (bon, qui propose les deux approchent :p), mais qui n'avait aucun mécanisme standard avant. Il me semble que COBOL permet les deux aussi. Et je suis loin de connaitre tous les langages.

Le problème est que tu considères la sémantique de la liste d'arguments comme allant de soi et étant une obligation aujourd'hui. Même si c'est très répandu, c'est loin d'être toujours le cas.
Le fait que les langages de programmation aient repris cette sémantique indique bien que tout le monde considère ça comme la manière correcte la plus pratique de faire.
Ma correction.

Sinon, cette faille est du même acabit qu'une injection SQL qui serait exploitable car le connecteur à la base de données, sensé protéger via l'utilisation de requêtes paramétrées, fait mal son boulot. Est-ce qu'on dit pour autant que ce sont les SGBD qui sont troués ? Ou les API qui sont mal utilisées ?
votre avatar
Sinon, cette faille est du même acabit qu'une injection SQL qui serait exploitable car le connecteur à la base de données, sensé protéger via l'utilisation de requêtes paramétrées, fait mal son boulot. Est-ce qu'on dit pour autant que ce sont les SGBD qui sont troués ? Ou les API qui sont mal utilisées ?
Ton analogie me fait penser à « magic_quotes », qu’on ne regrettera pas : une fonctionnalité pourrie, qu’il est impossible de faire marcher correctement.

Le comportement de windows est contre-intuitif et incohérent :

quand tu es dans ton programme, les arguments sont reçues sous forme de tableau de char
* quand tu appelles CreateProcess, les arguments doivent être fournis sous forme de ligne de commande

J’ai beaucoup de choses à reprocher au modèle fork/exec de posix, mais à minima il est cohérent dans la manière dont sont passés les arguments (qui date de bien avant C89, probablement avant C K&R, donc avant 73, bien avant windows).

Rajoute à ça que CreateProcess a un comportement différent sur les fichiers bat et les fichiers exe, et que si l’appelant ne précise pas l’extension il ne sait pas ce qui va être appelé, tu as une api qui est en réalité non prévisible.

Après, la responsabilité de la faille, c’est comme on veut. MS a la responsabilité d’avoir exposé une API pourrie (ce qui peut arriver), mais surtout de ne pas l’avoir remplacée par une API plus simple / prévisible depuis tout ce temps (c’est pas comme si l’api win32 était truffée de -Ex qui remplacent une api pourrie, conservée pour la compatibilité uniquement), ce qui est moins excusable.
votre avatar
[…] le standard n'existait pas à l'époque des premières versions de Windows.
Cela n'aurait rien changé qu'il existe, par l'habitude constante précédemment mentionnée de l'entreprise éditrice… problème systématique étant la véritable cause racine de la faille sujet de l'article.
votre avatar
Pour être plus précis, le standard derrière s'appelle Posix (et pas GNU C). Sauf que l'API Windows existe 1985, POSIX depuis 1988. Windows ne respecte donc pas un standard qui n'existait tout simplement pas l'époque des premières versions du système d'exploitation.
Ce que tu dis n'est pas possible le noyau NT n'existe que depuis juillet 1993 et ne se base justement pas sur DOS, par contre, il gare une certaines compatibilités, mais les applications Ms-DOS/Windows de l'époque ne fonctionne pas sous Windows NT.
votre avatar
En quoi n'est-ce pas possible ? Un changement majeur niveau noyau n'implique pas forcément un changement des API exposés.

Le changement de noyau NT reflétait surtout une modification profonde de la manière de communiquer avec le matériel. De manière très simpliste (car c'est loin d'être le seul changement mais sans doute le plus significatif), avant NT, les drivers pouvaient accéder directement au matériel (en by-passant complètement le noyau donc), ce qui n'est plus le cas avec NT.

Winapi, première version, date du 20 novembre 1985 d'après Wikipédia et existait bien avant le noyau NT.

Il ne faut pas confondre le noyau (qui gère entre autre le matériel) des API exposés pour les programmes.
votre avatar
En terme de manque de documentation...
"To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file."

(doc de CreateProcess)

Je ne vois pas où est la partie qui dit que tu peux exécuter un bat... (ça parle d'exécuter un module)
votre avatar
Le problème est plus compliqué que ça.

CreateProcess, quand il lance un exécutable (fichier exe) classique est correctement échappé. Le truc, c'est que si on lui fait exécuter un fichier de commandes windows (fichier bat ou cmd), il démarre en sous-main un interpréteur cmd.exe va traiter l'ensemble des paramètres comme une ligne dans un fichier de commande avec toutes les fonctionnalité associées beaucoup trop complexes pour un simple démarrage de script. Les règles d’échappement sont différentes, mais il y a aussi le fait que les variables d’environnement, mots clé et caractères spéciaux du langage batch sont traitées ...

Alors oui on peut dire que c'est un choix de fonctionnement de Windows et Rust doit s'adapter à la situation. C'est d'ailleurs ce que Rust a fait au final, là où Java a considéré que ce n'était pas son problème. Mais pour faire cela bien dès le début, encore aurait il fallu que ce comportement ait été correctement documenté. Or la documentation MSDN actuelle de CreateProcess ne dit rien de l'utilisation implicite de cmd.exe pour l'exécution des fichiers de commande, au contraire, elle propose de lancer cmd.exe manuellement.

Il est difficile de reprocher à Rust de ne pas avoir anticipé un comportement non documenté d'une API Windows dont les conséquence sur la sécurité avaient échappé a la plupart jusqu'à présent.
votre avatar
votre avatar
C'est d'ailleurs pour cela que dans cet article lié j'avais émis une inquiétude quant au discours axé "sécurité" autour de ce langage.

Il ne faut jamais relâcher sa vigilance et surtout ne pas reposer sur les discours de ce type et les croyances. Même si le langage est conçu avec une gestion permettant d'éviter certaines erreurs, il y a toujours des risques.

Et si j'ai cette opinion, c'est parce qu'on entend encore de nos jours le poncif éculé et mensonger de "je suis sous Linux je suis invulnérable aux malwares" (même chose pour Mac, alors qu'il y a peu une faille des processeurs M1 a été dévoilée). La sécurité IT ne repose pas sur une marque mais un ensemble de pratiques. Le problème lorsqu'on s'engouffre dans ce genre de fantasme, c'est qu'on baisse son attention.
votre avatar
Pour le coup le problème est aussi en C et C++. Rust en a hérité car il utilise la fonction CreateProcess de l'API Windows en C et C++. Et Rust considère que c'est un problème et l'a corrigé immédiatement alors que le problème va, semble-il, rester en l'état, au moins pour les langages Java, C et C++.

Donc même s'il n'est pas a l'abri de tous les problèmes, Rust reste clairement plus porté sur la sécurité que la majorité des langages.
votre avatar
La nuance de mon propos n'est pas de dire que X est mieux que Y. Mais de dire que X ou Y, peu importe, restons vigilants sur la sécurité et ne nous reposons pas sur des promesses, aussi avérées soient-elles.

D'où :
La sécurité IT ne repose pas sur une marque mais un ensemble de pratiques.
Choisir un langage apportant plus de sécurités fait donc parti de cet ensemble de pratiques. Auquel il faut ajouter la veille autour, et de ce qui est satellite. D'où mon exemple caricatural de "Linux c'est invulnérable aux malwares". Se reposer sur le discours de sécurité de Rust (que je n'ai jamais remis pas en cause, j'en suis incapable) est un risque en soit de réduire sa vigilance.

Exemple caricatural : le programme est plus sûr grace aux capacités du langage, tant mieux. Mais si la DB est exposée aux 4 vents, la sécurité est zéro pointé.

D'où, à nouveau :
La sécurité IT ne repose pas sur une marque mais un ensemble de pratiques.
Ma crainte au sujet du discours continu "Rust = plus sécurisé", c'est justement d'engendrer l'effet inverse.
votre avatar
Je comprend l'idée, mais je n'adhère pas vraiment au discours qui dit qu'on apporte plus d'attention à la sécurité quand on doit tout gérer, en tout cas c'est pas du tout ce que je constate dans la pratique.

Je suis d'autant moins convaincu que la philosophie de Rust contrairement à des langages comme Java, n'est pas d'automatiser les problématiques de sécurité pour les oublier, mais d'aider à les relever et à les prendre en charge, ce qui pousse au contraire à en prendre conscience. Rust m'a poussé a m’intéresser à la sécurité plutôt que l'inverse.

Je suis beaucoup plus conscient des problèmes de sécurité, y compris dans les autres langages, depuis que j'ai appris le Rust, et pas forcément que sur ce qui touche à la mémoire, car la communauté Rust ne tend pas vraiment à se reposer uniquement sur ce qu'offre le langage. Il y a notamment déjà pas mal d'outils qui permettent d'aller bien au delà des garanties mémoire du langage. De ce que j'ai pu constater, il y a beaucoup plus d'attention aux problématiques de sécurité en général dans les communautés Rust, que dans les communautés C et C++. C'est probablement pas pour rien que le problème a été identifié en Rust alors qu'il concerne bien d'autre langages dont certains depuis bien plus longtemps.
votre avatar
En fait, le problème provient bien de la façon dont est architecturé Windows si cette faille n'affecte que ce système. Bon, je suppose que la faille va être corrigée fissa, mais bon ...
votre avatar
Vive Powershell...

Les .bat .cmd, et la construction de lignes de commandes sous Windows est un enfer. Utilisant souvent des scripts pour lancer des processus, via une ligne de commande construite, j'ai souvent eu des problèmes avec les caractères d'échappement et l'encodage.
Ca m'a même value de remplacer un exe de office chargé de lancer le bon exe car IE encodait le caractère d'échappement: même Ms s'y perd...
votre avatar
Ça touche les langages qui ont été dev sous Unix puis portés sous Windows.

C'est bien une faille Rust (ou autre langage) le portage n'a pas su adapter les spécificités de lancement des programmes et .bat de Windows.

Ça fait une mauvaise pub pour Rust mais Windows n'en sort pas grandit non plus...
votre avatar
Le souci est que ça touche aussi des langages originaires Windows. Donc, sur c coup-ci, c'est davantage Windows (son système d'interprétation de commandes en fait) qui est à blâmer.
votre avatar
Je n'ai vu nulle part que ça touchait des langages originaires de Windows. T'as un lien là dessus ?
votre avatar

Une faille critique dans le langage Rust, Windows trinque

  • Ce qui se passe sous Windows

  • Le problème est présent dans d’autres langages

  • Pourquoi axer la communication sur Rust ?

Fermer