
Un interceptor de syscalls en espacio de kernel y un fallador aleatorizado
KRF is a Kernelspace Randomized Faulter.
Actualmente soporta los núcleos Linux y FreeBSD.
La inyección de fallos es una técnica de prueba de software que implica inducir fallos ("faults") en las funciones llamadas por un programa. Si la función llamada no ha realizado una comprobación y manejo de errores adecuados, estos fallos pueden resultar en un comportamiento no confiable de la aplicación o vulnerabilidades explotables.
A diferencia de los muchos sistemas de inyección de fallos en espacio de usuario existentes, KRF se ejecuta en espacio de kernel a través de un módulo cargado. Esto tiene varias ventajas:
LD_PRELOAD para la inyección.syscall(3) o ensamblador en línea.dlsym.También hay varias desventajas:
cr0 manualmente. Probablemente haya
una forma independiente de la arquitectura de hacerlo en Linux, en algún lugar.KRF reescribe la tabla de llamadas al sistema de Linux o FreeBSD: cuando se configura a través de krfctl, KRF reemplaza
las llamadas al sistema fallables con envoltorios delgados.
Cada envoltorio luego realiza una verificación para ver si la llamada debe fallarse utilizando un sistema
de direccionamiento configurable capaz de apuntar a un personality(2), PID, UID y/o GID específicos.
Si el proceso no debe fallarse, se invoca la llamada al sistema original.
Finalmente, la llamada objetivo se falla mediante una función de fallo aleatoria. Por ejemplo,
una llamada read(2) podría recibir uno de EBADF, EINTR, EIO, etc.
Puedes leer más sobre la implementación de KRF en nuestra publicación del blog.
NOTA: Si tienes Vagrant, simplemente usa el Vagrantfile y salta a los pasos de compilación.
KRF debería funcionar en cualquier kernel de Linux reciente (4.15+) con CONFIG_KALLSYMS=1.
Esto incluye el kernel predeterminado en Ubuntu 18.04 y probablemente muchas otras distribuciones recientes.
NOTA: Ignora esto si estás usando Vagrant.
Además de un conjunto de herramientas C (GCC probablemente sea necesario para Linux), las únicas dependencias de KRF deberían ser
libelf, los encabezados del kernel y Ruby (>=2.4, para generación de código).
Se requiere GNU Make en todas las plataformas; FreeBSD adicionalmente requiere BSD Make.
Para sistemas 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
o, si estás 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
o, para 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 tiene tres componentes:
krfx)krfexec)krfctl)krfmesg)Para cargar el módulo de kernel, ejecuta make insmod. Para descargarlo, ejecuta make rmmod.
Para el primer uso, podría ser útil lanzar sudo krfmesg en una terminal separada para ver los mensajes registrados por krfx.
KRF comienza en un estado neutral: no se interceptarán ni fallarán llamadas al sistema hasta que el usuario
especifique algún comportamiento a través de 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 mayoría de los usuarios deberían usar krfctl en lugar de manipular estos archivos manualmente.
En FreeBSD, estos mismos valores son accesibles a través de sysctl krf.whatever en lugar de procfs.
/proc/krf/rng_stateEste archivo permite a un usuario leer y modificar el estado interno del PRNG de KRF.
Por ejemplo, cada uno de los siguientes actualizará correctamente el estado:
echo "1234" | sudo tee /proc/krf/rng_state
echo "0777" | sudo tee /proc/krf/rng_state
echo "0xFF" | sudo tee /proc/krf/rng_state
El estado es un entero sin signo de 32 bits; intentar cambiarlo más allá de eso fallará.
/proc/krf/targetingEste archivo permite a un usuario establecer los valores utilizados por KRF para el direccionamiento de llamadas al sistema.
NOTA: KRF usa un personality predeterminado que actualmente no utiliza el kernel de Linux por defecto. Si cambias
esto, debes tener cuidado de evitar convertirlo en algo que Linux considere importante. man 2 personality
tiene los detalles.
echo "0 28" | sudo tee /proc/krf/targeting
Un personality de 28 está codificado en krfexec, y debe configurarse para que las cosas ejecutadas
por krfexec sean falladas.
/proc/krf/probabilityEste archivo permite a un usuario leer y escribir la probabilidad de inducir un fallo para una llamada al sistema (fallable) determinada.
La probabilidad se representa como un recíproco, por ejemplo, 1000 significa que, en promedio, el 0.1% de
las llamadas al sistema fallables serán falladas.
echo "100000" | sudo tee /proc/krf/probability
/proc/krf/controlEste archivo controla las llamadas al sistema que KRF falla.
NOTA: La mayoría de los usuarios deberían usar krfctl en lugar de interactuar directamente con este archivo —
el primero realizará la traducción de nombre a número de syscall automáticamente y proporcionará
mensajes de error más claros cuando algo salga mal.
# replace the syscall in slot 0 (usually SYS_read) with its faulty wrapper
echo "0" | sudo tee /proc/krf/control
Pasar cualquier número mayor que KRF_NR_SYSCALLS hará que KRF vacíe toda la tabla de llamadas al sistema,
devolviéndola al estado neutral. Dado que KRF_NR_SYSCALLS no es necesariamente predecible para
versiones arbitrarias del kernel de Linux, elegir un número grande (como 65535) está bien.
Pasar un número de syscall válido que carezca de un envoltorio de inyección de fallos hará que el write(2)
al archivo falle con EOPNOTSUPP.
/proc/krf/log_faultsEste archivo controla si KRF emite registros del kernel en syscalls falladas. Por defecto, no se emiten mensajes de registro.
NOTA: La mayoría de los usuarios deberían usar krfctl en lugar de interactuar directamente con este archivo.
# 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).Muchas gracias a Andrew Reiter por el port inicial de KRF a FreeBSD. El trabajo de Andrew se realizó en nombre del Grupo de Investigación Aplicada de Veracode.
KRF está licenciado bajo los términos de GNU GPLv3.
Consulta el archivo LICENSE para los términos exactos.