Giter Site home page Giter Site logo

intel / kernel-fuzzer-for-xen-project Goto Github PK

View Code? Open in Web Editor NEW
462.0 24.0 81.0 13.54 MB

Kernel Fuzzer for Xen Project (KF/x) - Hypervisor-based fuzzing using Xen VM forking, VMI & AFL

License: MIT License

Makefile 1.89% Shell 2.95% C 93.49% M4 1.36% Batchfile 0.07% Assembly 0.24%
xen fuzzing afl hypervisor linux-kernel

kernel-fuzzer-for-xen-project's Introduction

KF/x - Kernel Fuzzer for Xen Project

Hypervisor-based fuzzing using Xen and AFL. The tool utilizes Xen VM forks to perform the fuzzing, thus allowing for parallel fuzzing/multiple AFL instances to fuzz at the same time. Coverage guidance for AFL is achieved using Intel® Processor Trace or breakpoints.

Minimum hardware requirements: Intel CPU with VT-x and EPT enabled.

This project is licensed under the terms of the MIT license

Presentations

LSS2021 OSSummit2020 DEFCON29

Capability & Limitations

Using this tool you can fuzz both ring-0 (kernel-mode) and ring-3 (user-mode) code, including transition from one to the other using system calls.

Using VM forks for fuzzing on Xen restricts you to fuzz only code that does not perform any I/O operation. This means for example that the target code can't fetch data from disk or communicate over the network. All code and data used for running your target needs to be already in memory when fuzzing begins. Interrupts are blocked during fuzzing so code that relies on timers is also out-of-scope. Furthermore, fuzzing is currently limited to a single vCPU so you won't be able detect race-conditions.

Fuzzing memory that is located on DMA pages is possible but output written to DMA pages will never reach the device. Fuzzing addresses that are designated MMIO areas is not possible. However, if an input is read from MMIO and stored in the VM's normal memory, then that memory can be fuzzed if the harness is placed just after the MMIO read. During fuzzing writes to MMIO memory are discarded.

Contributions

PRs that are fixing bugs of any kind are welcome but this repository is intended to be only a reference you use to create your own fuzzing setups. We encourage you to fork it and tune it to suite your fuzzing needs. PRs to this repository that add extra features will be kept to a minimum as we try to keep this code-base simple.

Table of Contents

  1. Install dependencies
  2. Grab the project and all submodules
  3. Compile & Install Xen 3.b Install & Boot Xen from UEFI
  4. Create VM disk image
  5. Setup networking
  6. Create VM
  7. Grab the kernel's debug symbols & headers
  8. Configure the VM's console
  9. Build the kernel's debug JSON profile
  10. Compile & install Capstone
  11. Compile & install LibVMI
  12. Compile kfx
  13. Patch AFL
  14. Add harness
  15. Setup the VM for fuzzing
  16. Connect to the VM's console
  17. Insert the target kernel module
  18. Start fuzzing using AFL
  19. Debugging
  20. Intel Processor Trace
  21. Triaging crashes
  22. Advanced harnessing
  23. Coverage info
  24. FAQ

Setup instruction for Ubuntu:

The following instructions have been mainly tested on Debian Bullseye and Ubuntu 20.04. The actual package names may vary on different distros/versions. You may also find https://wiki.xenproject.org/wiki/Compiling_Xen_From_Source helpful if you run into issues.

1. Install dependencies


sudo apt-get install git build-essential libfdt-dev libpixman-1-dev libssl-dev libsdl1.2-dev autoconf libtool xtightvncviewer tightvncserver x11vnc uuid-runtime uuid-dev bridge-utils python3-dev liblzma-dev libc6-dev wget git bcc bin86 gawk iproute2 libcurl4-openssl-dev bzip2 libpci-dev libc6-dev libc6-dev-i386 linux-libc-dev zlib1g-dev libncurses5-dev patch libvncserver-dev libssl-dev libsdl-dev iasl libbz2-dev e2fslibs-dev ocaml libx11-dev bison flex ocaml-findlib xz-utils gettext libyajl-dev libpixman-1-dev libaio-dev libfdt-dev cabextract libglib2.0-dev autoconf automake libtool libjson-c-dev libfuse-dev liblzma-dev autoconf-archive kpartx python3-pip libsystemd-dev cmake snap gcc-multilib nasm binutils bc libunwind-dev ninja-build

2. Grab the project and all submodules


git clone https://github.com/intel/kernel-fuzzer-for-xen-project
cd kernel-fuzzer-for-xen-project
git submodule update --init

3. Compile & Install Xen


Make sure the pci include folder exists at /usr/include/pci. In case it doesn't create a symbolic link to where it's installed at:

sudo ln -s /usr/include/x86_64-linux-gnu/pci /usr/include/pci

Before installing Xen from source make sure you don't have any pre-existing Xen packages installed:

sudo apt-get remove xen-* libxen*

Now we can compile & install Xen

cd xen
echo CONFIG_EXPERT=y > xen/.config
echo CONFIG_MEM_SHARING=y >> xen/.config
./configure --disable-pvshim --enable-githttp --enable-ovmf
make -C xen olddefconfig
make -j4 dist-xen
make -j4 dist-tools
su -
make -j4 install-xen
make -j4 install-tools
echo "/usr/local/lib" > /etc/ld.so.conf.d/xen.conf
ldconfig
echo "none /proc/xen xenfs defaults,nofail 0 0" >> /etc/fstab
systemctl enable xencommons.service
systemctl enable xen-qemu-dom0-disk-backend.service
systemctl enable xen-init-dom0.service
systemctl enable xenconsoled.service
echo "GRUB_CMDLINE_XEN_DEFAULT=\"hap_1gb=false hap_2mb=false dom0_mem=6096M hpet=legacy-replacement iommu=no-sharept\"" >> /etc/default/grub
update-grub
reboot

