
Un intercettatore di syscall in spazio kernel e un fault-injector randomizzato
KRF è un Fault Randomizzato in Kernelspace.
Attualmente supporta i kernel Linux e FreeBSD.
L'iniezione di guasti (fault injection) è una tecnica di testing software che consiste nell'indurre fallimenti ("fault") nelle funzioni chiamate da un programma. Se il chiamato non ha eseguito una corretta verifica e gestione degli errori, questi fault possono portare a comportamenti inaffidabili dell'applicazione o vulnerabilità sfruttabili.
A differenza dei molti sistemi di iniezione di fault in userspace esistenti, KRF opera in kernelspace tramite un modulo caricato. Questo ha diversi vantaggi:
LD_PRELOAD per l'iniezione.syscall(3) o assembly inline.dlsym.Ci sono anche diversi svantaggi:
cr0. Esiste probabilmente un modo indipendente dall'architettura per farlo in Linux, da qualche parte.KRF riscrive la tabella delle system call di Linux o FreeBSD: quando configurato tramite krfctl, KRF sostituisce le syscall faulabili con wrapper sottili.
Ogni wrapper esegue quindi un controllo per determinare se la chiamata debba essere faultata utilizzando un sistema di targeting configurabile in grado di colpire una specifica personality(2), PID, UID e/o GID. Se il processo non deve essere faultato, viene invocata la syscall originale.
Infine, la chiamata target viene faultata tramite una funzione di fallimento casuale. Ad esempio, una chiamata read(2) potrebbe ricevere uno tra EBADF, EINTR, EIO e così via.
Puoi leggere di più sull'implementazione di KRF nel nostro post sul blog.
NOTA: Se hai Vagrant, usa semplicemente il Vagrantfile e salta ai passaggi di compilazione.
KRF dovrebbe funzionare su qualsiasi kernel Linux recente (4.15+) con CONFIG_KALLSYMS=1.
Questo include il kernel predefinito su Ubuntu 18.04 e probabilmente molte altre distribuzioni recenti.
NOTA: Ignora questa sezione se stai usando Vagrant.
Oltre a un toolchain C (GCC è probabilmente necessario per Linux), le uniche dipendenze di KRF dovrebbero essere libelf, gli header del kernel e Ruby (>=2.4, per la generazione del codice).
GNU Make è richiesto su tutte le piattaforme; FreeBSD richiede inoltre BSD Make.
Per sistemi con 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 # Installs module to /lib/modules and utils to /usr/local/bin
sudo make insmod # Loads module
oppure, se stai usando Vagrant:
git clone https://github.com/trailofbits/krf && cd krf
vagrant up linux && vagrant ssh linux
# inside the VM
cd /vagrant
make -j$(nproc)
sudo make install # Installs module to /lib/modules and utils to /usr/local/bin
sudo make insmod # Loads module
oppure, per FreeBSD:
git clone https://github.com/trailofbits/krf && cd krf
cd vagrant up freebsd && vagrant ssh freebsd
# inside the VM
cd /vagrant
gmake # NOT make!
gmake install-module # Installs module to /boot/modules/
sudo gmake install-utils # Installs utils to /usr/local/bin
gmake insmod # Loads module
KRF ha tre componenti:
krfx)krfexec)krfctl)krfmesg)Per caricare il modulo kernel, esegui make insmod. Per scaricarlo, esegui make rmmod.
Per il primo utilizzo potrebbe essere utile avviare sudo krfmesg su un terminale separato per visualizzare i messaggi registrati da krfx.
KRF inizia in uno stato neutro: nessuna syscall verrà intercettata o faultata finché l'utente non specifica un comportamento tramite krfctl:
# no induced faults, even with KRF loaded
ls
# tell krf to fault read(2) and write(2) calls
# note that krfctl requires root privileges
sudo krfctl -F 'read,write'
# tell krf to fault any program started by
# krfexec, meaning a personality of 28
sudo krfctl -T personality=28
# may fault!
krfexec ls
# tell krf to fault with a 1/100 (or 1%) probability
# note that this value is represented as a reciprocal
# so e.g. 1 means all faultable syscalls will fault
# and 500 means that on average every 500 syscalls will fault (1/500 or 0.2%)
sudo krfctl -p 100
# tell krf to fault `io` profile (and so i/o related syscalls)
sudo krfctl -P io
# krfexec will pass options correctly as well
krfexec echo -n 'no newline'
# clear the fault specification
sudo krfctl -c
# clear the targeting specification
sudo krfctl -C
# no induced faults, since no syscalls are being faulted
krfexec firefox
NOTA: La maggior parte degli utenti dovrebbe usare krfctl invece di manipolare questi file a mano. In FreeBSD, questi stessi valori sono accessibili tramite sysctl krf.whatever invece di procfs.
/proc/krf/rng_stateQuesto file permette all'utente di leggere e modificare lo stato interno del PRNG di KRF.
Ad esempio, ciascuno dei seguenti aggiornerà correttamente lo stato:
echo "1234" | sudo tee /proc/krf/rng_state
echo "0777" | sudo tee /proc/krf/rng_state
echo "0xFF" | sudo tee /proc/krf/rng_state
Lo stato è un intero senza segno a 32 bit; tentare di cambiarlo oltre questo fallirà.
/proc/krf/targetingQuesto file permette all'utente di impostare i valori utilizzati da KRF per il targeting delle syscall.
NOTA: KRF utilizza una personalità predefinita non attualmente usata dal kernel Linux di default. Se modifichi questo, dovresti fare attenzione a evitare di renderla qualcosa di cui Linux si preoccupa. man 2 personality ha i dettagli.
echo "0 28" | sudo tee /proc/krf/targeting
Una personalità di 28 è hardcodata in krfexec, e deve essere impostata affinché le cose eseguite da krfexec vengano faultate.
/proc/krf/probabilityQuesto file permette all'utente di leggere e scrivere la probabilità di indurre un fault per una data syscall (faulabile).
La probabilità è rappresentata come un reciproco, ad esempio 1000 significa che, in media, lo 0.1% delle syscall faulabili verrà faultato.
echo "100000" | sudo tee /proc/krf/probability
/proc/krf/controlQuesto file controlla le syscall che KRF faulta.
NOTA: La maggior parte degli utenti dovrebbe usare krfctl invece di interagire direttamente con questo file — il primo eseguirà automaticamente la traduzione da nome a numero delle syscall e fornirà messaggi di errore più chiari quando le cose vanno male.
# replace the syscall in slot 0 (usually SYS_read) with its faulty wrapper
echo "0" | sudo tee /proc/krf/control
Passare un numero qualsiasi maggiore di KRF_NR_SYSCALLS farà sì che KRF svuoti l'intera tabella delle syscall, riportandola allo stato neutro. Poiché KRF_NR_SYSCALLS non è necessariamente prevedibile per versioni arbitrarie del kernel Linux, scegliere un numero grande (come 65535) va bene.
Passare un numero di syscall valido privo di un wrapper di iniezione di fault causerà il fallimento della write(2) sul file con EOPNOTSUPP.
/proc/krf/log_faultsQuesto file controlla se KRF emette log del kernel sulle syscall faulty. Per impostazione predefinita, nessun messaggio di log viene emesso.
NOTA: La maggior parte degli utenti dovrebbe usare krfctl invece di interagire direttamente con questo file.
# enable fault logging
echo "1" | sudo tee /proc/krf/log_faults
# disable fault logging
echo "0" | sudo tee /proc/krf/log_faults
# read the logging state
cat /proc/krf/log_faults
ENOMEM).
personality(2).Molte grazie a Andrew Reiter per il porting iniziale di KRF su FreeBSD. Il lavoro di Andrew è stato svolto per conto dell'Applied Research Group presso Veracode.
KRF è concesso in licenza secondo i termini della GNU GPLv3.
Vedi il file LICENSE per i termini esatti.