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
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:
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
.
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
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.
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.
- When
ms-teams.exe
is running - 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:
After retrieving the chatService and skypeToken tokens, we can, for example, fetch the messages sent by the account.
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:
Since the access token comes from Microsoft Teams, our interactions are limited to the Microsoft Teams scope within the Microsoft Graph API: