PROJET AUTOBLOG


Gordon

source: Gordon

⇐ retour index

[Traduction] ECB : La mauvaise façon de faire

lundi 17 décembre 2012 à 00:00

Voici une traduction de Rogdham, un hacker que j’ai eu le plaisir de rencontrer au Nicelab. Ce billet est une explication très claire des problèmes de conception du mode ECB appliqué aux algorithmes de chiffrement.

ECB : La mauvaise façon de faire

1 — Electronic CodeBook

Le chiffrement par blocs (tels que l’AES) est une bonne méthode pour chiffrer un bloc (par exemple de 16 octets) de données en utilisant un algorithme qui a été démontré (quelle qu’en soit la méthode) comme sûr.

Le problème est que les messages que nous voulons envoyer chiffrés sont généralement plus longs que 16 octets. D’où le besoin de passer d’un algorithme par blocs à un algorithme par flux.

Pour ce faire, il existe différents modes d’opération. Parmi eux, ECB est le plus simple : on considère le message comme une suite consécutive de blocs, et on applique simplement le chiffrement à chaque bloc séparément. Cependant, ce mode est connu pour ne pas être sûr, étant donné qu’il est sujet aux analyses fréquentielles.

Tux chiffré en mode ECB : nous pouvons toujours reconnaître qu’il s’agit de la mascotte de Linux

L’image ci-dessus a été chiffrée en utilisant le mode ECB. Nous avons perdu des détails (comme les couleurs utilisées), mais il est assez clair que l’image originale était Tux, la mascotte de Linux.

Bon, cet exemple avait pour but de démontrer la faiblesse de ECB. Comment un attaquant s’y prendrait-il pour exploiter un algorithme de chiffrement sûr qui utiliserait le mode ECB ?

2 — Contexte

Comme vous l’avez peut-être lu dans mon précédent article, j’ai participé au PoliCTF, qui était une compétition de sécurité en CTF. Le challenge B1N 500 consistait à récupérer un fichier depuis un programme binaire.

La partie du programme qui nous intéressera pour le reste de cet article fonctionne comme tel :

  1. Il demande une clé à l’utilisateur.
  2. Il déchiffre des données avec AES en mode ECB en utilisant cette clé.
  3. Il enregistre les données dans un fichier appelé 0.bmp.

Comment vous y prendriez-vous ? Si vous voulez essayer, voici le fichier BMP obtenu lorsque l’on donne une clé vide au binaire. Bien sûr, il ne s’agit pas d’une image BMP valide.

J’ai réussi à obtenir le token lors de ce CTF, mais ça n’était que la première étape (sur 3) du challenge B1n 500. Malheureusement, nous n’avons pas été capables d’aller plus loin faute de temps, donc vous ne trouverez pas d’exploit complet ici.

3 — Exploit

L’exploit commence avec de l’analyse fréquentielle.

Étant donné qu’on sait qu’AES est utilisé, on peut assumer que les blocs font 16 octets. Donc on découpe le fichier en blocs, et on compte la répartition des différents blocs.

Analyse fréquentielle

Comme le montre le graphique ci-dessus, un même bloc représente 90% de la taille du fichier, le second 6,8%, et les autres sont à moins de 1%.

L’idée suivante est simple : remplaçons les blocs les plus fréquents par des pixels blancs, et les second plus fréquents par des pixels noirs, et laissons de côté les autres.

Mais nous avons un problème : quelle est la taille de l’image ? La méthode par tâtonnage est appropriée dans ce cas. Essayez différentes valeurs, et vous obtenez finalement une image valide !

Premier résultat

C’est loin d’être parfait, pour deux raisons :

Étant donné qu’il s’agissait d’un CTF, j’étais pressé par le temps, alors j’ai simplement ouvert mon éditeur d’images favori, effectué une transformation miroir et lu le token : Trolld. Mission accomplie.

Ceci dit, pour aller plus loin, nous devrions envisager ceci :

Ce qui donne le résultat suivant :

Second résultat

4 — Conclusion

Le mode ECB n’est pas sûr. Nous le savions déjà, mais cela a été particulièrement facile de savoir à quoi l’image ressemblait sans avoir la moindre idée de la clé.

