Institut de technologie du Massachusetts. Cours magistral # 6.858. "Sécurité des systèmes informatiques." Nikolai Zeldovich, James Mickens. 2014 année
Computer Systems Security est un cours sur le développement et la mise en œuvre de systèmes informatiques sécurisés. Les conférences couvrent les modèles de menace, les attaques qui compromettent la sécurité et les techniques de sécurité basées sur des travaux scientifiques récents. Les sujets incluent la sécurité du système d'exploitation (OS), les fonctionnalités, la gestion du flux d'informations, la sécurité des langues, les protocoles réseau, la sécurité matérielle et la sécurité des applications Web.
Cours 1: «Introduction: modèles de menace»
Partie 1 /
Partie 2 /
Partie 3Conférence 2: «Contrôle des attaques de pirates»
Partie 1 /
Partie 2 /
Partie 3Conférence 3: «Débordements de tampon: exploits et protection»
Partie 1 /
Partie 2 /
Partie 3 Vous pouvez utiliser la technique de deviner le "canari" à vos propres fins afin de découvrir la présence de "faibles", en termes de sélection, de bits. Autrement dit, si vous avez bien deviné, le serveur va redémarrer, et cela vous signalera que la valeur définie est assez facile à deviner. Ainsi, il est possible de vaincre les «canaris» randomisés, en supposant qu'après le redémarrage du serveur, leur valeur ne changera pas. Vous pouvez également utiliser des gadgets pour implémenter une séquence associée de plusieurs attaques.
Ensuite, nous examinerons une manière plus productive d'utiliser toutes ces méthodes pour déjouer la prévention de l'exécution des données, les espaces d'adressage aléatoires et les «canaris».
Tournons notre attention vers les architectures 64 bits au lieu des architectures 32 bits. Les premiers sont mieux adaptés à la randomisation, ils vous donnent donc beaucoup plus de «chance» de vous défendre contre un pirate. Et ces systèmes semblent beaucoup plus intéressants en termes de formation d'attaque.
Ce type d'architecture 64 bits a également été envisagé du point de vue de
BROP , une programmation orientée «aveugle» inversée. Pour simplifier, nous supposons que la seule différence entre les machines 64 bits et 32 bits est que sur une machine 64 bits, les arguments sont passés aux registres et, pour une machine 32 bits, à la pile.

Lorsqu'une fonction commence à être exécutée, il faut «regarder» dans certains registres pour trouver où se trouvent les arguments.
Passons maintenant à l'essentiel de la conférence d'aujourd'hui - qu'est-ce que la programmation orientée retour aveugle, ou
BROP . La première chose que nous allons faire est de trouver un gadget d'arrêt. N'oubliez pas que lorsque nous disons «gadget», nous entendons essentiellement des adresses de retour. Le gadget est identifié par l'adresse de retour, l'adresse de départ de la séquence d'instructions à laquelle nous voulons aller. Qu'est-ce qu'un gadget d'arrêt?
Essentiellement, il s'agit de l'adresse de retour à un endroit du code, cependant, si vous sautez là-bas, mettez simplement le programme en pause, mais ne le faites pas planter. C'est pourquoi cela s'appelle un gadget d'arrêt.
Vous pouvez sauter à un endroit du code, qui initie alors un appel système en veille, ou fait une pause, ou quelque chose comme ça. Il est possible que le programme "reste coincé" dans une boucle sans fin si vous sautez à cet endroit. Peu importe pourquoi l'arrêt se produit, mais vous pouvez imaginer quelques scénarios qui y mèneraient.
À quoi sert un gadget d'arrêt?
Dès que l'attaquant a réussi à vaincre le «canari» en utilisant la technique interactive de deviner les bits, il peut commencer à réécrire cette adresse de retour, l'adresse de retour et commence à «tâtonner» le gadget d'arrêt. Notez que la plupart des adresses aléatoires que vous pouvez placer sur la pile sont susceptibles de provoquer simplement une panne du serveur. Encore une fois, ce message est pour vous, l'attaquant, c'est une indication que ce que vous avez trouvé n'est pas un gadget d'arrêt. Parce que lorsque le serveur tombe en panne, votre socket se ferme et vous, en tant qu’attaquant, comprenez que vous n’avez pas utilisé le gadget d’arrêt. Mais si vous avez deviné quelque chose et que la prise reste ouverte pendant un certain temps, vous pensez: "Ouais, j'ai trouvé ce gadget d'arrêt!" L'idée de base de la première étape est donc de trouver ce gadget d'arrêt.

