L’utilisation d’un compilateur C obsolète est-elle un risque pour la sécurité?

Nous avons des systèmes de production en production dont personne ne se soucie et ces machines exécutent des versions anciennes de GCC comme GCC 3 ou GCC 2.

Et je ne parviens pas à convaincre la direction de la transformer en une version plus récente: ils disent: “Si ce n’est pas cassé, ne le répare pas”.

Comme nous maintenons une base de code très ancienne (écrite dans les années 80), ce code C89 comstack très bien sur ces compilateurs.

Mais je ne suis pas sûr que ce soit une bonne idée d’utiliser ces anciens éléments.

Ma question est:

L’utilisation d’un ancien compilateur C peut-elle compromettre la sécurité du programme compilé?

METTRE À JOUR:

Le même code est construit par Visual Studio 2008 pour les cibles Windows, et MSVC ne prend pas encore en charge C99 ou C11 (je ne sais pas si MSVC plus récent) et je peux le construire sur ma machine Linux en utilisant le dernier GCC. Donc, si nous ajoutions simplement un nouveau GCC, il serait probablement aussi bon qu’avant.

En fait, je dirais le contraire.

Il y a un certain nombre de cas où le comportement n’est pas défini par le standard C mais où il est évident que ce qui se produirait avec un “compilateur idiot” sur une plate-forme donnée. Des cas comme permettre à un entier signé de déborder ou d’accéder à la même mémoire via des variables de deux types différents.

Les versions récentes de gcc (et de clang) ont commencé à traiter ces cas comme des opportunités d’optimisation sans se soucier de modifier la façon dont le binary se comporte dans la condition “comportement indéfini”. C’est très grave si votre base de code a été écrite par des personnes qui ont traité C comme un “assembleur portable”. Au fil du temps, les optimiseurs ont commencé à examiner des morceaux de code de plus en plus gros lors de ces optimisations, augmentant les chances que le binary fasse autre chose que “ce que ferait un binary construit par un compilateur idiot”.

Il y a des commutateurs de compilateur pour restaurer le comportement “traditionnel” (-fwrapv et -fno-ssortingct-aliasing pour les deux que j’ai mentionnés ci-dessus), mais vous devez d’abord les connaître.

Bien qu’en principe un bogue de compilateur puisse transformer un code conforme en une faille de sécurité, je considère que le risque que cela soit négligeable dans le grand nombre de choses.

Il y a des risques dans les deux domaines d’action.


Les anciens compilateurs ont l’avantage de la maturité, et tout ce qui a été cassé dans ceux-ci a probablement (mais il n’y a pas de garantie) travaillé avec succès.

Dans ce cas, un nouveau compilateur est une source potentielle de nouveaux bogues.


En revanche, les compilateurs les plus récents disposent d’ outils supplémentaires :

  • GCC et Clang disposent désormais de désinfectants capables d’influer sur l’exécution pour détecter des comportements indéfinis de diverses sortes (Chandler Carruth, de l’équipe du compilateur Google, a déclaré l’année dernière qu’il s’attendait à une couverture complète)
  • Clang, au moins, le renforcement des fonctionnalités, par exemple l’ intégrité du stream de contrôle consiste à détecter les hi-jacks du stream de contrôle, il existe également des outils de protection contre les attaques de la stack (en séparant la partie contrôle de la stack). ; les fonctions de durcissement sont généralement faibles (<1% de la charge du processeur)
  • Clang / LLVM travaille également sur libFuzzer , un outil pour créer des tests unitaires de fuzzing instrumentés qui explorent intelligemment l’espace d’entrée de la fonction testée (en ajustant l’entrée pour prendre des chemins d’exécution non encore explorés).

L’instrumentation de votre binary avec les désinfectants (assainisseur d’adresses, assainisseur de mémoire ou assainisseur de comportement indéfini) et le fuzzing (en utilisant American Fuzzy Lop par exemple) a mis au jour des vulnérabilités dans un grand nombre de logiciels, voir par exemple cet article de LWN.net .

Ces nouveaux outils et tous les futurs outils vous sont inaccessibles, à moins que vous ne mettiez à niveau votre compilateur.

En restant sur un compilateur peu puissant, vous vous mettez la tête dans le sable et en croisant les doigts qu’aucune vulnérabilité n’est trouvée. Si votre produit est une cible de grande valeur, je vous invite à reconsidérer.


Remarque: même si vous ne mettez PAS à niveau le compilateur de production, vous pouvez utiliser un nouveau compilateur pour vérifier la vulnérabilité de toute façon; Sachez que puisque ces compilateurs sont différents, les garanties s’en

