---
title: 'Update Your Gems: Security Vulnerability in Cocaine'
teaser:
tags: web,ruby,open source,cocaine
author: Jon Yurek
published_on: 2013-10-25
---

We were recently informed of a security vulnerability in
[cocaine](https://github.com/thoughtbot/cocaine), our gem for running shell
commands. There is lots of work done in
[cocaine](https://github.com/thoughtbot/cocaine) in order to make sure that
nasty things like someone passing in `rm -rf /` into a command won't actually
do anything to your systems. But [Holger Just](http://twitter.com/meineerde)
was able to point out a potentially exploitable vulnerability that would let
you sneak in dangerous commands if you know how the command line was being
built.

The details of the problem are contained in
[CVE-2013-4457](http://seclists.org/oss-sec/2013/q4/157), but I can explain
better here.

## Interpolation

The way [cocaine](https://github.com/thoughtbot/cocaine) works is that it turns
user-supplied inputs into shell-safe strings before interpolating them into
your command. You give it a hash, and it replaces the keys in the string with
the shell-safe values of those keys, like so:

    line = Cocaine::CommandLine.new("echo", ":foo, :bar")
    line.command(foo: "Hello", bar: "world") # => "echo 'Hello', 'world'"

Notice that each of the interpolated values are shell quoted. This prevents
things like trying to slip an `rm -rf /` in there:

    line = Cocaine::CommandLine.new("cat", ":file")
    line.command(:file => "ohyeah?'`rm -rf /`.ha!") # => "cat 'ohyeah?'\\''`rm -rf /`.ha!'"

This is a safe command to run, even if it looks dangerous at first glance.

## Recursive Interpolation

The problem comes about due to the way the values are interpolated.
[cocaine](https://github.com/thoughtbot/cocaine) loops over the keys and
replaces each one with its value. The part where this breaks is when it does a
`gsub` during each iteration. Take the following example:

    line = Cocaine::CommandLine.new("echo", ":foo, :bar")
    line.command(foo: ":bar", bar: "`cat /etc/passwd`") # => "echo ''`cat /etc/passwd`'', '`cat /etc/passwd`'"

Assuming we have Ruby 1.9's ordered hashes, the `:foo` key is interpolated
first, replacing it with `:bar`, and the command line will look like `cat
':bar' :bar`. When the `:bar` key is interpolated, the first argument becomes
double-quoted, resulting in an unquoted subshell command in your command line.

(Incidentally, this exploit is still possible on Ruby 1.8, but less likely as
the hash keys aren't guaranteed to be returned in any specific order.)

## The Fix

This was brought to our attention Tuesday by [Holger
Just](http://twitter.com/meineerde), who also helpfully provided a fix as well.
The solution was to stop interpolating once for each key in your hash, and only
interpolate once ever.

`String#gsub` has a wonderful ability to take a block which will be executed
each time it finds a match, and it will use the result of that match as the
replacement. After this patch was applied, the resulting code above looks like
this:

    line = Cocaine::CommandLine.new("echo", ":foo, :bar")
    line.command(foo: ":bar", bar: "`cat /etc/passwd`") # => "echo ':bar', '`cat /etc/passwd`'"

And this is, again, safe to run. You can see the fix [on
GitHub](https://github.com/thoughtbot/cocaine/commit/5ede63e2d0bbbee84e2a1346122560bbde49a555).

## What you need to know

If you're using a [cocaine](https://github.com/thoughtbot/cocaine) version
between 0.4.0 and 0.5.2, you should upgrade to 0.5.3. If you're using a version
in the 0.3.x branch, you don't have to upgrade.

Because [Paperclip](https://github.com/thoughtbot/paperclip) 3.x depends on
[Cocaine](https://github.com/thoughtbot/cocaine), you should upgrade to the
latest version (3.5.2 as of this writing). If you can't, you should at least
`bundle update cocaine`. If you're still using
[Paperclip](https://github.com/thoughtbot/paperclip) 2.7.x, then you won't need
to update for this one, as it uses an older version of
[Cocaine](https://github.com/thoughtbot/cocaine).

[Paperclip](https://github.com/thoughtbot/paperclip) is probably not
exploitable to this vulnerability, though, as by default it only accepts one
user-supplied input into its shell commands. But better safe than sorry.