La deuxième étape consiste à rechercher des gadgets qui suppriment les entrées de pile à l'aide de la commande
pop . Par conséquent, vous devez utiliser cette séquence d'instructions soigneusement conçues pour savoir quand vous récupérez l'un de ces gadgets de pile. Cette séquence sera constituée de l'
adresse de sonde de l'adresse de sonde , de l'
adresse d'arrêt de l'adresse d'arrêt et de l'
adresse de panne système de l'
adresse de crash .
Ainsi, l'
adresse de la sonde est ce que nous allons mettre sur la pile. Ce sera l'adresse du gadget potentiel pour effacer la pile, l'
adresse d'arrêt - c'est ce que nous avons considéré dans la première étape, c'est l'adresse du gadget d'arrêt. L'
adresse de plantage sera alors simplement l'adresse du code non exécutable. Ici, vous pouvez simplement mettre l'adresse zéro (0 x 0), et si vous appliquez la fonction
ret à cette adresse et essayez d'y exécuter le code, cela entraînera un plantage du programme.

Nous pouvons donc utiliser ces types d'adresses pour savoir où ces gadgets nettoient la
pile de popping .
Je vais donner un exemple simple. Supposons que nous ayons deux exemples de
sondes différents, le piège
piège et le
piège d' arrêt . Supposons, avec l'aide de la
sonde, que nous allons "sonder" une adresse, supposons qu'elle commence par 4 et se termine par huit: 0x4 ... 8, et derrière elle se trouve la prochaine adresse de la forme 0x4 ... C. Hypothétiquement, nous pouvons supposer que l'une de ces deux adresses est l'adresse du gadget
popping de pile .
Le
piège piège obtiendra zéro adresses 0x0 et 0x0, et laissera le gadget
stop s'arrêter avoir des adresses arbitraires comme 0xS ... 0xS ..., cela n'a pas d'importance. Ce gadget d'arrêt pointe vers le code sleep (10), provoquant une pause du programme.
Commençons par l'opération de
sonde , qui efface certains registres et retourne dans l'ordre suivant:
pop rax; ret . Que va-t-il se passer? Lorsque le système passe à cette adresse, le pointeur de pile se déplace au milieu de notre gadget. Que va faire le gadget ici? À droite, effectuez l'opération
pop rax .

Et puis suit
ret , qui déplacera la fonction vers la ligne supérieure du gadget, c'est-à-dire, pour
arrêter , et la fonction s'arrêtera sans planter le programme entier. Ainsi, à l'aide de ce gadget, un attaquant peut dire que l'adresse de
sonde appartient à l'une de ces fonctions, ce qui efface la pile, car la connexion client serveur est ouverte.
Supposons maintenant que la deuxième adresse de
sonde pointe vers quelque chose comme
xor rax, rax, ret pour un registre.

Alors, que se passe-t-il si nous essayons de passer à ce gadget? Notez qu'il n'efface rien sur la pile, il modifie simplement le contenu des registres. Nous allons donc faire un retour à l'adresse 0x0 située ci-dessus. Et cela entraînera le crash du système. La connexion client au serveur sera rompue et le pirate comprendra qu'il ne s'agit pas d'un gadget pour effacer la pile de
pile .
De cette façon, vous pouvez utiliser une série de pièges et de gadgets plus étranges, par exemple, vous pouvez effacer deux éléments de la pile. Pour ce faire, il vous suffit de placer une autre instruction de
déroutement ici, et si ce gadget n'efface pas deux éléments, vous vous retrouverez dans l'un de ces déroutements, et le code cessera de s'exécuter. Le matériel de la conférence décrit une chose appelée
gadget BROP, qui est utile si vous n'aimez pas revenir à la programmation. Mais aujourd'hui, je vais également vous expliquer comment utiliser ces simples gadgets
pop pour lancer une attaque similaire. Une fois que vous aurez compris cela, gérer un
gadget BROP sera beaucoup plus facile.
Mais comprenez-vous tous comment nous pouvons utiliser la fonction de
sonde pour ces gadgets? Supposons que vous trouviez l'emplacement des extraits de code qui vous permet d'effacer la pile avec la fonction
pop , supprimez-en un élément, mais vous ne savez pas vraiment dans quel registre cette fonction
pop fonctionnera. Vous savez juste qu'elle est déjà prête à être exécutée. Mais vous devez savoir dans quel registre ces gadgets
pop fonctionneront, car dans une architecture 64 bits, les registres contrôlent l'emplacement des arguments de la fonction que vous souhaitez appeler.
Ainsi, notre objectif est de pouvoir créer des gadgets qui nous permettront de supprimer les valeurs que nous mettons dans certains registres de pile, et finalement nous lancerons un appel
système appel système , ce qui nous permettra de faire quelque chose de mal.
Alors maintenant, nous devons déterminer quels registres sont utilisés par
les gadgets
pop .

