Protecting against the command injection vulnerability in Net::FTP

Last week a vulnerability affecting Net::FTP, part of Ruby standard library, was uncovered by Etienne Stalmans (Great job 🙌) from the Heroku product security team.
This article will explain the vulnerability and describe how Sqreen protects you from that vulnerability.

Net::FTP in Ruby

The Net::FTP class enables you to easily implement the File Transfer Protocol.
The most common methods you would typically use are Net::FTP#get, #getbinaryfile, #gettextfile, put, #putbinaryfile, #andputtextfile. They’re all relying on Kernel#open under the hood.
Kernel#open is often used in ruby scripts to open files (it’s the open function that is available anywhere).

The Net::FTP implementation would often be using the basename of the file that was passed in:

def gettextfile(remotefile, localfile = File.basename(remotefile)) # :yield: line
result = nil
if localfile
f = open(localfile, "w")
# ... ...

The CVE-2017-17405 vulnerability

Let’s say I have an API that to let my users read a file based on a path they provide (powered by Net::FTP):

GET /files/myfile.txt

This will trigger the Ruby code:

open('myfile.txt')

However, there is a convenience wrapper built in the open method in Ruby: if the path starts with a “|”, it will execute code on the server:

GET /files/|cat%20/etc/passwd

This would trigger the Ruby code:

open('|cat /etc/passwd')

Which will run the following code in a shell:

cat /etc/passwd

And Voila! Your passwords are mine. 😛

How to fix this vulnerability

  1. Update your Ruby version if you’re using one of this version:
    • Ruby 2.2 series: 2.2.8 and earlier
    • Ruby 2.3 series: 2.3.5 and earlier
    • Ruby 2.4 series: 2.4.2 and earlier
    • Ruby 2.5 series: 2.5.0-preview1
    • prior to trunk revision r61242
  2. Fix your code
    1. If you can’t update your Ruby version just now, make sure you always sanitize your inputs. A simple check would be, for example, to forbid `localfile` (of Net::FTP) to ever contain a `|`.

Sqreen is already protecting your application

We are monitoring several security newsletters and received the notification about this vulnerability. Even if this vulnerability is not critical, we saw an opportunity for us to protect our customers without them having to fix the vulnerability or update their Ruby versions.

A traditional protection approach used by web application firewalls would be to implement a REGEX rule that checks whether the incoming HTTP requests contains specific patterns that would look like a command injection trying to inject things.
That implementation brings a huge performance impact (as every request needs to be checked), creates a lot of false positives and is impossible to maintain and configure.

To be able to offer a protection against the CVE-2017-17405 vulnerability we needed to have:

  • A protection that never triggers false positives. We will not break our clients apps. Period.
  • A protection that is fast to process and only brings a very limited performance impact on our clients applications.
  • That doesn’t require any maintenance or configuration hell.

This is where Sqreen makes the difference. Sqreen, is a library that sits in your application and that monitors the security of your app from the inside. Being inside the application gives us the whole context of requests handled by the app and allows us to develop attack detection rules that others just can’t match.

The protection logic that we decided to implement was to make sure that no path containing a “|” are opened based on a user input. As we are inside the application we know the user-generated inputs.
This creates a limited impact on performance, as we don’t need to check every request, and doesn’t create false positives, as a path starting with “|” coming from the user can only be an injection attempt.

That’s it! We tested this rule heavily on production traffic and released it to our customers in less than 24 hours. Our customers are now protected against this specific command injection in Ruby. Of course, they are also protected against all kind of injections (SQL, NoSQL, Shell), Cross-site scripting (XSS), Account Takeovers and suspicious user activities.

You can read more on how Sqreen detects and blocks SQL injections without false positives on this article “Block SQL injections, not your customers”.