Skip to content

Commit

Permalink
Merge pull request #250 from IntelLabs/docs/linux_dvkm_tutorial
Browse files Browse the repository at this point in the history
Docs: add linux dvkm tutorial
  • Loading branch information
Wenzel authored Oct 31, 2023
2 parents 964ada1 + 3b9f7e1 commit 9b64769
Show file tree
Hide file tree
Showing 15 changed files with 1,516 additions and 8 deletions.
6 changes: 3 additions & 3 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
furo==2023.5.20
furo==2023.9.10
GitPython==3.1.37
myst-parser==2.0.0
Sphinx==6.2.1
Sphinx==7.2.6
sphinx-copybutton==0.5.2
sphinx_design==0.4.1
sphinx_design==0.5.0
sphinxcontrib-mermaid==0.9.2
2 changes: 1 addition & 1 deletion docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ The project is structured around multiple components:
tutorials/introduction
tutorials/installation
tutorials/concepts
tutorials/fuzzing_linux_kernel
tutorials/linux/index
tutorials/windows/index
```

Expand Down
2 changes: 1 addition & 1 deletion docs/source/reference/fuzzer_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -671,7 +671,7 @@ Enable verbose output by setting Python [`logging`](https://docs.python.org/3/li

This option is also useful to dump and debug kafl configuration at load-time.

Example with [Fuzzing the Linux kernel](../tutorials/fuzzing_linux_kernel.md) tutorial, if `--verbose` switch were to be added to the command line:
Example with [Fuzzing the Linux kernel](../tutorials/linux/fuzzing_linux_kernel.md) tutorial, if `--verbose` switch were to be added to the command line:

First kAFL will dump the list of loaded configuration files.
Check the [load order](#configuration-sources-and-precedence)
Expand Down
2 changes: 1 addition & 1 deletion docs/source/tutorials/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ kAFL_hypercall(KAFL_HYPERCALL_RELEASE);

Now you are ready to configure one of our pre-baked kAFL targets, and start the fuzzer !

- ➡️ Continue by [fuzzing the Linux Kernel](./fuzzing_linux_kernel.md)
- ➡️ Continue by [fuzzing Linux targets](././linux/index.md)
- ➡️ Continue by [fuzzing Windows programs](./windows/index.md)
1 change: 0 additions & 1 deletion docs/source/tutorials/gui.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@ You should see 4 fields:
- `AddSan`: Executions returning with KASAN hypercall
- `Timeout`: Executions intercepted by QEMU timeout
- `Regular`: Executions returning with RELEASE hypercall

2 changes: 1 addition & 1 deletion docs/source/tutorials/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,5 +201,5 @@ The complete documentation regarding kAFL's installation is available at [refere
Now you are ready to configure one of our pre-baked kAFL targets, and start the fuzzer !
- ➡️ Continue by [fuzzing the Linux Kernel](./fuzzing_linux_kernel.md)
- ➡️ Continue by [fuzzing on Linux](./linux/index.md)
- ➡️ Continue by [fuzzing Windows programs](./windows/index.md)
201 changes: 201 additions & 0 deletions docs/source/tutorials/linux/dvkm/agent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# 3 - Building the agent

This section guides you through the process of implementing a kAFL agent, which includes both the harness and the specifics tailored for our Linux target.

## Agent protocol

The implementation of a kAFL agent can be broadly categorized into two main components:


1. Initialization
2. Harness

### Initialization

The initialization phase of the agent is responsible for:

- Configuring the agent settings to optimize fuzzing behavior.
- Mapping the payload buffer, which is shared among the Fuzzer, QEMU, and the VM.
- Setting Intel PT filters to enhance coverage precision and execution speed.
- Registering crash handlers to notify the fuzzer of target crashes and defining crash criteria.
- Specifying IP ranges to inform the fuzzer about Intel PT ranges, crucial for obtaining accurate coverage.

```{mermaid}
graph TD
subgraph Initialization Protocol
handshake["1. kAFL handshake"] --> hostconfig["2. Query host config"] --> guestconfig["3. Set guest agent config"]
guestconfig --> allocate["4. Allocate payload buffer"] --> mmap["5. Map payload buffer"] --> crash["6. Submit crash handlers"]
crash --> ranges["7. Submit Intel PT ranges"] -.-> cr3["8. Submit CR3"]
end
```

:::{note}
This protocol serves as a reference and is not strictly mandated.

However, Certain hypercalls must be executed in sequence.

Example: [`GET_HOST_CONFIG`](../../../reference/hypercall_api.md#get_host_config) before [`SET_AGENT_CONFIG`](../../../reference/hypercall_api.md#set_agent_config)
:::

The corresponding hypercalls to use:

1. Handshake: [`ACQUIRE` and `RELEASE`](../../../reference/hypercall_api.md#acquire--release)
2. Query host config: [`GET_HOST_CONFIG`](../../../reference/hypercall_api.md#get_host_config)
3. Set agent config: [`SET_AGENT_CONFIG`](../../../reference/hypercall_api.md#set_agent_config)
4. Map payload buffer: [`GET_PAYLOAD`](../../../reference/hypercall_api.md#get_payload)
5. Submit crash handlers: [`SUBMIT_PANIC` and `SUBMIT_KASAN`](../../../reference/hypercall_api.md#submit_panic--submit_kasan)
6. Submit Intel PT filters: [`RANGE_SUBMIT`](../../../reference/hypercall_api.md#range_submit)
7. Submit CR3 (optional): [`SUBMIT_CR3`](../../../reference/hypercall_api.md#submit_cr3)

:::{note}
For step 4 (Allocate payload buffer), there is no hypercall involved.

But please note that the payload buffer should be page-aligned.
:::

### Harness

After the agent initialization is complete, the harness logic comes into play.

The harness performs the following sequence of operations:

1. Write the next payload in the buffer: [`NEXT_PAYLOAD`](../../../reference/hypercall_api.md#next_payload)
2. Start Intel PT coverage: [`ACQUIRE`](../../../reference/hypercall_api.md#acquire--release)
3. Call target function
4. End Intel PT coverage, restore the guest snapshot: [`RELEASE`](../../../reference/hypercall_api.md#acquire--release)

:::{code-block} C
// 🔁 restore the snapshot and write next payload into the buffer
// (take a snapshot on first call)
kAFL_hypercall(HYPERCALL_KAFL_NEXT_PAYLOAD, 0);
// 🟢 start coverage feedback collection
kAFL_hypercall(HYPERCALL_KAFL_ACQUIRE, 0);
// ⚡ call fuzz target with the buffer
target_entry(payload_buffer->data, payload_buffer->size);
// ⚪ stop coverage feedback collection
kAFL_hypercall(HYPERCALL_KAFL_RELEASE, 0);
:::

## DVKM target

### Kernel crash

To handle kernel crashes with the kAFL agent for DVKM targets, we need to locate the Linux kernel crash routine and insert a PANIC hypercall at the relevant point.

Checkout the {octicon}`git-branch`[`agent_tutorial`](https://github.com/IntelLabs/kafl.linux/tree/agent_tutorial) branch of the [`kafl.linux`](https://github.com/IntelLabs/kafl.linux) repository.

It consists of 4 commits:

```{mermaid}
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'agent_tutorial'}} }%%
gitGraph
commit id: "arch/x86/include: add nyx_api.h"
commit id: "printk: replace _printk impl by kAFL hprintf"
commit id: "panic: insert kAFL PANIC hypercall in oops_exit"
commit id: "kasan: insert kAFL KASAN hypercall in kasan_report"
```

1. Adds the `nyx_api.h` hypercall header to the Linux sources.
2. Replaces the kernel's printk implementation by our own own implementation based on HPRINTF hypercall.
3. Inserts a PANIC hypercall in the `oops_exit` function of the kernel, invoked during the crash handling sequence.
4. Inserts a KASAN hypercall in the `kasan_report` function of the kernel. Further discussion on enabling KASAN will appear in the tutorial's [improvements](./improvements.md) section.


:::{code-block} c
---
caption: Altered `oops_exit` handler
linenos: yes
emphasize-lines: 11
---
/*
* Called when the architecture exits its oops handler, after printing
* everything.
*/
void oops_exit(void)
{
do_oops_enter_exit();
print_oops_end_marker();
kmsg_dump(KMSG_DUMP_OOPS);

kAFL_hypercall(HYPERCALL_KAFL_PANIC, 0);
}
:::

### Initialization

The kAFL agent's remaining code resides in userland, within [`test_dvkm.c`](https://github.com/Wenzel/Damn_Vulnerable_Kernel_Module/blob/kafl/test_dvkm.c)

Keys points:

The payload buffer is page-aligned using [`aligned_alloc`](https://linux.die.net/man/3/aligned_alloc).

:::{code-block} C
---
caption: Payload buffer paged-aligned allocation
---
kAFL_payload* payload_buffer = aligned_alloc((size_t)sysconf(_SC_PAGESIZE), host_config.payload_buffer_size);
:::

We ensure that the payload is in resident memory with [`mlock()`](https://man7.org/linux/man-pages/man2/mlock.2.html)

:::{code-block} C
---
caption: Locking payload buffer in resident memory
---
mlock(payload_buffer, host_config.payload_buffer_size);
:::

The IP ranges are identified by parsing `/proc/modules`, for the `dvkm` module

:::{code-block} C
---
caption: Detecting IP ranges for dvkm module
---
detectranges("/proc/modules", "dvkm");
...
static int detectranges(char *mapfile, char *pattern) {
// dvkm 24576 0 - Live 0xffffffffc0201000 (O)
ret = sscanf(line, "%s %lu %d - %s %lx", module_name, &module_size, &instances_loaded, load_state, &kernel_offset);
}
:::


### Harness

The harness is constructed around the `ioctl()` function call:

:::{code-block} C
---
caption: DVKM Harness
---
kAFL_hypercall(HYPERCALL_KAFL_NEXT_PAYLOAD, 0);
// prepare ioctl code and io_buffer struct range[0-0xC]
ioctl_code = payload_buffer->data[0] % 0xD;
ioctl_num = IOCTL(ioctl_code);
// write width, height and datasize
size_t write_size = sizeof(struct dvkm_obj) - sizeof(io_buffer.data);
memcpy((void*)&io_buffer, &payload_buffer[1], write_size);
// assign rest of payload_buffer to io_buffer.data
io_buffer.data = (char*)&payload_buffer->data[write_size+1];
// struct is now ready
kAFL_hypercall(HYPERCALL_KAFL_ACQUIRE, 0);
ioctl(fd, ioctl_num, &io_buffer);
kAFL_hypercall(HYPERCALL_KAFL_RELEASE, 0);
:::

- the `ioctl_code` is generated from the first payload byte, and modulo `0xD` ensures a valid IOCTL.
- `write_size` is calculated to only write the relevant fields (`width`, `height` and `datasize`) of the payload buffer.
- the remaining payload buffer fills the data pointer field.

For the `dvkm.c` module, we've limited the `INFO()` printk format strings to prevent output congestion during fuzzing:

:::{code-block} C
int Use_after_free_IOCTL_Handler(struct dvkm_obj *io)
{
INFO("[+] data: %.50s\n", kernel_data_buffer);
}
:::

Congratulations! You now have a comprehensive understanding of the kAFL agent tailored for the DVKM target.

Proceed to the next section to commence your fuzzing campaign.
73 changes: 73 additions & 0 deletions docs/source/tutorials/linux/dvkm/fuzzing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# 4 - Fuzzing campaign

## Running `kafl fuzz`

With all configurations and dependencies set, you're ready to commence the fuzzing campaign.

You can review the [`kafl.yaml`](https://github.com/IntelLabs/kafl.targets/blob/master/linux-user/dvkm/kafl.yaml) config file, where the `sharedir`, `qemu_kernel`, `qemu_initrd` and `qemu_append` parameters have already been configured:

:::{code-block} yaml
# exposing host files through "sharedir" interface
sharedir: '@format {env[PWD]}/sharedir'

# additional kAFL configuration
qemu_kernel: '@format {env[EXAMPLES_ROOT]}/linux-user/linux_kafl_agent/arch/x86/boot/bzImage'
qemu_initrd: '@format {env[EXAMPLES_ROOT]}/linux-user/scripts/kafl_initrd.cpio.gz'

# use hprintf=7 for full printk verbosity
qemu_append: root=/dev/vda1 rw nokaslr oops=panic nopti mitigations=off console=ttyS0 earlyprintk=serial,ttyS0 ignore_loglevel
:::

Ensure you are running inside the [kAFL virtualenv](../../installation.md#4-setting-kafl-environment--make-env).

To start fuzzing, run the following `kafl fuzz` command:

~~~shell
cd kafl/examples/linux-user/dvkm
(venv) $ kafl fuzz --purge --log-crashes
~~~

- [`--purge`](../../../reference/fuzzer_configuration.md#purge): removes the `$KAFL_WORKDIR` directory if it already exists before starting the new campaign.
- [`--log-crashes`](../../../reference/fuzzer_configuration.md#log_crashes): redirect hprintf log message to a log file, and to `$KAFL_WORKDIR/logs/` for any new found crashing or timeout payload.

:::{note}
You can increase the fuzzing speed by dedicating more processes to kAFL.

The default value is `1`, which means that 1 QEMU instance will be launched and fuzzed.

Depending on your target's ressources requirements and your system capabilities, you can allocate more CPUs with [`-p`](../../../reference/fuzzer_configuration.md#processes) parameter.
:::

```{code-block}
---
caption: kAFL Fuzzer execution
---
```

:::{Note}
For the full command-line reference, please refer to [Fuzzer Configuration](../../../reference/fuzzer_configuration.md) page.
:::

## Follow the progress with `kafl gui`

```{include} ../../gui.md
```

After a few minutes (depending on your system and resource allocation), you should start to see kAFL reporting crashes:

```{code-block} shell
---
caption: kAFL GUI crash founds
---
┏━━❮❰ Progress ❱❯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ ┃
┃ Paths: │ Bitmap: │ Findings: ┃
┃ Total: 38 │ │ Crash: 3 (N/A) 2m00s ┃
┃ Seeds: 22 │ Edges: 100 │ AddSan: 0 (N/A) None Yet ┃
┃ Favs: 38 │ Blocks: 149 │ Timeout: 18 (N/A) 28s ┃
┃ Norm: 1 │ p(col): 0.2% │ Regular: 38 (N/A) 1m27s ┃
┠──────────────────────────────────────────────────────────────────────────────┨
```

Once you've observed at least one crash, you can terminate the fuzzing process using `CTRL-C` and proceed to the next step of the analysis.
Loading

0 comments on commit 9b64769

Please sign in to comment.