This project is based on PEAS, which is a Python 2 library and command line application for running commands on an ActiveSync server e.g. Microsoft Exchange. It is based on research into Exchange ActiveSync protocol by Adam Rutherford and David Chismon of MWR.
I upgraded it to use Python 3.
- Python Requests library
Path | Functionality |
---|---|
peas/as_load_test.py |
The command line application for load test. |
peas/__main__.py |
The original command line application. |
peas/peas.py |
The PEAS client class that exclusively defines the interface to PEAS. |
peas/py_activesync_helper.py |
The helper functions that control the interface to pyActiveSync. |
peas/pyActiveSync/client |
The pyActiveSync EAS command builders and parsers. |
$ git clone https://github.com/s.freeside/peas ~/tools/peas-m && cd ~/tools/peas-m
$ python3 -m virtualenv --python=/usr/bin/python venv && source venv/bin/activate
(venv) $ pip install --upgrade 'setuptools<45.0.0'
(venv) $ pip install -r requirements.txt
pyinstaller --onefile peas/as_load_test.py
Note: https://stackoverflow.com/questions/25733467/no-module-named-when-using-pyinstaller If you are getting ModuleNotFoundError: No module named ... errors and you:
call PyInstaller from a directory other than your main script use relative imports in your script then your executable can have trouble finding the relative imports.
This can be fixed by:
- calling PyInstaller from the same directory as your main script
- removing any init.py files (empty init.py files are not required in Python 3.3+)
- or using PyInstaller's paths flag to specify a path to search for imports. E.g. if you are calling PyInstaller from a parent folder to your main script, and your script lives in subfolder, then call PyInstaller as such:
pyinstaller --paths=subfolder subfolder/script.py
.
Single user test
as_load_test.exe --accounts 1 --server mail.you_exchange_server.com --domain your_domain.com --user your_email_address --password "your_password"
Test with multiple users
First, create a batch of test users on Exchange server with some pattern. For instance,
#username format L10000.T10000
$firstname="L$start"
$lastname="T$start"
New-Mailbox -UserPrincipalName "$firstname.$lastname@$domain" -Alias "$firstname.$lastname" -Name "$firstname $lastname" -OrganizationalUnit "$ou" -Password $securepass -Firstname "$firstname" -LastName "$lastname" -DisplayName "$firstname $lastname" -ResetPasswordOnNextLogon $false
Then run the following command,
as_load_test.exe --accounts 100 --server mail.you_exchange_server.com --domain your_domain.com --password "your_password"
You can see the usage by running as_load_test.exe -h
C:\work\peas\dist>as_load_test.exe -h
usage: as_load_test.exe [-h] [--delay DELAY] --accounts ACCOUNTS --domain DOMAIN --server SERVER --password PASSWORD [--user USER] [--period PERIOD] [--report REPORT] [--quiet | --no-quiet] [--log LOG]
ActiveSync load tester
optional arguments:
-h, --help show this help message and exit
--delay DELAY the delay (in seconds) between each requests in a thread, default value: 0.1 (seconds)
--accounts ACCOUNTS the number of accounts to be tested
--domain DOMAIN the test domain, e.g. yourdomain.com
--server SERVER the exchange server, e.g. mail.yourdomain.com
--password PASSWORD mailbox password
--user USER username for single test, optional
--period PERIOD test period, in minutes, optional
--report REPORT report file name, default name: report.csv
--quiet, --no-quiet do not show result of every request
--log LOG error log file name, default name: activesync_test.log
$ python -m peas mx.megacorp.local
$ python -m peas -u 'MEGACORP\s.freeside' -p 'Passw0rd1!' mx.megacorp.local --check
$ python -m peas -u 'MEGACORP\s.freeside' -p 'Passw0rd1!' mx.megacorp.local --emails
$ python -m peas -O emails -u 'MEGACORP\s.freeside' -p 'Passw0rd1!' mx.megacorp.local --emails
$ python -m peas -u 'MEGACORP\s.freeside' -p 'Passw0rd1!' mx.megacorp.local --list-unc='\\DC02'
$ python -m peas -u 'MEGACORP\s.freeside' -p 'Passw0rd1!' mx.megacorp.local --list-unc='\\DC02\SYSVOL\megacorp.local'
Note: Using an IP address or FQDN instead of a hostname in the UNC path may fail.
$ python -m peas -u 'MEGACORP\s.freeside' -p 'Passw0rd1!' mx.megacorp.local --dl-unc='\\DC02\guestshare\file.txt'
$ python -m peas -u 'MEGACORP\s.freeside' -p 'Passw0rd1!' -o file.txt mx.megacorp.local --dl-unc='\\DC02\guestshare\file.txt'
$ python -m peas -u 'MEGACORP\s.freeside' -p 'Passw0rd1!' mx.megacorp.local --crawl-unc='\\DC02\SYSVOL\megacorp.local' [--pattern xml,ini] [--download]
$ python -m peas -u 'MEGACORP\s.freeside' -p 'Passw0rd1!' mx.megacorp.local --brute-unc [--prefix xyz]
Run python -m peas --help
for the latest options.
Options:
-h, --help show this help message and exit
-u USER username
-p PASSWORD password
-q suppress all unnecessary output
--smb-user=USER username to use for SMB operations
--smb-pass=PASSWORD password to use for SMB operations
--verify-ssl verify SSL certificates (important)
-o FILENAME output to file
-O PATH output directory (for specific commands only, not
combined with -o)
-F repr,hex,b64,stdout,stderr,file
output formatting and encoding options
--pattern=PATTERN filter files by comma-separated patterns (--crawl-unc)
--download download files at a given UNC path while crawling
(--crawl-unc)
--check check if account can be accessed with given password
--emails retrieve emails
--list-unc=UNC_PATH list the files at a given UNC path
--dl-unc=UNC_PATH download the file at a given UNC path
--crawl-unc=UNC_PATH recursively list all files at a given UNC path
PEAS can be imported as a library.
import peas
# Create an instance of the PEAS client.
client = peas.Peas()
# Display the documentation for the PEAS client.
help(client)
# Disable certificate verification so self-signed certificates don't cause errors.
client.disable_certificate_verification()
# Set the credentials and server to connect to.
client.set_creds({
'server': r'mx.megacorp.local',
'user': r'MEGACORP\s.freeside',
'password': r'Passw0rd1!',
})
# Check the credentials are accepted.
print("Auth result:", client.check_auth())
# Retrieve a file share directory listing.
listing = client.get_unc_listing(r'\\DC02\SYSVOL\megacorp.local')
print(listing)
# Retrieve emails.
emails = client.extract_emails()
print(emails)
To extend the functionality of PEAS, there is a four step process:
-
Create a builder and parser for the EAS command if it has not been implemented in
pyActiveSync/client
. Copying an existing source file for another command and then editing it has proved effective. The Microsoft EAS documentation describes the structure of the XML that must be created and parsed from the response. -
Create a helper function in
py_activesync_helper.py
that connects to the EAS server over HTTPS, builds and runs the command to achieve the desired functionality. Again, copying an existing function such asget_unc_listing
can be effective. -
Create a method in the
Peas
class that calls the helper function to achieve the desired functionality. This is where PEAS would decide which backend helper function to call if py-eas-client was also an option. -
Add command line support for the feature to the PEAS application by editing
peas/__main__.py
. A new option should be added that when set, calls the method created in the previous step.
PEAS has been tested on Kali 2.0 against Microsoft Exchange Server 2013 and 2016. The domain controller was Windows 2012 and the Exchange server was running on the same machine. Results with other configurations may vary.
py-eas-client support is limited to retrieving emails and causes a dependency on Twisted. It was included when the library was being evaluated but it makes sense to remove it from PEAS now, as all functionality can be provided by pyActiveSync.
The licence may be restrictive due to the inclusion of pyActiveSync, which uses the GPLv2.
The requirement to know the hostname of the target machine for file share access may impede enumeration.