
Перехватчик системных вызовов на уровне ядра и рандомизированный генератор сбоев.
KRF — это Kernelspace Randomized Faulter (ядерный рандомизированный сбойник).
В настоящее время поддерживаются ядра Linux и FreeBSD.
Инжекция сбоев (fault injection) — это техника тестирования ПО, при которой в вызываемые программой функции вносятся сбои («неисправности»). Если вызываемый код не выполнил надлежащую проверку и обработку ошибок, эти сбои могут привести к ненадёжному поведению приложения или эксплуатируемым уязвимостям.
В отличие от многочисленных пользовательских систем инжекции сбоев, KRF работает в пространстве ядра через загружаемый модуль. Это даёт несколько преимуществ:
LD_PRELOAD для инжекции.syscall(3) или встроенный ассемблер.dlsym.Есть и несколько недостатков:
cr0. Возможно, где-то в Linux есть архитектурно-независимый способ сделать это.KRF перезаписывает таблицу системных вызовов Linux или FreeBSD: при настройке через krfctl KRF заменяет сбойные системные вызовы тонкими обёртками.
Каждая обёртка затем выполняет проверку, должен ли вызов быть сбойным, используя настраиваемую систему таргетинга, способную нацелиться на конкретные personality(2), PID, UID и/или GID. Если процесс не должен быть сбойным, вызывается исходный системный вызов.
Наконец, целевой вызов сбоит через случайную функцию ошибки. Например, вызов read(2) может получить одну из ошибок EBADF, EINTR, EIO и так далее.
Подробнее о реализации KRF можно прочитать в нашем блог-посте.
ПРИМЕЧАНИЕ: Если у вас есть Vagrant, просто используйте Vagrantfile и переходите к шагам сборки.
KRF должен работать на любом относительно новом (4.15+) ядре Linux с включённым CONFIG_KALLSYMS=1.
Это включает стандартное ядро Ubuntu 18.04 и, вероятно, многие другие последние дистрибутивы.
ПРИМЕЧАНИЕ: Игнорируйте это, если вы используете Vagrant.
Помимо инструментария C (GCC, вероятно, необходим для Linux), единственными зависимостями KRF должны быть libelf, заголовочные файлы ядра и Ruby (>=2.4, для генерации кода).
GNU Make требуется на всех платформах; FreeBSD дополнительно требует BSD Make.
Для систем с 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
или, если вы используете 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
или для 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 состоит из трёх компонентов:
krfx)krfexec)krfctl)krfmesg)Чтобы загрузить модуль ядра, выполните make insmod. Чтобы выгрузить его, выполните make rmmod.
При первом использовании может быть полезно запустить sudo krfmesg в отдельном терминале, чтобы видеть сообщения, регистрируемые krfx.
KRF начинает в нейтральном состоянии: никакие системные вызовы не будут перехватываться или сбоить, пока пользователь не задаст поведение через 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
ПРИМЕЧАНИЕ: Большинству пользователей следует использовать krfctl вместо ручного редактирования этих файлов.
В FreeBSD эти же значения доступны через sysctl krf.whatever вместо procfs.
/proc/krf/rng_stateЭтот файл позволяет пользователю читать и изменять внутреннее состояние ГПСЧ KRF.
Например, каждая из следующих команд корректно обновит состояние:
echo "1234" | sudo tee /proc/krf/rng_state
echo "0777" | sudo tee /proc/krf/rng_state
echo "0xFF" | sudo tee /proc/krf/rng_state
Состояние — это 32-битное беззнаковое целое; попытка изменить его на значение вне этого диапазона приведёт к ошибке.
/proc/krf/targetingЭтот файл позволяет пользователю задавать значения, используемые KRF для таргетинга системных вызовов.
ПРИМЕЧАНИЕ: KRF использует personality по умолчанию, который в настоящее время не используется ядром Linux по умолчанию. Если вы измените это, будьте осторожны, чтобы не сделать его значимым для Linux. Подробности см. в man 2 personality.
echo "0 28" | sudo tee /proc/krf/targeting
Значение personality 28 жёстко закодировано в krfexec, и его необходимо установить, чтобы программы, запущенные через krfexec, подвергались сбоям.
/proc/krf/probabilityЭтот файл позволяет пользователю читать и записывать вероятность возникновения сбоя для заданного (сбойного) системного вызова.
Вероятность представляется как обратное число, например, 1000 означает, что в среднем 0.1% сбойных системных вызовов будут сбоить.
echo "100000" | sudo tee /proc/krf/probability
/proc/krf/controlЭтот файл управляет системными вызовами, которые KRF сбоит.
ПРИМЕЧАНИЕ: Большинству пользователей следует использовать krfctl вместо взаимодействия с этим файлом напрямую — первая выполняет автоматическое преобразование имён системных вызовов в номера и предоставляет более понятные сообщения об ошибках.
# replace the syscall in slot 0 (usually SYS_read) with its faulty wrapper
echo "0" | sudo tee /proc/krf/control
Передача любого числа больше KRF_NR_SYSCALLS приведёт к тому, что KRF сбросит всю таблицу системных вызовов, вернув её в нейтральное состояние. Поскольку KRF_NR_SYSCALLS не обязательно предсказуем для произвольных версий ядра Linux, можно выбрать большое число (например, 65535).
Передача корректного номера системного вызова, для которого нет обёртки инжекции сбоев, приведёт к тому, что write(2) в файл завершится с ошибкой EOPNOTSUPP.
/proc/krf/log_faultsЭтот файл управляет тем, будет ли KRF выдавать сообщения ядра о сбойных системных вызовах. По умолчанию сообщения не выдаются.
ПРИМЕЧАНИЕ: Большинству пользователей следует использовать krfctl вместо взаимодействия с этим файлом напрямую.
# 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).Особая благодарность Эндрю Райтеру за первоначальный порт KRF на FreeBSD. Работа Эндрю была выполнена от имени Applied Research Group в Veracode.
KRF распространяется на условиях GNU GPLv3.
См. файл LICENSE для точных условий.