├── .gitignore ├── input ├── ping ├── copy ├── lpush ├── hvals ├── hset ├── hincrby └── hstrlen ├── stats-demo.png ├── fuzz.sh ├── hotpatch.diff ├── setup.sh ├── Dockerfile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | .DS_Store -------------------------------------------------------------------------------- /input/ping: -------------------------------------------------------------------------------- 1 | PING 2 | PING "hello world" 3 | -------------------------------------------------------------------------------- /input/copy: -------------------------------------------------------------------------------- 1 | SET dolly "sheep" 2 | COPY dolly clone 3 | GET clone -------------------------------------------------------------------------------- /input/lpush: -------------------------------------------------------------------------------- 1 | LPUSH mylist "world" 2 | LPUSH mylist "hello" 3 | LRANGE mylist 0 -1 -------------------------------------------------------------------------------- /stats-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xbigshaq/redis-afl/HEAD/stats-demo.png -------------------------------------------------------------------------------- /input/hvals: -------------------------------------------------------------------------------- 1 | HSET myhash field1 "Hello" 2 | HSET myhash field2 "World" 3 | HVALS myhash 4 | -------------------------------------------------------------------------------- /input/hset: -------------------------------------------------------------------------------- 1 | HSET zz aa 1337 2 | HGET zz aa 3 | HSET zz bb "QQQQQQQQ" 4 | HGET zz bb 5 | HGETALL zz -------------------------------------------------------------------------------- /input/hincrby: -------------------------------------------------------------------------------- 1 | HSET myhash field 5 2 | HINCRBY myhash field 1 3 | HINCRBY myhash field -1 4 | HINCRBY myhash field -10 -------------------------------------------------------------------------------- /input/hstrlen: -------------------------------------------------------------------------------- 1 | HMSET myhash f1 HelloWorld f2 99 f3 -256 2 | HSTRLEN myhash f1 3 | HSTRLEN myhash f2 4 | HSTRLEN myhash f3 -------------------------------------------------------------------------------- /fuzz.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | AFL_PRELOAD=./preeny/x86_64-linux-gnu/desock.so \ 4 | afl-fuzz \ 5 | -i ./input/ \ 6 | -o output/ \ 7 | -x ./dict/ \ 8 | -m2048 \ 9 | ./redis/src/redis-server ./redis.conf 10 | -------------------------------------------------------------------------------- /hotpatch.diff: -------------------------------------------------------------------------------- 1 | diff --git a/./redis/src/networking.c b/./redis/src/networking.c 2 | index 50e4b71..6e4599b 100644 3 | --- a/./redis/src/networking.c 4 | +++ b/./redis/src/networking.c 5 | @@ -2172,6 +2172,7 @@ void readQueryFromClient(connection *conn) { 6 | /* There is more data in the client input buffer, continue parsing it 7 | * in case to check if there is a full command to execute. */ 8 | processInputBuffer(c); 9 | + exit(0); 10 | } 11 | 12 | void getClientsMaxBuffers(unsigned long *longest_output_list, -------------------------------------------------------------------------------- /setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cd /root/redis-dir 3 | 4 | # creating a dictionary 5 | mkdir dict && cd dict 6 | cat ../redis/src/server.c | grep Command, | sed 's/ //g' | grep -oP '{"(.*?)"' | sort | uniq | sed -e s/\"//g -e s/{//g > cmds.txt 7 | cat cmds.txt | while read line; do echo "${line}">${line}.cmd; done 8 | mv cmds.txt ../ 9 | cd ../ 10 | 11 | 12 | # making the crash results accessible via a shared volume (if we run the container with the -v arg) 13 | mkdir -p /root/host-share/ 14 | ln -s /root/host-share/ /root/redis-dir/output 15 | 16 | 17 | # adjusting network configs 18 | cp ./redis/redis.conf . 19 | sed 's/bind 127.0.0.1 -::1/bind 127.0.0.1/' ./redis/redis.conf | tee ./redis.conf 20 | mv ./redis/redis.conf ./redis/redis-OLD.conf -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM aflplusplus/aflplusplus:latest 2 | 3 | RUN mkdir /root/redis-dir 4 | ADD fuzz.sh /root/redis-dir/ 5 | ADD hotpatch.diff /root/redis-dir/ 6 | 7 | 8 | ADD input/ /root/redis-dir/input/ 9 | 10 | # compile redis-server with debug info using afl clang compiler 11 | 12 | RUN cd /root/redis-dir && \ 13 | git clone https://github.com/redis/redis.git && \ 14 | cd redis && \ 15 | git checkout 92bde12 && \ 16 | cd ../ && \ 17 | patch -p1 -i ./hotpatch.diff && cd redis/ && \ 18 | make MALLOC=libc CC=afl-clang-fast CXX=afl-clang-fast CFLAGS="-g3 -static" CXXFLAGS="-g3" 19 | 20 | 21 | # fetch preeny, install deps & compile shared object files 22 | RUN cd /root/redis-dir && \ 23 | git clone https://github.com/zardus/preeny.git && \ 24 | cd preeny && \ 25 | git checkout aaef77f94052aad5e4b019d7cbfd30aac323ccd7 && \ 26 | apt-get update && \ 27 | apt-get install -y libini-config-dev libseccomp-dev cmake && \ 28 | make 29 | 30 | 31 | 32 | # creating a fuzzing dictionary 33 | ADD setup.sh /root/redis-dir/ 34 | 35 | RUN cd /root/redis-dir/ && \ 36 | chmod +x setup.sh && \ 37 | chmod +x fuzz.sh && \ 38 | ./setup.sh 39 | 40 | CMD sh -c '/bin/bash' -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # redis-afl 2 | An automated(+beginner friendly) setup for compiling and fuzzing Redis w/ AFL++. 3 | 4 | More info about the process can be found here: https://0xbigshaq.github.io/2022/03/10/fuzzing-harder-part1 5 | 6 | # Build 7 | To build, run: 8 | ```sh 9 | docker build -t afl-redis-setup . 10 | ``` 11 | 12 | To start the container with a shared volume (in order for the results to be saved in the hosting machine) 13 | 14 | ```sh 15 | docker run --rm -v $(pwd):/root/host-share --privileged -it --workdir=/root afl-redis-setup 16 | ``` 17 | 18 | # Test 19 | 20 | To run a smoke test before starting the fuzzing session, run: 21 | ```sh 22 | AFL_PRELOAD=preeny/x86_64-linux-gnu/desock.so afl-showmap -m2048 -o/dev/null ./redis/src/redis-server ./redis.conf < <(echo "PING"); 23 | 24 | output: 25 | [+] Hash of coverage map: 5a20352f5b1cb7e5 26 | [+] Captured 1156 tuples (map size 38422, highest value 8, total values 2511) in '/dev/null'. 27 | ``` 28 | 29 | ```sh 30 | AFL_PRELOAD=preeny/x86_64-linux-gnu/desock.so afl-showmap -m2048 -o/dev/null ./redis/src/redis-server ./redis.conf < <(echo "SHUTDOWN"); 31 | 32 | output: 33 | [+] Hash of coverage map: 653954010db919f3 34 | [+] Captured 1170 tuples (map size 38422, highest value 8, total values 2553) in '/dev/null'. 35 | ``` 36 | 37 | # Run 38 | 39 | To start fuzzing, just run `./fuzz.sh` file :^) 40 | 41 | ![img0](./stats-demo.png) 42 | 43 | >**Note:** The fuzzing speed/execs per second will not be high if you don't have a strong machine. This can be solved in two approaches: The first approach is '_Trying Harder_', to apply this, just keep reading through the _Distributed Fuzzing_ section below. The 2nd approach is '_Trying Smarter_', this approach involves patching the server in a more specific way that cuts down the performance costs, such example can be found [here](https://github.com/0xbigshaq/apache-afl). 44 | 45 | # Distributed Fuzzing 46 | 47 | To run multiple instances of AFL++, use [afl-launch](https://github.com/bnagy/afl-launch#installation) and specify number of cores using the ``-n`` argument: 48 | ```sh 49 | AFL_PRELOAD=./preeny/x86_64-linux-gnu/desock.so ~/go/bin/afl-launch -n $(nproc) -i ./input/ -o output/ -x ./dict/ -m 2048 ./redis/src/redis-server ./redis.conf 50 | ``` 51 | 52 | # Areas for improvement 53 | 54 | The setup in this repo is based on [VolatileMinds approach from 2015](https://volatileminds.net/2015/08/20/advanced-afl-usage-preeny.html), I also added some useful improvements(below) 55 | 56 | - [X] Upgrading to a modern version(from 3.1 to 6.2.1) 57 | - [X] Automating the build proccess & hot-patch of Redis and preeny 58 | - [X] Adding instructions for scaled/Distributed Fuzzing 59 | - [X] Fixing compile flags for easier crash analysis (now side-dependencies are compiled with debug information, such as the embedded lua interpreter, etc.) 60 | - [X] Use `AFL_PRELOAD` instead of `LD_PRELOAD` (although I didn't notice a change, some docs are suggesting to use ``AFL_PRELOAD`` since ``LD_PRELOAD`` might override the fuzzer binary and possibly make the fuzzer melt into itself) 61 | - [X] Adding more samples for the `./input/` directory 62 | - [ ] Applying persistent fuzzing(`AFL_LOOP`) to increase the _execs per second_ rate 63 | - [ ] Adding utils like _AddressSanitizer_(ASAN) _UndefinedBehaviorSanitizer_(UBSAN) to the final build 64 | 65 | --------------------------------------------------------------------------------