
Un intercepteur d'appels système en espace noyau et injecteur de fautes aléatoires
KRF est un Générateur Aléatoire de Fautes dans l'Espace Noyau (Kernelspace Randomized Faultier).
Il supporte actuellement les noyaux Linux et FreeBSD.
L'injection de fautes est une technique de test logiciel qui consiste à induire des défaillances (« fautes ») dans les fonctions appelées par un programme. Si l'appelé n'a pas effectué une vérification et un traitement d'erreur appropriés, ces fautes peuvent entraîner un comportement peu fiable de l'application ou des vulnérabilités exploitables.
Contrairement aux nombreux systèmes d'injection de fautes en espace utilisateur, KRF fonctionne dans l'espace noyau via un module chargé. Cela présente plusieurs avantages :
LD_PRELOAD pour l'injection.syscall(3) ou du code assembleur inline.dlsym.Il y a aussi plusieurs inconvénients :
cr0 manuellement. Il existe probablement une manière indépendante de l'architecture de faire cela dans Linux, quelque part.KRF réécrit la table des appels système de Linux ou FreeBSD : lorsqu'il est configuré via krfctl, KRF remplace les appels système injectables par de fines enveloppes.
Chaque enveloppe effectue ensuite une vérification pour savoir si l'appel doit être fauté à l'aide d'un système de ciblage configurable capable de cibler un personality(2), PID, UID et/ou GID spécifique. Si le processus ne doit pas être fauté, l'appel système original est invoqué.
Enfin, l'appel ciblé est fauté via une fonction d'échec aléatoire. Par exemple, un appel read(2) pourrait recevoir l'une des erreurs EBADF, EINTR, EIO, etc.
Vous pouvez en savoir plus sur l'implémentation de KRF dans notre billet de blog.
REMARQUE : Si vous avez Vagrant, utilisez simplement le Vagrantfile et passez aux étapes de compilation.
KRF devrait fonctionner sur tout noyau Linux relativement récent (4.15+) avec CONFIG_KALLSYMS=1.
Cela inclut le noyau par défaut sur Ubuntu 18.04 et probablement de nombreuses autres distributions récentes.
REMARQUE : Ignorez ceci si vous utilisez Vagrant.
En dehors d'une chaîne d'outils C (GCC est probablement nécessaire pour Linux), les seules dépendances de KRF devraient être libelf, les en-têtes du noyau, et Ruby (>=2.4, pour la génération de code).
GNU Make est requis sur toutes les plateformes ; FreeBSD nécessite en plus BSD Make.
Pour les systèmes avec apt :
sudo apt install gcc make libelf-dev ruby linux-headers-$(uname -r)
git clone https://github.com/trailofbits/krf && cd krf
make -j$(nproc)
sudo make install # Installe le module dans /lib/modules et les utilitaires dans /usr/local/bin
sudo make insmod # Charge le module
ou, si vous utilisez Vagrant :
git clone https://github.com/trailofbits/krf && cd krf
vagrant up linux && vagrant ssh linux
# à l'intérieur de la VM
cd /vagrant
make -j$(nproc)
sudo make install # Installe le module dans /lib/modules et les utilitaires dans /usr/local/bin
sudo make insmod # Charge le module
ou, pour FreeBSD :
git clone https://github.com/trailofbits/krf && cd krf
cd vagrant up freebsd && vagrant ssh freebsd
# à l'intérieur de la VM
cd /vagrant
gmake # PAS make !
gmake install-module # Installe le module dans /boot/modules/
sudo gmake install-utils # Installe les utilitaires dans /usr/local/bin
gmake insmod # Charge le module
KRF a trois composants :
krfx)krfexec)krfctl)krfmesg)Pour charger le module noyau, exécutez make insmod. Pour le décharger, exécutez make rmmod.
Pour une première utilisation, il peut être utile de lancer sudo krfmesg dans un terminal séparé pour voir les messages enregistrés par krfx.
KRF commence dans un état neutre : aucun appel système ne sera intercepté ou fauté jusqu'à ce que l'utilisateur spécifie un comportement via krfctl :
# aucun défaut induit, même avec KRF chargé
ls
# dire à krf de fauter les appels read(2) et write(2)
# notez que krfctl nécessite les privilèges root
sudo krfctl -F 'read,write'
# dire à krf de fauter tout programme lancé par
# krfexec, c'est-à-dire une personnalité de 28
sudo krfctl -T personality=28
# peut fauter !
krfexec ls
# dire à krf de fauter avec une probabilité de 1/100 (soit 1 %)
# notez que cette valeur est représentée comme une réciproque
# donc par exemple 1 signifie que tous les appels système injectables seront fautés
# et 500 signifie qu'en moyenne un appel système sur 500 sera fauté (1/500 ou 0,2 %)
sudo krfctl -p 100
# dire à krf de fauter le profil `io` (et donc les appels système liés aux E/S)
sudo krfctl -P io
# krfexec passera aussi correctement les options
krfexec echo -n 'pas de nouvelle ligne'
# effacer la spécification de fautes
sudo krfctl -c
# effacer la spécification de ciblage
sudo krfctl -C
# aucun défaut induit, car aucun appel système n'est fauté
krfexec firefox
REMARQUE : La plupart des utilisateurs devraient utiliser krfctl plutôt que de manipuler ces fichiers à la main. Sous FreeBSD, ces mêmes valeurs sont accessibles via sysctl krf.quelquechose au lieu de procfs.
/proc/krf/rng_stateCe fichier permet à un utilisateur de lire et de modifier l'état interne du PRNG de KRF.
Par exemple, chacune des commandes suivantes mettra correctement à jour l'état :
echo "1234" | sudo tee /proc/krf/rng_state
echo "0777" | sudo tee /proc/krf/rng_state
echo "0xFF" | sudo tee /proc/krf/rng_state
L'état est un entier non signé de 32 bits ; toute tentative de le modifier au-delà échouera.
/proc/krf/targetingCe fichier permet à un utilisateur de définir les valeurs utilisées par KRF pour le ciblage des appels système.
REMARQUE : KRF utilise une personnalité par défaut qui n'est pas actuellement utilisée par le noyau Linux par défaut. Si vous modifiez cela, vous devez faire attention à ne pas en faire quelque chose dont Linux se soucie. man 2 personality donne les détails.
echo "0 28" | sudo tee /proc/krf/targeting
Une personnalité de 28 est codée en dur dans krfexec, et doit être définie pour que les choses exécutées par krfexec soient fautées.
/proc/krf/probabilityCe fichier permet à un utilisateur de lire et d'écrire la probabilité d'induire une faute pour un appel système donné (injectable).
La probabilité est représentée comme une réciproque, par exemple 1000 signifie qu'en moyenne, 0,1 % des appels système injectables seront fautés.
echo "100000" | sudo tee /proc/krf/probability
/proc/krf/controlCe fichier contrôle les appels système que KRF faute.
REMARQUE : La plupart des utilisateurs devraient utiliser krfctl plutôt que d'interagir directement avec ce fichier — le premier effectuera automatiquement la traduction des noms d'appels système en numéros et fournira des messages d'erreur plus clairs en cas de problème.
# remplacer l'appel système dans l'emplacement 0 (généralement SYS_read) par son enveloppe fautive
echo "0" | sudo tee /proc/krf/control
Passer un nombre supérieur à KRF_NR_SYSCALLS amènera KRF à vider toute la table des appels système, la ramenant à l'état neutre. Comme KRF_NR_SYSCALLS n'est pas nécessairement prévisible pour des versions arbitraires du noyau Linux, choisir un grand nombre (comme 65535) est acceptable.
Passer un numéro d'appel système valide qui n'a pas d'enveloppe d'injection de fautes fera échouer l'appel write(2) au fichier avec EOPNOTSUPP.
/proc/krf/log_faultsCe fichier contrôle si KRF émet ou non des journaux du noyau sur les appels système fautés. Par défaut, aucun message de journal n'est émis.
REMARQUE : La plupart des utilisateurs devraient utiliser krfctl plutôt que d'interagir directement avec ce fichier.
# activer la journalisation des fautes
echo "1" | sudo tee /proc/krf/log_faults
# désactiver la journalisation des fautes
echo "0" | sudo tee /proc/krf/log_faults
# lire l'état de la journalisation
cat /proc/krf/log_faults
ENOMEM).
personality(2).Un grand merci à Andrew Reiter pour le port initial de KRF vers FreeBSD. Le travail d'Andrew a été réalisé pour le compte de l'Applied Research Group chez Veracode.
KRF est sous licence GNU GPLv3.
Consultez le fichier LICENSE pour les termes exacts.