
Image : Images de livres d'archives Internet. Modifié par Opensource.com. CC BY-SA 4.0
Après avoir compilé le même code source, nous pouvons nous retrouver avec des binaires différents. Cela dépend des drapeaux que nous passons entre les mains du compilateur. Certains de ces indicateurs vous permettent d'activer ou de désactiver un certain nombre de propriétés liées à la sécurité du binaire.
Certains d'entre eux sont activés ou désactivés par le compilateur par défaut. C'est ainsi que des vulnérabilités peuvent survenir dans les fichiers binaires dont nous ne sommes pas conscients.
Checksec est un utilitaire simple pour déterminer quelles propriétés ont été incluses au moment de la compilation. Dans cet article je vais vous dire :
- comment utiliser l'utilitaire checksec pour trouver des vulnérabilités ;
- comment utiliser le compilateur gcc pour corriger les vulnérabilités trouvées.
Installation de checksec
Pour Fedora OS et autres systèmes basés sur RPM :
$ sudo dnf install checksec
Pour les systèmes basés sur Debian, utilisez apt.
Démarrage rapide avec checksec
L'utilitaire checksec se compose d'un seul fichier de script, qui est cependant assez volumineux. Grâce à cette transparence, vous pouvez savoir quelles commandes système pour rechercher des vulnérabilités dans les binaires sont exécutées sous le capot :
$ file /usr/bin/checksec /usr/bin/checksec: Bourne-Again shell script, ASCII text executable, with very long lines $ wc -l /usr/bin/checksec 2111 /usr/bin/checksec
Exécutons checksec sur l'utilitaire de navigation dans les répertoires (ls) :
$ checksec --file=/usr/bin/ls <strong>RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE</strong> Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols Yes 5 17 /usr/bin/ls
En exécutant la commande dans le terminal, vous recevrez un rapport sur les propriétés utiles de ce binaire et celles qu'il n'a pas.
La première ligne est la tête du tableau, qui répertorie les différentes propriétés de sécurité - RELRO, STACK CANARY, NX, etc. La deuxième ligne montre les valeurs de ces propriétés pour le binaire utilitaire ls.
Bonjour binaire !
Je vais compiler un binaire à partir du code C le plus simple :
#include <stdio.h>
int main()
{
printf(«Hello World\n»);
return 0;
}
Veuillez noter que jusqu'à présent, je n'ai pas passé un seul indicateur au compilateur, à l'exception de -o (c'est hors de propos, mais indique simplement où sortir le résultat de la compilation):
$ gcc hello.c -o hello
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, not stripped
$ ./hello
Hello World
Maintenant, je vais exécuter l'utilitaire checksec pour mon binaire. Certaines propriétés sont différentes des propriétés
ls ( ): $ checksec --file=./hello <strong>RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE</strong> Partial RELRO No canary found NX enabled No PIE No RPATH No RUNPATH 85) Symbols No 0 0./hello
Checksec vous permet d'utiliser une variété de formats de sortie, que vous pouvez spécifier avec l'option --output. Je vais choisir le format JSON et rendre la sortie plus descriptive avec l'utilitaire jq :
$ checksec --file=./hello --output=json | jq { «./hello»: { «relro»: «partial», «canary»: «no», «nx»: «yes», «pie»: «no», «rpath»: «no», «runpath»: «no», «symbols»: «yes», «fortify_source»: «no», «fortified»: «0», «fortify-able»: «0» } }
Analyse (checksec) et élimination (gcc) des vulnérabilités
Le fichier binaire créé ci-dessus possède plusieurs propriétés qui déterminent, disons, le degré de sa vulnérabilité. Je vais comparer les propriétés de ce fichier avec les propriétés du binaire ls (également répertoriées ci-dessus) et expliquer comment procéder à l'aide de l'utilitaire checksec.
Pour chaque élément, je vais en outre vous montrer comment éliminer les vulnérabilités trouvées.
1. Symboles de débogage
Je vais commencer simple. Certains symboles sont inclus dans le binaire au moment de la compilation. Ces symboles sont utilisés dans le développement de logiciels : ils sont nécessaires pour le débogage et la correction des bogues.
Les symboles de débogage sont généralement supprimés de la version du binaire que les développeurs publient pour une utilisation générale. Cela n'affecte en rien le fonctionnement du programme. Ce nettoyage (indiqué par le mot strip ) est souvent effectué pour économiser de l'espace, car le fichier devient plus léger une fois les caractères supprimés. Et dans les logiciels propriétaires, ces caractères sont souvent supprimés également parce que les attaquants ont la possibilité de les lire au format binaire et de les utiliser à leurs propres fins.
Checksec montre que les symboles de débogage sont présents dans mon binaire, mais ils ne le sont pas dans le fichier ls.
$ checksec --file=/bin/ls --output=json | jq | grep symbols «symbols»: «no», $ checksec --file=./hello --output=json | jq | grep symbols «symbols»: «yes»,
L'exécution de la commande file peut afficher la même chose. Les caractères ne sont pas supprimés.
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, <strong>not stripped</strong>
Comment fonctionne checksec
Exécutons cette commande avec l'option --debug :
$ checksec --debug --file=./hello
Étant donné que l'utilitaire checksec est un long script, vous pouvez utiliser les fonctions Bash pour l'examiner. Affichez les commandes que le script exécute pour mon fichier hello :
$ bash -x /usr/bin/checksec --file=./hello
Portez une attention particulière à echo_message - la sortie d'un message indiquant si le binaire contient des symboles de débogage :
+ readelf -W --symbols ./hello
+ grep -q '\.symtab'
+ echo_message '\033[31m96) Symbols\t\033[m ' Symbols, ' symbols=«yes»' '«symbols»:«yes»,'
L'utilitaire checksec utilise la commande readelf avec l'indicateur spécial --symbols pour lire un fichier binaire. Il imprime tous les symboles de débogage dans le binaire.
$ readelf -W --symbols ./hello
A partir du contenu de la section .symtab, vous pouvez connaître le nombre de symboles trouvés :
$ readelf -W --symbols ./hello | grep -i symtab
Comment supprimer les symboles de débogage après la compilation
L'utilitaire de bande nous y aidera.
$ gcc hello.c -o hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, for GNU/Linux 3.2.0, <strong>not stripped</strong>
$
$ strip hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, for GNU/Linux 3.2.0, <strong>stripped</strong>
Comment supprimer les symboles de débogage au moment de la compilation
Lors de la compilation, utilisez l'indicateur -s :
$ gcc -s hello.c -o hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=247de82a8ad84e7d8f20751ce79ea9e0cf4bd263, for GNU/Linux 3.2.0, <strong>stripped</strong>
Vous pouvez également vérifier que les symboles ont été supprimés à l'aide de l'utilitaire checksec :
$ checksec --file=./hello --output=json | jq | grep symbols «symbols»: «no»,
2. Canari
Canary (informateurs) sont des valeurs "secrètes" qui sont stockées sur la pile entre le tampon et les données de contrôle. Ils sont utilisés pour se protéger contre les attaques par débordement de tampon : si ces valeurs sont modifiées, cela vaut la peine de tirer la sonnette d'alarme. Lorsqu'une application est lancée, sa propre pile est créée pour elle. Dans ce cas, il s'agit simplement d'une structure de données avec des opérations push et pop. Un attaquant pourrait préparer des données malveillantes et les écrire dans la pile. Dans ce cas, la mémoire tampon peut déborder et la pile peut être endommagée. À l'avenir, cela entraînera un plantage du programme. L'analyse des valeurs canary vous permet de comprendre rapidement qu'un piratage s'est produit et de prendre des mesures.
$ checksec --file=/bin/ls --output=json | jq | grep canary
«canary»: «yes»,
$
$ checksec --file=./hello --output=json | jq | grep canary
«canary»: «no»,
$
, canary, checksec :
$ readelf -W -s ./hello | grep -E '__stack_chk_fail|__intel_security_cookie'
Activer Canari
Pour ce faire, lors de la compilation, nous utilisons le drapeau -stack-protector-all :
$ gcc -fstack-protector-all hello.c -o hello $ checksec --file=./hello --output=json | jq | grep canary «canary»: «yes»,
Maintenant, checksec peut nous dire en toute conscience que le mécanisme Canary est activé :
$ readelf -W -s ./hello | grep -E '__stack_chk_fail|__intel_security_cookie'
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (3)
83: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@@GLIBC_2.4
$
3. TARTE
La propriété PIE activée permet au code exécutable d'être placé arbitrairement en mémoire quelle que soit son adresse absolue :
PIE (Position Independent Executable) - code exécutable indépendant de la position. La capacité de prédire où et quelles zones de mémoire se trouvent dans l'espace d'adressage d'un processus fait le jeu des attaquants. Les programmes utilisateur sont chargés et exécutés à partir d'une adresse de mémoire virtuelle de processus prédéfinie à moins qu'ils ne soient compilés avec l'option PIE. L'utilisation de PIE permet au système d'exploitation de charger des sections de code exécutable dans des morceaux arbitraires de mémoire, ce qui le rend beaucoup plus difficile à déchiffrer.
$ checksec --file=/bin/ls --output=json | jq | grep pie «pie»: «yes», $ checksec --file=./hello --output=json | jq | grep pie «pie»: «no»,
Souvent, la propriété PIE n'est incluse que lors de la compilation des bibliothèques. Dans la sortie ci-dessous, hello est marqué comme exécutable LSB et le fichier libc de bibliothèque standard (.so) est marqué comme objet partagé LSB :
$ file hello
hello: ELF 64-bit <strong>LSB executable</strong>, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, not stripped
$ file /lib64/libc-2.32.so
/lib64/libc-2.32.so: ELF 64-bit <strong>LSB shared object</strong>, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4a7fb374097fb927fb93d35ef98ba89262d0c4a4, for GNU/Linux 3.2.0, not stripped
Checksec obtient ces informations comme ceci :
$ readelf -W -h ./hello | grep EXEC Type: EXEC (Executable file)
Si vous exécutez la même commande pour la bibliothèque, vous verrez DYN au lieu d'EXEC :
$ readelf -W -h /lib64/libc-2.32.so | grep DYN Type: DYN (Shared object file)
Activer PIE
Lors de la compilation du programme, vous devez spécifier les indicateurs suivants :
$ gcc -pie -fpie hello.c -o hello
Pour vous assurer que la propriété PIE est activée, exécutez la commande suivante :
$ checksec --file=./hello --output=json | jq | grep pie «pie»: «yes», $
Maintenant, notre fichier binaire (bonjour) changera son type d'EXEC à DYN :
$ file hello
hello: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb039adf2530d97e02f534a94f0f668cd540f940, for GNU/Linux 3.2.0, not stripped
$ readelf -W -h ./hello | grep DYN
Type: DYN (Shared object file)
4. NX
Les outils du système d'exploitation et du processeur vous permettent de configurer de manière flexible les droits d'accès aux pages de mémoire virtuelle. En activant la propriété NX (No Execute), nous pouvons empêcher que les données soient interprétées comme des instructions du processeur. Souvent, dans les attaques par débordement de tampon, les attaquants poussent du code sur la pile, puis essaient de l'exécuter. Cependant, en empêchant le code de s'exécuter dans ces segments de mémoire, de telles attaques peuvent être évitées. En compilation normale avec gcc, cette propriété est activée par défaut :
$ checksec --file=/bin/ls --output=json | jq | grep nx «nx»: «yes», $ checksec --file=./hello --output=json | jq | grep nx «nx»: «yes»,
Checksec utilise à nouveau la commande readelf pour obtenir des informations sur la propriété NX. Dans ce cas, RW signifie que la pile est en lecture/écriture. Mais comme cette combinaison ne contient pas le caractère E, il y a une interdiction d'exécuter du code à partir de cette pile :
$ readelf -W -l ./hello | grep GNU_STACK GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
Désactiver NX
Il n'est pas recommandé de désactiver la propriété NX, mais vous pouvez le faire comme ceci :
$ gcc -z execstack hello.c -o hello $ checksec --file=./hello --output=json | jq | grep nx «nx»: «no»,
Après la compilation, nous verrons que les permissions de la pile sont passées à RWE :
$ readelf -W -l ./hello | grep GNU_STACK GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
5. RELRO
Dans les binaires liés dynamiquement, un GOT (Global Offset Table) spécial est utilisé pour appeler des fonctions à partir de bibliothèques. Cette table est référencée par les binaires ELF (Executable Linkable Format). Lorsque la protection RELRO (Relocation Read-Only) est activée, le GOT devient en lecture seule. Cela vous permet de vous protéger contre certains types d'attaques qui modifient les enregistrements de la table :
$ checksec --file=/bin/ls --output=json | jq | grep relro «relro»: «full», $ checksec --file=./hello --output=json | jq | grep relro «relro»: «partial»,
Dans ce cas, une seule des propriétés RELRO est activée, donc checksec renvoie la valeur "partielle". Checksec utilise la commande readelf pour afficher les paramètres.
$ readelf -W -l ./hello | grep GNU_RELRO GNU_RELRO 0x002e10 0x0000000000403e10 0x0000000000403e10 0x0001f0 0x0001f0 R 0x1 $ readelf -W -d ./hello | grep BIND_NOW
Activer la protection complète (FULL RELRO)
Pour ce faire, lors de la compilation, vous devez utiliser les indicateurs appropriés :
$ gcc -Wl,-z,relro,-z,now hello.c -o hello $ checksec --file=./hello --output=json | jq | grep relro «relro»: «full»,
Ça y est, maintenant notre binaire a reçu le titre honorifique de FULL RELRO :
$ readelf -W -l ./hello | grep GNU_RELRO GNU_RELRO 0x002dd0 0x0000000000403dd0 0x0000000000403dd0 0x000230 0x000230 R 0x1 $ readelf -W -d ./hello | grep BIND_NOW 0x0000000000000018 (BIND_NOW)
Autres fonctionnalités de checksec
Le thème de la sécurité peut être étudié à l'infini. Même en parlant du simple utilitaire checksec dans cet article, je ne peux pas tout couvrir. Cependant, je mentionnerai quelques autres possibilités intéressantes.
Vérification de plusieurs fichiers
Il n'est pas nécessaire d'exécuter une commande distincte pour chaque fichier. Vous pouvez exécuter une commande pour plusieurs binaires à la fois :
$ checksec --dir=/usr/bin
Vérification des processus
L'utilitaire checksec vous permet également d'analyser la sécurité des processus. La commande suivante affiche les propriétés de tous les programmes en cours d'exécution sur votre système (vous devez utiliser l'option --proc-all pour ce faire) :
$ checksec --proc-all
Vous pouvez également sélectionner un processus à vérifier en spécifiant son nom :
$ checksec --proc=bash
Vérification du noyau
De même, vous pouvez analyser les vulnérabilités dans le noyau de votre système.
$ checksec --kernel
Prévenu est prévenu
Étudiez les propriétés de sécurité en détail et essayez de comprendre ce que chacune d'elles affecte exactement et quels types d'attaques elle peut empêcher. Checksec pour vous aider !
Les serveurs cloud de Macleod sont rapides et sécurisés.
Inscrivez-vous en utilisant le lien ci-dessus ou en cliquant sur la bannière et bénéficiez d'une remise de 10 % pour le premier mois de location d'un serveur de n'importe quelle configuration !