Bien sûr, cela a fonctionné parce que le format BMP n’est pas compressé, ou quoi que ce soit. La compression est un moyen d’agrandir l’entropie des données. Mais si l’attaquant a suffisamment de données, l’analyse fréquentielle fonctionnera toujours. Ceci dit, si vous augmentez l’entropie de façon à ce que vos données aient l’air aléatoires, vous serez en sécurité. Devinez quoi ? C’est exactement ce que fait le chiffrement.

Et il est inutile d’utiliser un mauvais mode de chiffrement par-dessus un bon. Contentez-vous d’en utiliser un meilleur.

Vers le web Troipoinzéro !

lundi 17 décembre 2012 à 00:00

Vers le web Troipoinzéro !

lundi 17 décembre 2012 à 00:00

[Traduction] ECB : La mauvaise façon de faire

lundi 17 décembre 2012 à 00:00

Howto pratique : l’installation d’une Gentoo encore plus sécurisée

dimanche 19 août 2012 à 01:01

Il y a un certain temps, j’avais écrit un billet expliquant étape par étape l’installation d’une Gentoo Linux avec des partitions intégralement chiffrées. Ma récente aventure avec FreeBSD sur une de mes machines, et mon retour sous Gentoo me donne l’occasion de pousser un peu plus loin cet objectif.

En effet, le simple fait de chiffrer un disque ne le rend pas totalement sécurisé par miracle. La faiblesse d’un système de chiffrement, en pratique, repose souvent dans la clé et la façon de la gérer par l’utilisateur. Parce qu’un chiffrement Rijndael bien barbu ne vaut rien si sa clé est la date de naissance de l’utilisateur, naturellement. Sans tomber dans cet extrême, la solution retenue précédemment avait quelques faiblesses :

Il existe donc une solution pas très complexe à cela, mais que je n’avais pas voulu essayer à l’époque (par peur de voir trop gros d’un coup, l’autre raison étant que les explications claires n’étaient pas encore écrites sur le guide utilisé) : stocker le noyau et l’initramfs sur un support amovible, qui serait sur moi en permanence, et qui par ailleurs contiendrait la clé de déchiffrement du disque, chiffrée en GPG. La dernière fois, utiliser GPG ne me semblait pas être une bonne idée, mais le fait de stocker la clé sur un support externe apporte une solution convenable à ce problème : on a maintenant une authentification forte (car basée sur 2 méthodes distinctes) pour déverrouiller la machine : il faut à la fois posséder le support externe et connaître la passphrase pour accéder au disque. Par ailleurs, au lieu d’avoir plusieurs partitions que l’on déverrouillera avec la même clé, on peut utiliser LVM pour créer des partitions logiques, stockées sur une seule partition physique, qui, elle, sera chiffrée en premier lieu.

En plus de cela, j’ai choisi de partir sur le projet Gentoo Hardened, qui est un ensemble de modifications et d’ajouts pensés pour la sécurité, sur la distribution Gentoo. Concrètement, il s’agit de l’ajout de PaX, SElinux, GRsec et autres modules améliorant la sécurité du système en consolidant les accès systèmes, fichiers, etc. Cette « déclinaison » de la Gentoo a la réputation d’être le meilleur choix de sécurité pour du GNU/Linux (les systèmes BSD étant encore un cran au-dessus, pour peu qu’on sache les utiliser comme il faut). Et pour finir, j’ai souhaité garder un système Libre, sans exception. Bien que non référencée dans les distributions GNU-compliant par la Free Software Foundation, deux petites astuces permettent de se prémunir de tout code non-libre dans son beau système. Non seulement parce que je crois qu’utiliser du logiciel libre me permet d’avoir confiance dans mon système, mais également par principe, sachant que j’utilise très peu de logiciels non-libres sur mes autres systèmes. C’est en quelque sorte un défi.

Disque dur chiffré, le retour

Pour rappel, les améliorations du système précédent sont donc :

Au moment de partitionner le disque, nous allons donc créer une seule partition, puisque c’est LVM qui, à l’intérieur, s’occupera de la segmenter en partitions logiques. Dans cet article, on supposera que le disque dur est /dev/sda. Avec fdisk ou votre outil de partitionnement préféré, supprimez donc tout, puis créez une unique partition qui prendra toute la capacité disponible (ce sera /dev/sda1). Cette partition sera un conteneur LUKS, qui, une fois ouvert, contiendra le VG de LVM, dans lequel seront nos 3 partitions : root (/), home (/home) et swap (non monté). Mais pour commencer, on génère la clé. Pour cela, je pioche dans le générateur de pseudo-aléa /dev/random une quantité raisonnable de données, que je découpe proprement pour avoir une chaîne alphanumérique de 255 caractères.

