Security advisory for Cargo

Note: This is a cross-post of the official security advisory. The official
post contains a signed version with our PGP key, as well.

The Rust team was recently notified of a security concern when using older versions of Cargo to build crates which use the package rename feature added in newer versions of Cargo. If you're using Rust 1.26.0, released on 2018-05-10, or later you're not affected.

The CVE for this vulnerability is CVE-2019-16760.

Overview

Cargo can be configured through Cargo.toml and the [dependencies] section to depend on different crates, such as those from crates.io. There are multiple ways to configure how you depend on crates as well, for example if you depend on serde and enable the derive feature it would look like:

serde = { version = "1.0", features = ['derive'] }

Rust 1.31.0 introduced a new feature of Cargo where one of the optional keys you can specify in this map is package, a way to rename a crate locally. For example if you preferred to use serde1 locally instead of serde, you could write:

serde1 = { version = "1.0", features = ['derive'], package = "serde" }

It's the addition of the package key that causes Cargo to compile the crate differently. This feature was first implemented in Rust 1.26.0, but it was unstable at the time. For Rust 1.25.0 and prior, however, Cargo would ignore the package key and and interpret the dependency line as if it were:

serde1 = { version = "1.0", features = ['derive'] }

This means when compiled with Rust 1.25.0 and prior then it would attempt to download the serde1 crate. A malicious user could squat the serde1 name on crates.io to look like serde 1.0.0 but instead act maliciously when built.

In summary, usage of the package key to rename dependencies in Cargo.toml is ignored in Rust 1.25.0 and prior. When Rust 1.25.0 and prior is used Cargo will ignore package and download the wrong dependency, which could be squatted on crates.io to be a malicious package. This not only affects manifests that you write locally yourself, but also manifests published to crates.io. If you published a crate, for example, that depends on serde1 to crates.io then users who depend on you may also be vulnerable if they use Rust 1.25.0 and prior.

Affected Versions

Rust 1.0.0 through Rust 1.25.0 is affected by this advisory because Cargo will ignore the package key in manifests. Rust 1.26.0 through Rust 1.30.0 are not affected and typically will emit an error because the package key is unstable. Rust 1.31.0 and after are not affected because Cargo understands the package key.

In terms of Cargo versions, this affects Cargo up through Cargo 0.26.0. All future versions of Cargo are unaffected.

Mitigations

We strongly recommend that users of the affected versions update their compiler to the latest available one. Preventing this issue from happening requires updating your compiler to either Rust 1.26.0 or newer.

We will not be issuing a patch release for Rust versions prior to 1.26.0. Users of Rust 1.19.0 to Rust 1.25.0 can instead apply the provided patches to mitigate the issue.

An audit of existing crates published to crates.io using the package key has been performed and there is no evidence that this vulnerability has been exploited in the wild. Our audit only covers the crates currently published on crates.io: if you notice crates exploiting this vulnerability in the future please don't hesitate to email security@rust-lang.org in accordance with our security policy.

Timeline of events

  • Wed, Sep 18, 2019 at 13:54 UTC - Bug reported to security@rust-lang.org
  • Wed, Sep 18, 2019 at 15:35 UTC - Response confirming the report
  • Wed, Sep 18, 2019 - Cargo, Core, and crates.io teams confer on how best to handle this
  • Thu, Sep 19, 2019 - Confirmed with Elichai plan of action and continued to audit existing crates
  • Mon, Sep 23, 2019 - Advisory drafted, patches developed, audit completed
  • Mon, Sep 30, 2019 - Advisory published, security list informed of this issue

Acknowledgments

Thanks to Elichai Turkel, who found this bug and reported it to us in accordance
with our security policy.

6 Likes

This seems like not the best way to handle this. If I'm a malicious actor, I now simply need to scan Crate.io for packages using this feature and then go squat the names. Might it not be better to prereserve/block all such names right now on Crates.io? Announcing it without any mitigations seems like it is inviting trouble.

1 Like

That wouldn't be an effective mitigation, as it would only cover crates published so far on crates.io. Proprietary codebases, open source projects not published on crates.io (like binaries) and future crates would still be affected by this vulnerability.

The only real solution here is to either upgrade to a supported Rust version, use a patched compiler from a distribution or apply the patches and compile rustc yourself.

1 Like

I take it there is not a way to specify a version field in Cargo.toml, so that a crate can opt-out of successfully compiling when built with an affected version of cargo?

I looked, but did not find one, perhaps a build.rs hack?

That's not implemented today, but there is an RFC in its final comments period adding a field in the Cargo.toml specifying the Rust version. That RFC doesn't cover all use cases (which are expected to be discussed in future RFCs), but it should cover the use case you mentioned.

build.rs hacks won't help you for this vulnerability: dependencies' build.rses are usually executed before the main crate's one, so an attacker would get code executed on the machine before your build.rs starts.

1 Like

I don't know if the problem I encountered could lead to a similar vulnerability. If you don't use -- before command line arguments, e.g., cargo run --features foo bar, bar gets interpreted as a feature when the previous version of cargo treated bar as an argument. If bar is a legitimate feature of some crate, the user's program could behave in an unexpected manner. I'm not devious enough to know if that's an exploitable vulnerability, but I expect some folks on this list are.

That doesn't seem exploitable to me.

By the way, if you think there might be a vulnerability in one of our projects please follow our security vulnerability guidelines and send an email to security@rust-lang.org with the details instead of posting about it publicly. Thankfully this wasn't the case, but if a vulnerability is disclosed before the security team had a chance to look at it and prepare a fix, the ecosystem might be endangered.

Also, don't worry about wasting our time if you are unsure whether you found a vulnerability: we're happy to triage incoming reports, and spam emails waste much more time anyway :upside_down_face:

1 Like

Still, given this, it's probably a good idea to add to the cargo "backwards compatibility checklist" to consider the behavior of old cargo upon seeing a new manifest.

One way to consider if something is "exploitable" is to ask, "Is it undefined behavior?". In this case, the answer appears to be "YES". So, I would not be too quick to consider this a non-exploitable thing.