Votre code compilé contient des bogues pouvant être exploités. Les bogues proviennent de trois sources: des bogues dans votre code source, des bogues dans le compilateur et les bibliothèques, et un comportement indéfini dans le code source que le compilateur devient un bogue. (Le comportement indéfini est un bogue, mais pas encore un bogue dans le code compilé. Par exemple, i = i ++; en C ou C ++ est un bogue, mais dans votre code compilé, il peut augmenter de 1 et être OK, ou définir i à un peu de bric-à-brac et être un bug).

Le taux de bogues dans votre code compilé est probablement faible en raison des tests et de la correction des bogues dus aux rapports de bogues des clients. Il y a donc peut-être eu un grand nombre de bogues au départ, mais cela a diminué.

Si vous effectuez une mise à niveau vers un compilateur plus récent, vous risquez de perdre des bogues introduits par les bogues du compilateur. Mais ces bogues seraient tous des bugs qui, à votre connaissance, n’ont été trouvés et personne n’a été exploité. Mais le nouveau compilateur peut avoir des bogues, et surtout, les compilateurs les plus récents ont plus tendance à transformer le comportement non défini en bogues dans le code compilé.

Vous aurez donc beaucoup de nouveaux bogues dans votre code compilé. tous les bogues que les pirates pourraient trouver et exploiter. Et à moins que vous ne fassiez beaucoup de tests et que vous laissiez votre code aux clients pour trouver des bogues pendant une longue période, la sécurité sera moindre.

Si ce n’est pas cassé, ne le répare pas

Votre patron a raison de dire que le facteur le plus important est la protection des entrées, des sorties et des débordements de tampons. L’absence de ceux-ci est invariablement le maillon le plus faible de la chaîne, quel que soit le compilateur utilisé.

Cependant, si la base de code est ancienne et que des travaux ont été mis en place pour atténuer les faiblesses du K & R C utilisé, comme le manque de sécurité, les risques d’insécurité, etc., posez la question suivante: / Les normes C11 cassent tout?

Pourvu qu’il y ait un chemin clair pour migrer vers les nouvelles normes C, ce qui pourrait induire des effets secondaires, il serait préférable d’essayer une fourchette de l’ancienne base de code, de l’évaluer et de mettre en place des contrôles le compilateur plus récent a un effet sur les jeux de données d’entrée / sortie.

Ensuite, vous pouvez le montrer à votre patron, ” Voici la base de code mise à jour, refactorisée, plus conforme aux normes C99 / C11 acceptées par l’indussortinge … “.

C’est le pari qui devrait être mis en balance, très soigneusement , la résistance au changement pourrait apparaître dans cet environnement et pourrait refuser de toucher les choses les plus récentes.

MODIFIER

Je me suis contenté de restr assis quelques minutes avant de réaliser que le code généré par K & R pouvait être exécuté sur une plate-forme 16 bits. La mise à niveau vers un compilateur plus moderne risquerait de briser la base du code. , cela pourrait avoir des effets secondaires amusants sur les structures utilisées pour les jeux de données d’entrée / sortie, ce qui est un autre facteur important à prendre en compte.

De plus, comme OP a mentionné l’utilisation de Visual Studio 2008 pour créer la base de code, l’utilisation de gcc pourrait induire l’introduction dans l’environnement de MinGW ou de Cygwin, susceptible d’avoir un impact sur l’environnement, sauf si la cible est Linux. vaut le coup, peut-être inclure des commutateurs supplémentaires au compilateur pour minimiser le bruit sur l’ancienne base de code de K & R, l’autre chose importante est d’effectuer beaucoup de tests pour s’assurer qu’aucune fonctionnalité n’est cassée, peut s’avérer être un exercice douloureux.

L’utilisation d’un ancien compilateur C peut-elle compromettre la sécurité du programme compilé?

Bien sûr, cela peut être le cas si l’ancien compilateur contient des bogues connus dont vous savez qu’ils affectent votre programme.

La question est, est-ce que c’est? Pour vous en assurer, vous devez lire le journal des modifications complet de votre version à la date actuelle et vérifier chaque bogue corrigé au fil des ans.

Si vous ne trouvez aucune preuve de bogues du compilateur qui affecterait votre programme, la mise à jour de GCC uniquement pour cette raison semble un peu paranoïaque. Il ne faut pas oublier que les nouvelles versions peuvent contenir de nouveaux bogues, qui n’ont pas encore été découverts. Beaucoup de changements ont été apportés récemment avec le support GCC 5 et C11.

Cela étant dit, le code écrit dans les années 80 est probablement déjà rempli de failles de sécurité et de recours à un comportement mal défini, quel que soit le compilateur. Nous parlons de pré-standard C ici.

