0x00 Preface

---

Previous articles introduced accessing Exchange resources using hash via SOAP XML messages. While this lower-level communication protocol makes implementation more cumbersome, it aids in understanding communication protocol principles and vulnerability exploitation.

If the goal is simply to develop a more efficient program for resource access, the Python library exchangelib can be utilized.

This article will cover the usage of exchangelib, provide open-source code, and demonstrate automated email downloading and attachment extraction.

0x01 Introduction

---

This article will cover the following topics:

  • Usage of exchangelib
  • Development details
  • Open-source code

0x02 Usage of exchangelib

---

Reference materials:

https://github.com/ecederstrand/exchangelib

https://ecederstrand.github.io/exchangelib/

1. Simple login test

Code as follows:

from exchangelib import Credentials, Account, Configuration, DELEGATE
credentials = Credentials(username='MYWINDOMAIN\\myuser', password='topsecret')
config = Configuration(server='outlook.office365.com', credentials=credentials)
account = Account(primary_smtp_address='[email protected]', config=config,
autodiscover=False, access_type=DELEGATE)
for item in account.inbox.all().order_by('-datetime_received')[:100]:
print(item.subject, item.sender, item.datetime_received)

If the Exchange server certificate is untrusted, add the following code to ignore certificate verification:

from exchangelib.protocol import BaseProtocol, NoVerifyHTTPAdapter
BaseProtocol.HTTP_ADAPTER_CLS = NoVerifyHTTPAdapter

To suppress the InsecureRequestWarning output, add the following code:

import urllib3
urllib3.disable_warnings()

Complete code is as follows:

from exchangelib import Credentials, Account, Configuration, DELEGATE
from exchangelib.protocol import BaseProtocol, NoVerifyHTTPAdapter
BaseProtocol.HTTP_ADAPTER_CLS = NoVerifyHTTPAdapter
import urllib3
urllib3.disable_warnings()
credentials = Credentials(username='MYWINDOMAIN\\myuser', password='topsecret')
config = Configuration(server='outlook.office365.com', credentials=credentials)
account = Account(primary_smtp_address='[email protected]', config=config,
autodiscover=False, access_type=DELEGATE)
for item in account.inbox.all().order_by('-datetime_received')[:100]:
print(item.subject, item.sender, item.datetime_received)

2. Use plaintext or hash login

Using plaintext login:

credentials = Credentials('MYWINDOMAIN\\myuser', 'topsecret')

Using hash login:

credentials = Credentials('MYWINDOMAIN\\myuser', '00000000000000000000000000000000:7C451851EA87B63EC7692126416D01EB')

3. Count the number of emails

Example code:

n = a.inbox.all().count()

4. Search within a specified time range

Example code:

for item in account.inbox.filter(datetime_received__gt=EWSDateTime(2021, 1, 20, tzinfo=account.default_timezone)):
print(item.subject, item.sender, item.datetime_received)

5. Specify the download quantity:

Specify the first 10:

first_ten = a.inbox.all()[:10]

Specify the last 10:

last_ten = a.inbox.all()[:-10]

Specified interval:

next_ten = a.inbox.all()[10:20]

6. Folder enumeration

Can traverse all folders under the mailbox user, example code:

print(account.root.tree())

7. Compiling Python scripts into exe

If compiling a Python script developed using exchangelib into exe format, using the command pyinstaller -F test.py will result in an error, indicating: No time zone found with key UTC

Solution:

pyinstaller --collect-all tzdata --onefile test.py

0x03 Development details

---

1. Communication protocol

exchangelib also utilizes SOAP XML messages to access Exchange resources using hash

2. Mail saving

In exchangelib, XML-formatted email content is automatically parsed, allowing direct extraction of corresponding information when saving emails.

Note that the string \r\n in the returned results can be replaced with line breaks to improve data readability.

Example code:

filtered_items = email.inbox.all()
for item in items:
with open(item.id, "w") as fw:
fw.write(str(item).replace('\\r\\n','\r\n'))

3. Conditional Matching

exchangelib supports Advanced Query Syntax (AQS)

AQS reference:

https://docs.microsoft.com/en-us/exchange/client-developer/web-service-reference/querystring-querystringtype

Using AQS enables date searches, with search format examples:

sent:>=2021/1/1 AND sent:<=2021/12/30
received:>=2021/1/1 AND received:<=2021/12/30

For keyword searches, AQS cannot be used directly; you may choose to receive all emails and then perform string matching.

0x04 Open Source Code

---

The complete code has been uploaded to GitHub at the following address:

An open-source project

Supports login with plaintext and NTLM Hash, the code supports the following features:

  • Supports custom Exchange servers and Office365 (outlook.office365.com)
  • download, download emails and extract attachments, with options to specify mailbox folders and download quantities
  • search, email search and download, supports syntax for keywords, time, length, etc.
  • listfolder, enumerate all user folders

When downloading emails, the email username is used as the parent folder, and different operations create different subfolders. When using the search function to create subfolders, special characters (such as >) that cannot be used as folder names are removed to avoid issues.

0x05 Summary

---

This article introduces the usage of exchangelib, the open-source code ewsManage_exchangelib_Downloader.py, which enables access to Exchange resources using hash authentication.

If you want to quickly develop a program for accessing EWS resources, exchangelib is recommended.