MFA Slipstream PoC
For a demo and attack explanation in the form of a short video, check out my previous post here: Phishing Your Way Past Multi-Factor Authentication
If you're just looking for the code, you can find it here: GitHub - MFA Slipstream
This code walkthrough assumes the following:
- You are familiar with Social Engineering and running a phishing campaign
- You know how to configure DNS, which record types to choose, etc.
Attack Platform Overview
The attack really isn't that much different than a normal phishing attack, other than it uses some nifty tooling to do everything in real-time.
We have 3 components to our attack setup:
- Attacker machine -
mfa_slipstream.py- This is where the
mfa_slipstream.pytool runs. It handles collecting username and password data, it facilitates the transition from the username and password to the collection of MFA tokens, as well as handles collecting the MFA tokens themselves.
- Phishing Stage 1 -
- Phishing Stage 2 -
collect_userpass.jsto display the correct authentication type and details to the user.
An overview of how the components interact with each-other is illustrated in the following diagram:
Below is a more detailed explanation of each of the 3 components.
The attacker machine is where
I tried to keep the code self documenting and well commented, so I won't spend too much time here. What I want to do here is break down the different components within, and give a high level view of what they do. Links below are to the locations in the code on GitHub within the tool
MFA Slipstream Stage 0 - This is the tool setup stage. It doesn't interact with the phishing pages, it just gets the attacker's browser ready for action. This stage is a single method,
go_to_signin(). It is called at the bottom of
mfa_slipstream.py as part of launching Firefox and navigating to the phishing landing page. The protected page is visited by the attacker's browser, then a test email account is entered into the login form, which triggers any secondary redirect to the target's page.
O365 often does a double redirect. After entering an email address matching the target's domain, you are sometimes redirected a second time to the actual login page. STAGE 0 takes care of getting the attacker's machine onto the correct and final login page.
MFA Slipstream Stage 1 - This stage accepts the username and password from the Phishing Stage 1.
dossologin() parses out the username and password, passing them to
do_usernamepass_login() to do the login.
do_usernamepass_login() does the login and collects the authentication type. It also collects the last 2 digits of the target's phone number where relevant, so it can return it to the calling function. It does this using Microsoft's already existing constants declared on the login page, then returns the authentication type to
MFA Slipstream Stage 2 - This stage does one of two things, depending on the authentication type being used. If the authentication type calls for a one-time password to be entered,
domfa() is used to collect that information and pass it to
do_mfa_code_entry(), which uses Selenium to enter the MFA code and take a screenshot of the authenticated page.
If the authentication type is just an "approve" prompt,
checkload() is used by the phishing page to check whether or not the target has clicked "approve" on their app notification. Once the target has approved the login, an appropriate response is returned to the calling phishing page.
quicksave()- This takes a screenshot and saves the current session cookies to a file on disk for later use.
enable_cors()- Adds CORS support, for when your attacker box is on a different domain.
class SSLWSGIRefServer()- Used for adding SSL/TLS support to the app server.
Also some utility functions that are documented enough to be self explanitory.
LET IT RIP section just prints the application banner, launches the Selenium controlled Firefox browser, and starts the Bottly.py server.
Everything for Phishing Stage 1 is contained in
document.getElementById to collect up the username and password the user entered and encodes them for transfer. It then ships them back to the attacker machine at the
/dossologin route (MFA Slipstream Stage 1).
From there the attacker machine logs in to the target page. Once logged in
mfa_slipstream.py detects the authentication type the user is using (SMS, Voice, Authenticator App, etc.) using MFA Slipstream Stage 1. The attacker machine then sends that information, as well as the last 2 digits of the user's phone number (for display purposes) back to the phishing page.
Everything for Phishing Stage 2 is contained in
Let's actually start with Part 2. It is executed first, but is labeled Part 2 because it is located further down the actual code of the phishing page. Part 2 is mostly a bunch of parsing. It takes in URL components, parses them into variables, then depending on the authentication type configures the page to display correctly.
One interesting piece of code is
waitForMFASApprove(). First, think about the different types of authentication methods: we have SMS, we have voice, and we have a one-time password from the mobile app. All of these are driven by a user entering a code, and clicking the sign in button. The one odd authentication method is the notification type.
In practice, for the notification type, the service sends the user a notification and the user just clicks "Approve". There is no code entered. This is where
waitForMFASApprove() comes in. When we run into that authentication type, we wait for the user to approve the login, which will log our attacker in, and only then do we allow the target to move on to the error page.
Part 1 is the function
doMFAAuth(). This is pretty straightforward, and actually simpler than Part 2. All we need to do here is collect the token the user entered, send it back to the attacker so they are logged in using MFA Slipstream Stage 2 of
mfa_slipstream.py, then dump them off to an error page using
Deployment Tips and Tricks
NGINX - I used nginx as my reverse proxy, and can't recommend it enough. When you want to have a single server hosting your email server, phishing metrics collection, and phishing landing page, nginx is the way to go. Especially if you want to serve multiple domains, and have a quick and easy setup. You can then clone it to a new VM/AMI instance and have multiple campaigns going with minimal effort.
Multiple Gateways / Scaling - Unlike Evilginx, this solution is not built to scale well. The way this issue was overcome in a company-wide type Social Engineering engagement was to leverage the cloud. Create multiple gateways for users. Say, batch 1 is 10 users, they all will hit a unique landing page, which leads to 10 distinct attacker boxes and MFAS Stage 2 collection pages. It works, its just a little slow. In very targeted attacks against just a few users at a time, this wasn't a big issue.