Our Blog

thumbscr-ews – a python EWS tool

Reading time ~6 min

Something I have found myself doing more and more often is using Exchange Web Services (EWS) to bypass 2FA. I do this so that I could look through mail for accounts I have compromised. The 2FA bypass is due to a common misconfiguration which can leave EWS unprotected, and has been known about for ages, mostly from the Black Hills post in 2016. However, most of the tooling appears to be written in PowerShell, and being the lazy person I am I prefer not to start up a Windows VM when I want to see if I can access a persons email. Hence I started just using a small script around the amazing exchangelib where I would just retrieve the top 10 emails using the example provided in the help documentation. I was doing this often enough that I decided to make a more useful tool.

If you are in a rush, the tool can be found here: https://github.com/sensepost/thumbscr-ews. The rest of this post will detail some of the features implemented in thumbscr-ews to date.

A really simple example from the exchangelib documentation follows, which will show the top 10 emails I mentioned:

from exchangelib import Credentials, Account

credentials = Credentials('john@example.com', 'topsecret')
account = Account('john@example.com', credentials=credentials, autodiscover=True)

for item in account.inbox.all().order_by('-datetime_received')[:10]: 
     print(item.subject, item.sender, item.datetime_received)

While this was great, it did not get me other useful things such as the GAL, and every time I wanted to do that, I would end up re-writing the script. So using MailSniper as inspiration I wrote a program called thumbscr-ews leveraging exchangelib. I chose the name thumbscr-ews because I liked the idea of an information extraction device and a word that ends in EWS. I considered MailSniper.py similar to how bloodhound has bloodhound.py but decided against that.

A lot of functionality is already built into exchangelib, and thumbscr-ews builds on top of the example code provided above. A common flow using exchangelib would be to get an account, get a folder of interest, loop over objects in that folder and print interesting information. If you wanted to search through mail for example, you would filter() on the objects retrieved from that folder. Keep in mind that the search variable does use a specific format for more fancy searching.

mails = account.inbox.filter("Body:Password").order_by('-datetime_received')

Implementing features with exchangelib were pretty straight forward; except for the GAL retrieval where I had to use MailSnipers ResolveNames method of searching for users starting from aa to zz. ResolveNames being a way to query the GAL for specific search strings. The result is also limited to only 50 or so entrys, so you have to get specific enough with the search that you are not missing entrys, but not so specific that you are take 5 years to complete the operation.

Getting this working I had to do some fiddling with the exchangelib building blocks. While exchangelib provided a ResolveNames call, it was only used under the hood by other functions with the call method. I had to do some scratching around the code base to work out how its used:

# Login to the account
account = Account(username, credentials=credentials, autodiscover=True, access_type=DELEGATE)

# Get a list of aa -> zz
atoz = [''.join(x) for x in itertools.product(string.ascii_lowercase, repeat=2)]
for entry in atoz:
# Search for sub strings (entry) and print out all returned. 
    for names in ResolveNames(account.protocol).call(unresolved_entries=(entry,)):

Implementing this gave me the GAL in a nice, easy to read output. On top of this I added the ability to search for specific strings as well, so you could further filter the results:

Dump/Search through the GAL

A nice feature MailSniper has is the ability to test for delegated access. I implemented this using exchangelib as well:

Check for delegated access

Another common operation is to read the mails of your user, or a user whose Inbox you have access to, so I added the ability to list some mails as well as search through them (Be warned though, the search functionality seems a little dodgy at times). Additionally, I added a delegate flag to most of the other commands so that you can read other peoples mail, etc.

Read other peoples mail
… or read our own mails

Now that you have listed some mails, there may be some juicy looking attachments, so I made it possible to download those as well using the mail ID which is printed when viewing the mails.

Download Attachments

Exchangelib has a way of printing out the entire file structure of a target mailbox; some of which may not even include mails and may be some other weird object. So I added a way to browse the file structure and print the raw objects within a specific folder. This also means that I don’t have to build in support for specific objects which may only come into play once. Once again I added a search parameter to filter the folder structure so that you can exclude a lot of the noise if wanted:

Browse file structure (Thanks exchangelib, you are amazing)
Print random objects supported by exchangelib which are not mails

Finally, I thought it might be cool to be able to brute-force. However, I feel tools like SprayingToolkit have an edge here, and your should probably give that a try rather:

Horizontal brute-forcing.

One more thing. As classic hacker synchronicity would have it, while preparing to write this blog post I noticed that another project with the same idea was created very recently by @yayatiAOD. Make sure you have a look at that project as well.


The project showed me that the permission_set.permissions field existed on a mail folder which I hadn’t noticed, whereas I was just attempting to access the folder before.

The thumbscr-ews tool is still a bit of a work in progress, however, has already proven pretty useful for me. I think next on the agenda will be adding the ability to send mail. Feel free to test it out and contribute! The source code is available here: https://github.com/sensepost/thumbscr-ews.