Pour ce faire, nous pouvons profiter de l'appel système de pause. Si l'appel système est suspendu, l'exécution du programme est suspendue et il n'accepte aucun argument. Et cela signifie que le système ignore tout dans les registres.
En fait, pour trouver l'instruction de pause que nous exécutons, vous pouvez lier tous ces gadgets
pop afin que nous puissions tous les mettre sur la pile, et entre chacun d'eux, nous
insérons un numéro d'appel système pour faire une pause. Ensuite, nous verrons si nous pouvons "suspendre" le programme de cette façon. Permettez-moi de vous donner un exemple concret.
Ici, nous mettons un gadget dans la barre d'adresse de retour qui efface les registres
RDI et leur applique la fonction
ret . Au-dessus, nous mettrons un
numéro d'appel système en pause.
Supposons que nous ayons un autre gadget situé au-dessus qui implémente la fonction
pop dans un autre registre, par exemple
RSI , puis
ret . Et puis nous mettons à nouveau le
numéro d'appel système en pause. Et nous le faisons pour tous les gadgets que nous avons trouvés, puis nous nous retrouvons au sommet de la pile avec l'adresse putative pour
syscall .

Rappelez-vous à nouveau comment vous utilisez ces appels système. Vous devez mettre le
numéro syscall dans le registre
RAX , puis appeler la fonction
libc syscall , qui est sur le point d'effectuer l'appel système demandé.
Alors, que se passe-t-il lorsque nous exécutons ce code? Nous allons venir ici, à la ligne d'
adresse ret , nous allons sauter à l'adresse de ce gadget, il faut noter que l'attaquant sait que ce gadget, situé à droite, supprime quelque chose de la pile, mais ne sait pas encore dans quel registre il se trouve.
Donc, si nous sautons à l'
adresse ret , que se passe-t-il? Il utilisera la fonction
pop pour suspendre l'
appel système , dans un registre
inconnu de l'attaquant, puis nous continuerons à grimper cette chaîne d'opérations dans la pile.
Ce faisant, nous espérons que l'un de ces gadgets exécutera la fonction
pop pour le
numéro d'appel système dans le registre
RAX correspondant. Donc, au moment où nous arrivons ici, en haut de la pile, sur le chemin de "gâcher" tous les registres qui ont un
numéro d'appel système à mettre en pause, nous espérons qu'il nous reste un registre, ce qui devrait être correct. Parce que si l'un de nos gadgets le fait, et puis, lorsque nous faisons
ret , après un certain temps, nous reviendrons ici, en haut de la pile, nous obtiendrons une pause. Encore une fois, la pause sert de signal à l'attaquant. Parce que si cette adresse supposée pour
syscall était incorrecte, le programme échouera.
Alors qu'est-ce qui nous permet de faire cette phase d'attaque? Nous ne savons toujours pas dans quels registres se trouvent les gadgets
pop , mais nous savons que l'un d'eux libérera le
RAX que nous voulons contrôler. Et nous connaissons probablement l'adresse
syscall , car nous avons réussi à interrompre le système.
Une fois que nous avons fait cela, nous pouvons vérifier ces gadgets un par un et savoir lequel met le système en pause. En d'autres termes, nous avons tout coupé entre la ligne d'
adresse ret et le haut de la pile pour passer directement à l'
appel système . Nous vérifierons si une pause ou une défaillance du système s'est produite. Si un échec se produit, nous identifions le gadget qui l'a provoqué, par exemple, il est en bas à droite, c'est
pop rdi . Débarrassez-vous de lui et essayez le suivant. Mettez ici dans la ligne ci-dessus l'
adresse ret l'adresse réelle pour
syscall . Pouvons-nous suspendre le programme? Oui, cela signifie que nous avons appris que ce gadget
pop devrait libérer
RAX . Est-ce clair?
Public: Donc, un moyen de deviner l'adresse d'un appel système est juste un transfert aveugle de gadgets?
Professeur: oui, et il existe des moyens d'optimiser ce processus dans les supports de cours lorsque vous utilisez l'extension de fichier
PLT et des choses similaires. Avec la simple attaque que j'ai décrite, il vous suffit de coller l'adresse ici et de vous assurer qu'elle a provoqué une pause ou non. À la suite du test, nous découvrons l'emplacement de
syscall . Nous découvrirons où se trouve l'instruction qui exécute
pop RAX . Nous avons également besoin de gadgets qui exécutent également la fonction
pop dans certains autres registres. Vous pouvez effectuer des tests similaires pour eux. Par conséquent, au lieu de
suspendre le numéro d' appel système, utilisez
push pour une autre commande qui, par exemple, utilise les arguments
RAX et
RDI .
Ainsi, vous pouvez utiliser le fait que pour tout ensemble particulier de registres que vous souhaitez contrôler, il existe une sorte d'
appel système qui donnera à un attaquant un signal pour savoir si vous l'avez réussi ou non. Ainsi, à la fin de cette phase, vous aurez l'adresse
syscall et l'adresse du tas de gadget, qui vous permettent de faire
apparaître des registres arbitraires.
Et maintenant passons à la 4ème étape, qui sera appelée
écriture - enregistrement. La quatrième étape consiste à enregistrer un appel système. Pour appeler en
écriture , nous devons disposer des gadgets suivants:
pop rdi, ret;
pop rsi, ret;
pop rdx, ret;
pop rax, ret;
syscall
Comment ces registres sont-ils utilisés par un appel système? Le premier est le socket, ou, plus généralement, le descripteur de fichier que vous allez transférer pour l'écriture. Le second est le tampon, le troisième est la longueur de ce tampon, le quatrième est le numéro d'appel système et le cinquième est l'
appel système lui-même.
Donc, si nous trouvons tous ces gadgets, nous pouvons contrôler les valeurs qui sont incorporées dans les arguments, qui, à leur tour, sont placées dans ces registres, car nous les avons simplement «poussés» sur la pile.
Que devrait être une prise? Ici, nous devons deviner un peu. Vous pouvez profiter du fait que Linux limite le nombre de connexions ouvertes simultanées pour un fichier qui atteint une valeur de 2024. Et aussi qu'il devrait être le minimum de tous les disponibles.
Je me demande ce que nous allons insérer dans le pointeur tampon? C'est vrai, nous avons l'intention d'utiliser le fragment de texte du programme ici, nous allons le mettre dans un pointeur quelque part dans le code du programme. Cela nous permet de lire le fichier binaire hors de la mémoire en utilisant l'appel client correct à partir du socket. Ensuite, l'attaquant peut prendre ce fichier binaire, l'analyser hors ligne hors ligne, en utilisant
GDB ou un autre outil pour savoir où tout cela se trouve. L'attaquant sait que maintenant, chaque fois que le serveur «plante», le même ensemble aléatoire de choses y sera stocké. Alors maintenant qu'un attaquant peut trouver les adresses et les décalages du contenu de la pile, il peut attaquer ces gadgets directement. Il peut attaquer directement d'autres vulnérabilités, comprendre comment ouvrir le shell et autres. En d'autres termes, à l'endroit où vous avez fourni le binaire avec le pirate, vous avez été vaincu.
Voici comment fonctionne l'attaque
BROP . Comme je l'ai dit, dans les documents de cours, il existe de nombreuses façons d'optimiser ces processus, mais vous devez d'abord comprendre le matériau principal, sinon l'optimisation perd son sens. Par conséquent, vous pouvez me parler d'optimisation individuellement ou après le cours.
Pour l'instant, il suffit de dire que ce sont les bases de la façon dont vous pouvez lancer une attaque
BROP . Vous devez trouver le gadget d'
arrêt , rechercher les gadgets qui remplissent la fonction d'entrées de pile
pop , savoir dans quels registres ils se trouvent, où se trouve
syscall , puis lancer l'
écriture , en fonction des informations obtenues.

