department-of-veterans-affairs / va.gov-team

Public resources for building on and in support of VA.gov. Visit complete Knowledge Hub:
https://depo-platform-documentation.scrollhelp.site/index.html
282 stars 203 forks source link

Client-side cookie encryption vulnerable to brute force decryption #23867

Closed f1337 closed 3 years ago

f1337 commented 3 years ago

Summary

This request is based on an chain-exploit from Metasploit. It is based on our using an encrypted cookie for session storage.

Exploit outline

  1. Visit va.gov, login, get encrypted session cookie from vets-api.
  2. Brute force decrypt the encrypted session cookie, getting the contents and the encryption key.
  3. Add arbitrary Ruby objects (Kernel, for example) into the decrypted session contents.
  4. Re-encrypt the contetns using the key obtained in step #2, replacing the old cookie.
  5. Connect to vets-api, sending the altered encrypted session cookie.
  6. Rails will decrypt the cookie, then Marshal.load everything in the contents, including protected class access.
  7. Get a shell via Kernel.exec, then get our MPI certificates, then have full access to MPI.
  8. If the session cookie is encrypted using the same Rails secret key as the database, then the actor will also have full access to decrypt all the PII we cache.

Suggested Remediation

@f1337 recommends we start rotating session keys, or move to server side session storage. However, database sessions can be SLOW, so consider storing sessions in redis, shared memory, etc.

AC

ericboehs commented 3 years ago

We should also use a separate key for our session encryption vs our database encryption. More investigation on how this cookie encryption key is set/stored is needed.

ericboehs commented 3 years ago

This has been bumped in priority. Let's pick it up next sprint.

LindseySaari commented 3 years ago

Great article comparing/contrasting various session storage options

LindseySaari commented 3 years ago

This gem may be exactly what we're looking for!

Scratch that... it looks like this is only for signed key rotation "Graceful secret key rotation for the signed cookie store in Rails"

LindseySaari commented 3 years ago

Here's an example that gracefully rotates the keys

LindseySaari commented 3 years ago

va.gov has two cookies that I'm aware of. The api_session cookie belongs to the vets-api session, but we also have an sso_cookie, which pertains to the shared cookie between MHV and va.gov. If we ever do rotate the sso key, this will need to be coordinated between va.gov and MHV (https://dsva.slack.com/archives/CQHBJ5U06/p1624652893152200?thread_ts=1624650862.151000&cid=CQHBJ5U06)

The sso cookie is custom encrypted via the SSOEncryptor/Aes256CbcEncryptor whereas the vets_api_session cookie is encrypted/decrypted via the the secret_key_base value

LindseySaari commented 3 years ago

vets-api Key Rotation

ZenHub Ticket

This explains how session cookies are used in vets-api along with encryption methods. Also explained is how vets-api encrypts db attributes with proposed solutions on how to rotate encryption keys gracefully.

Session Cookies in vets-api

Background

The vets-api api_session cookie gets encrypted using the apps secret_key_base The secret key base is stored in credstash and accessed via settings.yml. Each environment has it’s own secret_key_base, located in the devops repo. Rails 5 introduced encrypted creds, which is typically where the secret_key_base would be stored, but vets-api uses credstash for sensitive key storage.

Since vets-api is in api_only mode, out of the box it doesn’t include the session/cookie middleware. The middleware was extended in order to add back the session layer. See application.rb here.

Proposed Solution

The proposed solution is to gracefully rotate out the old keys (secret_key_base) in order to avoid logging users out that have a current session.

Upon visiting vets-api with a session encrypted with the old key, the session will be rotated and re-encrypted with the new secret_key_base value. Given this, currently logged in users have a chance to visit vets-api, get their cookie read with the old_secret_key_base and have it rewritten with the new secret_key_base.

I’ve created a branch and started to poke at the cookie_rotation initializer which will gracefully rotate cookies.

In the devops repo, I’ve created a branch that adds the settings config for the secret_key_base and old_secret_key_base values. I’ve created the old_secret_key_base values in credstash, which are a copy of the current secret_key_base values. I still need to create version 2 of the secret_key_base values. I believe you can generate new keys via the rails secret command.

Malicious User Scenario

Currently, local and dev environment login is broken (see slack conversation) so I did a test in staging. I was able to successfully decrypt the cookie, given that I had the secret_key_base. A malicious actor could hijack a session. See the steps below:

Steps to reproduce:

  1. Logged in - staging environment
  2. Grabbed my session cookie (api_session)
  3. Grabbed the staging secret_key_base out of credstash (pasted into secret_key_base locally in Settings.yml)
  4. Started up the rails console
  5. Copied the decrypt_session_cookie method from the cookies_spec
  6. Called the method and passed in my cookie as a parameter
  7. Session was successfully decrypted

Next steps for api_session cookie key rotation:

Questions & Unknowns?

Deploy strategy

Questions:

Here are some of the articles that I found useful:

DB encryption/attr_encrypted gem:

sso_session cookie

Background info