
Identify public-facing Microsoft Exchange servers and determine their software versions

This tool scans several common public-facing services on a target host in order to detect Microsoft Exchange Server and fingerprint its software version if possible.
To do this, it uses the following techniques:
Detected build numbers are matched against a version table scraped from Microsoft that covers every release from Exchange Server 5.5 through Exchange Server Subscription Edition (SE), including Service Pack (SP), Cumulative Update (CU), Security Update (SU), Hotfix Update (HU), and Update Rollup (RU) levels. Matches are reported with a product name, release date, and CPE strings in the NVD dictionary format.
The recommended way to run the scanner is with Docker, which requires no local dependencies beyond Docker itself. To run the script directly instead, it was designed for use on 64-bit Debian Linux, but should work with any Linux variant supported by Python 3, dnsx, and fingerprintx (with minor modifications to the installation script).
Build the image. It bundles Python, the pinned Python packages, and version-locked dnsx and fingerprintx binaries, and scrapes the Exchange Server version table from Microsoft at build time:
docker build -t exchange-scanner .
Then run the scanner, passing the same arguments you would pass to the script (described under Usage below):
# Scan a single target
docker run --rm exchange-scanner -q -t 192.168.5.3
# Scan a list of targets (mount the file into the container)
docker run --rm -v "$PWD/targets.txt:/app/targets.txt" exchange-scanner -l targets.txt
Rebuild the image to refresh the version table.
Run scripts/install-requirements.sh to install the required tools, packages, and other dependencies. Python packages are pinned to specific versions in scripts/requirements.txt.
Feel free to re-run scripts/get-exchange-versions.py separately at any time to update the software version table found in exchange-versions.json.
Run the exchange-scanner.py script with -h to view usage details:
❯ ./exchange-scanner.py -h
usage: exchange-scanner.py [-h] [-q] [-d] [-x TIMEOUT] [-o OUTFILE] (-t TARGET | -l LIST)
optional arguments:
-h, --help show this help message and exit
-q, --quiet Suppress log output (except errors)
-d, --debug Print debugging messages
-x TIMEOUT, --timeout TIMEOUT
Set timeout for HTTP requests (defaults to 3 seconds)
-o OUTFILE, --outfile OUTFILE
Save output to a file instead of writing to stdout
-t TARGET, --target TARGET
A single hostname or IP address to scan
-l LIST, --list LIST File containing a list of hostnames and/or IP addresses to scan (one per line)
Required: -t or -l
-t with a single hostname or IP address. If the hostname resolves to multiple IPs, the script will scan each of them separately.-l with the name of the file.Logging: To suppress most log output, use -q. To view more log detail, use -d.
Timeout: To change the maximum response time per HTTP request, specify a timeout value in seconds (the default is 3). Keep in mind that a single IP address can generate over 200 requests under certain circumstances (though most scans don't reach this volume), so a shorter timeout is generally preferred.
Output: Scan results are printed in JSON format by default. To save output to a file instead of printing to stdout, use -o with a filename.
$ ./exchange-scanner.py -q -t 192.168.5.3
[
{
"hostname": "192.168.5.3",
"ip_address": "192.168.5.3",
"build_number": "15.2.1118",
"exchange_services": [
"https://192.168.5.3:443",
"imaps://192.168.5.3:993"
],
"product_name": "Exchange Server 2019 CU12 (2022H1)",
"major_version": "2019",
"update": "Cumulative Update 12",
"security_update": null,
"release_date": "2022-04-20",
"recalled": false,
"cpes": [
"cpe:2.3:a:microsoft:exchange_server:2019:cumulative_update_12:*:*:*:*:*:*",
"cpe:2.3:a:microsoft:outlook_web_access:2019:cumulative_update_12:*:*:*:*:*:*"
]
}
]
Usage of this tool for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state, and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program.
This project is licensed under the GNU General Public License.
recalled field in its output.