Make sure to pick the Xen entry in GRUB when booting. You can verify you booted into Xen correctly by running xen-detect.

Note that we assign 6GB RAM to dom0 above which is a safe default but feel free to increase that if your system has a lot of RAM available.

3.b Booting from UEFI

If Xen doesn't boot from GRUB you can try to boot it from UEFI directly

su -
mkdir -p /boot/efi/EFI/xen
cp /usr/lib64/efi/xen.efi /boot/efi/EFI/xen
cp /boot/vmlinuz /boot/efi/EFI/xen
cp /boot/initrd.img /boot/efi/EFI/xen

Gather your kernel boot command line's relevant bits from /proc/cmdline. Copy & paste the following into /boot/efi/EFI/xen/xen.cfg:

[global]
default=xen

[xen]
options=console=vga hap_1gb=false hap_2mb=false
kernel=vmlinuz console=hvc0 earlyprintk=xen <YOUR KERNEL'S BOOT COMMAND LINE>
ramdisk=initrd.img

Create an EFI boot entry for it:

efibootmgr -c -d /dev/sda -p 1 -w -L "Xen" -l "\EFI\xen\xen.efi"
reboot

You may want to use the -C option above instead of -c if you are on a remote system so you can set only the next-boot to try Xen. This is helpful in case the system can't boot Xen and you don't have remote KVM to avoid losing access in case Xen can't boot for some reason. Use efibootmgr --bootnext <BOOT NUMBER FOR XEN> to try boot Xen only on the next reboot.

4. Create VM disk image


20GB is usually sufficient but if you are planning to compile the kernel from source you will want to increase that.

dd if=/dev/zero of=vmdisk.img bs=1G count=20 

5. Setup networking


You can follow this tutorial to setup dnsmasq to provide DHCP to your VMs.

Alternatively, you can configure a static networking as follows:

Create a network bridge using NetPlan at /etc/netplan/02-xenbr0.yaml:

network:
  version: 2
  renderer: networkd
  bridges:
    xenbr0:
      dhcp4: no
      addresses: [ 10.0.0.1/24 ]

Apply the NetPlan configuration:

su -
netplan generate
netplan apply

Enable IP forwarding:

su -
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf
sysctl --system

Enable NAT and save the iptables rule, make sure to change eth0 to match your interface name facing the internet:

su -
iptables -A FORWARD -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
apt-get install iptables-persistent

6. Create VM


Create the domain configuration file by pasting the following, for example into debian.cfg, then tune it as you see fit. It's important the VM has only a single vCPU.

name="debian"
builder="hvm"
vcpus=1
maxvcpus=1
memory=2048
maxmem=2048
hap=1
boot="cd"
serial="pty"
vif=['bridge=xenbr0']
vnc=1
vnclisten="0.0.0.0"
vncpasswd='1234567'
usb=1
usbdevice=['tablet']
vga="stdvga"
nomigrate=1
# Make sure to update the paths below!
disk=['file:/path/to/vmdisk.img,xvda,w',
      'file:/path/to/debian.iso,xvdc:cdrom,r']

Start the VM with:

sudo xl create debian.cfg

You can connect to the VNC session using your favorite VNC viewer or by simply running:

vncviewer localhost

In case it's a remote system replace localhost with the IP of the system; note however that the VNC connection is not encrypted so it may be better to setup an SSH tunnel to connect through.

Follow the installation instructions in the VNC session. If you use static networking then configure the IP manually to 10.0.0.2/24 with a default route via 10.0.0.1, and choose a DNS server of your own choosing (for example 9.9.9.9).

7. Grab the kernel's debug symbols & headers


Inside the VM, using Debian, you can install everything right away

su -
apt-get update && apt-get install linux-image-$(uname -r)-dbg linux-headers-$(uname -r)

On Ubuntu to install the Kernel debug symbols please follow the following tutorial: https://wiki.ubuntu.com/Debug%20Symbol%20Packages

From the VM copy /usr/lib/debug/boot/vmlinux-$(uname -r) and /boot/System.map-$(uname -r) to your dom0, for example using scp.

8. Configure the VM's console


Inside the VM, edit /etc/default/grub and add console=ttyS0 nokaslr nopti to GRUB_CMDLINE_LINUX_DEFAULT line. Run update-grub afterwards and reboot. Note that adding nokaslr and nopti are optional but can make triaging crashes easier.

9. Build the kernel's debug JSON profile


Back in dom0, we'll convert the dwarf debug information to json that we copied in Step 7. We'll need Go 1.13 or newer for this. You can install it using snap as follows:

sudo snap install --classic go

If you distro's repository has go 1.13 or newer you can also install it from there (package name is golang-go).

Now we can build dwarf2json and generate the JSON profile. Change the paths to match your setup and make sure your dom0 has enough RAM as this may take up a lot of it.

cd dwarf2json
go build
./dwarf2json linux --elf /path/to/vmlinux --system-map /path/to/System.map > ~/debian.json
cd ..

10. Compile & install Capstone


We use a more recent version from the submodule (4.0.2) then what most distros ship by default. If your distro ships a newer version you could also just install libcapstone-dev.

cd capstone
mkdir build
cd build
cmake ..
make
sudo make install
sudo ldconfig
cd ../..

11. Compile & install LibVMI


cd libvmi
autoreconf -vif
./configure --disable-kvm --disable-bareflank --disable-file
make -j4
sudo make install
sudo ldconfig
cd ..

