![](https://malwaredevil.com/wp-content/uploads/2020/12/16Xt2oBXZKwpWxrSCy9N7QQ-fNTTXr-150x150.png)
Introduction
This blogpost will describe the concept of cookie theft against Chromium browsers and how this technique can be utilized to access web applications without credentials. This technique is mapped to MITRE ATT&CK under Steal Web Session Cookie (T1539).
On a recent engagement, my team was ceded access on a macOS box as a low-privileged user and needed access to Google Chrome cookies. Cookies are an extremely useful form of credential when up against an environment with services behind some form of single sign-on. By stealing cookies, an attacker can authenticate to web applications, without plaintext credentials, and bypass defensive controls such as multi-factor authentication (MFA).
Conveniently enough, a thread within the #mythic channel of the BloodHoundGang Slack discussed this exact topic:
![](https://cdn-images-1.medium.com/max/861/1*6Xt2oBXZKwpWxrSCy9N7QQ.png)
Within the thread, there was a fantastic blogpost from 2018 by @mangopdf that mentioned a method of stealing cookies using Chrome’s remote debug port. My coworker, @hotnops, was quickly able to write a Python script to gather the cookies from Chrome using the technique documented in the blogpost to achieve our objective.
After the engagement, I wanted to implement the technique myself in Golang for two reasons:
- Learn more about how the technique works under the hood
- Teach myself Golang by diving into a project
The tool can be found on my GitHub here: https://github.com/slyd0g/WhiteChocolateMacademiaNut
Before diving into the tool’s implementation, let’s understand what is happening under the hood.
Note: All credits go to @mangopdf for the original discovery of this technique, this work wouldn’t have been possible without it.
HTTP Cookies
At a high level, cookies are a small piece of data that keeps track of a user’s current “state” with a website.
Cookies are key-value pairs that have a name (life) and value (42) field. Each cookie is applicable to a specific domain (https://google.com) and a path (/). I’ve shown below how Chrome DevTools has defined the Cookie object:
![](https://cdn-images-1.medium.com/max/831/1*dsa9egH2Am0q2pkMJll-tg.png)
As attackers, we are particularly interested in cookies that are given to users after they have successfully authenticated to a website. These “authentication” cookies are how the website knows the user has already logged in so they don’t have to type their username/password again on their next visit.
How Google Chrome Protects Cookies
Google Chrome encrypts cookies differently depending on the operating system (OS).
- On macOS, Google Chrome’s encryption key is stored within the Keychain which can be unlocked with the user’s plaintext password
- On Windows, Google Chrome version 80+ encrypts cookies with an AES-256 key that is encrypted with DPAPI
By starting Chrome with a remote debug port, we are able to bypass this encryption and utilize Chrome to decrypt the cookies for us.
Chromium Remote Debugging
Note: This attack applies to all Chromium based browsers including Google Chrome and the new Microsoft Edge
--remote-debugging-port=9222
Chromium provides the ability to specify a remote debugging port from the command line, which allows us to debug and interact with the browser.
--headless
Additionally, Chromium allows us to run it in headless mode. This means it can be run without a window displayed to the user (!). This is great for attackers since they can start Chromium with a debug port completely transparent to the user.
--user-data-dir=/path/to/data_dir
This flag allows us to specify the user’s data directory, where Chromium stores all of its application data such as cookies and history.
If you start Chromium without specifying this flag, you’ll notice that none of your bookmarks, favorites, or history will be loaded into the browser. It will appears to be a fresh install of Chromium.
For a full list of command line switches refer to https://peter.sh/experiments/chromium-command-line-switches/.
A particularly “interesting” one was:
![](https://cdn-images-1.medium.com/max/1024/1*KL9wbWNeLo9DRjuLHA0wJQ.png)
The /json Endpoint
Armed with the ability to start a headless instance of Chromium, with the user’s profile loaded, and the remote debug port enabled, what can an attacker do?
Instead of navigating to the HTML page at http://localhost:9222, your application can discover available pages by requesting: http://localhost:9222/json and getting a JSON object with information about inspectable pages along with the WebSocket addresses that you could use in order to start instrumenting them. (Source)
Let’s curl this endpoint and see what information we can obtain.
slyd0g@mac:~|⇒ curl http://localhost:9222/json
[ {
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/B74953F6EF3844659A1A0DD0A7A1602A",
"faviconUrl": "https://static.xx.fbcdn.net/rsrc.php/yo/r/iRmz9lCMBD2.ico",
"id": "B74953F6EF3844659A1A0DD0A7A1602A",
"title": "Facebook - Log In or Sign Up",
"type": "page",
"url": "https://www.facebook.com/",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/B74953F6EF3844659A1A0DD0A7A1602A"
}, {
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/70EAE4FE55B93F79DC2F1637AEE663EC",
"faviconUrl": "https://miro.medium.com/1*m-R_BkNf1Qjr1YbyOIJY2w.png",
"id": "70EAE4FE55B93F79DC2F1637AEE663EC",
"title": "Editing Cookies rule the world, not my actual title – Medium",
"type": "page",
"url": "https://medium.com/p/34c4f468844e/edit",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/70EAE4FE55B93F79DC2F1637AEE663EC"
}, {
...
...
}, {
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/ED4BA5705C20D08C5DF0B5FD076799EE",
"id": "ED4BA5705C20D08C5DF0B5FD076799EE",
"title": "1Password extension (desktop app required)",
"type": "background_page",
"url": "chrome-extension://aomjjhallfgjeglblehebfpbcfeobpgk/_generated_background_page.html",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/ED4BA5705C20D08C5DF0B5FD076799EE"
}]
I’ve highlighted some interesting information returned by Chromium. This endpoint returns information about currently open tabs as well as installed browser extensions.
Additionally, the webSocketDebuggerUrl field is of particular interest to us
All Your Cookies Are Belong To Us
Connecting to the webSocketDebuggerUrl using a Web Socket client such as wsc we are presented with a terminal of some sort:
slyd0g@mac:~|⇒ wsc ws://localhost:9222/devtools/page/69BCB23AE200374A0FE8E638E5F9783F
Connected to ws://localhost:9222/devtools/page/69BCB23AE200374A0FE8E638E5F9783F
> ls
< {"error":{"code":-32700,"message":"JSON: invalid token at position 0"}}
@mangopdf discovered the bug report that lead to the creation of the Network.getAllCookies feature. Lots of other interesting methods here Walking in their footsteps, we are able to dump all the unencrypted Chromium cookies.
![](https://cdn-images-1.medium.com/max/1024/1*r32VOLW07T1JPLCQTXCdHQ.png)
Introducing WhiteChocolateMacademiaNut
I wrote WhiteChocolateMacademiaNut in Golang to automate the above process.
The code used below can be found here: https://github.com/slyd0g/WhiteChocolateMacademiaNut
![](https://cdn-images-1.medium.com/max/703/1*i88X4cfgR4oSBx-uvOy_iA.png)
The important command line switches are the port you set the debugger to listen on (-p || -port) and what you want to dump (-d || -dump). You can dump open tabs/extensions or cookies.
The format and grep switches are to assist operators by dumping the cookies in a JSON format that can easily be loaded into another browser. The format flag modified will output cookies in JSON format with an expirationDate field set to 10 years in the future.The following examples may show Microsoft Edge or Google Chrome, but it should be noted this applies to all Chromium-based browsers.
Targeting Chromium on Windows
Scenario 1: Targeting User’s Cookies
Start Chrome/Edge with the user’s profile, in headless mode, and start the debugging port.
Opsec Note: If performing this through C2 such as Cobalt Strike, consider spoofing your parent process ID (PPID) to a PID of a running chrome.exe or msedge.exe. Bonus points if you are able to pull the command line arguments and find the original parent process which starts with no command line arguments. Additionally, it’s preferred that you proxy WhiteChocolateMacademiaNut to your target instead of dropping it to disc.
![](https://cdn-images-1.medium.com/max/1024/1*QwQF3h5iFIPrArvjfkipcg.png)
![](https://cdn-images-1.medium.com/max/1024/1*1FnbtW6p2ZiWKfFnbacuyw.png)
# Microsoft Edge
PS C:UsersJohnDesktop> Start-Process "msedge.exe" -ArgumentList '--remote-debugging-port=9222 --headless --user-data-dir="C:UsersJohnAppDataLocalMicrosoftEdgeUser Data"'
# Google Chrome
PS C:UsersJohnDesktop> Start-Process "chrome.exe" -ArgumentList '--remote-debugging-port=9222 --headless --user-data-dir="C:UsersJohnAppDataLocalGoogleChromeUser Data"'
Run WhiteChocolateMacademiaNut to dump cookies using the debug port:
![](https://cdn-images-1.medium.com/max/813/1*CXi_im2gPo0Fxzs38sqvzA.png)
Awesome, we dumped all of the user’s cookies!
It should be noted that you can perform this attack even when an instance of the browser is already running. However, if we attempt to dump the user’s current open tabs/extensions this will fail.
![](https://cdn-images-1.medium.com/max/696/1*oV0VJVH-KckFIU_lt_eO-g.png)
![🙁](https://s.w.org/images/core/emoji/13.0.1/72x72/1f641.png)
This makes sense because we are interacting with the remote debug port for the headless instance we started, not the user’s current browser instance.
Scenario 2: Dumping Tabs and Browser Extensions
If we want to dump the user’s current tabs and browser extensions, we will have to start the debugging port on their current browser. Unfortunately, we can’t simply just start their current process with additional command line arguments.
![](https://cdn-images-1.medium.com/max/946/1*WsJNmbyRZcoLDuj0DsxnHw.png)
Enter the –restore-last-session flag. This is used by Chromium when your browser unexpectedly crashes to help restore your previous tabs. As an attacker, we can simply kill all instances of Google Chrome and restart the browser with a remote debugging port. This is shown below in PowerShell:
Get-Process "chrome" | Stop-Process
Start-Process "chrome.exe" -ArgumentList '--remote-debugging-port=9222 --restore-last-session --user-data-dir="C:UsersJohnAppDataLocalGoogleChromeUser Data"'
![](https://cdn-images-1.medium.com/max/888/1*th6y7wfW12Ln8F2pwpMAIw.png)
Opsec Note: You can also restart the browser early in the morning before the user unlocks their machine. This will make it look like browser crashed while they were asleep.
Once you’ve restarted the user’s browser, you can dump their open tabs, extensions, as well as cookies!
![](https://cdn-images-1.medium.com/max/685/1*YPtS_D3s_SmLxZ8SFbFp2A.png)
Targeting Chromium on macOS
Unfortunately, on macOS the –headless flag does not play well with the –user-data-dir flag.
![](https://cdn-images-1.medium.com/max/1024/1*ZgMrzgGnBGoNCmuu2Y2oAg.png)
Initially, I thought this was because another Chrome instance was locking access to the database file. However, the error still occurred when I tried to start a headless instance of Chrome without any other instances running.
@_EthicalChaos pointed out the -2 error number was unusual and found where this occurs in Chromium source: https://chromium.googlesource.com/chromium/src/+/master/sql/database.cc#1433
![](https://cdn-images-1.medium.com/max/1024/1*TnsDatj03i83YPknRieAhw.png)
I investigated different command line arguments but was unable to get headless Chrome to work with a user data directory that I specified. The exact command works when running headful Chrome:
# Cannot dump cookies with headless
/Applications/Google Chrome.app/Contents/MacOS/Google Chrome --remote-debugging-port=9222 --user-data-dir="/Users/slyd0g/Library/Application Support/Google/Chrome" --crash-dumps-dir=/tmp --disable-gpu --disk-cache-dir=/tmp --headless
# Can dump cookies with the same command without --headless
/Applications/Google Chrome.app/Contents/MacOS/Google Chrome --remote-debugging-port=9222 --user-data-dir="/Users/slyd0g/Library/Application Support/Google/Chrome" --crash-dumps-dir=/tmp --disable-gpu --disk-cache-dir=/tmp
This could be a bug within Chromium for macOS, but I haven’t had the chance to fully investigate it.
We can get around this with the same trick we used for Windows by killing the user’s Chrome processes and restarting it with the appropriate command line arguments.
Opsec Note: You can also restart the browser early in the morning before the user unlocks their machine. This will make it look like browser crashed while they were asleep. Additionally, it’s preferred that you proxy WhiteChocolateMacademiaNut to your target instead of dropping it to disc.
slyd0g@mac:~|⇒ killall Google Chrome
slyd0g@mac:~|⇒ /Applications/Google Chrome.app/Contents/MacOS/Google Chrome --remote-debugging-port=9222 --user-data-dir="/Users/slyd0g/Library/Application Support/Google/Chrome" --restore-last-session
DevTools listening on ws://127.0.0.1:9222/devtools/browser/20054ab4-19af-4896-9b0d-7e3c44571d68
2020-12-16 15:31:01.926 1PasswordNativeMessageHost[2312:1080733] 70700015 [EXT_NMH:(Secondary Thread 0x7ff81eccf270):OPNMXPCConnection.swift] E connect() | Connection Established
From my testing, the user doesn’t even have to click anything to restore their tabs. The tabs they previously had open will automatically open again.
Once our debugger port is listening, we can dump open tabs, installed extensions, and cookies like usual.
![](https://cdn-images-1.medium.com/max/1024/1*pAihQyU27NdrG1-tRvZbDg.png)
Targeting Linux
The above techniques should work against Chromium browsers for Linux as well, but I’ll leave that as an exercise for the reader.
Go out and compile WhiteChocolateMacademia and give it a try
Detections
- Monitor for Chromium browsers starting with the –remote-debug-port command line argument. This shouldn’t be happening unless a user is doing some browser or extension development.
- Monitor parent-process relationships and look for processes that shouldn’t normally be spawning browser processes.
Conclusion
To recap, cookie theft is a method for attackers to obtain access to web applications without the need for the user’s actual credentials. Additionally, stealing cookies can bypass defensive controls such as multi-factor authentication (MFA).
Chromium browsers have a command line argument that allow you to remotely debug the browser. Interacting with this remote debugger port, we are able to dump all open tabs, installed extensions, and cookies. It should be noted that this technique is OS independent.
Thanks for taking the time to read this post, I hope you learned a little about the cookie theft and Chromium remote debugging!
I’d like to reiterate that the technique documented here is not novel and has been well documented by @mangopdf’s blogpost in my references. I wanted to bring light to the technique again as it was extremely useful for a recent engagement as well as take the opportunity to write something in Go.
References
Since the BloodHound thread won’t be viewable in a few weeks as the messages become stale, I’d like to also credit @domchell and @rkervell for extensive discussion about the topic in the thread and linking to @mangopdf’s blogpost.
- slyd0g/WhiteChocolateMacademiaNut
- Stealing Chrome cookies without a password
- defaultnamehere/cookie_crimes
Hands in the Cookie Jar: Dumping Cookies with Chromium’s Remote Debugger Port was originally published in Posts By SpecterOps Team Members on Medium, where people are continuing the conversation by highlighting and responding to this story.
The post Hands in the Cookie Jar: Dumping Cookies with Chromium’s Remote Debugger Port appeared first on Malware Devil.
https://malwaredevil.com/2020/12/17/hands-in-the-cookie-jar-dumping-cookies-with-chromiums-remote-debugger-port/?utm_source=rss&utm_medium=rss&utm_campaign=hands-in-the-cookie-jar-dumping-cookies-with-chromiums-remote-debugger-port
No comments:
Post a Comment