Tech Note - Okta Verify Bypass

A quick update - after publishing this tech note, we were notified that Adam Chester (@_xpn_) published similar research into Okta Verify/FastPass on his blog back in March. We're glad to see that this topic has gotten broad exposure in the security community, and if you're interested you should read his blog post, which covers not only attacks like this one, but also additional attacks against various Okta configurations. Enjoy reading!

Introduction

At GitLab, our Red Team conducts security exercises that emulate real-world threats. This helps us assess and improve the effectiveness of the people, processes, and technologies used to keep our organization secure.

During Red Team operations, our team often aims to hijack user sessions by stealing browser cookies. Typically, we will have an attacker virtual machine where we place stolen browser cookies and launch our own browser to assume user sessions. However, this approach does not work with Okta sessions due to Okta Verify's device verification process.

In May 2023, the GitLab Red Team discovered and responsibly disclosed to Okta a technique that could bypass Okta Verify's host attestation checks.

Okta has since released features to prevent this technique, and it is important to note that this was not a remotely exploitable vulnerability at the time of discovery. Instead, it was a bypass that could be used in very specific scenarios where an endpoint was already compromised by malware.

This tech note will explain the research background, detail the attack technique, and discuss potential mitigations and detection strategies.

Okta Verify Background

Okta Verify is a local application that runs on user devices and provides host attestation and user identity information. When logging in via Okta's Single Sign-on (SSO), the Okta web application communicates with the local Okta Verify application to do device attestation and verification. This prevents remote attackers from accessing Okta sessions, even if they obtain stolen login credentials or browser cookies. Okta Verify uses secure authentication methods like TouchID and/or Yubikey WebauthN, making this difficult to bypass.

In summary, Okta Verify prevents unauthorized access to user accounts from other devices, even if the user's password or browser cookies are stolen.

Okta Verify Web Flow

This section describes our research findings on how Okta Verify operates, but should not be considered an official guide.

When logging in to Okta, establishing an Okta session, or using Okta SSO, your browser makes a web request to the local Okta Verify application. On MacOS, Okta Verify (typically installed at /Applications/Okta Verify.app/Contents/MacOS/Okta Verify) binds a web listener on TCP port 8769. Typically, the Okta web application makes a series of requests to this endpoint (if available) to trigger a validation mechanism within the Okta Verify app. The requests made are:

OPTIONS /probe
GET /probe
OPTIONS /challenge
POST /challenge

The POST to /challenge contains a base64 encoded JWT payload as the challengeRequest parameter. Here is a sample decoded token (header and payload, JWT signature is omitted):

Header:

{
  "kid": "RbH3P_vRgT9xMF1QzCwF8ZqkYgxNSZgH1J5WwBVg0g0",
  "typ": "okta-devicebind+jwt",
  "alg": "RS256"
}

Payload:

{
  "iss": "https://<target>.okta.com",
  "aud": "okta.63c081db-1f13-1234-882f-e79e1e5e2da7",
  "exp": 1700741376,
  "iat": 1700741076,
  "jti": "ftxmarbSqcDs_bABl8UZohaaasZ7M2acRt",
  "nonce": "o3Vp1ysOFfOqvaaanX-g",
  "transactionId": "ftxmarbSqcDs_bABl8aaahpBasZ7M2acRt",
  "signals": [
    "id",
    "displayName",
    "platform",
    "manufacturer",
    "model",
    "osVersion",
    "serialNumber",
    "udid",
    "sid",
    "imei",
    "meid",
    "tpmPublicKeyHash",
    "secureHardwarePresent",
    "deviceAttestation",
    "deviceIntegrity",
    "screenLockType",
    "diskEncryptionType",
    "clientInstanceBundleId",
    "clientInstanceDeviceSdkVersion",
    "clientInstanceId",
    "clientInstanceVersion"
  ],
  "verificationUri": "https://<target>.okta.com/idp/authenticators/autlowku66CST4R8aaa7/transactions/ftxmarbSqcDs_bABl8UZohpBasaaa2acRt/verify",
  "mdmAttestationIssuers": [
    {
      "issuerDN": "MIGQMRMwEQYKCZImiZPyLGQBGRYaaaatMRQwEgYKCZImiZPyLGQBGRYEb2t0YTEWMBQGCgmSJomT8ixkARkWBmdpdGxhYjEdMBsGA1UECwwUMDBvNjR4cDQ2TlhBSVN4V1YzNTYxLDAqBgNVBAMMI09yZ2FuaXphdGlvbiBJbnRlcm1lZGlhdGUgQXV0aG9yaXR5",
      "aki": "RqVKgyXdDE7q2HKde5/aaaaTtpgQ="
    }
  ],
  "keyTypes": [
    "userVerification",
    "proofOfPossession"
  ],
  "orgId": "00o64xp46aaaaSxWV356",
  "appInstanceName": "Okta Dashboard",
  "method": "signed_nonce",
  "requestReferrer": "https://target.okta.com",
  "userId": "00uuhi53qnaaaa6rk356",
  "loginHint": "user@target.com",
  "userMediation": "REQUIRED",
  "userVerification": "PREFERRED",
  "ver": 0
}

