Brahim El Fikhi 4 min

From a red team’s perspective, it could be interesting to retrieve access tokens, as they allow the access of confidential information such as Teams chats, emails, SharePoint documents, etc. Moreover, access tokens can also be used to send emails or Teams messages on behalf of the user, which can be leveraged for lateral movement or social engineering attacks.

In this article, we will explore how to extract access tokens from Office desktop applications, with a particular focus on Microsoft Teams. The main goal is to retrieve the access tokens stored on the disk.

Table of Contents

State of the art

Initially, the auth_cookie was stored in plaintext inside the SQLite database Cookies, which was located inside the following folder:

  • AppData\Local\Microsoft\Teams\Cookies

This technique was presented by Vectra.

However, this configuration has changed on recent Microsoft Teams versions and the access token is not stored anymore in plaintext inside the SQLite database.

Other techniques were developed to collect the access token in memory (see Mr.D0x blogpost).

Analysis

To locate where the authentication tokens are written, we used ProcMon from the SysInternals tools with the following filters:

  • Operation is WriteFile
  • Process Name is msedgewebview2.exe

Procmon result

Why targeting msedgewebview2.exe instead of ms-teams.exe?

During authentication, an embedded browser window opens and displays content from login.microsoftonline.com. From the Task Manager, we observed that ms-teams.exe spawns a child process named msedgewebview2.exe. When analyzing the ms-teams.exe process with ProcMon, we noticed that aside from log files, no interesting files are written during authentication. However, when analyzing the msedgewebview2.exe, we observed that it behaves like a Chromium-based browser and writes to the Cookies file (as shown in the screenshot above).

Extraction of the authtoken

By analyzing the Cookies database, we identified three interesting fields: Cookies db

  • host_key: domain name to which the cookie is linked.
  • name: name of the cookie.
  • encrypted_value : value of the encrypted cookie

We observed that the value of encrypted_value contains a constant tag present in all encrypted cookies: 0x76 0x31 0x30, which corresponds to v10.

Encrypted cookie

This tag defined the version of Chromium

According to the Chromium source code, this encryption format corresponds to the use of DPAPI (Data Protection API) for securing cookie values.

The key used to encrypt the cookie values is stored in the following JSON file:

  • C:\Users\user\AppData\Local\Packages\MSTeams_8wekyb3d8bbwe\LocalCache\Microsoft\MSTeams\EBWebView\Local State

The encrypted_key is inside the os_crypt field as shown below:

"os_crypt": {
    "app_bound_encrypted_key": "xxxx",
    "audit_enabled": true,
    "encrypted_key": "RFBBUEkXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
  },

We guessed that DPAPI was used because, when the Base64 value was decoded, the value starts with DPAPI prefix - which explicitly indicates that the key is encrypted using DPAPI.

Once the key is recovered, we can get the nonce of the encrypted cookie, allowing it to be decrypted.

  • The first 3 bytes are for the tag (e.g. “v10”)
  • The 12 following bytes are for the nonce
  • The remaining bytes corresponds to the encrypted part

Encrypted cookie schema

Now, it is possible to decrypt the cookie using AES-256-GCM, according to the Chromium documentation.

Proof of Concept

We successfully developed a Proof of Concept in Rust to dynamically retrieve the DPAPI key, decode it and decrypt the cookie to extract the Teams access tokens.

Decrypted Cookie

Limitation

One of the limitations is that if the ms-teams.exe application is running (even in the background), it’s impossible to open the Cookies file because the process locks it. So you need to kill first ms-teams.exe to get access to Cookies file.

File locking of Cookie

  1. When ms-teams.exe is running
  2. After ms-teams.exe is killed

How to use the access token

Manually

To interact with the Microsoft Teams Graph API, you need two tokens:

  • skypeToken
  • chatService

To retrieve these tokens, the following requests can be performed:

skypeToken

chatService

After retrieving the chatService and skypeToken tokens, we can, for example, fetch the messages sent by the account.

Message send

There are some interesting API endpoints:

  • Retrieve Teams conversation (GET request): <chatService>/v1/users/ME/conversations?view=msnp24Equivalent&pageSize=500
  • Read messages on Teams conversation (GET request): <conversationlink>?startTime=0&view=msnp24Equivalent&pageSize=200
  • Send messages on a Teams conversation (POST request): <conversationlin>/messages with this body
{
  "messagetype": "RichText/Html",
  "content": "Hello from API",
  "contenttype": "text"
}

GraphSpy

After retrieving the cookie, it can be leveraged with the post-exploitation tool GraphSpy to interact with Microsoft Graph API, allowing reading or sending emails, accessing or sending Teams messages, and more - all in the context of the compromised user.

To use it, we simply need to insert the auth_token recovered into GraphSpy and from there, we can start exploring:

auth token in GraphSpy

Since the access token comes from Microsoft Teams, our interactions are limited to the Microsoft Teams scope within the Microsoft Graph API:

Microsoft teams on GraphSpy

Resources