Skip to Main Content
May 14, 2019

Owning O365 Through Better Brute-Forcing

Written by TrustedSec

TL;DR:

  1. User Enumeration is key.
  2. Done enumerating? Do more.
  3. The classic passwords still work.
  4. Once you get some credentials, get more.

Office 365 (O365) has become a trend in organizations. More and more, administrators are offloading their mail to The Cloud™. No longer are admins shackled to their Exchange servers, executing patch after patch in hopes of staying ahead of the evil hackers! No, Microsoft will take care of that now. Sleep soundly, Exchange server admins, your mail servers are in good hands. While it is increasingly common for organizations to swap their on-premises Exchange servers for O365, they still face the same threats. Like the Exchange servers that came before it, O365 also has a user-enumeration flaw. And, since Microsoft does not consider user-enumeration to be a bug, O365 is as great of a target for brute-force attacks as any Exchange server ever was. For this reason, O365 is one of the first things I look for in an engagement, because it will often result in credentials.

How can we tell if an organization uses O365? We can check with a single URL:

https://login.microsoftonline.com/[email protected]&xml=1

Figure 1 - Testing for O365

If the 'NameSpaceType' indicates 'Managed,' then O365 is in use. Other values are 'Federated,' for Federated Active Directory, and 'Unknown,' if no record exists.

It's Not a Bug, It's a Feature!

Microsoft doesn't consider user enumeration to be a security problem. It has demonstrated this by refusing to fix a well-known user enumeration timing bug in Outlook Web Access (OWA) for years (http://h.foofus.net/?p=784). It has likewise refused to fix a similar issue in Microsoft Lync/Skype for Business (https://github.com/nyxgeek/lyncsmash). In fact, the Microsoft security reporting page states quite clearly that it does not consider user enumeration to be a problem—it's not a bug, it's a feature.

O365 has an even better 'feature' than the previous incarnations. Instead of relying on a timing attack, you can simply submit an authentication attempt against the ActiveSync service and examine the HTTP response code. As expected, a 200 indicates a valid username/password, while a 404 indicates the username does not exist.

Response Code Description
200 Successful login (good user/password)
401 Valid Username, bad password
403 Valid Username, good password, 2FA required
404 Invalid Username

