web-dev-qa-db-fra.com

La division par zéro est-elle une vulnérabilité de sécurité?

Même si parfois les bogues logiciels et les vulnérabilités sont considérés comme le même concept, il doit y avoir au moins un aspect distinct entre eux, et je pense que le plus important est exploitabilité (ce dernier ayant la propriété).

Ce qui m'intéresse, c'est que, même après avoir vu de nombreux cas où des bogues de division par zéro sont signalés comme des problèmes logiciels, je peux difficilement trouver une attaque (autre que DoS) en utilisant la division par -les bogues zéro. Je sais que tous les types de bogues n'ont pas le même impact sur un système en termes de sécurité, mais existe-t-il une méthode d'attaque qui utilise des bogues de division par zéro pour atteindre quelque chose de différent du DoS, comme l'escalade de privilèges par exemple?

81
Gwangmu Lee

Le problème est qu'un gestionnaire d'exceptions sera appelé pour gérer la division par zéro. En général, les attaquants savent que les gestionnaires d'exceptions ne sont pas aussi bien testés que les flux de code normaux. Votre flux logique principal peut être sain et minutieusement testé, mais un gestionnaire d'exceptions peut être déclenché par des interruptions se produisant n'importe où dans le code dans son champ d'application.

int myFunction(int a, int b, SomeState state) {

    state(UNINITIALIZED);
    try {
        state.something(a/b);
        state(NORMAL);
    }
    catch () {
        state.something(b/a);
        state(INVERTED);
    }
    return retval;
}

Cette horrible pseudocode illustre une façon d'exploiter la faille. Disons qu'un état non initialisé est en quelque sorte vulnérable. Si cette routine est appelée, l'état est d'abord non initialisé. Si b est zéro, il intercepte l'exception et essaie de faire une autre logique. Mais si a et b sont tous les deux nuls, il lance à nouveau, laissant l'état non initialisé.

La division par zéro elle-même n'était pas la vulnérabilité, c'est le mauvais code qui l'entoure qui est possible d'exploiter.

105
John Deters

Pour ajouter un autre artificiel mais basé sur un exemple réel:

Il y a de nombreuses (nombreuses) lunes, mon lycée exécutait Novell Netware et l'avait configuré de sorte que les élèves ne pouvaient pas exécuter directement une invite de lecture (ce qui était ennuyeux si vous aviez par exemple besoin de formater une disquette). Cependant, il a été découvert que si vous saisissiez un mot de passe de plus de X caractères (c.-à-d. Que vous maintenez simplement une touche enfoncée pendant un certain temps), cela ferait planter le réseau et ramènerait le système à ... une invite de lecture. Il s'agit très probablement d'un dépassement de mémoire tampon, mais le principe est le même: si vous avez affaire à un système (en particulier un système embarqué) qui gère au moins certains plantages inattendus en vous faisant passer en mode maintenance, alors il y a un problème de sécurité .

50
Foon

Comme les autres réponses le suggèrent, cela dépend entièrement de l'ensemble du système. Ce n'est pas seulement le code, mais aussi la plate-forme, le langage et le compilateur.

Par exemple, en c ++, la division par zéro sur les entiers est un comportement non défini. L'une des règles concernant ce langage particulier est que le compilateur peut supposer qu'un comportement indéfini ne se produira jamais et qu'il est autorisé à faire quoi que ce soit s'il le fait.

Tout inclut un crash avec un message d'erreur et un nettoyage, un crash sans message d'erreur et tout laisser dans un état étrange, ou le plus terrifiant, essayez de continuer comme si de rien n'était.

Maintenant, dans la pratique, la plupart des bons compilateurs modernes essaient de planter proprement s'ils frappent quelque chose comme ça, mais cela devrait expliquer clairement pourquoi l'hypothèse est qu'il s'agit d'une vulnérabilité. Si vous venez de créer votre code avec un compilateur différent ou des paramètres différents, et que vous ne changez même pas le code, ce que le programme fait pourrait passer d'un crash propre à l'effacement de votre base de données.

19
Josiah

Au total, avec un serveur Web mal configuré, un environnement PHP pourrait révéler le chemin physique vers le script lorsqu'une division par zéro est provoquée (fuite de chemin interne):

<?php
$denominator = $_GET['number'];
echo 1/$denominator;

