Foreward
I recently taught a InfoSec class around Chrome DFIR, and created a few custom scripts. Some of these scripts could easily be weaponized to harvest credentials, hijack web sessions, and gather additional attributions and metadata between the target and their browsing patterns. As a result, the specific scripts will not be shared, however we will provide how similar scripts can provide an positive impact, in a different perspective, with a few expectations: the user is explicitly prompted for credentials, and can cancel/close the script when prompted prior to returning any information.
In A Different Light
With the majority of corporate and enterprise tools migrating to SaaS or cloud-first architecture model from native client applications, automating and extending functionality has reached a new level in the information revolution. In the days of native apps, ideas and “ah-ha!” moments which sparked creativity would be snuffed when one would come to realize accessing GTK, WinUI or Cocoa elements would be nearly impossible without bandaids and hacks. Today, with the rise in web-based applications, accessibility and reachability was met with both a consistent transport mechanism, HTTP, and rendering framework, the HTML DOM.
The majority of applications out there might already expose access to system data through a sanctioned REST or GraphQL interface, but sometimes, that data can only most commonly be accessed via the browser. As part of the flow, you login to the application which stores local cookies for managing your access to the application. The cookies would then be sent to the application in future requests to maintain session state.
What if there was no API? How could you programmatically get access to the application and query it’s data?
Fresh out of the Oven
Chrome Cookies are stored in a sqlite3 database on the local filesystem. This differs from how many people remember Internet Explorer 11 cookies on Windows, where they were written to a flat file, unencrypted. Thankfully, sqlite3 is a SQL-compliant standard and makes interacting with the data easily manageable.
When looping querying Cookies, there are several pertinent fields which are returned, including Expiry timestamps, domains, the name, path and values. Also included in the dataset is a column named “EncryptedValue” which requires an additional decryption key in order to read it. On macOS, this key is stored within the Keychain.
Unlocking the Cookie Jar
On macOS, Chrome will store the decryption key within the Keychain. It’s stored with the Service named, “Chrome Safe Storage”, and with the account named, “Chrome.”
When querying the keychain, depending on the request type and keychain item location, the user will be prompted with a popup requesting the local users’ password to authorize the query.
Using the go-keychain package, we are able to structure a keychain query and return the decryption key for the Chrome cookies.
Putting it Together
Once you have the cookies, and the decryption key, getting the raw cookie value is easy. This cookie can then be added to the headers of outbound HTTP requests, which would act as if a browser was used to make the request. There are times where this approach will not work, such as when Cookies are validated service-side to match issuing User-Agents, or when a cookie has expired.

Member discussion