That vendored OpenSSL 🦀 most of us rely on probably needs a patch ..

MissMissM (she/her)
Rustaceans 🦀 Security
6 min readJun 19, 2022

--

Photo by Diana Polekhina on Unsplash

Security advisories and patches are part of reality of life and there is nothing new about them. The problem I’m exploring here is more akin the binary of the Rust 🦀 app you and your users might have built yesterday might need re-building today.

It’s very easy to miss or disregard this — for example that typical vendored OpenSSL — a convenient wrapper-fetcher-builder that brings the OpenSSL C library — has been often statically linked into things we typically build while working in Rust 🦀 — whether we Rustaceans 🦀 realise it or not — that requires any kind of SSL/TLS use with a few exceptions.

Reality Disclaimer — What this post is not about

It is also too easy to direct blame to what some consider “legacy” OpenSSL C-library — I would not cast any blame or dictate that the vendored library is bad — it’s a long topic on it’s own.

Neither is the vendoring as this simply provides much needed convenience to make things conveniently happen for both the developer and users stat whilst managing to stay out of everyone’s way so much that we don’t realise it is even there — for both good and bad.

That OpenSSL C-Library has seen significant battle testing and development over the years and there has been numerous attempts to come up with alternatives including Rustls that is Re-written in Rust (RiR)

Back to The Problem

If your Rust 🦀 app simply talks to the internet — there is a chance of this using the vendored OpenSSL C-Library and being vulnerable if you haven’t re-built it for a while.

Another problem with the current tooling attempting to deal with the static’y nature of Rust 🦀 dependency linking being is what ever you — or your users — compiled and linked a month ago — may be vulnerable as well whilst you as a maintainer perhaps might have patched your current version — either explicitly via Cargo.lock or implicitly by Cargo possibily picking up the latest fixed version.

This is different to dynamically linking to “system” dependencies where one can just replace a dependant shared system component — like in Linux with those .so’s — without re-building the whole dependency chain stuffed typically into one binary by statically linking when building the Rust target.

Cargo.lock in your repo is the current but not the perfect best practice

Easiest way to check whether your app uses say a vulnerable vendored OpenSSL C-Library — for whatever you compile Today but not what compiled Yesterday — is when you build your project is to look at the Cargo.lock file and see if any / what openssl-src you will get to find out if something you will compile Today will be vulnerable — whatever you compiled Yesterday will be different.

This is why it is a good idea generally to leave your Cargo.lock in your repository so there is a history of what exact dependencies were probably / perhaps used to compile your binary given a look back in time.

However this is only one environment / feature combination representative snapshot — e.g. your Cargo.lock generated in Mac OSX/Linux with full featureset may indicate vulnerable but Windows binaries with less features might be not and so on — but it will be a great start!

So how do vendored OpenSSL versions reflect C-Library versions .. ?

That vendored OpenSSL or the openssl-src crate typically builds from the OpenSSL 1.1.1 stream where the OpenSSL C-Library version that it brings in is reflected via the openssl-src crate versions if you look what comes after the plus sign of the crate version openssl-src itself is.

RustSec has a database on per crate basis and you can lookup from GitHub repository per crate basis for openssl-src to see all the related advisories.

This advisory database in turn is synced up to GitHub Advisory Database (GHSA) from the RustSec database.

GitHub dependabot might have pinged you already about it

If you would have added Cargo.lock to your GitHub repository then dependabot — if you’ve chosen to use it that is — would have started recently to ping or pester you about this if your .lock file has picked it up previously.

We’ve recently ensured that the dependabot will not pester anyone to switch into 300 stream reflecting OpenSSL 3.0 C-library unnecessarily for anyone who is using properly patched 1.1.1 OpenSSL version.

However dependabot will not ping you either if your Cargo.lock was not in the repo and / or dependabot was not simply enabled in your repository.

Neither will dependabot will ping if you or any of your users who have in the past built a binary using a vulnerable version of OpenSSL C-Library where the things get tricky —

But how about the existing binaries?

However a big problem now is that what was built a while ago may be vulnerable and should be re-compiled stat —

This is why it is crucial to have some auditability for your binaries.

Current tooling is living “In the Now” ..

The usual tooling — considered stable — in this space neglects any binary checks and uses at best the Cargo.lock — either the good practice recommended repository derived one or less recommended ad-hoc derived Cargo.lock via Cargo.toml to see what your dependencies are today.

However if you have built / deployed something over a month ago — or some of your users (e.g. library) have — involving vendored OpenSSL then they will be vulnerable and probably without knowing this fact.

Cargo Audit to the rescue?

Cargo audit only checks your Cargo.lock — the same what dependabot uses — but there is another solution by the very same author — Shnatsel (GitHub) who has also developed auditable binary format ..

So what can we use to audit those already compiled binaries?

If you’ve been on the edge and already used Shnatsel’s rust-audit (GitHub) — which introduced binary embedded auditable format in Reddit post via auditable crate — but is not stable yet.

Shnatsel has worked on rust-lang/rfcs#2801 Request For Comments (RFC) for it in effort to stabilise this — and if you’ve used this already then all kudos to you!

Thanks Shnatsel for all the hard work making this happen!

Simply check and cross-check the timestamp of the compiled binaries

But however for the rest of us alternatively — for now — considering the criticality of this we could also just simply check the timestamp of the compiled binary.

And if the binary is more than a month old..

Then you should be re-compiling it.

I hope this helped / helps someone

I hope this helped someone to gain more insight into what is going in the security side of things — whether it was something you compiled Today or Yesterday — and perhaps avoid any potential exploits that are out there for OpenSSL now and in the future.

People might also consider the alternatives like rustls but vendored OpenSSL still has it’s uses and the story around OpenSSL dependency is more complicated that can fit into this post.

Request-level isolation the next step in bringing another isolation layer

I’ll be writing posts in the future around how to do request-level isolation in Lunatic Erlang+Elixir/OTP inspired WebAssembly-WASI VM/Runtime to further mitigate any exploits that some C-code like OpenSSL might bring in the future — security is about layers and I am excited what is happening on this space to mitigate these riks.

--

--