A tool called Office365UserEnum (https://bitbucket.org/grimhacker/office365userenum) was released a couple years back, and it does a great job of quickly enumerating users. There's a blog post that goes into more detail on how the attack works (https://grimhacker.com/2017/07/24/office365-activesync-username-enumeration/). The best part is that it targets Microsoft servers, so many organizations might not detect the attack.

Figure 2 O365 User Enumeration

When parsing the results of this tool for successful logins, I like to use grep -v to remove the 404 and 401 responses. This will leave you with a short list of the 200s, as well as any valid 2FA (403), and any responses that generated an error (400 or 500). In my experience, it is worth double-checking and manually testing any returned 400 codes, as this could indicate a successful login that failed for some other reason.

Better Brute-Forcing

The O365 brute-force requires a list of email addresses to attack. I would recommend searching password dumps (e.g., LinkedIn or Adobe breach) for email addresses. Some sites like hunter.io specialize in providing email addresses for different organizations. A fairly reliable method is to generate your own by using a list of statistically-likely-usernames (https://github.com/insidetrust/statistically-likely-usernames) and turning them into email addresses. This is easy to do in Linux:

awk '{print
$1"@domain.com"}' top-formats.txt > top-formats.domain.txt

With a list of targets, we can proceed with our attack! Specify a password (Spring2019) and we're on our way.

If we're lucky, we will get a set of credentials off the bat. If we fail, it's probably because we haven't done a good enough job of user enumeration. Check the size of your target organization on LinkedIn or Wikipedia. Know how many users you should expect to find. If you have an organization of 50,000 but only have 500 valid accounts, you've got some work to do.

If you're banging your head against the wall trying to find new names to try, revisit your valid users list. Take any last names and use a one-liner to combine first names with your already-discovered last names.

while read firstname; do
while read lastname; do echo "[email protected]"; done
< lastnames.txt ; done < firstnames.txt

Example Output:

[email protected]

[email protected]

[email protected]

[email protected]

[email protected]

[email protected]

[email protected]

[email protected]

[email protected]

When all else has failed, I've had luck using a Google dork with LinkedIn to discover new names for enumeration.

site:linkedin.com
intext:<company name>

Remember that a company may have multiple username formats in play. If you're failing to find more usernames with a given format, try other formats. The statistically-likely-usernames list has a top-formats.txt that works well (https://github.com/insidetrust/statistically-likely-usernames/blob/master/top-formats.txt).

Create a new target address list, excluding any addresses that have been tested already. For this, you can use the comm utility to compare a list of users that have been tested already, against a list of untested users. In the example below, we are taking only unique values from the 'new_users_generated.txt' file that do not exist in the users_tried.txt file, and we output the resulting list to a 'new_users_untried.txt' file.

comm -13 users_tried.txt new_users_generated.txt > new_users_untried.txt

Obviously, the bigger the organization, the better. Unless the organization has a crazy-awesome password policy, you will get an account.

In my experience, most brute-force failures stem from a failure to enumerate enough users. SOMEBODY has a bad password. One password is all we need.

Classic Passwords Still Work

As long as a 90-day password change policy exists, so will seasonal and month-related passwords. This style of password should be your go-to for initial attempts:

Spring2019

Spring19

Spring19!

April2019

Funny enough, stupid classics also still work:

Password1

P@ssw0rd

Password123

Company1

Company123

These are not uncommon. You'd be surprised. If you're not finding any users with these passwords, I would recommend going back to user enumeration and finding more users.

NOTE: By default, O365 has a lockout policy of 10 tries, and it will lock out an account for one (1) minute. However, if it is synced with on-premises, this means that the actual lockout could be much lower. Additionally, Azure AD allows for custom lockout settings (https://docs.microsoft.com/en-us/azure/active-directory/authentication/howto-password-smart-lockout). Keep this in mind while testing.

We Got One! (Now Let's Get More)

Assuming all goes well, and you have pushed your user enumeration and password cracking enough, you should hopefully have a credential. With our single credential, we can then connect to Microsoft Online Services with PowerShell, and pull down a full user list, group list, group membership, alternate domains, and a plethora of other information—basically, LDAP queries via O365 and PowerShell. I've compiled the various PowerShell commands into a script: o365recon (https://github.com/nyxgeek/o365recon).

Figure 3 - Connecting to O365 with o365recon

Usage is simple: run the script, it will prompt for credentials. Enter credentials, and it will use those to connect and retrieve what information it can.

Figure 4 - Retrieving User List via o365recon

Now, with a full user list, go back and brute-force again. I've gone from a single credential to hundreds of credentials with a single run (the place had good usernames, bad passwords).

Be sure to compare your list of compromised accounts against the group membership. Oftentimes, only certain users will be in a VPN access group. At any rate, going through the group names and memberships will give insight into your target's environment.

Figure 5 - Retrieving Group Membership via o365recon

In addition to group membership, reviewing the full user details can also be helpful. One problem you may encounter is the Active Directory username may not match the email address. For instance, the O365 username may be '[email protected]' but the internal username may be 'jsmith'. This is more common in Federated setups. By examining the user data pulled down from Microsoft Online Services, email addresses can sometimes be linked to their internal AD usernames. In the example below, the 'ProxyAddresses' field contains references to other email aliases. Another useful field is the 'LastPasswordTimestamp' field, which will help you gauge how long your current credentials will work.

Figure 6 - Retrieving Detailed User Information via o365recon

If you cannot find the internal username via the aforementioned method, you can try to gather them with the 'Get-ADUsernameFromEWS' module from MailSniper (https://github.com/dafthack/MailSniper).

Now What?

Log into O365 Outlook on the web. Check for draft emails containing passwords, check for notes that are saved. Check their OneDrive and SharePoint Online.

Search for single-factor portals. Be aware that their internal username format may not match email address format. Citrix and VPNs are obvious choices. Maybe they have an old OWA server still installed, where it might be possible to use Ruler to get a shell via Homepage attack (https://github.com/sensepost/ruler). At the very least, you might be able to use your valid user account to phish some more powerful users.

You've fought and scrapped, and after some intense brute-forcing, you have gathered a handful of valid usernames and passwords. Now, go forth and plunder.

A Word to the Defenders

How can we stop attackers preying on weak passwords?

The best solution is to enable multi-factor authentication (MFA) for all O365 accounts. Add MFA to everything. If there's something you can't set up with MFA, burn it down, or make it only accessible via VPN (which you also have configured with MFA). If every external-facing portal on your IP space is MFA, then it doesn't matter if an account's password is compromised. Passwords alone are insufficient to protect important external resources.

Additional guidance on defending against this type of attack can be found in Microsoft's Azure AD and ADFS Best Practices for Defending Against Password Spray Attacks page:

https://www.microsoft.com/en-us/microsoft-365/blog/2018/03/05/azure-ad-and-adfs-best-practices-defending-against-password-spray-attacks/

Depending on your Office subscription, you may already have access to some tools like Office 365 Advanced Threat Protection. More information can be found here: https://docs.microsoft.com/en-us/office365/securitycompliance/office-365-ti

Finally, check out this great talk by Sean Metcalf that addresses many issues affecting O365 admins, such as getting your logs into your security information and event management (SIEM).

Video: https://www.youtube.com/watch?v=1loGEPn_n7U

Slide Deck: https://adsecurity.org/wp-content/uploads/2019/04/2019-BSidesCharm-YouMovedtoOffice365NowWhat-Metcalf.pdf