Test that base VMI works with:

sudo vmi-process-list --name debian --json ~/debian.json

12. Compile kfx


autoreconf -vif
./configure
make -j4

13. Setup AFL


By default you should use AFL++ from https://github.com/AFLplusplus/AFLplusplus. No custom patches are necessary but you need to set an environment variable to ensure fork VMs are cleaned up when AFL++ exits:

git clone https://github.com/aflplusplus/aflplusplus
cd aflplusplus
make
sudo make install
export AFL_KILL_SIGNAL=15

If you decide to use plain AFL you need to patch it with the KF/x provided patch as such:

cd AFL
patch -p1 < ../patches/0001-AFL-Xen-mode.patch
make
sudo make install
cd ..

14. Add harness to target kernel module or function


The target kernel module needs to be harnessed using two CPUID instructions with leaf 0x13371337. See the testmodule folder for an example.

static inline void harness(void)
{
    unsigned int tmp;

    asm volatile ("cpuid"
                  : "=a" (tmp)
                  : "a" (0x13371337)
                  : "bx", "cx", "dx");
}

You can insert the harness before and after the code segment you want to fuzz:

    harness();
    x = test();
    harness();

15. Setup the VM for fuzzing


Start ./kfx with the --setup option specified. This will wait for the domain to issue the harness CPUID and will leave the domain paused. This ensures that the VM is at the starting location of the code we want to fuzz when we fork it. To get the target address and size from the harness, use -c option.

sudo ./kfx --domain debian --json ~/debian.json --setup -c

You may optionally want to do this in a screen session, or you will need a separate shell to continue.

16. Connect to the VM's console


sudo xl console debian

You should see a login screen when you press enter. Proceed to login.

17. Insert the target kernel module


There is a testmodule included with the repository, you can copy it into the VM and compile it simply by running make. Afterwards, load it via:

sudo insmod testmodule.ko

The VM's console should now appear frozen. This is normal and what's expected. You can exit the console with CTRL+]. The kfx should have now also exited with a message Parent ready.

18. Start fuzzing using AFL


Everything is now ready for fuzzing to begin. The kernel fuzzer takes the input with --input flag, its size via --input-limit and the target memory address to write it to via --address. With AFL the input file path needs to be @@. You also have to first seed your fuzzer with an input that doesn't produces a crash in the code segment being fuzzed.

mkdir input
mkdir output
echo -n "not_beef" > input/beef

If you use AFL++:

sudo -E afl-fuzz -i input/ -o output/ -- ./kfx --domain debian --json ~/debian.json --input @@ --input-limit 8 --address 0x<KERNEL VIRTUAL ADDRESS TO WRITE INPUT TO>

If you use plain AFL:

sudo afl-fuzz -i input/ -o output/ -m 1500 -X -- ./kfx --domain debian --json ~/debian.json --input @@ --input-limit 8 --address 0x<KERNEL VIRTUAL ADDRESS TO WRITE INPUT TO>

You can also specify the --limit option of how many control-flow instructions you want to encounter before timing out the fuzz iteration. This is an alternative to the AFL built-in time-out model.

The speed of the fuzzer will vary based on how much code you are fuzzing. The more code you are exercising the fewer iterations per second you will see. The testmodule included with the project has been observed to produce a speed of 200-600 iterations per second on i5 family CPUs. Don't forget: you can run multiple instances of the fuzzer to speed things up even further by utilizing more CPU cores on your machine.

After you are finished with fuzzing, the VM can be unpaused and should resume normally without any side-effects.

19. Debugging


You can run the kernel fuzzer directly to inject an input into a VM fork without AFL, adding the --debug option will provide you with a verbose output.

sudo ./kfx --domain debian --json ~/debian.json --debug --input /path/to/input/file --input-limit <MAX SIZE TO WRITE> --address 0x<KERNEL VIRTUAL ADDRESS TO WRITE INPUT TO>

20. Intel Processor Trace


Using Intel Processor Trace to collect the coverage trace information can significantly boost your fuzzing speed. You can check whether your processor supports this feature by running xl info and checking whether vmtrace is present in the line starting with virt_caps. If it's missing, your processor doesn't support this mode.

For this mode to activate you also have to add the following line to your VM's config before you start it:

vmtrace_buf_kb=65536

Make sure your dom0 Linux kernel is a recent one. For example linux-image-5.10.0-1019-oem in the Ubuntu 20.04 repository has been confirmed to work. Linux 5.11 or newer will also work.

When the VM is booted with this option set you can activate Intel PT decoding using the kfx option --ptcov. You can adjust the buffer size up to 4GB in case you are fuzzing large code-segments. Beware that each fork will get an individual PT buffer allocated for it, so keep in mind the total memory limit your system has.

Using this coverage tracing mode is more restrictive then the default. You can only fuzz code when the address space doesn't change (ie. no user-to-kernel switch, no process-switch). You also need Xen to run in bare-metal mode, it's not supported in a nested environment.

21. Triaging crashes


After AFL finds a crash the first thing to do is to verify that the crash is reproducible. You can use kfx to run your target with the crashing input recorded by AFL as this:

kfx --domain ubuntu-20.04 --json 5.4.0.json --address 0xffff8880334652b0 --input output/crashes/id\:000000\,sig\:06\,src\:000008\,op\:int16\,pos\:13\,val\:+128 --input-limit 16 --keep --debug

With the --debug flag specified you will see a verbose output of kfx and you will be able to see which sink point the input reaches. The --keep option will leave the VM forks paused after kfx exits, so you can examine the callstack using GDB:

Online debugging

