Token stealing is getting harder. Instead, stealing whole logged-in browser instances may be an easier and more generic approach. One attack, known as “browser-in-the-middle” (BitM), makes it possible to virtually place a user in front of our browser and request them to log in for us. One of my old work buddies referred to it as “phishing with dynamite” after using it on a few social engineering campaigns.
Cuddlephish is an open-source, point-and-shoot implementation of BitM for penetration testers.
But before we get into BitM and its advantages, let’s do a quick refresher on its predecessor, the “token stealing” attack.
Token stealing is a phishing technique geared towards stealing session cookies (i.e., “tokens”); not just usernames and passwords. Attackers use this method to conduct post-exploitation on a target web service even when multi-factor authentication (MFA) is in place. By stealing the token and injecting it into a new browser, the attacker can impersonate the compromised user for as long as the token remains valid.
Token stealing attacks rely on setting up a “transparent proxy” on the phishing server. The phishing server simply takes any HTTP request sent to it and sends the exact same request to the target service. Each HTTP response is then sent back to the target user’s browser after making some minor substitutions, such as swapping any reference to the domain for the target service with the phishing domain. This attack not only allows attackers to view the user’s credentials in HTTP POST requests, but they can also log “Set-Cookie” response headers from the server that, after the user answers the authentication and MFA prompts, contain the session cookie (token). For many web services that utilize traditional session-based authentication, this attack can be an easy point-and-shoot solution to bypass MFA.
If you’ve ever phished with a transparent proxy, you probably noticed that it does not work out-of-the-box against all websites. Here are a few common scenarios where transparent proxies run into issues:
Multiple Domains Involved in the Login Process (e.g. SAML and OAuth)
Transparent proxies need to perform a translation between the phishing domain, and the target service’s domain for each HTTP request, and back again for each HTTP response. So, what happens when the login flow for a website requires interacting with multiple domains? To make sure all credential material traverses our transparent proxy, we need to start making additional substitutions.
We can either buy another domain and set up another transparent proxy to work in tandem with our main proxy or write a bunch of custom substitution rules to make the attack work in a single domain. In either case, we need to reverse engineer the login flow of the target application, which can be time consuming and difficult to troubleshoot. If anything changes about the login flow, we might be back to square one and have to reverse engineer the new flow.
JavaScript Protections Against This Very Attack
What happens if some code on the login page checks window.location before sending credential material to the server and only sends the credentials if the browser is on the real login page? While this is a form of client-side validation and an attacker can patch it, it still does a great job of thwarting out-of-the-box token stealing attacks. The attacker must spend time reverse engineering the code on the login page to determine the code snippet responsible for the security check and write a rule to patch out the security check when that piece of code is requested by the victim’s browser.
Add in the fact that JavaScript is easy to obfuscate and time-consuming to deobfuscate. With very little effort, a developer can significantly increase the cost for any would-be attacker. If an attacker did go through all the effort to reverse engineer and patch out a security check like this, their solution would be extremely brittle and likely to break if anything about the login flow of the site changed. For example, what if we randomize the security check itself?
Websites That Do Not Use Session Tokens
Single-page apps that use WebAssembly and AJAX to handle authentication flows might not negotiate a session cookie at all; instead, they may hold an authentication token in memory on the browser tab. Refreshing the page requires the user to log in again so if there is no session cookie to steal, then token stealing attacks will not work for these services.
I think this line from Cuddlephish’s README, as emphasized by “the hacker known as Alex”, sums it up well:
While it may seem elaborate, it’s actually not that hard to pull off. Here’s how to BitM in three easy steps:
Step One: Screen Share
Stream a video of your browser to the user’s browser. WebRCT was literally designed to do this, so we’ll use WebRTC:
Grab a code sample from Google, host it on your server, and we’ve got the screen sharing part figured out.
Step Two: Collect User Inputs
We can use built-in browser events like mousemove and keyDown to collect user inputs and send them to our server via WebSockets:
Step Three: Replicate User Inputs
Send the user’s inputs to your browser with Puppeteer. Puppeteer streamlines common browser automation tasks, so we can use functions like mouse.move() and keyboard.down() to emulate the user’s inputs in our browser.
That’s it! You now have a working BitM attack! The user thinks they are seeing their browser on the target site, but it’s really a video feed of your browser. They interact with the video while you mimic their actions on the real browser using automation, and they see the effects of their inputs in the video feed.
If we now add some logic to spawn another browser when a new user visits the site, and track each browser with a unique ID, we can also make it scale automatically. Here’s what that looks like in practice:
We Can Still Steal Tokens! 😊
With the Storage.getCookies and DOMStorage.getDOMStorageItems Chrome Devtools functions, we can extract ALL cookies from a browser and all local storage items, like JSON web tokens (JWTs), to inject in a new browser.
We Can Still Target Services With No Session Tokens!
This is a big deal because you can use this technique to exploit some high-value target services that are completely immune to token stealing attacks. Since we have full control over our automated browser instances, we can simply substitute the target user’s inputs with our own at any point. We can essentially give them the boot and “drive” the browser instance that they logged in for us.
It Has Other Interesting Bells and Whistles!
We can do some other fun stuff like pass control to and from our target user (e.g., imaging the IT help desk scenarios), send the user a file via JavaScript, or redirect the user somewhere else once they’ve logged in. We also get a full key log from each user.
What if a user enters their password in the authentication prompt, but notices there is something “phishy” about the site just before clicking submit? Too late! We’ve already got their password at this point! Having the user’s password can also be useful when using BitM to backdoor a compromised user’s account by adding your own MFA option. Some services require the user to re-enter their account password to perform these sensitive actions.
It’s Point-and-Shoot!
One of the best things about the BitM attack is that we do not need to do any special configuration for each target service. We no longer need to waste time reverse engineering the target service to get our phishing site up and running; we just provide the tool with the login URL for our target service.
While you can configure a few more details about each target service for more advanced features like payload delivery, the basic attack should work out-of-the-box against any website.
Go check out cuddlephish! It’s a weaponized BitM attack designed for penetration testers:
https://github.com/fkasler/cuddlephish
If you want the best chance of establishing video feeds to your target users, it is best to use a TURN server. The example in the project uses a public STUN server to broker the WebRTC connection in both the broadcast.html (server side) and cuddlephish.html (client side):
The config block for a TURN server is left in the project for reference but commented out. TURN servers relay all the video traffic after the peer negotiation, so there aren’t any (trustworthy) public TURN servers out there to use. However, setting one up is extremely easy. Just check out this great tutorial . You basically install coturn; tweak a couple of config options for your password, server IP, etc.; and run it.
BitM attacks will not work against sites that require client certificates for authentication; that is, unless the attacker can social engineer the target user into uploading a copy of their certificate before authenticating.
It will also not work when hardware authentication devices like Yubikeys are used for MFA. The device will not recognize the phishing site as a domain it has been configured to authenticate to. Any bypass requires code execution on the target user’s system and at that point, we would already have better options for stealing access to web services.
Due to the increasing complexity of web authentication methods, and intentional defenses, token stealing attacks are becoming more difficult to execute. Instead, we can use Browser-in-the-Middle attacks as a layer of abstraction to make advanced credential stealing attacks easy and target agnostic. I have released a weaponized BitM attack tool called Cuddlephish to allow penetration testers to use this technique on their campaigns and raise awareness of this attack vector.