Git Product home page Git Product logo

krf's Introduction

KRF

CI

KRF is a Kernelspace Randomized Faulter.

It currently supports the Linux and FreeBSD kernels.

What?

Fault injection is a software testing technique that involves inducing failures ("faults") in the functions called by a program. If the callee has failed to perform proper error checking and handling, these faults can result in unreliable application behavior or exploitable vulnerabilities.

Unlike the many userspace fault injection systems out there, KRF runs in kernelspace via a loaded module. This has several advantages:

  • It works on static binaries, as it does not rely on LD_PRELOAD for injection.
  • Because it intercepts raw syscalls and not their libc wrappers, it can inject faults into calls made by syscall(3) or inline assembly.
  • It's probably faster and less error-prone than futzing with dlsym.

There are also several disadvantages:

  • You'll probably need to build it yourself.
  • It probably only works on x86(_64), since it twiddles cr0 manually. There is probably an architecture-independent way to do that in Linux, somewhere.
  • It's essentially a rootkit. You should definitely never, ever run it on a non-testing system.
  • It probably doesn't cover everything that the Linux kernel expects of syscalls, and may destabilize its host in weird and difficult to reproduce ways.

How does it work?

KRF rewrites the Linux or FreeBSD system call table: when configured via krfctl, KRF replaces faultable syscalls with thin wrappers.

Each wrapper then performs a check to see whether the call should be faulted using a configurable targeting system capable of targeting a specific personality(2), PID, UID, and/or GID. If the process shouldn't be faulted, the original syscall is invoked.

Finally, the targeted call is faulted via a random failure function. For example, a read(2) call might receive one of EBADF, EINTR, EIO, and so on.

You can read more about KRF's implementation in our blog post.

Setup

Compatibility

NOTE: If you have Vagrant, just use the Vagrantfile and jump to the build steps.

KRF should work on any recent-ish (4.15+) Linux kernel with CONFIG_KALLSYMS=1.

This includes the default kernel on Ubuntu 18.04 and probably many other recent distros.

Dependencies

NOTE: Ignore this if you're using Vagrant.

Apart from a C toolchain (GCC is probably necessary for Linux), KRF's only dependencies should be libelf, the kernel headers, and Ruby (>=2.4, for code generation).

GNU Make is required on all platforms; FreeBSD additionally requires BSD Make.

For systems with apt:

sudo apt install gcc make libelf-dev ruby linux-headers-$(uname -r)

Building

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

or, if you're using 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

or, for 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

Usage

KRF has three components:

  • A kernel module (krfx)
  • An execution utility (krfexec)
  • A control utility (krfctl)
  • A kernel module logger (krfmesg)

To load the kernel module, run make insmod. To unload it, run make rmmod.

For first time use it might be useful to launch sudo krfmesg on a separate terminal to see messages logged from krfx.

KRF begins in a neutral state: no syscalls will be intercepted or faulted until the user specifies some behavior via 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

Configuration

NOTE: Most users should use krfctl instead of manipulating these files by hand. In FreeBSD, these same values are accessible through sysctl krf.whatever instead of procfs.

/proc/krf/rng_state

This file allows a user to read and modify the internal state of KRF's PRNG.

For example, each of the following will correctly update the state:

echo "1234" | sudo tee /proc/krf/rng_state
echo "0777" | sudo tee /proc/krf/rng_state
echo "0xFF" | sudo tee /proc/krf/rng_state

The state is a 32-bit unsigned integer; attempting to change it beyond that will fail.

/proc/krf/targeting

This file allows a user set the values used by KRF for syscall targeting.

NOTE: KRF uses a default personality not currently used by the Linux kernel by default. If you change this, you should be careful to avoid making it something that Linux cares about. man 2 personality has the details.

echo "0 28" | sudo tee /proc/krf/targeting

A personality of 28 is hardcoded into krfexec, and must be set in order for things executed by krfexec to be faulted.

/proc/krf/probability

This file allows a user to read and write the probability of inducing fault for a given (faultable) syscall.

The probability is represented as a reciprocal, e.g. 1000 means that, on average, 0.1% of faultable syscalls will be faulted.

echo "100000" | sudo tee /proc/krf/probability

/proc/krf/control

This file controls the syscalls that KRF faults.

NOTE: Most users should use krfctl instead of interacting with this file directly โ€” the former will perform syscall name-to-number translation automatically and will provide clearer error messages when things go wrong.

# replace the syscall in slot 0 (usually SYS_read) with its faulty wrapper
echo "0" | sudo tee /proc/krf/control

Passing any number greater than KRF_NR_SYSCALLS will cause KRF to flush the entire syscall table, returning it to the neutral state. Since KRF_NR_SYSCALLS isn't necessarily predictable for arbitrary versions of the Linux kernel, choosing a large number (like 65535) is fine.