En accédant à script.php? Number = 0, nous avons pu voir:

Avertissement: Division par zéro dans /var/www/html/script.php à la ligne 3.

16

Bien que je dis parfois dans mes ateliers et conférences que tous les problèmes de sécurité dans les logiciels sont essentiellement des bogues, l'inverse n'est pas automatiquement vrai.

Je n'irais cependant pas vers l'exploitabilité comme argument. Ce n'est pas parce que vous et moi ne pouvons pas trouver un moyen d'exploiter que quelqu'un de plus rusé ne le fera pas un jour. Il faudrait fournir une preuve formelle qu'il n'est pas exploitable. Bonne chance avec des preuves formelles dans les langages de programmation actuels.

Donc, pour répondre à votre question: Une division par zéro pourrait être une vulnérabilité ou une partie de celle-ci. Typiquement cela entraînera une exception et donc l'arrêt du logiciel. En soi, cela pourrait être une information importante pour un attaquant (il sait maintenant qu'une variable était nulle).

Il est peu probable que la division par zéro seul soit une vulnérabilité de sécurité, mais dans un contexte plus large, elle peut faire partie du problème.

Donc, pour fermer la boucle, vous devez toujours traiter tous les bugs au sérieux. Si vous ne pouvez pas être sûr (par exemple, votre division utilise une variable que vous ne contrôlez pas), vous devez l'attraper et la gérer, la sécurité ou non. "Ouais, c'est un bug mais je ne vois pas comment il pourrait être exploité" n'est pas un état d'esprit soucieux de la sécurité.

13
Tom

La division par zéro n'est pas intrinsèquement une vulnérabilité de sécurité.

Cependant, si vous pouvez faire planter un serveur d'applications et rester hors ligne en le divisant par zéro, cela peut constituer une vulnérabilité de déni de service.

10

Je pense qu'en fin de compte, votre réponse dépendra du système individuel en jeu. Comment le système gère-t-il les tentatives de division par 0? S'il est élégant, vos options d'attaque sont limitées ou inexistantes. S'il fait quelque chose de génial, vous pouvez probablement y entrer avec quelque chose.

Fondamentalement, aucune attaque standard ne peut en résulter - que je sache de toute façon - mais les ordinateurs peuvent toujours mal gérer les bogues, et une mauvaise gestion des bogues est à l'origine de nombreuses vulnérabilités.

5
securityOrange

Si un programme ne reçoit que des données fiables et s'il ne doit répondre à aucune exigence comportementale lorsqu'il reçoit des données conçues de manière malveillante, il peut être possible de générer un code légèrement plus efficace qu'il ne le faudrait s'il existe des exigences comportementales a dû se réunir pour toutes les données.

Étant donné que certaines tâches impliquent uniquement le traitement de données fiables, tandis que d'autres impliquent le traitement de données provenant de sources non fiables, la norme C permet aux implémentations qui sont destinées uniquement aux anciennes tâches de faire des optimisations qui seraient inappropriées dans celles destinées à ces dernières et à celles destinées à convenir à ces dernières tâches pour offrir des garanties qui entraveraient inutilement les optimisations qui pourraient être utiles lors du traitement des premières.

Malheureusement, la norme ne fournit aucun moyen par lequel les implémentations peuvent indiquer quels types de garanties comportementales elles offriront au-delà de celles prescrites par la norme. Lorsque C89 a été écrit, les auteurs s'attendaient à ce que "le marché" puisse faire un meilleur travail que les auteurs de la norme pour déterminer quels types d'implémentations devraient prendre en charge quelles "extensions populaires" en se comportant au moins de façon assez prévisible dans les cas où la exigences et une telle attitude n'a pas changé même si elle ne correspond plus au marché des compilateurs d'aujourd'hui.

Étant donné quelque chose comme:

if (x != 0) launch_nuclear_missiles();
... and then, possibly in another function
z=y/x;

certaines personnes considéreraient un compilateur qui remplace le "si" par un appel inconditionnel à launch_nuclear_missiles() comme supérieur à celui qui n'appelle que launch_nuclear_missiles si x est différent de zéro, mais un tel comportement ne serait approprié que lors du traitement de tâches qui n'impliqueront jamais une entrée non fiable.

