Sitemap

CVE-2022–45875: Apache DolphinScheduler vulnerable to Improper Input Validation leads to RCE

8 min readJun 22, 2023

Introduction

Improper Input Validation leads to command injection/RCE in Apache DolphinScheduler has been found and registered as #CVE-2022–45875

The affected version 3.0.1 and prior versions; version 3.1.0 and prior versions.

What is Apache DolphinScheduler

Apache DolphinScheduler is a modern data workflow orchestration platform with a powerful user interface, dedicated to solving complex task dependencies in the data pipeline and providing various types of jobs available out of the box.

Press enter or click to view image in full size

Build the lab

I’m using docker on Ubuntu server 20.04

Install docker

  • apt update
  • apt install docker && docker-compose

Setup DolphinScheduler

  • DOLPHINSCHEDULER_VERSION=3.0.0
  • docker run --name dolphinscheduler-standalone-server -p 12345:12345 -p 25333:25333 -d apache/dolphinscheduler-standalone-server:"${DOLPHINSCHEDULER_VERSION}"

Verify the container is running:

  • docker ps -a
Press enter or click to view image in full size

Now open the following link in your browser:

Press enter or click to view image in full size
Press enter or click to view image in full size

Reproduce the vulnerability

As we can understand from the advisor and from the static analysis and the patch diffing, the vulnerability existed in the Alert script plugin which is an alert that happens based on specific settings. The alert has multiple types, one of the types is “script” where you are telling the software to run the following alert (which will run the script) if those settings happened.

Create a bash script

  • First, we need to create a bash script that we will use for the alarm
  • Access the docker container

sudo docker exec -it <container_id> bash

  • Go to /tmp

cd /tmp

  • Create the bash script, I’m making the script to create a file just as a way to check if the alarm got triggered or not.

echo "touch /tmp/alarm001finished" > alarm001.sh

Press enter or click to view image in full size
  • Change the permissions of the script so Apache DolphineScheduler can access it.
  • chmod 777 alarm001.sh
Press enter or click to view image in full size

Create a tenant

Press enter or click to view image in full size
Press enter or click to view image in full size
  • Assign the Tenant to the admin user
Press enter or click to view image in full size

Create the alarm

Press enter or click to view image in full size
Press enter or click to view image in full size
  • '; echo "This is a shell injection attack" > /tmp/injection.txt; #
Press enter or click to view image in full size

Create the project

  • Under project we can run the process we want that eventually it will trigger the alarm which it’s vulnerable to command injection.
Press enter or click to view image in full size
Press enter or click to view image in full size

Create workflow definition

  • Click on the project name “proj_001”
  • Go to workflow definition
Press enter or click to view image in full size
Press enter or click to view image in full size
  • Drag and Drop shell
  • Once you drop it, it will open this
Press enter or click to view image in full size
  • In the script you can write whatever you like, it’s what the shell process will do.
  • Now confirm
  • After the confirm, it will look like this, click save
Press enter or click to view image in full size
  • It will ask you for Workflow basic information
  • After you click confirm, it will take you to this
  • Click that button to make this workflow online
Press enter or click to view image in full size
  • You can notice the color changed.
Press enter or click to view image in full size
  • Click on the start button
  • The notification strategy can be all or success which means when or based on what the alarm will be triggered.
  • Click confirm, it will take you to the Workflow instance
Press enter or click to view image in full size
Press enter or click to view image in full size

Check docker

  • ls
Press enter or click to view image in full size
  • You can see “alarm001finished” and this is the file created by alarm001.sh script
  • Also, you can see injection.txt, this is the file created by the command injection.

Static Analysis

Let’s analyze the source code of the alarm script plugin.

  • Download the source code from here