Passing a valid syscall number that lacks a fault injection wrapper will cause the write(2) to the file to fail with EOPNOTSUPP.

/proc/krf/log_faults

This file controls whether or not KRF emits kernel logs on faulty syscalls. By default, no logging messages are emitted.

NOTE: Most users should use krfctl instead of interacting with this file directly.

# 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

TODO

  • Allow users to specify a particular class of faults, e.g. memory pressure (ENOMEM).
    • This should be do-able by adding some more bits to the personality(2) value.

Thanks

Many thanks go to Andrew Reiter for the initial port of KRF to FreeBSD. Andrew's work was performed on behalf of the Applied Research Group at Veracode.

Licensing

KRF is licensed under the terms of the GNU GPLv3.

See the LICENSE file for the exact terms.

krf's People

Contributors

artemdinaburg avatar disconnect3d avatar dmur1 avatar hmwildermuth avatar layderv avatar lpirl avatar woodruffw avatar zedd-08 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

krf's Issues

Split Linux and FreeBSD codegen

The Linux and FreeBSD codegen for syscall wrappers don't share much in common, and attempting to parametrize the single file to handle both has led to some ugliness. We should split them, especially as #80 will complicate Linux's codegen.

FreeBSD: Copy only syscall function pointers

The current FreeBSD module copies the entire sysent structure for each syscall slot, which requires a lot more memory than necessary (and possibly exposes us to concurrency issues, depending on whether some of the other fields can be modified).

Instead, we should just copy the sy_call entries.

Support FreeBSD in krfctl

krfctl should use sysctl(3) on FreeBSD to accomplish the same things it currently does with procfs on Linux.

In terms of implementation, we should probably have a structure like src/krfctl/<platform>/platform.c (or whatever) with common interfaces defined in krfctl.h.

krfctl -h is an invalid option

Krfctl should:

  • display help when run with no arguments
  • include h in its getopt specification(?) so it does not report invalid option -- 'h' when it is passed

Those things can be seen below:

$ ~/krf/src/krfctl/krfctl
$ ~/krf/src/krfctl/krfctl -h
/home/vagrant/krf/src/krfctl/krfctl: invalid option -- 'h'
usage: krfctl <options>
options:
 -h                          display this help message
 -F <syscall> [syscall...]   fault the given syscalls
 -P <profile>                fault the given syscall profile
 -c                          clear the syscall table of faulty calls
 -r <state>                  set the RNG state
 -p <prob>                   set the fault probability
 -L                          toggle faulty call logging
 -T <variable>=<value>       enable targeting option <variable> with value <value>
 -C                          clear the targeting options
targeting options:
 personality, PID, UID, GID, and INODE

32-bit support