Il existe un risque pour la sécurité lorsqu’un développeur malveillant peut se faufiler dans un bogue de compilateur. Selon la quantité de bogues connus dans le compilateur utilisé, la porte dérobée peut sembler plus ou moins discrète (en tout cas, le fait est que le code est correct, même si alambiqué, au niveau de la source). un compilateur non bugué ne trouvera pas la porte dérobée, car la porte dérobée n’existe pas dans ces conditions). Pour des points de déni supplémentaires, le développeur malveillant peut également rechercher lui-même des bogues de compilateur inconnus auparavant. Encore une fois, la qualité du camouflage dépendra du choix des bugs du compilateur trouvés.

Cette attaque est illustrée sur le programme sudo dans cet article . bcrypt a écrit un excellent suivi pour les minificateurs Javascript .

En dehors de cette préoccupation, l’évolution des compilateurs C a été d’exploiter un comportement indéfini de plus en plus agressif, de sorte que l’ancien code C écrit de bonne foi serait plus sûr compilé avec un compilateur C de l’époque, ou compilé à -O0 (mais de nouvelles optimisations exploitant UB sont introduites dans les nouvelles versions des compilateurs, même avec -O0 ).

Les compilateurs plus anciens peuvent ne pas être protégés contre les attaques de piratage connues. La protection contre le bris de stack, par exemple, n’a pas été introduite avant GCC 4.1 . Donc, oui, le code compilé avec des compilateurs plus anciens peut être vulnérable de manière à protéger les nouveaux compilateurs.

Un autre aspect à prendre en compte est le développement d’un nouveau code .

Les compilateurs plus anciens peuvent avoir un comportement différent pour certaines fonctionnalités de langage que ce qui est standardisé et attendu par le programmeur. Cette incompatibilité peut ralentir le développement et introduire des bogues subtils pouvant être exploités.

Les compilateurs plus anciens offrent moins de fonctionnalités (y compris les fonctionnalités linguistiques!) Et ne s’optimisent pas non plus. Les programmeurs contourneront ces lacunes – par exemple en réimplémentant les fonctionnalités manquantes ou en écrivant un code intelligent, obscur mais plus rapide – créant de nouvelles opportunités pour la création de bogues subtils.

Nan

La raison est simple, l’ancien compilateur peut avoir d’anciens bogues et exploits, mais le nouveau compilateur aura de nouveaux bogues et exploits.

Vous ne corrigez aucun bogue en effectuant une mise à niveau vers un nouveau compilateur. Votre changement de vieux bugs et exploits pour de nouveaux bogues et exploits.

Il existe une probabilité plus élevée que tous les bogues de l’ancien compilateur soient bien connus et documentés, par opposition à l’utilisation d’un nouveau compilateur. Des actions peuvent donc être sockets pour éviter ces bogues en les codant. Donc, d’une manière qui ne suffit pas comme argument pour la mise à niveau. Nous avons les mêmes discussions où je travaille, nous utilisons GCC 4.6.1 sur une base de code pour les logiciels embarqués et il y a une grande réticence (parmi la direction) à passer au dernier compilateur en raison de la crainte de nouveaux bogues non documentés.

Votre question se divise en deux parties:

  • Explicite: “L’utilisation du compilateur le plus ancien présente-t-elle un risque plus grand” (plus ou moins comme dans votre titre)
  • Implicite: “Comment puis-je persuader la direction de mettre à niveau”

Vous pouvez peut-être répondre à la fois en trouvant une faille exploitable dans votre base de code existante et en montrant qu’un nouveau compilateur l’aurait détecté. Bien sûr, votre direction peut dire «vous avez trouvé cela avec l’ancien compilateur», mais vous pouvez faire remarquer que cela a coûté un effort considérable. Ou vous l’exécutez via le nouveau compilateur pour trouver la vulnérabilité, puis exploitez-le si vous êtes en mesure / autorisé à comstackr le code avec le nouveau compilateur. Vous voudrez peut-être demander l’aide d’un pirate ami, mais cela dépend de leur confiance et de la possibilité / de leur permettre de leur montrer le code (et d’utiliser le nouveau compilateur).

Mais si votre système n’est pas exposé à des pirates, vous devriez peut-être être plus intéressé par une mise à niveau du compilateur: MSVS 2013 Code Analysis trouve très souvent des bogues potentiels beaucoup plus tôt que MSVS 2010 et supporte plus ou moins C99 / C11 – Vous ne savez pas si c’est le cas officiellement, mais les déclarations peuvent suivre des instructions et vous pouvez déclarer des variables for -loops.