Si l'on sait que les compilateurs que l'on utilise maintiendront les types de garanties comportementales faibles que les compilateurs à usage général offraient naturellement, et qui facilitent l'écriture de programmes qui pourraient répondre à des contraintes comportementales faibles même lorsqu'ils reçoivent des intrants malveillants, alors la division par zéro pourrait ne pas poser de failles de sécurité. Avec des compilateurs à optimisation agressive qui ne sont pas conçus pour convenir à des tâches impliquant une entrée non fiable, cependant, il sera nécessaire d'ajouter suffisamment de logique de contrôle de sécurité pour annuler tous les avantages que de telles "optimisations" auraient pu offrir.

5
supercat

mais existe-t-il une méthode d'attaque qui utilise des bogues de division par zéro pour réaliser quelque chose de différent du DoS, comme une élévation de privilèges par exemple?

Recherche rapide sur la base de données CVE de MITRE révélera que la division par zéro cause principalement un déni de service, mais un cas particulier CVE-2005-0998 permet autre chose:

Description Le module Web_Links pour PHP-Nuke 7.6 permet aux attaquants distants d'obtenir des informations sensibles via un paramètre show non valide, ce qui déclenche une division par zéro PHP qui fuit le chemin d'accès complet du serveur.

Cela ne permet pas en soi une escalade de privilèges ou des attaques spécifiques, mais en général, les informations sensibles peuvent être utiles pour créer d'autres attaques. En d'autres termes, la division par zéro par elle-même pourrait n'être qu'un tremplin.

4

Je vois d'où tu viens. En soi, il est difficile de voir comment une erreur arithmétique peut être utilisée pour provoquer tout ce qui nécessite une entrée de données (comme l'exécution de code).

Cependant, selon la langue, cela pourrait provoquer des problèmes de contrôle de flux ou planter sélectivement des threads, etc. C'est le genre de chose qui vous permet d'aggraver d'autres problèmes ou, si vous êtes vraiment (mal) chanceux, d'entraîner directement le déclenchement d'une voie qui provoque doubler quelque chose d'exploitable. N'oubliez pas que la saisie des données et le bogue ne doivent pas nécessairement être les mêmes.

C'est un peu artificiel, mais disons par exemple que vous avez déplacé un objet déjà dans une liste liée en ajoutant une copie à la tête, puis en itérant jusqu'à ce que vous le trouviez et supprimiez la copie. Si vous faites cela dans un thread et pour une raison quelconque (disons que quelqu'un met une instruction print stats dans la boucle d'itération qui est incorrecte), il est possible que la liste soit en mauvais état avec 2 copies du même élément. Lorsque cela est nettoyé: double gratuit ...

Si vous pouvez contrôler le contenu de ce nouvel élément et faire en sorte que la statistique entraîne une division par zéro tout en contrôlant suffisamment le contenu, cela pourrait bien être exploitable.

Peu probable, mais possible.

3
ANone

La question pourrait être mieux formulée comme "Pourquoi le déni de service est-il considéré comme une vulnérabilité de sécurité?". Lors de l'exécution de Linux sur un processeur x86, si le processeur divise un entier par 0 (ou la valeur minimale signée divisée par -1, merci supercat), le programme reçoit un signal SIGFPE. Sous Windows, c'est une opération illégale. La norme C/C++ considère qu'il s'agit d'un comportement non défini, ce qui signifie que vous ne pouvez pas savoir de manière fiable ce qui se passera simplement en regardant le code.

Normalement, comme pour de nombreuses vulnérabilités de déni de service, mais pas toutes, cela entraînera un "plantage". Voici quelques possibilités de ce que vous pouvez exploiter lorsqu'un programme a une terminaison anormale:

  • Il pourrait permettre à un autre programme d'utiliser les mêmes ressources que le premier (comme un port) et prétendre être le même programme.
  • Le plantage peut contourner toutes les opérations de nettoyage utilisées par le programme qui pourraient être exploitées.
  • Si le code de sortie du programme est utilisé dans un autre programme qui ne vérifie pas l'état du signal du PID du processus enfant, il pourrait penser que le programme a renvoyé un code d'erreur. Par exemple, sur ma machine Linux, les 2 programmes suivants définissent tous les deux $? (La variable utilisée dans les scripts Shell pour représenter le code retour de la commande précédente) sur 136: int main(int argc, char** argv) { return 1 / (argc - 1); } et int main() { return 136; }
  • Un plantage peut entraîner des choses comme des vidages mémoire qui peuvent contenir de la mémoire protégée comme des clés de sécurité, des mots de passe et la recette de Coca-Cola.

