Running RESTler on Docker
By: Pedro Tôrres
Tags: Fuzzing, Rest Api, Restler, Swagger

RESTler is a fuzzing tool developed by Microsoft that allows you to test services just by providing the Swagger specification of their REST APIs. The specification is analyzed and tests are generated to exercise the entire API.

It was recently open-sourced and the repository still doesn’t have pre-compiled binaries to download and you need to build the project yourself. It wouldn’t be a problem if wasn’t by the .NET-ism of the project that builds stuff with arbitrary scripts and that generates a ton of artifacts instead of a single binary.

Given that, I decided to write a Dockerfile and run everything inside Docker instead of dealing with a bunch of files that I don’t know where to places inside my file system.

FROM

I started by looking for a base image that I could use, the requirements were .NET Core and Python. Well, I can install Python wherever I want to, so I just worried about .NET Core. The documentation of .NET Core shows how to install it in basically every single Linux distributions on the face of the Earth. Kudos to Microsoft!

First I tried Ubuntu, but I didn’t want to add a custom package repository by installing a .deb (???) with dpkg.

Then I went to Alpine and tried to follow the instructions there. This time, the only option provided to install .NET Core was to download and run a shell script. By doing so, the script doesn’t work because it strictly requires bash, not ash.

I gave up installing .NET Core manually and I started to looking for a image with it pre-installed. I couldn’t find a official one on Docker Hub, but I quickly found that Microsoft had those images on their on registry.

Finally we have the first line of our Dockerfile.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1

COPY

With a base image in hands, I cloned the repository from GitHub (http://github.com/microsoft/restler-fuzzer) and placed the Dockerfile on the root of the project.

The second line was easy to write, I just copied everything from the repository to the container image.

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 COPY . /usr/local/src/restler-fuzzer

RUN

Inspecting the base image, I’ve noticed that Python was already installed. But unfortunately it was Python 2 and I needed Python 3, not that big of a deal, I just ran apt-get to install it. I also had to make it the default Python version of the system.

Then I compiled the project by executing the build-restler.py script located on the root of the project with a couple of flags. It worked!

At this point I had a lot of artifacts that I didn’t know where to place. So I just threw them all in /opt . 🤷‍♂️

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 COPY . /usr/local/src/restler-fuzzer RUN apt-get update && \

apt-get install -y python3 && \

rm -fR /var/lib/apt/lists/* && \

update-alternatives --install /usr/bin/python python /usr/bin/python3 0 && \

python3 /usr/local/src/restler-fuzzer/build-restler.py --repository_root_dir /usr/local/src/restler-fuzzer --dest_dir /opt

ENTRYPOINT and WORKDIR

My object by the end of this tutorial is to have an alias for RESTler that uses docker run to execute everything inside the container as if it was just a regular binary.

To accomplish this, I’ve set the entry-point as the RESTlet main binary, and the work-dir as /mnt .

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 COPY . /usr/local/src/restler-fuzzer RUN apt-get update && \

apt-get install -y python3 && \

rm -fR /var/lib/apt/lists/* && \

update-alternatives --install /usr/bin/python python /usr/bin/python3 0 && \

python /usr/local/src/restler-fuzzer/build-restler.py --repository_root_dir /usr/local/src/restler-fuzzer --dest_dir /opt ENTRYPOINT [ "/opt/restler/Restler" ] WORKDIR /mnt

alias

Before setting the alias, we need to build the Docker Image. I decided to use the same name of the repository as its tag.

docker build -t microsoft/restler-fuzzer .

With that, the alias can be set.

alias restler='docker run --rm -v "$(pwd)":/mnt -it microsoft/restler-fuzzer'

On the alias I’m creating a temporary non-persistent container using the image we just built and with the current directory mounted inside the container at /mnt .

Now we can use the restler command as if it was an executable installed on the machine… almost. There is one caveat, you cannot reference files using absolute paths or relative paths that goes to places above the current directory.

restler compile

The first step in using RESTlet is to compile the Swagger specification of the service being tested.

For the purpose of this tutorial, we’ll be using the Swagger Petstore (https://petstore.swagger.io). This is an example, provided by Swagger itself, of a service that has its API described using the specification language.

Then let’s download the service’s swagger.json e compile it using RESTler.

wget https://petstore.swagger.io/v2/swagger.json restler compile --api_spec swagger.json

You can see that the compilation process created 3 folders: Compile , RestlerLogs , and… ~ (???). We will be using the files inside the first directory in the next steps.

restler test

The test mode of RESTler allows you to check that everything is working as expected and also calculates the coverage of the fuzzing. Let’s execute and see what happens.

restler test --grammar_file ./Compile/grammar.py --dictionary_file ./Compile/dict.json --settings ./Compile/engine_settings.json

In my run of the command, the output was as follows:

Log share was not specified. Logs will not be uploaded.

Starting task Test...

Request coverage (successful / total): 12 / 20

Bugs were found!

Bug buckets: main_driver_500: 1

Task Test succeeded.

Collecting logs...

A new folder called Test was created with some interesting files inside. They report the tests performed, the bugs found, and the request the returned 500.

In ./Test/RestlerResults/experiment*/bug_buckets/main_driver_500_1.txt we have the exact input performed and every byte of the output returned, for example.

restler fuzz-lean

The fuzz-lean mode executes every request compiled into the RESTler Grammar and perform some checks to see if some bug can be found within a small time period.

restler fuzz-lean --grammar_file ./Compile/grammar.py --dictionary_file ./Compile/dict.json --settings ./Compile/engine_settings.json

The output of the command was:

Log share was not specified. Logs will not be uploaded.

Starting task FuzzLean...

Request coverage (successful / total): 11 / 20

Bugs were found!

Bug buckets: PayloadBodyChecker_500: 69

main_driver_500: 1

Task FuzzLean succeeded.

Collecting logs...

The results of the fuzzing will be stored inside the FuzzLean folder and has the same files as the test mode, but with more data.

restler fuzz

The fuzz mode is a more complex fuzzing test that uses BFS to find more bugs on the REST API. This is an aggressive mode and can degrade the service being tested and even cause outage. Be cautious with this test and DON’T run it against service you don’t own or that you don’t have authorization to do so.

Very similar to the fuzz-lean command, the fuzz mode only requires an extra flag to run. The time budget must be set as the number of hours you would like to spend fuzzing the service.

restler fuzz --grammar_file ./Compile/grammar.py --dictionary_file ./Compile/dict.json --settings ./Compile/engine_settings.json --time_budget 1

This time the folder created with the results is the one called Fuzz , but with the same structure as before.

Final Thoughts

RESTler is a very powerful, yet very simple fuzzing tool that can be used by only providing the Swagger specification of a service. It allows you to perform a robust fuzzing test with little effort.

As an open-source tool, it has a lot to evolve and may become more friendly very soon. And with the community contributions, it will definitely become even more powerful in the years to come.