gdbsx -a <vm fork domid> 64 4567 &
gdb vmlinux -ex 'target remote :4567'

The vmlinux file should be your target kernel's debug file. In the GDB session you can take a look at the stack-trace of the execution by running backtrace. To access more advanced kernel debugging features of GDB (the lx- commands) it may be necessary to build your target kernel from source and run the gdb command from the kernel source folder.

If you are debugging a loadable kernel module that's in the Linux kernel source tree you will need to load the symbols for it using lx-symbols. If you are debugging a module that's out of tree you will manually have to add the symbols while specifying the base-address of where the module is loaded at:

lx-lsmod
add-symbol-file </path/to/module.ko> <module base address>

Alternatively, you can get full single-step coverage leading up the sink point using the forkvm, rwmem and stepper tools that accompany kfx.

forkvm <parent domid>
rwmem --domid <vm fork domid> --write 0xffff8880334652b0 --file output/crashes/id\:000000\,sig\:06\,src\:000008\,op\:int16\,pos\:13\,val\:+128 --limit 16
stepper --domid <vm fork domid> --limit 100000 --stop-on-address <sink address> > stepper.log
cat stepper.log | awk '{ print $2 }' | addr2line -e vmlinux -f -p

In the above snipped we manually created a fork VM from the parent and then wrote the crash-causing input into the target buffer. These are exactly the steps kfx performs when it performs fuzzing as well. The stepper tool enable singlestepping of the entire VM and runs until limit number of instructions have been executed or the CPU reaches an instruction specified in stop-on-address. Here you want to specify the sink's address that you know will be reached by this execution from the above step when we ran kfx with --debug. The stepper output simply logs each instructions that was executed, so we store that log in a file. As the last step, we just look up each address in the kernel's debug image using addr2line the get the exact function name and source line that was executed. This is often more accurate to pinpoint the crashing code-site then a stack backtrace would be.

Offline debugging

You can use the included capture-vmcore tool to capture a vmcore from the forked VM. Some prerequisites for this to work:

  • An arbitrary kdump kernel must be loaded prior to running kfx. This can be done by adding a crashkernel=128M to the kernel cmdline and running kexec -a -p /boot/vmlinux --reuse-cmdline from userspace.
  • The sink point must (forcibly) result in a kernel crash. I.e unpausing the VM at the sink point should result in a kdump kexec attempt.

Capture, compress, and analyze the vmcore using:

capture-vmcore --domid <vm fork domid> --json 5.4.0.json --out /tmp/vmcore
makedumpfile -c -d 31 /tmp/vmcore -x vmlinux /tmp/dumpfile
crash vmlinux /tmp/dumpfile 

22. Advanced harnessing


In case you want to add more then one harness to your target code, you can use the extended harness type as your start harness:

static inline void harness_extended(unsigned int magic, void *a, size_t s)
{
    asm volatile ("cpuid"
                  : "=a" (magic), "=c" (magic), "=S" (magic)
                  : "a" (magic), "c" (s), "S" (a)
                  : "bx", "dx");
}

This allows you to use any CPUID as your start marker so you can differentiate between them when running --setup with the --magic-mark <magic> option. For the end harness you will still have to use 0x13371337 as the magic CPUID.

To also transfer extra information about the target memory and size, during the --setup step add -c so that kfx will know that extra information is transfered via the CPUID instruction. This also eliminates the need for adding any printks the your target and copying it from the console. The reason why we use RSI ("S") above instead of RBX or RDX is because Xen clobbers the registers used by CPUID before we have a chance to see them (only the lower 32-bits of RAX and RCX will be visible to kfx). Feel free to place extra information into other general purpose registers as needed, you will be able to examine them by running xen-hvmctx <domainid>.

Subsequently, you can avoid having to pass the input address and limit to the fuzzing step by again using -c. This will retrieve harness information that was stashed to VM CPU registers by the setup phase. Note that both conditions are necessary - using an extended harness in code AND running the setup step with -c.

You can also use software breakpoints (0xCC) as your harness which can be placed by standard debuggers like GDB. Use --harness-type breakpoint for this mode, which is particularly useful when you don't have access to the target's source-code to compile it with the CPUID-based harness. You will need to determine the start byte of the harness that was overwritten by the breakpoint and specify that to kfx with --start-byte <byte>.

23. Coverage info


Often times it is necessary to understand what code the fuzzer is exercising and discovers in order to find additional sink points of interest. By specifying --record-codecov <filename> on the KF/x command-line it will keep track of all instruction pointers that were discovered across all fuzzing iterations. By issuing signal 10 (kill -10 <pid>) to the KF/x process this information will be saved into a the filename specified. The same information is also saved when the KF/x process exits.

24. FAQ


Can I run this on ring3 applications?

You likely get better performance if you run AFL natively on a ring3 application but nothing prevents you from running it via this tool. You would need to adjust the sink points in src/sink.h to catch the crash handlers that are called for ring3 apps. For example do_trap_error in Linux handles segfaults, you would probably want to catch that.

Can I fuzz Windows?

This tool currently only targets Linux. You can modify the harness to target Windows or any other operating system by adjusting the sink points in src/sink.h that are used to catch a crash condition. You could also manually define the sink points' addresses in case the operating system is not supported by LibVMI. In case you want to fuzz closed-source portions of Windows where you can't inject the cpuid-based harness, you can use --harness breakpoint to switch to using breakpoints as your harness. This allows you to mark the code-region to fuzz with a standard debugger like WinDBG. You will find additional information in the Wiki

Can I just pipe /dev/random in as fuzzing input?

Yes! You can use --loopmode to simply read input from whatever source you want and pipe it into the VM forks. In this mode coverage trace is disabled so you will see more iterations per second.