head -c 255 /dev/random | uuencode -m - | head -n -1 | tail -n +2 | tr -d '\n' | gpg --symmetric -a > sda1.gpg #puis on agite sauvagement la souris, ou on tape comme un goret sur son clavier, pour générer de l’entropie

Nous avons notre clé. Notez que nous avons pris soin de supprimer les retours à la ligne pour éviter une mauvaise surprise avec les pipes, bien qu’il ne devrait pas en avoir. Créons maintenant le conteneur LUKS.

gpg --quiet --decrypt sda1.gpg | cryptsetup -d - -v --cipher serpent-cbc-essiv:sha256 --key-size 256 luksFormat /dev/sda1

Notez bien le « -d - », il permet d’éviter le genre d’ennuis cité ci-dessus, et surtout, il sera utilisé par défaut par le script d’initialisation, alors il vaut mieux utiliser la même méthode. Ensuite, pour ouvrir le conteneur :

gpg --quiet --decrypt sda1.gpg | cryptsetup -d - luksOpen /dev/sda1 vg0

Le nom du volume LUKS (ici vg0) importe peu, et je fais confiance à votre imagination fertile pour attribuer un nom qui vous fera briller en société. Notre volume étant ouvert (mais pas formaté, ni monté) dans /dev/mapper/vg0, nous allons pouvoir en faire un VG, justement, et crééer les partitions à l’intérieur.

pvcreate /dev/mapper/vg0
vgcreate vg0 /dev/mapper/vg0 #le premier vg0 est le nom du volume, tandis que /dev/mapper/vg0 est la partition LUKS ouverte. Faut suivre hein
#on peut créer nos LV. Mettons que notre disque fait 100Go
lvcreate -n root -L 49g vg0
lvcreate -n home -L 49g vg0
lvcreate -n swap -L 2g vg0

Vous aurez compris que l’on a assigné 2Go de swap (l’équivalent de notre mémoire physique, au cas où on voudrait suspendre le système), puis que l’on a bêtement redistribué équitablement à /home et /. N’hésitez pas à personnaliser ces valeurs pour correspondre à vos besoins. Nous avons maintenant nos partitions, prêtes à recevoir un filesystem (XFS dans mon cas). Si jamais vous devez reprendre votre installation (à cause d’une tentative infructueuse de boot, par exemple), vous risquez de vous demander comment retrouver vos volumes LVM, après avoir déchiffré /dev/sda :

vgscan && vgchange -ay