https://github.com/apache/dolphinscheduler/archive/refs/tags/3.0.0.zip

  • Go to dolphinscheduler-3.0.0\dolphinscheduler-alert\dolphinscheduler-alert-plugins\dolphinscheduler-alert-script\src\main\java\org\apache\dolphinscheduler\plugin\alert\script\ScriptSender.java
  • The class starts with defining some variables
  • This method will get the value of those parameters from ScriptParamsConstants.java
Press enter or click to view image in full size
  • Here it will do six things
Press enter or click to view image in full size

1. validate script path in case of injections

2. Check if the file existed in the first place

3. Check that the script is a file

4. We have an array called cmd here where the execution of the script happens and the injection as well.

5. We have an if statement checks if there is no error, it will set the alert status to true and the alert message.

6. Finally, if there is any error we the alarm message with the exit code, and the error will be logged as well.

We are interested in point number 4.

String[] cmd = {"/bin/sh", "-c", scriptPath + ALERT_TITLE_OPTION + "'" + title + "'" + ALERT_CONTENT_OPTION + "'" + content + "'" + ALERT_USER_PARAMS_OPTION + "'" + userParams + "'"};
int exitCode = ProcessUtils.executeScript(cmd);

The injection happens because this constructs a shell command by concatenating the scriptPath, title, content, and userParams strings without validating or sanitizing them.

For more understanding let’s see how the cmd variable value will look like in case of valid data input.

  • scriptPath = alarm001.sh
  • ALERT_TITLE_OPTION = -t
  • ALERT_CONTENT_OPTION = -c
  • ALERT_USER_PARAMS_OPTION = -p

The final result:

/bin/sh -c /path/to/alarm001.sh -t 'title' -c 'content' -p 'paramtest'

The developers assume that the input will be between ' ' therefore anything between single quotes ' ' can't be escaped or injected.

BUT if the attacker has the ability to close the single quotes ' ' first, after that inject a command, it will be treated as a separate command by the /bin/sh

So, with our payload, the final result will look like this:

/bin/sh -c /path/to/script.sh -t 'title' -c 'content' -p ''; echo "This is a shell injection attack" > /tmp/injection.txt; #'

How to test this?

Go to your terminal (you can test inside the docker container itself) and try this command

/bin/sh -c /path/to/script.sh -t 'title' -c 'content' -p '; echo "This is a shell injection attack" > /tmp/injection.txt; #'

Nothing will happen, the injection.txt file won’t be created.

Now try it like this

/bin/sh -c /path/to/script.sh -t 'title' -c 'content' -p ''; echo "This is a shell injection attack" > /tmp/injection.txt; #'

You will find that the injection.txt is created.

Press enter or click to view image in full size

Patch Diffing

You can check the changes on the vulnerable endpoint ScriptSender.java from here:

https://github.com/apache/dolphinscheduler/commit/1b7000281e28a44d4de3ed60c3c872582e3d7cb3

  • They added space in the comments
  • Removed the cmd array from here
Press enter or click to view image in full size
  • Here they added three if statements, basically those statements check if the parameter value contains a single quote '
  • if it is, the alarm won’t be executed therefore we will not move to line 102 and execute the command constructed in the cmd array variable.
  • also, the code will log the error and set the message to “shell script illegal user params” and the userParams value.
Press enter or click to view image in full size

Is there a bypass for this?

I don’t think so, I tried. every time you try to inject anything without escaping the single quotes ' ' it will be treated as a string as we saw before in the Static analysis.

Mitigation

Users should upgrade to version 3.0.2 or 3.1.1.

Final Thoughts

There is not much to say about this. Command injection is always my favorite vulnerability, this is really easy to reproduce and exploit.

The issue is solutions like this are not always public therefore you will find more use for it when you find such a solution inside the network company during internal pentesting for example.

Also, another restriction here is that you will need to create a malicious alarm, and to do that you need permissions, after that almost any user can exploit this.

I will show you later how to get RCE and gain access, so stay tuned and join vsociety 😏.

Resources:

--

--

vsociety
vsociety

Written by vsociety

vsociety is a community centered around vulnerability research

No responses yet