SaltStack: further injection vulnerabilities

The fix for CVE-2020-28243 in SaltStack may have prevented command injection – but it wasn’t enough.

In February 2021, our security researcher Mat Rollings disclosed a local privilege escalation (CVE-2020-28243) in SaltStack's Salt via specially crafted process names. However, while the initial fix SaltStack published did prevent command injection, it wasn’t enough; it didn’t prevent argument injection. This second vulnerability was much less severe than the original CVE, but still could have caused a low-impact denial of service.

The SaltStack fix

Let's start by understanding the first fix SaltStack released in an attempt to sanitize the package names to prevent the command injection. To do this, we can look at the difference between the two versions of code, available as a diff.

SaltStack’s original fix for CVE-2020-28243

At a first glance, this all looks good. SaltStack made the following changes:

TOPICS
CVEs
Threats
PUBLISHED

24 March 2021

Mat Rollings,
Senior Application Security Engineer
Twitter: @stealthcopter
Blog: sec.stealthcopter.com

  • Removal of shell=True, which prevents command chaining or redirection using a control character like >, ||, &&, or ;
  • Addition of shlex, a command shell sanitizing library, in an attempt to sanitize the command

The SaltStack error

The developer that added this fix made an error. Their usage of shlex does not provide any additional protection. The shlex.split function takes an input string and splits it into the command and its arguments using spaces as the delimiter. We control the package variable, which means we can inject additional arguments into the command.

This advanced form of command injection is known as argument injection. Typical command injection can fail if subshells or redirection are blocked, yet argument injection can still work under the same conditions – and even when sanitization occurs.

The screenshot below shows the dpkg command running with two inputs. The first is a regular package name, and the second is one that will be interpreted as an argument and cause unintended execution paths.

dpkg running with a regular package name and an unexpected argument

Triggering an argument injection like this might not seem exciting at first. But it can cause crashes in poorly written applications where unexpected output is not accounted for, eventually leading to a denial of service. If we manage to directly control the output, we may be able to control the execution flow of the application, leading to further compromise.

More advanced argument injections can achieve code execution, but these rely on finding a suitable argument to be abused. Unfortunately, none of the arguments that can be passed to the three package commands, dpkg, repoquery or opkg, appear to be exploitable for this purpose, so code execution does not appear possible.
However, we can bring about a denial of service by causing the command to hang indefinitely. A fifo file, created using the mkfifo command, is a file that cannot be read until it is written to. This can cause any program reading from this file to hang.

Example command that will cause a hang in dpkg-query

When a custom admin directory is set, dpkg-query will attempt to read a file in this directory named status. As we have made a fifo file with this name in the tmp directory, the command will hang indefinitely.

When this process hangs, both the master and minion will continue to function as normal but the process that called the restart check will hang indefinitely. As such, this exploit is pretty low in severity.

What happened next?

Coordinated disclosure is a process of sharing the suggested solution with the reporter before it’s released publicly. The reporter can then test and propose modifications where it does not adequately fix the vulnerability. Unfortunately, SaltStack initially published the security fix without coordinated disclosure with the author. If they had communicated on the solution, the issue would have been spotted and a secondary fix wouldn’t have been necessary.

Thankfully, the second time around SaltStack shared the fix for approval before publication. This is a step in the right direction and shows more of a proactive than reactive approach to security, which is always better in the long run.

The new SaltStack fix

The final solution SaltStack implemented avoids the need to use shlex to escape the command string as it builds an array that is passed to popen. This ensures that the package name can only ever be a single argument as spaces and quotes will be escaped automatically.

SaltStack’s updated fix for CVE-2020-28243

A further addition was the -- argument in each of the cmd_pkg_query variables. In most POSIX compatible commands, the -- argument signifies the end of all option arguments, and all subsequent arguments are treated as string literals, even if they begin with - or --. This little-known tip is handy when passing user-controlled parameters to commands as it can protect against argument injection. For more information, check out these Utility Syntax Guidelines.

Conclusion

The security fix for the original vulnerability was released on 4th February 2021, and the updated fix was released on the 23rd March 2021. You can find out more about CVE-2020-28243 over on the SaltStack announcement here.

Fixing security vulnerabilities is a difficult task to get right, especially when the pressure is on to hit a deadline. Are you confident your web applications are secure? Immersive Labs’ series on Python Secure Coding is the best place to practice and level up your skills in identifying, exploiting, securing and validating common vulnerabilities in web applications. Log in to check this out.

If you're an Immersive Labs user, book a demo today for a tour of our human cyber readiness platform.

We help businesses to increase and evidence human capability in every part of cybersecurity.