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...
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$ ```