How Sigstore quickly patched an upstream vulnerability


Summary

On October 3, 2022, Dex, the federated identity provider that Sigstore uses to issue identity tokens, published CVE-2022-39222 with a GitHub Security Advisory. Sigstore was vulnerable to this CVE, but we were able to quickly mitigate the vulnerability in June before an official fix was published.

Details

On June 13, 2022, Joern Schneeweisz from the GitLab Security Research Team disclosed a vulnerability to Sigstore where an attacker executing a phishing campaign against a user can acquire a user’s identity token through a backchannel. Upon further investigation, we determined that the vulnerability was in Dex, the federated identity provider that Sigstore uses to issue identity tokens.

At a high level, when a user requests an identity token for Sigstore, Dex federates the authentication to a third-party connector such as Google, Microsoft or GitHub. After verifying the identity token from the connector, Dex constructs an identity token for Sigstore.

As part of the OAuth/OpenID Connect (OIDC) authorization flow, Dex constructs a code that can be exchanged for an identity token. Dex also provides an API method to fetch a generated OAuth code given a request ID. The request ID comes from a certain parameter, state, on the request to the federated identity provider. A typical life of a request is as follows:

  1. A user is directed to oauth2.sigstore.dev/auth to select a federated connector. This will include a callback URI so that Dex can redirect the user back to the client that requested the login.
  2. A user selects a federated connector, such as accounts.google.com.
  3. Dex constructs a redirect to the federated connector. This includes a callback URI so that the federated connector redirects back to Dex, and a state parameter that is persisted by Dex.
  4. The user is redirected to the federated connector, and authenticates. The connector redirects the user back to the callback URI. The redirect will include the state parameter and a new code parameter.
  5. Dex handles the callback, exchanging the code for the user’s identity token from the federated connector, persisting the claims of the token. Dex generates an OAuth code. Then Dex redirects the browser to a Dex /approval endpoint with the state request ID.
  6. Dex uses the request ID to look up the OAuth code, and builds a redirect to the original callback with the code.
  7. The client will then call Dex to exchange the code for a Sigstore identity token.

The vulnerability was due to a predictable request ID. An attacker that initiates a login request to Dex could intercept the state parameter and attempt to request a code from /approval before the user can. The phishing attack is as follows:

  1. A victim navigates to a malicious website.
  2. The malicious webserver initiates a connection with a Dex instance directly for a specific federated connector — https://oauth2.sigstore.dev/auth/auth/https://accounts.google.com?access_type=online&client_id=sigstore&nonce=1AaJAimQU9CbeOFsNra1d7CJTWB&redirect_uri=http://localhost:40393/auth/callback&response_type=code&scope=openid+email&state=1AaJAjhpUmsB25csCo5muvorMTl
  3. Dex returns a 302 Redirect to the federated connector — https://accounts.google.com/o/oauth2/v2/auth?client_id=237800849078-hri2ndt7gdafpf34kq8crd5sik9pe3so.apps.googleusercontent.com&redirect_uri=https://oauth2.sigstore.dev/auth/callback&response_type=code&scope=openid+email&state=g3dkmpontsr4ugocoddjx72ef. The attacker records the state parameter value g3dkmpontsr4ugocoddjx72ef which will be used as the request ID later on.
  4. The malicious website redirects the victim’s browser to the federated connector.
  5. The user authenticates to the connector. If they have authenticated before, they may not be presented with an authentication challenge.
  6. While the user goes through the authentication flow with the connector, the malicious webserver will repeatedly request /approval?req=g3dkmpontsr4ugocoddjx72ef. Once the user has successfully authenticated, if the webserver is able to call /approval before the victim’s browser calls /approval, then an attacker can fetch the Dex OAuth code which can be exchanged for an identity token.

Response and Fix

On June 14, Sigstore maintainers reached out to Dex maintainers to report the vulnerability. Dex acknowledged the vulnerability, created a draft GitHub Security Advisory, and Sigstore and Dex collaborated on a patch to fix the vulnerability. The vulnerability was assigned CVE-2022-39222 (GitHub Security Advisory), and Dex published a fix for the vulnerability on October 3, 2022.

Sigstore maintains a private fork of the Dex repository to quickly patch bugs or customize behavior. This allowed Sigstore maintainers to quickly patch the vulnerability and roll out a fix by June 14.

To mitigate this vulnerability, we added message authentication to the /approval endpoint in the form of an HMAC. To construct an HMAC, a secret value is concatenated with a message before being hashed. It is cryptographically impossible to reverse the hash, nor is it possible to construct the HMAC without the secret value. The /approval endpoint will require that an HMAC be presented along with the request ID. The only way to get the HMAC is through the redirect from the federated connector to Dex (/callback), and the attacker cannot intercept this redirect. Requests from an attacker repeatedly requesting the /approval endpoint will be rejected.