Et vos partitions vous attendront sagement dans /dev/vg0/*. Activez le swap avec « mkswap /dev/vg0/swap && swapon /dev/vg0/swap », puis continuez l’installation classique du système. Après la compilation du noyau, il est inutile de l’installer, car nous n’utiliserons de toutes façons pas le /boot. À la place, installez sys-kernel/dracut, qui se chargera de générer un initramfs tout beau pour vous. Mais avant, spécifiez les modules que vous souhaitez compiler (j’indique mon propre choix, utile pour cette installation) :

echo 'DRACUT_MODULES="crypt crypt-gpg lvm"' >> /etc/make.conf
echo 'sys-kernel/dracut device-mapper' >> /etc/portage/package.use
emerge -va dracut

Ensuite, il vous faut un support amovible, tel qu’une bête clé USB. Commençons par effacer son MBR : après l’avoir insérée, et vérifié son nom de device (ici, /dev/sdb), installez syslinux, puis tapez :

emerge -va syslinux
dd if=/dev/zero of=/dev/sdb bs=1024k count=5 conv=notrunc
mke2fs -m0 /dev/sdb1
mkdir tempdir && cd tempdir
cp /usr/portage/distfiles/syslinux-*.tar.bz2 .
tar -xvjf syslinux-*.tar.bz2
cd syslinux-*
cat mbr/mbr.bin > /dev/sdb
mkdir /mnt/usb
mount /dev/sdb1 /mnt/usb
cd /mnt/usb
cp /usr/src/linux/arch/<arch>/boot/bzImage . #remplacez <arch> par votre architecture
cp syslinux-<version>/com32/menu/menu.c32 .
cp chemin/vers/sda1.gpg .

Il ne reste plus qu’à générer l’initramfs avant de rendre le médium bootable. À ce stade, vous êtes toujours en chroot, avec un système dont il y fort à parier dont le noyau diffère de celui que vous venez de compiler. Dracut va donc logiquement couiner, ne trouvant pas les modules du noyau actuel. Il faut donc d’abord générer les dépendances de modules pour le noyau actuel, puis générer l’initramfs en lui indiquant le bon répertoire pour les modules :

depmod `uname -r`

Avant d’utiliser dracut, on va le configurer un peu, histoire de s’assurer qu’il chargera les bons modules (ça serait bête de ne pas pouvoir utiliser gpg, par exemple). Tout ce que j’ai eu à faire a été de modifier cette ligne dans /etc/dracut.conf :

add_dracutmodules+="crypt crypt-gpg lvm dm selinux"

Nous sommes enfin prêts à générer l’image.

dracut -k /lib/modules/`uname -r`
mv initramfs-* initramfs-`uname -r`.img

Il nous reste cependant à configurer le programme de démarrage, extlinux. Créez un fichier extlinux.conf dans /mnt/usb, et adaptez son contenu selon le mien :

DEFAULT menu.c32
TIMEOUT 100
PROMPT 0
LABEL Gentoo
    MENU LABEL Gentoo ^Linux
    MENU DEFAULT
    KERNEL bzImage
    APPEND root=/dev/vg0/root rd.luks.key=/sda1.gpg initrd=initramfs-<version>.img

Vérifiez scrupuleusement les paramètres de boot, dans « APPEND ». Ils sont la principale source de boot foireux. Si vous désirez choisir une keymap précise (par défaut, ce sera en qwerty), ajoutez « vconsole.keymap=<keymap> », en insérant le nom de la keymap, fr-dvorak-bepo pour moi. On finit donc :

extlinux .
cd
umount /mnt/usb
sync

Et on tente le reboot. Naturellement, vérifiez que votre BIOS vous permet de booter sur un support amovible. N’oubliez d’ailleurs pas de brancher ledit support, ça évitera un arrachage de cheveux tout à fait inopportun. Si vous avez bien fait les choses, un joli menu s’affichera, vous proposant de booter votre noyal. Après validation, celui-ci se chargera, exécutera l’initramfs, qui analysera alors le disque, et, trouvant le keyfile que vous lui avez indiqué, vous demandera sa passphrase. Méfiez-vous de la keymap, si vous ne l’avez pas fixée manuellement. Ensuite, le système continue paisiblement son boot. Félicitations, vous venez de faire un pas de plus vers la paranoïa sécuritaire.

Gentoo, GNU/Linux libre ?

Pour finir, deux mots sur l’astuce évoquée, pour bénéficier d’un système 100% libre selon les termes très stricts de la FSF. Si vous êtes habitué de Portage, vous savez qu’il connaît la licence de chaque paquet, et que, si vous tentez d’installer certains programmes pas trop libres, il vous demandera d’accepter explicitement cette nouvelle licence. Et bien Portage permet de définir la politique d’acceptation des licences, et c’est très bien fait, car il possède des « sets » de licences, permettant de les trier facilement. Il suffit de spécifier votre choix dans /etc/make.conf. Par exemple, j’ai opté pour ça :

ACCEPT_LICENCE="-* @FREE"

Ce qui n’accepte que les programmes sous licence libre agréée par la FSF. Comme vous pouvez le voir, c’est simplissime. Il y a encore une chose à faire : le noyau Linux contient, dans ses sources officielles, des morceaux de code binaire non libre, essentiellement des drivers. Il existe un script permettant d’analyser les sources, de débusquer ces blobs et de les supprimer. Ainsi, les sources que vous compilerez seront propres. Naturellement, ça implique de se passer desdits drivers. Encore une fois, Portage nous vient en aide, puisqu’il offre un USE flag disponible sur tous les noyaux (par exemple sys-kernel/hardened-sources pour moi).

echo 'sys-kernel/hardened-sources deblob' >> /etc/portage/package.use
emerge -va hardened-sources

Et voilà le travail. Le script met un peu de temps à faire son boulot, mais ensuite, votre noyau ne contiendra que du logiciel libre ! Et avec les 2 astuces combinées, vous êtes certain de ne pas ajouter de logiciels non-libres sur votre système, hormis exceptions explicitement créées.