The exploit is actually simple but when I first encountered CVE-2020–11978, I did some quick google searches and didn’t find any available exploits.
I’ve always been a user of publicly available exploits when doing CTFs and boxes in hackthebox. So this time I decided to try to look into and write my own. This blog post describes that process .
Finding the CVE
Details of the CVEs
As mentioned the RCE is present in the example DAG
load_examples = True and the Experimental API allows unauthenticated by default.
load_examples = True auth_backend = airflow.api.auth.backend.default
With the two vulnerabilties we can devlop an exploit that uses the vulnerability in the example DAGs shipped with Airflow and trigger this remotely using the open backend API. By default, you are able to remotely trigger this because the back-end API of Airflow allows unauthenticated requests even if the authentication in the web UI is configured.
To find the vulnerable DAG, we can read through each example DAG. But because these were patched in 1.10.11, we can limit our search to example DAGs that had changes between 1.10.10 and 1.10.11. You can do this is github by using: https://github.com/apache/airflow/compare/1.10.10…1.10.11
We see the following change to the
And we see an update to the docs related to this
This practically confirms that
example_trigger_target_dag.py is the example DAG that CVE-2020–11978 refers to.
Testing and writing the exploit
With this it was straightforward to test and write the exploit. First I tested this with Airflow’s web UI.
The main steps were:
- Enable the
- Create a DAG run with the config above
- Validate the
Then I replicated the steps needed to do this using Airflow Experimental API and tested it using docker to make it repeatable (https://github.com/pberba/CVE-2020-11978/blob/main/docker-compose.yml)
In terms of structuring the repository and the details to put about the POC, I learned from the works of other repos. The one I liked was https://github.com/RhinoSecurityLabs/CVEs
Submission to Exploit DB
Finally, I decided to try to submit it to exploit DB. I just followed https://www.exploit-db.com/submit
I just needed to add some header comments on the exploit code. I waited and they accepted it.
Of course, writing exploits with CVEs is a lot easier than actually discovering the CVEs. In this particular case, the actual vulnerability was very easy to find and exploit. Nonetheless, going through this process was very instructive for me.
Notes on remediation
The relevant advisory links are:
Remove Example DAGs
If you already have examples disabled by setting
load_examples=False in the config then you are not vulnerable.
You can update to
>=1.10.11 or remove the vulnerable DAG is
Deny access to experimental API
If you start a new Airflow instance using
>=1.10.11 , then
deny_all is already set for
auth_backend by default in
[api] auth_backend = airflow.api.auth.backend.deny_all
airflow.api.auth.backend.default still allows unauthenticated requests to the API even for
>=1.10.11. So if you have an existing Airflow instance which
auth_backend = airflow.api.auth.backend.default then even after upgrading to
>=1.10.11, then the REST API is still public.
>=2.0.0, the experimental API is disabled but has a more powerful stable API.