How do I shutdown the VM after I'm done?

You can issue xl shutdown <domain name> to initiate shutdown. If there are VM forks active, you need to issue xl destroy <domain id> for each fork before shutdown.

Any tricks to increase performance?

To max out performance you can boot Xen with "dom0_max_vcpus=2 sched=null spec-ctrl=no-xen" which assigns only 2 vCPUs to dom0, disables the scheduler and speculative execution hardening features. You can also add "smt=0" to disable hyper-threading. Make sure your system has enough physical cores to run each vCPU as they get pinned.

Is it possible to run the tool nested?

Yes, it has been tested running on top of VMware Workstation. In the VMware VM's CPU settings make sure to enable the "Virtualize Intel VT-x/EPT" option. Performance will be lower as compared to running it directly on the hardware.


*Other names and brands may be claimed as the property of others

kernel-fuzzer-for-xen-project's People

Contributors

aashays avatar andyhhp avatar arkivm avatar clupuishere avatar deyixtan avatar qazwsxedcrfvtg14 avatar ranok avatar tklengyel avatar v-p-b 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

kernel-fuzzer-for-xen-project's Issues

Error building Xen

I was following the instruction in the README for installing Xen on Ubuntu 20.04. Running make for the target dist-tools produces the following error:

make[6]: *** No rule to make target '/home/jay/kernel-fuzzer-for-xen-project/xen/tools/libs/light/../../../tools/libacpi/build.c', needed by 'build.o'.  Stop.
make[6]: *** Waiting for unfinished jobs....

I looked through some of the Makefiles but I could not point out what the issue was.

I'd appreciate your help in fixing this issue.

Handling 0 length inputs (and/or improve IPC)

inject_input() reports failure when input length is 0:

https://github.com/intel/kernel-fuzzer-for-xen-project/blob/master/src/main.c#L25

As a result, afl_report() is not called, and AFL assumes that calibration failed (well, it kind of did), terminating the setup.

IMO a 0 length input can be valid, so inject_input() could just exit early with success. I'm not familiar enough with the code yet to decide if failed input injections should terminate the fuzzing process or the harness should just report back to AFL like nothing happened, but this part may need some improvement too.

[FAQ request] How my target runs exactly? What rules are there for harnessing?

I think FAQ lacks some technical info and rules for placing harness function (cpuid).

1.a) Does kfx make new VM fork every single harness-to-harness loop or does it manipulate registers to make such a loop (like WinAFL does)?

1.b) If new VM fork is made for each loop, I don't need to restore state, right?

2.a) Is it OK to add harness inside of tested function itself? I can't call my tested function directly (and harness it as in tutorial), because it's a callback function, so I don't even know all the places from which it's getting called. (i know i can also wrap the whole function inside of another one but i'm affraid to mess up when dealing with huge portions of code in complex systems)

2.b) What if I have multiple returns from such a function with harness inside? Do I just insert call to harness instead of each return statement? Or is it absolutely required to wrap my tested code as a function and put exactly two calls to harness?

3.a) Can I put harness calls in completely different modules? The first harness would be in module X, which receives data (e.g. from the procfs), the second harness call would be after the data have been parsed in module Y.

3.b) Can I put harness in both usemode binary and in kernel module? E.g. usermode application receives data from TCP socket, parses it somehow, then sends parsed result to kernel module (ioctl, procfs, etc.), where I expect my module to crash.

