POV: You're performing a pentest/red team engagement against a fairly hardened environment. You have, through creativity and perseverance, landed an implant on a workstation. Your session has low privileges, but the user may have local admin or associated higher-priv accounts. You're trying to remain stealthy, and normal lateral movement techniques might get detected. You need creds, but how to get them when everyone's watching you?
Answer: use SSO against itself by listening in on the browser.
TLDR: Chromium-based browser extensions can be used to collect user secrets after initial exploitation. I made a tool called Crux to make it easy.
We all know that Chrome extensions can be bad news. Plenty of research has gone into how actual attackers use Chrome extensions to redirect search engines, log keystrokes, and generally become gigantic pains.
Despite this common behavior, it is not a technique I tend to observe amongst my red team friends. I suspect that may be a training issue, as so much of the offensive side of the house focuses on Windows and Active Directory-based exploitation. The web and the browser are left relatively alone. Granted, exploiting the browser seems like a more circuitous route. But if done well, the payoff can be substantial.
I'll also point out that offensive security professionals' goal is not just to "win," but to emulate the threats posed by actual evildoers. That means if the bad guys are using browser-based attacks, so should the red team—at least sometimes.
Exploring Chrome Extensions
Chrome extensions are nothing but glorified web applications. Depending on what components you define in the root manifest, an extension can contain UI components, menu additions, or even background service workers. That last part is largely where the offensive appeal comes from.
Why Target Extensions?
The Chrome Extension API details all the amazing capabilities of Chrome Extensions, but in particular for offensive operations, I'm interested in:
Cookies: Grab sessions and other sensitive data in cookies
Storage: Grab sensitive data from localStorage
webRequest: Intercept and analyze all requests/responses
webNavigation: Intercept and analyze all page visits
It should be clear from the existence of extensions like FoxyProxy and Proxy SwitchyOmega that full proxying of web data is possible in Chrome. Now, those extensions are doing so with the full knowledge of the user. But we'll be intercepting this traffic silently.
I really can't overstate the value of the information available in the browser—especially in a SSO environment. In essence, this attack methodology makes single sign-on, normally considered a security benefit, into a massive liability.
Imagine an Okta environment, where the primary identity is tied to Active Directory. If we capture the Okta login, not only do we capture the Windows password for the user, but the Okta session token, potentially allowing access to further applications. As the user continues navigating, using other accounts (maybe even higher-privileged ones), we'll keep capturing data and finding ways to pivot.
Attack Surface Area
One of the beautiful things about Chrome Extensions is that they are usable in almost every Chromium-based browser there is. That of course includes Chrome, but it also includes Edge, plus weirdos like Brave, Vivaldi, and Opera. But with Edge available, that means every modern Windows system is going to have an injection target for us. That's pretty huge.
Installing Chrome Extensions
Installing Chrome extensions usually occurs manually, via sources like the Chrome Webstore or the Microsoft Store. That being said, Google and Microsoft provide alternative methods for installing extensions—most of which require special privileges on a system or in the domain. They include:
- Local/Group Policy configurations (Windows)
- Local files that reference webstore extension IDs or self-hosted URLs (Mac/Linux)
In our scenario, neither of these is viable. But that's okay, because it turns out there are more unusual ways to load extensions into Chromium browsers.
In particular, one of Chromium's many command-line options is
--load-extension. This will load an unpacked (meaning just the source folder) extension with a provided path. Doing so will launch a new browser instance with the extension loaded silently in the background.
As it turns out, this is exactly how the ChromeLoader malware does its dirty work. Specifically, it closes all existing Chrome sessions and reopens with
--last-saved-session to restore prior tabs, and
--load-extension to point to the malicious extension.
There's a very specific reason why we can't just add our code to existing extensions. Chromium browsers use a content verification system that involves deep hashing of contents to ensure the files have not been modified after installation. If the checks fail, the extension is removed. Unfortunately, the checks happen on any invocation of the extension, so that's not a viable attack vector as of now.
Of the prior research I could find into this attack vector, CursedChrome stands out as a mature framework for handling compromised browser sessions through malicious extensions. The capabilities of the CursedChrome extension are expansive, and definitely worth exploring as a production payload. However, as they mention in their documentation, the deployment of the extension is not a problem solved by CursedChrome. In fairness, this article won't solve that for you either, but it will speak to how to leverage the technique the bad guys are using for your own offensive work.
To make things a little easier, I've thrown together a little listening server and proof-of-concept extension to play with. Crux is designed to send URL visits, form data, and cookies from the target browser and receive them in an easy-to-read format. It also handily will highlight any form data that may be a password, and any cookie that may be a session cookie.
To use Crux, first establish a listening server that'll be accessible to your target machine(s). The only requirements for the server are Python's Flask and Rich libraries, so to get those going, simply:
pip3 install flask rich
After that, clone the Crux repo to your listening server. Then:
cd crux/server flask run $PORT
Your server is now listening! Now to get the extension to your target.
Let's start from post exploitation. Make sure you have Crux downloaded to your attack box. Before moving forward, make sure you change the
SERVER_HOST variable in
background.js to match your listening server.
Then, using whatever technique you like, upload the
extension folder in the Crux repo to the target. You can put it anywhere that's writeable, but I recommend tossing it in the
Extensions folder for the user. This is where things get interesting. Chromium-based browser put their extension code in different places depending on OS. On Windows, it'll be in
C:\Users\$USER\AppData\Local\Google\Chrome\User Data\Default\Extensions. On macOS, it'll be
~/Library/Application Support/Google/Chrome/User Data/Default/Extensions. On Linux, it'll be
~/.config/google-chrome/Default/Extensions. Note that the
Default folder is the first browser profile. Additional profiles will be named
Profile N. Also, for alternative browsers like Edge, you'll have to modify the path accordingly. The folder structure is the same though.
I recommend dropping the extension here because it's writeable and won't look out of place. Just good OPSEC.
Once the folder is in place on the target, use any technique you like to launch Chrome (or other browsers) following this pattern:
& 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe' --load-extension='C:/path/to/extension'
No that's not a typo—forward slashes on the extension arg. I don't know why.
You also have the option of pulling the Chromeloader trick and first shutting down every running browser process, then restarting the browser with both
--reload-last-session. This will relaunch the browser with the previously closed tabs, almost like a browser update.
Don't forget you an also give the browser a URL from the command line for further social engineering trickery!
With Crux launched, you should start seeing traffic in the server.
Any form requests will be captured as well.
As secrets come in, your escalation and pivoting possibilities increase! As long as the browser window is open, you'll be getting data.
What kind of purple teamer would I be without discussing how to catch this technique?
The biggest giveaway is the process creation of the browser with
--load-extension. That's weird enough that it should be a low false-positive detection that merits inclusion in your queries. As long as you're collecting process creation data through EDR or Sysmon events, this is observable. If not, well, yikes.
I believe there's a lot more to research in this area: in particular I believe there may be ways to bypass content verification. I also would like to dive more into remote loading of extensions to learn exactly how the browser determines if a source is authorized. But for now, I'm excited to play with Crux in demos and hear from my red team pals how it works for them during engagements!