It appears that this request to /challenge triggers the Okta Verify application to perform device attestation to verify the authenticity of the device. Okta Verify then sends a set of signals to a remote URL (likely verificationUri) to confirm the device is trusted. This attestation process happens out-of-band from the web SSO flow. Okta likely has a backend process that validates the attestation data and then allows the original SSO login to proceed. The attestation data, encoded as a JWT, is passed in a POST parameter named challengeRequest.

Bypass Okta Verify with a C2 Agent

Bypass Technique Overview

This architecture allows attackers to steal Okta's device verification capability from a compromised machine and authenticate to Okta using a stolen session from the safety of an attacker machine.

Typical session theft via cookie theft techniques do not work with Okta sessions due to Okta Verify's device verification process. Instead of simply needing the Okta session cookies, we also need Okta Verify to send attestation data to the verificationUri from earlier. To bypass this protection, we used malware with SOCKS proxy capability.

Using malware, the attacker does the following:

  1. Steals browser cookies in order to get the Okta session cookies (left as an exercise for the reader)
  2. Sets up the SOCKS proxy to proxy from the attacker browser into the victim machine using the malware
  3. Runs a port 8769 session forwarding HTTP listener on the attacker machine to capture and forward requests through the SOCKS proxy
  4. Establishes the stolen Okta login session on the attacker's machine
  5. Profit

This technique enables attackers to access compromised Okta sessions stealthily from their own machine.

A high level diagram of this attack technique is below.

High Level Attack Path Diagram

Component: Browse via C2 with SOCKS Proxy

To successfully forward requests, we need a way to capture requests from the attacker's browser and send them to the victim machine. Although this can be done a number of ways (SSH tunnels, dedicated SOCKS tool like chisel, etc), we used the C2 agent that we often use in operations, Poseidon as our SOCKS proxy. This allowed us to forward the port 8769 traffic as needed and make our malicious browsing appear to come from the victim's own machine. This helps bypass any sorts of detections that look for sessions being used on multiple machines, typically via IP.

Component: Verification Request Forwarder

By default, the web requests to localhost:8769 will not be sent through our SOCKS proxy. We wrote a script that binds to localhost:8769 on the attacker machine and forwards that traffic through the SOCKS proxy. The script captures incoming requests, extracts the challengeRequest JWT from the POST, and resends the request via the proxy to the Okta Verify app on the victim's machine.

You can access our full interceptor script POC in this GitLab project

Resulting Auth Flow

The resulting authentication flow is as follows:

  1. Attacker steals browser cookies from a user who has already authenticated to Okta. The attacker imports these cookies into their own browser.
  2. The attacker accesses the Okta application at https://target.okta.com/app/UserHome
  3. Okta sees the valid session cookies, but sends a series of requests to attacker_machine:8769 to validate device attestation via Okta Verify
  4. The script on the attacker's machine forwards these requests through the proxy to the victim machine
  5. Okta Verify on the victim machine receives the forwarded request, performs device attestation, and sends data to the verificationUri
  6. The Okta back-end validates the POST request and passes on successful attestation to the original request. The attacker's browser is allowed to proceed to https://target.okta.com/app/UserHome
  7. This process repeats for each SSO attempt, as long as the original session remains valid

Remediation and Detection

Okta Verify Controls

Legitimate requests to the Okta Verify app should originate from Okta-controlled application endpoints. If requests to the Okta Verify app were made over HTTPS and signed with a certificate that Okta Verify could validate, it would be much harder for attackers to intercept and forward the requests from the their machine to the victim machine.

We shared a draft report of this tech note with Okta and heard back from them within a couple days. Okta shared that their Early Access Trusted App Filters feature should mitigate this threat. This feature "allows orgs to manage which applications can invoke Okta FastPass in Windows, and in Chrome and Firefox for macOS. Specifically, trusted app filters enable the blocking of unsigned binaries and creating an allowlist of binaries."

Due to some test constraints we have around Early Access features, we have not re-tested the attack in an environment with Trusted App Filters enabled.

Theoretically, that feature should help prevent this attack, as our malware would be unable to forward the web requests to Okta Verify. However, attacker-in-the-browser attacks (using something like CursedChrome) could provide another attack vector. We plan on investigating further once Trust App Filters goes to General Availability.

Local Remediation/Detection

Defenders can look for indicators of this attack on user machines themselves. Firstly, attackers must steal browser cookies in order to hijack an Okta session. This can be done in various ways, such as stealing user keychain data or injecting a malicious chrome extension - techniques that can be detected on the host.

If you have host network telemetry, you could detect the man-in-the-middle forwarding of network comms to port 8769. Normally, only web browsers on your hosts should communicate with localhost:8769. If another process connects to that port, that could indicate an attack. Look for non-browser processes by their name, file location (typically browsers are in /Applications on MacOS), or whether they are signed.

Additionally, check your Okta logs for changes in the host operating system or user agent string, where your users are in the same session but have different OS's or UA's. If you can filter through noise around OS and browser updates, you may be able to catch the attacker machine/browser being different than the victim's machine/browser in the OS and UA.

Summary

This tech note detailed how the GitLab Red Team bypassed Okta Verify's device attestation and successfully used stolen browser cookies to clone Okta sessions. We provided recommendations to address the bypass indicators of compromise for defensive teams to detect these attacks, particularly in Mac environments. Similar techniques may also apply to Windows hosts.

At GitLab, we value feedback on our work. If you have any questions or comments, please open an issue in the project where we shared our session forwarding script.