Security Advisory for rustdoc, 2018-07-05

676 views
Skip to first unread message

Steve Klabnik

unread,
Jul 6, 2018, 3:13:30 PM7/6/18
to rustlang-security-announcements
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

# Security Advisory for rustdoc, 2018-07-05

The Rust team was recently notified of a security vulnerability affecting
rustdoc plugins. If you are not using rustdoc plugins, you are not affected.
We're not aware of any usage of this feature.

We are applying for a CVE for this vulnerability, but since there is no
embargo, we have not filed for one yet. Once a CVE is assigned, we'll make a
second post to make mention of the CVE number.

## Quick Overview

This vulnerability is an instance of CWE-427: Uncontrolled Search Path
Element[1].

Rustdoc, if not passed the `--plugin-path` argument, defaults to
/tmp/rustdoc/plugins. /tmp is world-writable on many systems, and so an
attacker could craft a malicious plugin, place it in that directory, and the
victim would end up executing their code. This only occurs when the
`--plugin` argument is also passed. If you're not using that argument, then
the loading, and therefore the bug, will not happen.

Because this feature is very difficult to use, and has been deprecated for
almost a year[2] with no comments on its usage, we don't expect this to
affect many users. For more details, read on.

## Background

Rustdoc has a "plugins" feature that lets you extend rustdoc. To write a
plugin, you create a library with a specific exposed symbol. You instruct
rustdoc to use this plugin, and it will load it, and execute the function as
a callback to modify rustdoc's AST.

This feature is quite hard to use, because the function needs to take as
input and return as output Rustdoc's AST type. The Rust project does not ship
a copy of `librustdoc` to end users, and so they would have to synthesize
this type on their own. Furthermore, Rust's ABI is unstable, and so
dynamically loading a plugin is only guaranteed to work if the plugin is
compiled with the same compiler revision as the rustdoc that you're using.
Beyond that, the feature and how to use it are completely undocumented.

Given all of this, we're not aware of any usage of plugins in the wild,
though the functionality still exists in the codebase.

## Description of the attack

If you pass the `--plugins` parameter, let's say with "foo", and *do not*
pass the `--plugin-path` parameter, rustdoc will look for the "foo" plugin
in /tmp/rustdoc/plugins. Given that /tmp is world-writable on many systems,
an attacker with access to your machine could place a maliciously crafted
plugin into /tmp/rustdoc/plugins, and rustdoc would then load the plugin,
and execute the attacker's callback, running arbitrary Rust code as your
user instead of theirs.

## Affected Versions

This functionality was introduced into rustdoc on December 31, 2013, in commit
14f59e890207f3b7a70bcfffaea7ad8865604111 [3]. That change was to rename
/tmp/rustdoc_ng/plugins to /tmp/rustdoc/plugins; The addition of this
search path generally came with the first commit to this iteration of rustdoc,
on September 22, 2013, in commit 7b24efd6f333620ed2559d70b32da8f6f9957385 [4].

Rust 1.0 was released in May of 2015, about 18 months later, and as such,
this vulnerability affects:

* Every nightly we've produced since September 22, 2013
* Every beta release of Rust
* These specific Rust releases:
  * 1.27.0
  * 1.26.2
  * 1.26.1
  * 1.26.0
  * 1.25.0
  * 1.24.1
  * 1.24.0
  * 1.23.0
  * 1.22.1
  * 1.22.0
  * 1.21.0
  * 1.20.0
  * 1.19.0
  * 1.18.0
  * 1.17.0
  * 1.16.0
  * 1.15.1
  * 1.15.0
  * 1.14.0
  * 1.13.0
  * 1.12.1
  * 1.12.0
  * 1.11.0
  * 1.10.0
  * 1.9.0
  * 1.8.0
  * 1.7.0
  * 1.6.0
  * 1.5.0
  * 1.4.0
  * 1.3.0
  * 1.2.0
  * 1.1.0
  * 1.0.0
  * 1.0.0-beta
  * 1.0.0-alpha.2
  * 1.0.0-alpha
  * 0.12.0
  * 0.11.0
  * 0.10
  * 0.9
  * 0.8

## Mitigations

To prevent this bug from happening on any version of Rust, you can always
pass the `--plugin-path` flag to control the path. This only applies if
you use the `--plugin` flag in the first place.

For Rust 1.27, we'll be releasing a 1.27.1 on Tuesday with the fix, which
consists of requiring `--plugin-path` to be passed whenever `--plugin`
is passed.

We will not be releasing our own fixes for previous versions of Rust, given
the low severity and impact of this bug. The patch to fix 1.27 should be
trivially applicable to previous versions, as this code has not changed in
a very long time. The patch is included at the end of this email. If you
need assistance patching an older version of Rust on your own, please reach
out to Steve Klabnik, st...@steveklabnik.com, and he'll be happy to help.