KRF currently makes x86 specific assumptions, and may make x86_64-specific assumptions. We should try to eliminate the latter (if they exist) so that it can be built with support for x86_32-specific syscalls (e.g., iopl(2) in #6).

This has two parts:

  • Investigating the general feasibility of x86_32 bit builds + ensuring that KRF actually works when built in 32-bit
  • Refactoring the codegen/specs to only generate syscalls for the relevant platform (e.g., not emitting iopl(2) on x86_64)

Linux Kernel Oops on module unload

A kernel oops is often caused on module unload. This is because a process can start a faulted blocking syscall, such as wait4 or select, then have the syscall table flushed and the module unloaded, so that when the syscall continues execution it is in the memory where the kernel module once was, causing a page fault.

Syscall support: miscellaneous

We should support faulting these syscalls.

  • vhangup
  • sysctl
  • prctl
  • arch_prctl
  • adjtime
  • adjtimex
  • pivot_root
  • iopl
  • nfsservctl
  • readahead

That list is not exhaustive; there are definitely others.

Each of the above probably belongs to one or more groups.

Investigate cparser for improving codegen

Right now, the codegen specs (e.g. read.yml) include hardcoded proto and parm fields that correspond to the prototype and parameter list for each syscall. This is confusing, hard to read, and error prone. We should really use something like pag's cparser to parse syscall.h and generate these fields for us.

Speed up the module build

Two things are currently making the module build slow:

  • The codegen is re-run on every build, even when a particular spec hasn't changed
  • -jN isn't being respected, probably for silly reasons

Krfmesg should detect if krfx is there

If krfx module is not enabled, the krfmesg shows an unambigous error:

vagrant@ubuntu-bionic:~$ lsmod | grep krf
vagrant@ubuntu-bionic:~$ sudo krfmesg
krfmesg: socket: Protocol not supported

It should detect if krfx is there and inform that it is not up.

Add FreeBSD to the CI

We should do this to make sure we don't (completely) break FreeBSD when making Linux-only changes.

Handle the new pt_regs syscall convention

As of Linux 4.17, x86_64 kernels built with CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y no longer use the userspace parameter list for syscalls, but instead use a single pt_regs* argument which they unpack internally.

As part of these changes, the sys_* prototypes are no longer exposed directly on x86_64 via linux/syscalls.h. This requires us to make some codegen changes:

"#{ASMLINKAGE} #{TYPEOF}(sys_#{call}) krf_sys_#{call};"

and similar should become something like:

"#{ASMLINKAGE} #{SYSCALL_RET_TYPE} (*krf_sys_#{call})(pt_regs*);"

We should be able to handle this difference conditionally by checking the running kernel config, e.g. via /lib/modules/$(uname -r)/build/.config. This requires additional codegen cruft, which indicates that it's maybe finally time to split FreeBSD's codegen out from Linux's.

Track headers as dependencies in the build

The build doesn't currently track header files as dependencies for targets, meaning that it's possible to produce a broken build by changing a constant/macro in a header file. We should use gcc -MM or makedepend (or whatever) to declare header dependencies.

Use CTLFLAG_ANYBODY to make sysctls world-writable on FreeBSD

From sysctl(9):

Additionally, any of the following	optional flags may also	be specified:

     CTLFLAG_ANYBODY  Any user or process can write to this sysctl.

So, our sysctls should be CTLFLAG_RW | CTLFLAG_ANYBODY. There are also some other interesting flags documented there that might be worth reading through, but we don't need them here.

This will improve the fix for #31 provided in #36, and will remove the need to run as root (as well as my terrible workaround hack).

Switch to GitHub actions

We should use GitHub actions to ensure that KRF builds, instead of Travis. We'll still need to use Cirrus for FreeBSD.

Netlink-based logging

We currently log faults via dmesg. This works, but it would probably be more efficient (and neat) to log them (and other program artifacts) via a netlink socket like audit and netfilter do.

Parallel Builds in FreeBSD

Running a parallel build (eg. gmake -j2) does not succeed on FreeBSD.

When src/module/freebsd/Makefile calls the Makefile.module with BSDMake, the following error occurs and the build is cancelled: make[1]: illegal argument to -j -- must be positive integer!

Make install rule

Would install krfexec and krfctl in /usr/bin (or whatever prefix).

Should it also internally call the insmod rule?

Make re-codegens every time

Make re-codegens every time, even if the dependencies (codegen/codegen, codegen/*/*.yml) are the same. This makes make all rebuild the krf module every time.

Add a mode for skipping faults during dynamic loading

One problem with faulting programs under KRF is that KRF might decide to inject a fault during the dynamic link/load phase, aborting ld-linux.so instead of the actual target image. This usually isn't helpful, since it doesn't indicate any mistakes in the target itself.

It should be possible to check the loaded program's name via the current task, probably via comm. We should use that (or whatever other field) to provide a configurable option for not faulting if it matches any of the common dynamic loader names.

This should also be possible on FreeBSD, but we can start with Linux-only.

Syscall support: sched_* family

Docs: man 2 and https://filippo.io/linux-syscall-table/

These belong in the proc group, at the very least.

Specific calls:

  • sched_getparam
  • sched_setparam
  • sched_getscheduler
  • sched_setscheduler
  • sched_getattr
  • sched_setattr
  • sched_getaffinity
  • sched_setaffinity
  • sched_get_priority_max
  • sched_get_priority_min
  • sched_rr_get_interval
  • sched_getcpu

sched_yield always succeeds on Linux, so there's no point in adding it.

Remove expansion macros

Since so much of the code that uses macros (e.g KRF_DEFINE) is codegen'd, it would be much more readable and easier to debug compiler errors if the macros were removed and the full form of the phrase was done in the codegen script.

Kernel abstraction

This issue will serve as a tracker for individual issues.

To make porting KRF to other kernels easier, we'll need to provide abstractions for (at least) the following operations:

  • Setting various configuration variables (krf_rng_state, krf_probability, others in config.c). We currently use procfs for these on Linux.
  • Replacing a syscall at a given slot and reverting that slot back to its original call.
  • Twiddling cr0 (on systems where necessary).

Some potential challenges:

  • All targeting is currently done via personality(2), which is Linux-only. #9 will fix this.

Application-specific profiles

Right now we provide auto-generated profiles for different kinds of syscalls (e.g. fs, io, ipc) as well as an all profile that contains every faultable syscall. In addition to these, it would be nice to allow users to define custom profiles for krfctl that encapsulate their application-specific needs.

As suggested by @MrSynAckSter.

Remove personality procfs file

Due to its integration into the new targeting system, the personality procfs file is obsolete, and should be removed.

This would also require hardcoding a personality value (28 is the current default) into krfexec.

krfctl describe profiles

Krfctl should be able to:

  • list available profiles that can be passed to krfctl -P <profile>.
  • describe profiles, i.e., tell that mm stands for memory management syscalls and what syscalls are included.
  • maybe also have a descriptive describe profile mode where also the possible faults are listed? Though I am not sure if this is that useful as this can be grabbed directly from syscalls yaml files.

Refactor profile generation

Right now, krfctl gets its profiles from a profiles.yml file that looks like this:

fs:
  - read
  - write
  # ...

This is annoying to maintain: we have to update each profile manually whenever new syscalls are added. To resolve this, we should integrate profiles into the syscall definition files.

Old:

# chdir.yml
proto: const char __user *filename
parms: filename
errors:
  - EACCES
  - EFAULT
  # ...

New:

# chdir.yml
proto: const char __user *filename
parms: filename
errors:
  - EACCES
  - EFAULT
  # ...
profiles:
  - fs
  # ...

Configurable targeting strategies

Right now, KRF can only target a process based on its personality(2) mask. This works really well, but we should support other techniques.

Candidate techniques (not exhaustive):

  • PID (or list of PIDs)
  • GID (or list of GIDs)
    • A very early version of KRF did this, and it worked fine. We should re-add it.
  • Processes that have a given file/file-like object open

Since we're operating in user context during a syscall, we should be able to target on anything present in task_struct. There's probably additional context we can safely test.

Doing this will also give us a nice interface for writing a kernel-independent targeting system, which will help with #8.

Add CI for linting, building

We probably shouldn't actually install this on Travis or whatever, but having automatic clang-format and build checks would be nice.

Add a "conservative" build option

Right now, each syscall specification is defined with pretty much every possible error. This means that each syscall can fault with a wide variety of errors, but also that any given error might not make sense given the system's state and/or parameters passed to the syscall.

For example, sendto(2) specifies EAGAIN and EWOULDBLOCK as potential errors, even when the underlying socket is blocking. It's unlikely that a target application would filter error codes and notice a nonsensical ones like the above, but it wouldn't hurt to have a "conservative" build mode that only generates faulty syscalls for a sensible subset of all possible errors.

So, a spec like this:

proto: int fd, void __user *buff, size_t len, unsigned int flags, struct sockaddr __user *addr, int addr_len
parms: fd, buff, len, flags, addr, addr_len
errors:
  - EACCES
  - EAGAIN
  - EWOULDBLOCK
  - EALREADY
  - EBADF
  - ECONNRESET
  - EDESTADDRREQ
  - EFAULT
  - EINTR
  - EINVAL
  - EISCONN
  - EMSGSIZE
  - ENOBUFS
  - ENOMEM
  - ENOTCONN
  - ENOTSOCK
  - EOPNOTSUPP
  - EPIPE

would probably need to add a field like this:

basic_errors:
  - EBADF
  - ECONNRESET
  - EFAULT
# etc

To avoid duplication, we could just remove the basic_errors from the main errors list and combine the two during codegen (when not building in conservative mode).

Figure out fault inheritance on FreeBSD with krfexec

We should come up with a way to have child processes (arbitrarily deep, not just the first level of children) inherit the targeted status of the original krfexec'd parent on FreeBSD.

Some ideas:

  • In our fault wrapper, check whether our current PID is the child of the targeted PID
    • This is cheap for the first level of children, since FreeBSD's struct proc contains p_oppid. For deeper levels we'll probably have to walk up the tree, which would (probably) be too intensive and not very kthread/MP-safe
  • LD_PRELOAD or otherwise wrap fork inside krfexec spawned programs, so that fork checks whether it's the child of a process being faulted and adds the new PID to a set of faulted PIDs
    • This requires us to update the targeting system to allow multiple PIDs, which we should do at some point anyways
  • Maybe something really crazy where we wrap the dynamic linker and put some magic somewhere in the loaded program
  • Maybe a special memory region + a new targeting mode that krfexec uses by default instead? Child processes should inherit things like memory maps, so this might not be too hard to do
  • Similar to the dynamic linker: could we abuse the ELF auxiliary vector for this?

Investigate core dumping behavior on FreeBSD

One of the easiest things we can do to improve triage on FreeBSD is ensure that the faulted process always dumps its core on a crash. On Linux we accomplish that with a configured core_pattern and setrlimit; we should figure out whether FreeBSD needs similar/separate steps.

Individual components:

  • Use KRF to crash (not just exit) a program on FreeBSD
  • See whether it dumps core by default
  • If it doesn't, apply whatever methods are needed to krfexec

It's possible that krfexec will need system-specific function calls for configuring core dumping behavior (or maybe setrlimit will be good enough).

Support FreeBSD in krfexec

For the time being, this is probably as simple as only #includeing and using the personality(2) call on Linux.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.