Decrypting Zoom Team Chat: Forensic Analysis of Encrypted Chat Databases
- Introduction
- Analyzing the Disk Image and Identifying the Ransomware
- Tracing User Activity: Chrome and Discord
- Zoom Team Chat Artifact Discovery and Analysis

Introduction
Zoom Team Chat is becoming a common communication tool in many organizations, but digital forensic analysis of its artifacts is rarely discussed. Beyond meetings and calls, chat messages, shared files, and conversation metadata stored by Zoom Team Chat can reveal important traces about user activities and interactions.
In this article, I’ll explore what kind of evidence can be recovered from Zoom Team Chat, based on a real CTF challenge I created. I’ll also include a short walkthrough of the overall challenge flow, leading up to the discovery of Zoom Team Chat artifacts, for those interested in the full picture.
If you’re interested in the full challenge background, scenario, and attachments, you can read the detailed description here
If you’re only interested in Zoom Team Chat analysis, feel free to skip ahead to Zoom Team Chat Artifact Discovery and Analysis.
Analyzing the Disk Image and Identifying the Ransomware
We were given a disk image (.ad file) to analyze. After loading it into FTK Imager, the first thing we noticed was that a lot of files were encrypted. It was clear that the system had been hit by ransomware.
When looking for program execution artifacts, we also found that registry data was broken or incomplete, making it difficult to trace what had been run on the system. Since common artifacts were either encrypted or damaged, we had to look for alternatives. One of the artifacts that still existed was Windows jumplists, which record data about programs recently executed or files opened.
C:\Users\xxx\AppData\Roaming\Microsoft\Windows\Recent\CustomDestinations
Using eric zimmerman’s jump list explorer tool, we can see that there is a suspicious hta file in the user data folder, and we know that chromesetup should use exe not hta.

I won’t go too deep into the encryption logic, the point is that there is obfuscated javascript that can be deobfuscated with online tools, and the full decryptor is included here.
Tracing User Activity: Chrome and Discord
While exploring the Documents folder, we found a file named “My todo list.pdf”, which contained a list of tasks. One of the entries mentioned:

This line suggested that the current Windows password might still be weak or unchanged, which could become useful later.
According to the challenge description, the user was last seen spending time on Google Chrome. So from there, we moved on to analyzing Chrome’s browsing history to look for more evidence of user activity.
C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\History
With sqllite viewer, we can check the history in the url table and also see that the user accessed discord from chrome browser.

To recover messages on discord accessed with a browser, we can try analyzing the cache of chrome and parsing it with chromecacheview.
C:\Users\xxx\AppData\Local\Google\Chrome\User Data\Default\History\Cache\Cache_Data

There are 2 discord chats that lead us to analyze the zoom chat artifact which is the main point of this challenge.

Zoom Team Chat Artifact Discovery and Analysis
After following the trail from Chrome and Discord, we turned our attention to Zoom, suspecting that important evidence might be stored there. Specifically, we were looking for traces of Zoom Team Chat, which could reveal user communications and shared files. Zoom stores its application data under:
C:\Users\xxx\AppData\Roaming\Zoom\data\
Inside this data folder, we found two important files:
- Main database directly under the data folder, used to store general Zoom account and session information (zoomus.enc.db).
- User specific database inside a subfolder named with the user's XMPP JID, which contains the Zoom Team Chat data (zoomus.async.enksdb).
C:\Users\xxx\AppData\Roaming\Zoom\data\<XMPP JID>\
Here’s where it gets interesting:
- Both databases are encrypted using SQLCipher with custom parameters, page size 1024 and KDF iterations set to 4000.
- Zoom uses different keys to encrypt each one.
- The user_key needed to decrypt the user specific database can only be derived after obtaining the main_key linked to the main database in the data folder.
This layered key setup makes analyzing Zoom Team Chat data more complex than typical app data.
Finding the main_key
In the Zoom data folder, there’s a file named zoom.us.ini that contains the key to decrypt the main database. But this key is encrypted with DPAPI.
The key is stored as a base64-encoded string under the variable name win_osencrypt_key, and it starts with the marker ZWOSKEY. To extract the actual encrypted key, it’s necessary to parse the value of win_osencrypt_key and strip the ZWOSKEY prefix, leaving only the base64-encoded DPAPI blob.

Noticing a reference to a weak Windows password in the PDF, cracking it became the next step. The encrypted master key needed for DPAPI decryption is stored in:
C:\Users\xxx\AppData\Roaming\Microsoft\Protect\<SID>\
Using dpapimk2john, the master key was converted to a hash and cracked with John the Ripper and the rockyou wordlist.
DPAPImk2john --sid="S-1-5-21-676146416-2632849227-3912224545-1000" --masterkey="883366a6-1eea-45de-b0b4-37c38ada44f6" --context="local" > hash.txt
john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt

After cracking the Windows local password, the next step is to decrypt the DPAPI-encrypted master key. This can be done using Mimikatz, which allows us to recover the master key needed to decrypt DPAPI-protected data. Once the master key is decrypted, it can be used to decrypt the win_osencrypt_key value extracted from zoom.us.ini.
dpapi::masterkey /in:dbb54711-8c3e-4a0a-b585-cfae51bbe54d /sid:S-1-5-21-676146416-2632849227-3912224545-1000 /password:chizuru /protected
dpapi::blob /masterkey:a77bac8f85212e3e1bdcc7f9bcb97776ca5d4458aaa4b498664abfa7b4c3ca967931949074118ffde80f31e69623b270457029c988fc8e8cdb44819376130085 /in:"keyblob" /out:keyblob.txt

Decrypting main database
Although the goal is to obtain the key to decrypt the user specific database, decrypting the main database isn’t required. However, to solve the challenge, there is one important piece of information inside the main database which is the email address used for the Zoom account (If you want to know all tables and fields, you can check this link).
The email is stored in the zoom_user_account_enc table, under the uid column.

All other fields in that table are encrypted. Decrypting those fields is not necessary to solve the challenge, but it is possible. If you want to decrypt other fields such as uname or zoom_uid, you can use the script provided below:
from Crypto.Cipher import AES
from Crypto.Hash import SHA256
from base64 import b64decode
main_key = ""
key = SHA256.new(main_key).digest() # Derive AES key from main_key
raw = b64decode("encrypted_field_value")
iv, tag, data = raw[1:13], raw[-16:], raw[19:-16] # Extract IV, authentication tag, and ciphertext
plaintext = AES.new(key, AES.MODE_GCM, iv).decrypt_and_verify(data, tag) # Decrypt and verify
print(plaintext.decode('utf-8'))
Decrypting user specific database
To decrypt the user-specific database, you need the user’s Zoom credentials both email and password — or an active user session tied to the Zoom account. Unfortunately, this is a major limitation when analyzing Zoom Team Chat artifacts. Unlike other apps that store everything locally, Zoom does not store all the required keys on disk. Instead, it relies on a key called kwk (Key Wrapping Key), which is:
- Unique for each user.
- Stored securely on Zoom’s server, not saved in any local file.
To obtain the kwk, Zoom requests it dynamically during login or session refresh. To obtain the kwk, Zoom requests it dynamically during login or session refresh. Session refresh happens when Zoom re-establishes its session with the server — for example, when the app is reopened while the user is still logged in but hasn’t entered credentials again.
This means capturing kwk requires monitoring the Zoom application at runtime, while it’s actively communicating with Zoom servers. For this purpose, RhobitoB API monitoring can be used to intercept and log API calls made by Windows applications, including Zoom. This allows capturing the moment kwk is fetched from the server and extracting it for later use in decrypting the user-specific database.
How user_key is Derived
Once both main_key and kwk are available, Zoom performs the following steps to derive the user_key used to decrypt the user specific database:
- Take the first 42 bytes of the main_key, then calculate its SHA256 hash.
- Calculate SHA256 hash of kwk.
- Concatenate both SHA256 hashes (as hex strings), then convert the concatenated string from hex to bytes.
- SHA256 hash the resulting byte data.
- Base64 encode the final SHA256 hash
Recap on This Challenge’s Case
In this challenge, we already obtained:
- The Zoom email address from the zoom_user_account_enc table in the main database.
- The Zoom password, which was found in Discord chat logs, described as a combination of the Windows local password plus _Arkavidia_1337!!!.
So, for this specific scenario, we had everything needed to proceed — except for the kwk.
Capturing kwk
To capture kwk, Zoom’s external DLL responsible for cryptographic operations must be monitored. Zoom uses libcrypto-3-zm.dll to handle SHA256 calculations.The simplest way is to hook the SHA256 API and inspect the data passed to it. This DLL is located at:
C:\Users\xxx\AppData\Roaming\Zoom\bin_00\libcrypto-3-zm.dll

Profit
With all the required elements — main_key and kwk — collected, we can now derive the user_key and decrypt the Team Chat database. The following Python script calculates the user_key:
import hashlib, base64
main_key = b'L4jYqZnRF/ZrwJuMcVvPOFqklFzqtMPj554VF82B9g' # First 42 bytes of main_key
kwk = b'nB9oO3Kg8XA+gzd6O+k8YMq+iGCpDmHAe9m0iqtJY3w='
h1 = hashlib.sha256(main_key).hexdigest()
h2 = hashlib.sha256(kwk).hexdigest()
final = hashlib.sha256(bytes.fromhex(h1 + h2)).digest()
print(base64.b64encode(final).decode())
Once we have the user_key, the Zoom Team Chat database can be decrypted, and here is a sample of the recovered chat data (If you want to know all tables and fields, you can check this link):


Conclusion
Analyzing Zoom Team Chat artifacts is not straightforward — it requires both local keys and the server-side kwk, which makes full decryption challenging. In this case, we managed to recover all required data by combining forensic analysis and API monitoring. Note that this article focused on Team Chat artifacts — other encrypted databases, meeting records, call logs, and local configurations are also part of Zoom’s ecosystem but were not covered here.
Thank you for reading, and I hope this write-up was useful. Feel free to share any thoughts or feedback.