On beta and nightly we will be removing plugins entirely.

## Timeline of events

* Tue, Jul 3, 2018 at 11:57 PM UTC - Bug reported to secu...@rust-lang.org
* Tue, Jul 3, 2018 at 12:13 PM UTC - Steve responds, confirming the bug
* Weds, Jul 4, 2018 - Steve works up an initial patch
* Thu, Jul 5, 2018 at 6:00 PM UTC - Rust team decides to not embargo this bug
* Fri, Jul 6, 2018 at 12:38 AM - Final patch created after feedback from Red Hat


## Acknowledgements

Thanks to Red Hat Product Security, which found this bug. And specifically to
Josh Stone, who took their findings and reported it to us in accordance with
our security policy https://www.rust-lang.org/security.html, as well as providing
feedback on the patch itself. You can find their bug at [5].

## Links

1: https://cwe.mitre.org/data/definitions/427.html
2: https://github.com/rust-lang/rust/issues/44136
3: https://github.com/rust-lang/rust/commit/14f59e890207f3b7a70bcfffaea7ad8865604111
4: https://github.com/rust-lang/rust/commit/7b24efd6f333620ed2559d70b32da8f6f9957385
5: https://bugzilla.redhat.com/show_bug.cgi?id=1597063

## Patch for 1.27.0

diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 059d416989..19bfc74063 100644
- --- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -681,8 +681,14 @@ where R: 'static + Send,
             }
         }

+        if !plugins.is_empty() && plugin_path.is_none() {
+            eprintln!("ERROR: You must pass --plugin-path to use --plugins");
+            std::process::exit(1);
+        }
+
+
         // Load all plugins/passes into a PluginManager
- -        let path = plugin_path.unwrap_or("/tmp/rustdoc/plugins".to_string());
+        let path = plugin_path.unwrap_or("/usr/lib64/rustdoc/plugins".to_string());
         let mut pm = plugins::PluginManager::new(PathBuf::from(path));
         for pass in &passes {
let plugin = match passes::PASSES.iter()
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEDZniA9/+HS99C0jQ2ucX7+lCRUEFAls/vLcACgkQ2ucX7+lC
RUGqcA/+JFgR9VUbDcVzkfMW1DdUQ4T0AH+IdW8LWY/5gcwcIQ9wA1j5mOeHZ4mn
CV80nWycc4FB4YQnJY8P8GtzKeDiCcW+FzXxT6mGgfNxBJLDaaAldYW45btk1Uif
6HeoGJGjaHcqPQ4u3TOHuixMjaWnUGEYqMKCIhwNX2duQFY9VCTYft0LdhCbD2+C
r2XnZX2cDSiKYk36G6Ms04rPzAJU2ORE8G40gcG+Z1ID3kin2oALcRbMMhDS+gxw
TWlB1LGK/4xOCknIJ3QZHzeNX7mJ/KcsRqZaBdwlLC59tcY/JlQdmy1keIM0AwEw
ygmL9WL31lfiON1glnV8XjuWJV9rIpUYQuaiCOfI4bCcWlKGFxaS+jUo8qDiAS3i
/96sAkkWlqvOm3CVuGjtXt0qaU8TIWANL8Q5fjg1wcXA3gaObVDQ+Vavr+CRBWXQ
oD8crsuPDy+TRAvBfVVPPGODm7MZ+iPhxdvFs+JiKVtkrafxLg03Obc03ddq99kO
UpKovnhJE+MN6dMI9eQfS63ZhMZzr2pIUOqZOntz8FiMbbSxCtkdO3OHC/znsCmu
0Oro8A0wQgMKKsZuXK5WxgWGEqek1h4a/elbfNNAaAc0RfiLoOSBEaxC4ZFxSaLL
Kxra36nKQmZyCRNeaIA56HMRm7dU2RhpKDxm11/9cNJr6slECwI=
=UW5I
-----END PGP SIGNATURE-----

Steve Klabnik

unread,
Jul 6, 2018, 4:07:00 PM7/6/18
to rustlang-security-announcements
All,

It's come to my attention that I messed up the dates slightly:

* Tue, Jul 3, 2018 at 11:57 PM UTC - Bug reported to secu...@rust-lang.org
* Tue, Jul 3, 2018 at 12:13 PM UTC - Steve responds, confirming the bug

The latter should be AM; I answered the email after 16 minutes. When I
converted my local time to UTC, I missed the rollover.

(Not signing this message because it's so trivial a correction.)

Steve Klabnik

unread,
Jul 10, 2018, 10:46:18 AM7/10/18
to rustlang-security-announcements
We have been assigned CVE-2018-1000622; you can find it on the web here: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000622

Additionally, I would like to thank Lubomir Rintel, who was apparently the initial reporter here. Congrats and thank you! <3
Reply all
Reply to author
Forward
0 new messages