RVM command injection when automatically loading environment variables from files in $PWD

2017-02-16T00:00:00
ID SSV:92687
Type seebug
Reporter Root
Modified 2017-02-16T00:00:00

Description

RVM, by default, hooks cd and automatically detects the presence of certain files in the directory being changed to. These files and their mechanics are detailed at <https://rvm.io/workflow/projects>.

The code that parses these files is available at <https://github.com/rvm/rvm/blob/master/scripts/functions/rvmrc_project> (look for the __rvm_load_project_config function). The code, as of a vulnerable commit, is available at <https://github.com/rvm/rvm/blob/b04c0158d/scripts/functions/rvmrc_project#L61>.

The parsing of these files allows for the exporting of environment variables into the current shell. For example, to set the environment variable FOO to the value "bar":

  • .versions.conf should contain the line "env-FOO=bar"

  • Gemfile should contain the line "#ruby-env-FOO=bar" (Note that the parsing of Gemfile throws a notice in the user's shell)

  • .ruby-version, .rbfu-version or .rbenv-version should be accompanied by a file named .ruby-env which should contain the line "FOO=bar"

In all of the above cases, it is critical that the file also specifies a version of Ruby that satisfies RVM. This may be a version of Ruby that the user has installed via RVM, or it may be the magic value "system" to specify that the base system's Ruby should be used. This always satisfies RVM, even when there is no Ruby installed on the base system.

An example of setting an environment variable using .versions.conf:

```text rvm@773eb63af1cc:~$ mkdir test

rvm@773eb63af1cc:~$ cat > test/.versions.conf ruby=system env-FOO=bar ^D

rvm@773eb63af1cc:~$ echo $FOO

rvm@773eb63af1cc:~$ cd test

rvm@773eb63af1cc:~/test$ echo $FOO bar ```

The code that parses environment variables fails to properly sanitize data before using it in an eval statement, leading to command injection. The buggy code, as of a vulnerable commit, is available at <https://github.com/rvm/rvm/blob/b04c0158d/scripts/functions/rvmrc_project#L271-L320>

The code wraps the value (e.g. bar as per the example above) in double-quotes and performs escaping on key shell metacharacters by prefixing them with \. However, instances of \ itself in the value are not escaped. A metacharacter becomes properly escaped, but a metacharacter preceded by a backslash becomes an escaped backslash followed by the metacharacter. For example:

  • bar$(id) becomes "bar\$(id)" which is safe
  • bar\$(id) becomes "bar\\$(id)" which causes execution of the id command.

This behaviour can be used to achieve arbitrary command execution when a user changes into a directory with malicious contents.

POC

```text rvm@e6aeaf6d79ec:~$ mkdir poc

rvm@e6aeaf6d79ec:~$ cat > poc/.versions.conf ruby=system env-FOO=bar\$(sh -c 'echo; echo Command injection as:; id; echo' >&2) ^D

rvm@e6aeaf6d79ec:~$ cd poc

Command injection as: uid=1000(rvm) gid=1000(rvm) groups=1000(rvm)

rvm@e6aeaf6d79ec:~/poc$ ```