Alors, passez rapidement en revue le sujet, comment vous défendez-vous contre
BROP ? Donc, la chose la plus évidente que vous avez est la re-randomisation. Puisque le fait que les serveurs «tombés en panne» ne réapparaissent pas, ne créent pas d'eux-mêmes des versions aléatoires, agit comme un signal qui donne à un attaquant la possibilité de tester diverses hypothèses sur le fonctionnement des programmes.
Un moyen facile de vous protéger est de vous assurer que vous
exécutez plutôt que
fork lorsque vous relancez votre processus. Parce que lorsque vous exécutez le processus, vous créez un tout nouvel espace situé au hasard, du moins c'est ce qui se passe sous Linux. Sous Linux, lorsque vous compilez à l'aide de
PIE ,
Position Independent Execution , un exécutable indépendant de l'emplacement, lorsque vous utilisez
exec, vous n'obtenez qu'un nouvel espace d'adressage aléatoire.
La deuxième façon consiste simplement à utiliser Windows, car ce système d'exploitation n'a fondamentalement pas l'équivalent d'une fonction
fork . Cela signifie que lorsque vous relancez un serveur sous Windows, il aura toujours un nouvel espace d'adressage aléatoire.
Quelqu'un ici a demandé ce qui se passerait si, après un dysfonctionnement du serveur, il ne se déconnectait pas? Donc, si lors d'une panne de serveur, nous «capturons» cette erreur et gardons la connexion ouverte pendant un certain temps, nous pouvons confondre l'attaquant afin qu'il ne reçoive pas de signal sur l'échec et pense qu'il a trouvé la bonne adresse.
Dans ce cas, votre attaque
BROP se transformera en attaque
DOS . Parce que vous venez de recevoir tous les processus zombies potentiels qui existent. Ils sont inutiles, mais vous ne pouvez pas les laisser aller plus loin, sinon vous devrez supprimer ces informations.
Une autre chose à laquelle vous pourriez penser est de faire le contrôle des frontières dont nous avons parlé auparavant. Les matériaux de la conférence disent que cette méthode est improductive, car elle est 2 fois plus chère, mais vous pouvez toujours l'utiliser.
Voici comment
BROP fonctionne. Quant aux devoirs, une question plus délicate y est posée: que faire si vous utilisez un hash de l'heure actuelle? Autrement dit, la durée pendant laquelle vous redémarrez le programme. Est-ce suffisant pour empêcher ce type d'attaque? Notez que le hachage ne vous fournit pas comme par magie des bits d'entropie si les données que vous entrez dans le hachage sont facilement prévisibles. Si votre hachage contient des milliards de bits, cela n'a pas d'importance. Mais si vous n'avez que quelques significations, l'attaquant les devinera. Bien sûr, il est préférable d'utiliser un hachage temporel aléatoire que de ne rien utiliser pour se protéger contre un pirate informatique, mais cela ne vous fournira pas la sécurité sur laquelle vous devez compter.
La version complète du cours est disponible
ici .
Merci de rester avec nous. Aimez-vous nos articles? Vous voulez voir des matériaux plus intéressants? Soutenez-nous en passant une commande ou en le recommandant à vos amis, une
réduction de 30% pour les utilisateurs Habr sur un analogue unique de serveurs d'entrée de gamme que nous avons inventés pour vous: Toute la vérité sur VPS (KVM) E5-2650 v4 (6 cœurs) 10 Go DDR4 240 Go SSD 1 Gbps à partir de 20 $ ou comment diviser le serveur? (les options sont disponibles avec RAID1 et RAID10, jusqu'à 24 cœurs et jusqu'à 40 Go de DDR4).
Dell R730xd 2 fois moins cher? Nous avons seulement
2 x Intel Dodeca-Core Xeon E5-2650v4 128 Go DDR4 6x480 Go SSD 1 Gbps 100 TV à partir de 249 $ aux Pays-Bas et aux États-Unis! Pour en savoir plus sur la
création d'un bâtiment d'infrastructure. classe utilisant des serveurs Dell R730xd E5-2650 v4 coûtant 9 000 euros pour un sou?