En outre, en raison des optimisations, le compilateur peut choisir de supposer que le diviseur ne peut jamais être égal à 0 et potentiellement optimiser le code absent que vous n'avez peut-être pas pris en compte. Cela ne s'applique qu'aux langages compilés comme C et C++ qui ont un comportement non défini (ou son équivalent) pour la division par zéro. Je ne pouvais pas produire de code optimisé qui le fasse en utilisant les exemples de certaines autres réponses dans clang ou gcc, mais rien n'empêche eux ou d'autres compilateurs de le faire dans le passé ou dans le futur.

2
Erroneous

Il y a ici des réponses très précises. Je conviens qu'une division (non gérée) par problème zéro est principalement un problème fonctionnel - mais cela ne signifie pas qu'elle ne peut pas être exploitée comme un problème de sécurité. En effet, il y a tellement de bugs qui peuvent être exploités la classification de "fonctionnel" vs "sécurité" lorsqu'elle est appliquée aux bugs semble quelque peu inutile.

Vous rejetez une catégorie de problèmes de sécurité (DOS) sans aucune discussion sur ce que vous considérez comme hors de portée ici. Le scénario le plus évident est que le déclenchement de l'erreur entraînera l'arrêt de l'instance du programme. Mais il y a des effets plus subtils - s'il provoque l'écriture de grandes quantités de données dans le système de fichiers (comme un core dump), il présente une avenue pour un deuxième type de DOS, en remplissant le système de fichiers.

Jefrey mentionne la divulgation d'une application PHP - mais les messages d'erreur, les journaux et les vidages de mémoire fournissent de riches motifs de sélection pour les personnes essayant d'extraire des informations auxquelles ils ne devraient pas avoir accès à partir de programmes écrits dans n'importe quelle langue.

Si la fin prématurée du programme conduit à une sorte de réponse, un attaquant a les moyens de déclencher un programme spécifique au moment de son choix - il a un certain contrôle sur ce que fait votre système, ce qui peut faciliter une escalade d'attaque/de privilège.

Même si le système de contrôle est manuel, il existe toujours un état de transition pour le système où les comportements de sécurité sont moins bien définis et où des actifs tels que des phrases secrètes, des mots de passe et des clés privées sont en vol.

1
symcbean

Voici un exemple où cela pourrait faire une différence.

Il est 100% artificiel - mais de nombreuses classes d'exploits sont identifiées par des exemples théoriques "artificiels", jusqu'à ce que des exemples spécifiques soient trouvés.

C'est un pseudo-code.

try:
   get the average cpu usage per logged in user
   if this average is too high, tell the person logging on that we're busy.
   get login credentials
   verify login credentials
   if credentials are invalid, tell them to go away
   restrict access for this session to the correct access for this user
if there is an exception:
   do nothing

let the user in

Dans ce cas, le processeur moyen par utilisateur connecté peut être calculé comme "le processeur total/nombre d'utilisateurs connectés" - s'il n'y a pas d'utilisateurs connectés, vous obtenez "diviser par 0" et il passe directement à "si il existe une clause d'exception. Cela inclut la restriction de l'accès - la nouvelle session peut avoir un accès correspondant au dernier utilisateur à se connecter ou un accès illimité.

Bien que cet exemple montre tout le code ensemble, votre code principal appelle souvent quelque chose, qui appelle quelque chose d'autre, et les deux aspects qui interagissent (dans ce cas, la division par 0 et le fait que l'exception est ignorée) peuvent s'exécuter en plusieurs parties du code qui sont très éloignés.

Comme beaucoup de gens l'ont mentionné, la division par zéro (en soi) n'est pas un bogue de sécurité, ni même un bogue. Pour autant que je sache, la plupart (peut-être tous!) Des langages de programmation ont des informations clairement documentées sur ce qui se passe lorsque la division par zéro se produit. Je pense que presque toutes les langues qui ont des exceptions, ont une exception pour la division par zéro (j'ai vu une calculatrice mécanique coincée dans une boucle sans fin).

1
AMADANON Inc.