From de9d1ff4a09a72c8bd4bb892f146646296f3f2fa Mon Sep 17 00:00:00 2001 From: vanhauser-thc Date: Tue, 11 Oct 2022 12:43:06 +0200 Subject: doc fixes --- qemu_mode/README.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'qemu_mode/README.md') diff --git a/qemu_mode/README.md b/qemu_mode/README.md index 3ebfc54c..4ed2f298 100644 --- a/qemu_mode/README.md +++ b/qemu_mode/README.md @@ -13,8 +13,8 @@ afl-cc. The usual performance cost is 2-5x, which is considerably better than seen so far in experiments with tools such as DynamoRIO and PIN. -The idea and much of the initial implementation comes from Andrew Griffiths. The -actual implementation on current QEMU (shipped as qemuafl) is from Andrea +The idea and much of the initial implementation comes from Andrew Griffiths. +The actual implementation on current QEMU (shipped as qemuafl) is from Andrea Fioraldi. Special thanks to abiondo that re-enabled TCG chaining. ## 2) How to use QEMU mode @@ -30,17 +30,13 @@ glib2-devel). Once the binaries are compiled, you can leverage the QEMU tool by calling afl-fuzz and all the related utilities with `-Q` in the command line. -Note that QEMU requires a generous memory limit to run; somewhere around 200 MB -is a good starting point, but considerably more may be needed for more complex -programs. The default `-m` limit will be automatically bumped up to 200 MB when -specifying `-Q` to afl-fuzz; be careful when overriding this. - In principle, if you set `CPU_TARGET` before calling ./build_qemu_support.sh, you should get a build capable of running non-native binaries (say, you can try `CPU_TARGET=arm`). This is also necessary for running 32-bit binaries on a 64-bit system (`CPU_TARGET=i386`). If you're trying to run QEMU on a different architecture, you can also set `HOST` to the cross-compiler prefix to use (for example `HOST=arm-linux-gnueabi` to use arm-linux-gnueabi-gcc). +Another common target is `CPU_TARGET=aarch64`. You can also compile statically-linked binaries by setting `STATIC=1`. This can be useful when compiling QEMU on a different system than the one you're planning @@ -219,9 +215,6 @@ program may be utilizing. In particular, it does not appear to have full support for AVX2/FMA3. Using binaries for older CPUs or recompiling them with `-march=core2`, can help. -Beyond that, this is an early-stage mechanism, so fields reports are welcome. -You can send them to . - ## 14) Alternatives: static rewriting Statically rewriting binaries just once, instead of attempting to translate them @@ -230,4 +223,4 @@ with peril, because it depends on being able to properly and fully model program control flow without actually executing each and every code path. For more information and hints, check out -[docs/fuzzing_binary-only_targets.md](../docs/fuzzing_binary-only_targets.md). \ No newline at end of file +[docs/fuzzing_binary-only_targets.md](../docs/fuzzing_binary-only_targets.md). -- cgit 1.4.1 From ca2e8a1bf65d6f5d33244c9c7971a21294dc932b Mon Sep 17 00:00:00 2001 From: Dawin Schmidt Date: Mon, 6 Feb 2023 08:38:20 -0500 Subject: Add Qemu deferred initialization example --- .../README.deferred_initialization_example.md | 201 +++++++++++++++++++++ qemu_mode/README.md | 2 + 2 files changed, 203 insertions(+) create mode 100644 qemu_mode/README.deferred_initialization_example.md (limited to 'qemu_mode/README.md') diff --git a/qemu_mode/README.deferred_initialization_example.md b/qemu_mode/README.deferred_initialization_example.md new file mode 100644 index 00000000..0ba04b79 --- /dev/null +++ b/qemu_mode/README.deferred_initialization_example.md @@ -0,0 +1,201 @@ +# Fuzz ARM32 Python Native Extensions in Binary-only Mode (LLVM fork-based) + +This is an example on how to fuzz Python native extensions in LLVM mode with deferred initialization on ARM32. + +We use Ubuntu x86_64 to run AFL++ and an Alpine ARMv7 Chroot to build the fuzzing target. + +Check [Resources](#resources) for the code used in this example. + +## Setup Alpine ARM Chroot on your x86_64 Linux Host + +### Use systemd-nspawn + +1. Install `qemu-user-binfmt`, `qemu-user-static` and `systemd-container` dependencies. +2. Restart the systemd-binfmt service: `systemctl restart systemd-binfmt.service` +3. Download an Alpine ARM RootFS from https://alpinelinux.org/downloads/ +4. Create a new `alpine_sysroot` folder and extract: `tar xfz alpine-minirootfs-3.17.1-armv7.tar.gz -C alpine_sysroot/` +5. Copy `qemu-arm-static` to Alpine's RootFS: `cp $(which qemu-arm-static) ./alpine/usr/bin/` +6. Chroot into the container: `sudo systemd-nspawn -D alpine/ --bind-ro=/etc/resolv.conf` +7. Install dependencies: `apk update && apk add build-base musl-dev clang15 python3 python3-dev py3-pip` +8. Exit the container with `exit` + +### Alternatively use Docker + +1. Install `qemu-user-binfmt` and `qemu-user-static` +2. Run Qemu container: ```$ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes``` +3. Run Alpine container: ```$ docker run -it --rm arm32v7/alpine sh``` + +## Build AFL++ Qemu Mode with ARM Support + +First, build AFL++ as described [here](https://github.com/AFLplusplus/AFLplusplus/blob/dev/docs/INSTALL.md). Then, run the Qemu build script: + +```bash +cd qemu_mode && CPU_TARGET=arm ./build_qemu_support.sh +``` + +## Compile and Build the Fuzzing Project +Build the native extension and the fuzzing harness for ARM using the Alpine container (check [Resources](#resources) for the code): +```bash +ALPINE_ROOT= +FUZZ= +sudo systemd-nspawn -D $ALPINE_ROOT --bind=$FUZZ:/fuzz +CC=$(which clang) CFLAGS="-g" LDSHARED="clang -shared" python3 -m pip install /fuzz +clang $(python3-config --embed --cflags) $(python3-config --embed --ldflags) -o /fuzz/fuzz_harness.a /fuzz/fuzz_harness.c +exit +``` + +Manually trigger bug: +```bash +echo -n "FUZZ" | qemu-arm-static -L $ALPINE_ROOT $FUZZ/fuzz_harness.a +``` + +## Run AFL++ +Make sure to start the forkserver *after* loading all the shared objects by setting the `AFL_ENTRYPOINT` environment variable (see [here](https://aflplus.plus/docs/env_variables/#5-settings-for-afl-qemu-trace) for details): + +Choose an address just before the `while()` loop, for example: +```bash +qemu-arm-static -L $ALPINE_ROOT $ALPINE_ROOT/usr/bin/objdump -d $FUZZ/fuzz_harness.a | grep -A 1 "PyObject_GetAttrString" + +00000584 : + 584: e28fc600 add ip, pc, #0, 12 +-- + 7c8: ebffff6d bl 584 + 7cc: e58d0008 str r0, [sp, #8] +... +``` + +Check Qemu memory maps using the instructions from [here](https://aflplus.plus/docs/tutorials/libxml2_tutorial/): +>The binary is position independent and QEMU persistent needs the real addresses, not the offsets. Fortunately, QEMU loads PIE executables at a fixed address, 0x4000000000 for x86_64. +> +> We can check it using `AFL_QEMU_DEBUG_MAPS`. You don’t need this step if your binary is not PIE. + +Setup Python environment variables and run `afl-qemu-trace`: +```bash +PYTHONPATH=$ALPINE_ROOT/usr/lib/python3.10/ PYTHONHOME=$ALPINE_ROOT/usr/bin/ QEMU_LD_PREFIX=$ALPINE_ROOT AFL_QEMU_DEBUG_MAPS=1 afl-qemu-trace $FUZZ/fuzz_harness.a + +... +40000000-40001000 r-xp 00000000 103:03 8002276 fuzz_harness.a +40001000-4001f000 ---p 00000000 00:00 0 +4001f000-40020000 r--p 0000f000 103:03 8002276 fuzz_harness.a +40020000-40021000 rw-p 00010000 103:03 8002276 fuzz_harness.a +40021000-40022000 ---p 00000000 00:00 0 +40022000-40023000 rw-p 00000000 00:00 0 +``` + +Finally, setup Qemu environment variables... +```bash +export QEMU_SET_ENV=PYTHONPATH=$ALPINE_ROOT/usr/lib/python310.zip:$ALPINE_ROOT/usr/lib/python3.10:$ALPINE_ROOT/usr/lib/python3.10/lib-dynload:$ALPINE_ROOT/usr/lib/python3.10/site-packages,PYTHONHOME=$ALPINE_ROOT/usr/bin/ +export QEMU_LD_PREFIX=$ALPINE_ROOT +``` + +... and run AFL++: +```bash +mkdir -p $FUZZ/in && echo -n "FU" > $FUZZ/in/seed +AFL_ENTRYPOINT=0x400007cc afl-fuzz -i $FUZZ/in -o $FUZZ/out -Q -- $FUZZ/fuzz_harness.a +``` + +## Resources + +### setup.py + +```python +from distutils.core import setup, Extension + +module = Extension("memory", sources=["fuzz_target.c"]) + +setup( + name="memory", + version="1.0", + description='A simple "BOOM!" extension', + ext_modules=[module], +) +``` + +### fuzz_target.c + +```c +#define PY_SSIZE_T_CLEAN +#include + +#pragma clang optimize off + +static PyObject *corruption(PyObject* self, PyObject* args) { + char arr[3]; + Py_buffer name; + + if (!PyArg_ParseTuple(args, "y*", &name)) + return NULL; + + if (name.buf != NULL) { + if (strcmp(name.buf, "FUZZ") == 0) { + arr[0] = 'B'; + arr[1] = 'O'; + arr[2] = 'O'; + arr[3] = 'M'; + } + } + + PyBuffer_Release(&name); + Py_RETURN_NONE; +} + +static PyMethodDef MemoryMethods[] = { + {"corruption", corruption, METH_VARARGS, "BOOM!"}, + {NULL, NULL, 0, NULL} +}; + +static struct PyModuleDef memory_module = { + PyModuleDef_HEAD_INIT, + "memory", + "BOOM!", + -1, + MemoryMethods +}; + +PyMODINIT_FUNC PyInit_memory(void) { + return PyModule_Create(&memory_module); +} +``` + +### fuzz_harness.c + +```c +#include + +#pragma clang optimize off + +int main(int argc, char **argv) { + unsigned char buf[1024000]; + ssize_t size; + + Py_Initialize(); + PyObject* name = PyUnicode_DecodeFSDefault("memory"); + PyObject* module = PyImport_Import(name); + Py_DECREF(name); + + if (module != NULL) { + PyObject* corruption_func = PyObject_GetAttrString(module, "corruption"); + + while ((size = read(0, buf, sizeof(buf))) > 0 ? 1 : 0) { + PyObject* arg = PyBytes_FromStringAndSize((char *)buf, size); + + if (arg != NULL) { + PyObject* res = PyObject_CallFunctionObjArgs(corruption_func, arg, NULL); + + if (res != NULL) { + Py_XDECREF(res); + } + + Py_DECREF(arg); + } + } + + Py_DECREF(corruption_func); + Py_DECREF(module); + } + + // Py_Finalize() leaks memory on certain Python versions (see https://bugs.python.org/issue1635741) + // Py_Finalize(); + return 0; +} +``` diff --git a/qemu_mode/README.md b/qemu_mode/README.md index 4ed2f298..92038737 100644 --- a/qemu_mode/README.md +++ b/qemu_mode/README.md @@ -66,6 +66,8 @@ allows to move the forkserver to a different part, e.g., just before the file is opened (e.g., way after command line parsing and config file loading, etc.) which can be a huge speed improvement. +For an example, see [README.deferred_initialization_example.md](README.deferred_initialization_example.md). + ## 4) Persistent mode AFL++'s QEMU mode now supports also persistent mode for x86, x86_64, arm, and -- cgit 1.4.1