3.c) Same as in previous question, but what if data flows from kernel module to usermode application and I expect both module and app to crash? (both binary and module do some parsing and i'd like to catch bugs in both)

Thanks in advance!

Report crash when tracing fails

I managed to catch exceptions on Windows without providing the KPGD address to the fuzzer with #10. This is probably because the exception handler address is still mapped to the VA space of the user-mode process.

I had to tweak my test program though: instead of doing a call to 0x0, now I trigger a write access violation. This was important, because in the original setup, the tracer failed to follow the branch to the unmapped address. This results in a tracing error without registering a crash.

I think the correct behavior would be to set the crash flag in these cases, since trying to branch to an invalid address is a scenario worth reporting.

Unable to request new process from fork server (OOM?)

Hi there, i'm sangjun who very interested in this project.

I'm trying to fuzz windows Device driver with xen-fuzz.
following this url.
https://github.com/intel/kernel-fuzzer-for-xen-project/wiki/Fuzzing-Windows
https://youtu.be/2M9U9mXVRrk?t=247

But, there is just one problem in last step. ( afl-fuzz OOM)

bu sioctl!SioctlDeviceControl

./kfx --setup --domain windows-1 --harness breakpoint --start-byte 0x48


root@b:/home/b/kernel-fuzzer-for-xen-project# xl list
Name                                        ID   Mem VCPUs      State   Time(s)
Domain-0                                     0  6096    20     r-----    3291.4
windows-2                                    8  8192     1     ------    1134.7
windows-1                                   10  8192     1     --p---     235.7


root@b:/home/b/kernel-fuzzer-for-xen-project# xen-hvmctx 10 |grep rdx
            rcx 0xffffcc81fbff42c0     rdx 0xffffcc81f6134c30

root@b:/home/b/kernel-fuzzer-for-xen-project# ./rwmem --domid 10 --read 0xffffcc81f6134c48 --file irp --limit 8
Init vmi, init_events: 0 init_paging 1 domain (null) domid 10 json (null) kvmi (null)
Read operation success: 8 bytes from 0xffffcc81f6134c48

root@b:/home/b/kernel-fuzzer-for-xen-project# xxd -e irp | awk '{ print "0x" $3 $2}'
0xffffcc81f4608c00

root@b:/home/b/kernel-fuzzer-for-xen-project# ./rwmem --domid 10 --read 0xffffcc81f4608c00 --file string --limit 8
Init vmi, init_events: 0 init_paging 1 domain (null) domid 10 json (null) kvmi (null)
Read operation success: 8 bytes from 0xffffcc81f4608c00
root@b:/home/b/kernel-fuzzer-for-xen-project# xxd string
00000000: 5468 6973 2053 7472                      This Str
root@b:/home/b/kernel-fuzzer-for-xen-project#

root@b:/home/b/kernel-fuzzer-for-xen-project# xen-hvmctx 10 |grep rsp
            rdi 0xffffcc81f6134c30     rsp 0xfffffc073446f7f8
root@b:/home/b/kernel-fuzzer-for-xen-project# ./rwmem --domid 10 --read 0xfffffc073446f7f8 --file ret --limit 8
Init vmi, init_events: 0 init_paging 1 domain (null) domid 10 json (null) kvmi (null)
Read operation success: 8 bytes from 0xfffffc073446f7f8
root@b:/home/b/kernel-fuzzer-for-xen-project# xxd -e ret | awk '{ print "0x" $3 $2}'
0xfffff80351611385

root@b:/home/b/kernel-fuzzer-for-xen-project# xxd cc
00000000: cc                                       .

root@b:/home/b/kernel-fuzzer-for-xen-project# ./rwmem --domid 10 --write 0xfffff80351611385 --file cc --limit 1
Init vmi, init_events: 0 init_paging 1 domain (null) domid 10 json (null) kvmi (null)
Write operation success: 1 bytes to 0xfffff80351611385


//irp.systembuffer -> 0xffffcc81f4608c00
root@b:/home/b/kernel-fuzzer-for-xen-project# ./AFL/afl-fuzz -i input -o output -X -m none -- ./kfx --harness breakpoint --start-byte 0x48 --domain windows-1 --json /home/b/Downloads/volatility3-2.5.0/windows10.json --address 0xffffcc81f4608c00 --input @@ --input-limit 8 --sink KiDispatchException --ptcov

Screenshot from 2023-11-05 14-31-48

Screenshot from 2023-11-05 14-28-16

I thinks dom0 can't afford to get vm fork memory. ( I guess so )
but that is not make sense.
because i use 84G RAM. and in your youtube, the dom0 use 8G too.
Screenshot from 2023-11-05 14-34-33

Is there any idea?

Unable to request new process from fork server (OOM?)

Hi,

When I test the fuzzer, sometimes the fuzzed process (kfx) returned directly, and AFL will show error Unable to request new process from fork server (OOM?)

but when I comment out the code sanity check of the first input file (

input_file = fopen(input_path,"r"); // Sanity check
to
fclose(input_file); // Closing for now, will reopen when needed
) , the program goes well then.

It seems because when sanity check happens, AFL hasn't create the testcase .cur_input yet.

Network setting problem

Hi,

My system is ubuntu 20.04 ,and I follow the "5. Setup networking" to setup the network for vm, but still can't get to the internet.

details:
after I run "sudo netplan apply"

"ifconfig" shows like this :

enp4s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.1.4  netmask 255.255.255.0  broadcast 192.168.1.255
        inet6 abbb:xxxx:xxxx:abbb:fdxx:5xx4:abbb:aad8  prefixlen 64  scopeid 0x0<global>
        inet6 xxxx::e1ef:xxxx:xxxx:abbb  prefixlen 64  scopeid 0x20<link>
        inet6 xxxx:xxxx:18dd:xxxx:xxxx:xxxx:xxxx  prefixlen 64  scopeid 0x0<global>
        ether xx:2f:xx:xx:1x txqueuelen 1000  (Ethernet)
        RX packets 2080  bytes 1577412 (1.5 MB)
        RX errors 0  dropped 24  overruns 0  frame 0
        TX packets 1920  bytes 306404 (306.4 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 4772  bytes 1296112 (1.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 4772  bytes 1296112 (1.2 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

xenbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.0.1  netmask 255.255.255.0  broadcast 10.0.0.255
        inet6 fxxx::1xxx:8xxf:fxx:c7xx prefixlen 64  scopeid 0x20<link>
        ether xx:2x:0x:3x:x  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 17  bytes 1978 (1.9 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

and I run "sudo iptables -t nat -A POSTROUTING -o enp4s0 -j MASQUERADE" to enable the iptables.
and "sudo xl create debian.cfg" to install the debian.

when running a vm, "ifconfig" shows a new netcard:

vif1.0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        ether fe:ff:ff:ff:ff:ff  txqueuelen 32  (Ethernet)
        RX packets 53  bytes 3438 (3.4 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 40  bytes 4510 (4.5 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

and "brctl show" shows:

bridge name	bridge id		STP enabled	interfaces
xenbr0		8000.feffffffffff	no		vif1.0

during the installation , I configured the network manually to 10.0.0.2/24 with a default gateway and name server which is 10.0.0.1.
And during the installation will show "can't connect to mirror", and can't get to the internet after booting the vm system. ( ping 10.0.0.1 is fine.)

Where's the problem, thank you !

VM Network issue

Hello! I'm interested in your project, But there is some issue...

I finished to step 6, but the vm doesn't have network

i Try to add nameservers in /etc/netplan/02-xenbr0.yaml,

root@b:~# cat /etc/netplan/02-xenbr0.yaml network: version: 2 renderer: networkd bridges: xenbr0: dhcp4: no addresses: [ 192.168.2.200/24 ] gateway4: 192.168.1.1 nameservers: addresses: [8.8.8.8]

like this, but vm can't connect to internet

And my environment like this

`
OS : Ubuntu 20.04
network environment:

root@b:~# ifconfig
enp4s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.47 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 fe80::3f91:fee5:7668:56e4 prefixlen 64 scopeid 0x20
ether 04:7c:16:16:95:19 txqueuelen 1000 (Ethernet)
RX packets 3142668 bytes 4723579072 (4.7 GB)
RX errors 0 dropped 9 overruns 0 frame 0
TX packets 803889 bytes 57822615 (57.8 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10
loop txqueuelen 1000 (Local Loopback)
RX packets 29295 bytes 22806428 (22.8 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 29295 bytes 22806428 (22.8 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

vif4.0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
ether fe:ff:ff:ff:ff:ff txqueuelen 32 (Ethernet)
RX packets 96 bytes 13446 (13.4 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2 bytes 260 (260.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

wlo1: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
ether 8c:b8:7e:96:c7:1f txqueuelen 1000 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

xenbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.2.200 netmask 255.255.255.0 broadcast 192.168.2.255
inet6 fe80::a852:f3ff:fe11:2204 prefixlen 64 scopeid 0x20
ether fe:ff:ff:ff:ff:ff txqueuelen 1000 (Ethernet)
RX packets 311 bytes 44592 (44.5 KB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 103 bytes 14821 (14.8 KB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
`

Lastly, I try to iptables -t nat -A POSTROUTING -o enp4s0 -j MASQUERADE, but that command can't help...

What can i do in like this situation?? Can you give me the advise??

Reopen file for new input

Currently get_input() just keeps reading the same file descriptor, that can only supply finite amount of data (see #6). I think the harness should seek to position 0 on new input (and this is somehow confirmed by my tests), but I'm not quite sure how exactly Linux handles fd's when the underlying contents are overwritten.

VM network settings failed

Hi, i'm sangjun who interested in Xen-fuzz.

Now, i'm settings fuzz evironment to use this project.

But, i stucked with VM network settings.

i follow this command. but in Vm machine, there is no network connected.

iptables -t nat -A POSTROUTING -o enp4s0 -j MASQUERADE

Can you tell more detail about network settings??

I would really appreciate it if you could write more kindly about vm network settings.

Thanks

Performance monitoring

What is the recommended way to monitor the performance of the (physical) CPU's while running KF/x?

top in dom0 only sees the vCPU's of dom0.

As far as I can tell xentop can't provide meaningful information, because the (forked) domains (dis)appear too fast.

Paging mode not set during harness setup

I'm harnessing a 32-bit Windows userland target using breakpoints. Restoring the "start byte" doesn't work, because vmi_write_8() can't translate the virtual address, so the setup fails.

I placed some debug prints in libvmi and found that vmi->arch_interface not being set caused the lookup failure:

https://github.com/libvmi/libvmi/blob/master/libvmi/write.c#L102

I set the last parameter of setup_vmi(...,init_pm) to true - now the restoration seems to work, but I'm not sure what's the proper fix for this:

https://github.com/intel/kernel-fuzzer-for-xen-project/blob/master/src/setup.c#L113

(I'm using an older symbol JSON, but as far as I can tell the paging mode is determined based on VCPU info, so I guess this shouldn't be a problem.)

Error building Xen dist-tools on Debian

I'm trying to follow the README to set this up on Debian 10. The dist-tools make target for Xen fails with the following error:

In file included from /home/b/kernel-fuzzer-for-xen-project/xen/tools/qemu-xen-dir/include/qemu/timer.h:4:0,                                                                                                                                  
                 from /home/b/kernel-fuzzer-for-xen-project/xen/tools/qemu-xen-dir/include/qemu/timed-average.h:29,                                                                                                                           
                 from /home/b/kernel-fuzzer-for-xen-project/xen/tools/qemu-xen-dir/include/block/accounting.h:28,                                                                                                                             
                 from /home/b/kernel-fuzzer-for-xen-project/xen/tools/qemu-xen-dir/include/block/block_int.h:27,                                                                                                                              
                 from /home/b/kernel-fuzzer-for-xen-project/xen/tools/qemu-xen-dir/block/file-posix.c:30:                                                                                                                                     
/usr/include/linux/swab.h: In function ‘__swab’:                                                                                                                                                                                              
/home/b/kernel-fuzzer-for-xen-project/xen/tools/qemu-xen-dir/include/qemu/bitops.h:20:34: error: "sizeof" is not defined, evaluates to 0 [-Werror=undef]                                                                                      
 #define BITS_PER_LONG           (sizeof (unsigned long) * BITS_PER_BYTE)                                                                                                                                                                     
                                  ^                                                                                                                                                                                                           
/home/b/kernel-fuzzer-for-xen-project/xen/tools/qemu-xen-dir/include/qemu/bitops.h:20:41: error: missing binary operator before token "("                                                                                                     
 #define BITS_PER_LONG           (sizeof (unsigned long) * BITS_PER_BYTE)                                                                                                                                                                     
                                         ^                                                                                                                                                                                                    
cc1: all warnings being treated as errors                                                                                                                                                                                                     
make: *** [/home/b/kernel-fuzzer-for-xen-project/xen/tools/qemu-xen-dir/rules.mak:69: block/file-posix.o] Error 1                                                              

I think this may be the same problem described here:

https://www.mail-archive.com/[email protected]/msg702900.html

Edit: here are the (seemingly) relevant Debian and Ubuntu tickets:

Can you advice on how to resolve this issue?

Scaling

Hi,

Thanks for this great project !

I have some issues with scaling properly, when using multiple connected AFL fuzzers.
Maybe you have some suggestions on how to reduce bottlenecks ?

On my test case, with a 4-core with HT (so 8 logical cores), and intel pt coverage

1 core =~1500 exec/s
4 core =~2500 exec/s

I tried to play with affinity and CPU pinning, but it doesn't significantly change the performance.
I know I should not expect linear scaling, but maybe there are xen/other knobs I can tweak to reduce the gap ?

AFL++ communication error - Pipe read returns 0

I ran into an annoying non-deterministic bug when using KF/x with AFL++. I traced back the issue to the fact that sometimes the pipe read by AFL++ here returns 0 (but not -1, that would indicate an error):

https://github.com/AFLplusplus/AFLplusplus/blob/stable/src/afl-forkserver.c#L172

Retrying here instead of returning doesn't improve the situation, because all subsequent reads also return 0.

This results in read_s32_timed returning 0, that ultimately ends up killing the setup phase with the well known "Unable to communicate with fork server" message:

https://github.com/AFLplusplus/AFLplusplus/blob/stable/src/afl-forkserver.c#L191

https://github.com/AFLplusplus/AFLplusplus/blob/stable/src/afl-forkserver.c#L1299

I'm not entirely sure which write this should be on KF/x's side, but I could confirm, that afl_wait successfully writes the 4 expected bytes to the pipe. As far as I understand, the next communication should be by afl_report, so I suspect that something must go wrong between these two lines:

https://github.com/intel/kernel-fuzzer-for-xen-project/blob/master/src/main.c#L93

https://github.com/intel/kernel-fuzzer-for-xen-project/blob/master/src/main.c#L123

Interestingly, it feels that running KF/x improves the success rate, but I don't have the data to support this.

Tagging in @domenukk, hoping he can shed some light on the expected behavior at AFL++ side.

Error installing debian iso

Is there a recommended debian iso?
I tried https://cdimage.debian.org/debian-cd/current/amd64/iso-cd/
More of an issue with vnc viewer than xen.
At the vnc step 6 vncviewer localhost
the screen(doesn't fit the buffer) shows up and i can move the arrows keys but when i select an install option it crashes with:

Connected to RFB server, using protocol version 3.8
Performing standard VNC authentication
Password: 
Authentication successful
Desktop name "QEMU (debian)"
VNC server default format:
  32 bits per pixel.
  Least significant byte first in each pixel.
  True colour: max red 255 green 255 blue 255, shift red 16 green 8 blue 0
Warning: Cannot convert string "-*-helvetica-bold-r-*-*-16-*-*-*-*-*-*-*" to type FontStruct
Using default colormap which is TrueColor.  Pixel format:
  32 bits per pixel.
  Least significant byte first in each pixel.
  True colour: max red 255 green 255 blue 255, shift red 16 green 8 blue 0
Same machine: preferring raw encoding
Rect too large: 720x480 at (0, 0)

Fail to fork xen domain when running on AMD processor

First of all thank you for your awesome work!

I followed your instructions step by step with Ubuntu 18.04 LTS (kernel 5.4.0-42-generic) as Xen host (dom0) and Ubuntu 18.04 LTS (kernel 5.4.0-42-generic) as Xen guest (domU). Xen host is itself an ESXi virtual machine with both Hardware Virtualization and IOMMU enabled.
xen-detect returns this: "Running in PV context on Xen V4.14."

Steps 1-17 of instructions work fine, but when I start AFL, I get the following result (fork server handshake failed):
image

When trying --loopmode (instead of AFL) kfx fails with message "Parent is ready" followed by "Domain fork failed".
When trying to manually issue ./forkvm 32 (where 32 is domid of Xen guest paused with kfx --setup), it also fails with message "Forking VM 32 failed", I have even added some printfs in src/forkvm.c file and found out that failing function is xc_memshr_fork that seems to be the issue.

Any ideas how to fix that? Does it have to run in hardware virtualization mode? If so, how could I troubleshoot Xen host not using HV provided by ESXi?

Saving and restoring setup state: Device model spawn failed

Saving and restoring already setup domains is a huge productivity boost, that as far as I remember worked great a couple months back. Now when I want to save/restore domains like this:

# xl save -p target_domain target_setup.sav
# xl destroy target_domain 
# xl restore -p target_setup.sav

... I get an error similar to the following:

Loading new save file target_setup.sav (new xl fmt info 0x3/0x0/1579)
 Savefile contains xl domain config in JSON format
Parsing config from <saved>
xc: info: Found x86 HVM domain from Xen 4.15
xc: info: Restoring domain
xc: info: Restore successful
xc: info: XenStore: mfn 0xfeffc, dom 0, evt 1
xc: info: Console: mfn 0xfefff, dom 0, evt 2
libxl: error: libxl_dm.c:3102:device_model_spawn_outcome: Domain 1:domain 1 device model: spawn failed (rc=-3)
libxl: error: libxl_dm.c:3318:device_model_postconfig_done: Domain 1:Post DM startup configs failed, rc=-3
libxl: error: libxl_create.c:1833:domcreate_devmodel_started: Domain 1:device model did not start: -3
libxl: error: libxl_aoutils.c:646:libxl__kill_xs_path: Device Model already exited
libxl: error: libxl_domain.c:1182:libxl__destroy_domid: Domain 1:Non-existant domain
libxl: error: libxl_domain.c:1136:domain_destroy_callback: Domain 1:Unable to destroy guest
libxl: error: libxl_domain.c:1063:domain_destroy_cb: Domain 1:Destruction of domain failed

How can I save/restore domains after kfx --setup ...?

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.