Microsoft Defender for Endpoint: The Latest Versions of Antivirus Engine & Signatures

In a previous episode on Microsoft Defender for Endpoint, I described how to get a list of antivirus engine and signatures versions for the hosts in your infrastructure using the Microsoft Graph API. But the problem remains. You know the versions that are currently installed on the hosts. But where can you get the latest versions that should be installed there?

I haven’t found any pretty solution for this. I parse public html pages on the Microsoft site I’ll show you how I do it. If you know something better, please write in the comments.

Antivirus engine versions

You can see the engine version on the defenderupdates page.

It can be obtained using a simple function:

import requests
import re

def get_defender_av_engine_version():
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        'Accept-Language': 'en-US,en;q=0.5',
        'Connection': 'keep-alive',
        'Upgrade-Insecure-Requests': '1',
    }
    response = requests.get('https://www.microsoft.com/en-us/wdsi/defenderupdates', headers=headers)
    vers = re.findall("<li>Engine Version: <span>([^<]*)</span></li>", response.text)
    version = ""
    if vers:
        version = vers[0]
    return version

print(get_defender_av_engine_version())

Output:

1.1.18500.10

In fact, this is just one GET request and parsing of the html-page. As a result, we get the version of the Microsoft Defender for Endpoint antivirus engine, which can be compared with the versions of the engine installed on the hosts.

Antivirus signature versions

Now let’s look at the signature versions. There is such a page antimalware-definition-release-notes.

The one latest version may be enough for the Defender engine, because it doesn’t change very often. But this will not work for signatures. Signatures are updated several times a day, and requiring all hosts to have the latest version of signatures at any given time is simply impractical. What can be done? We can get a “signature version -> the release date” mapping and select the versions that work for us. The page has a drop-down list of all available versions.

With a GET request, I get a list of these versions and then make separate requests to get the dates for them.

import requests
import re

def get_top_20_signature_versions():
    base_url = "https://www.microsoft.com/en-us/wdsi/definitions/antimalware-definition-release-notes"
    r = requests.get(base_url)
    signature_versions = list()
    for line in r.text.split("\n"):
        if "dropDownOption" in line:
            signature_versions.append(re.sub("[ \r]","",re.sub("<[^>]*>","",line)))
    return signature_versions[:20]

def get_release_date(signature_version):
    base_url = "https://www.microsoft.com/en-us/wdsi/definitions/antimalware-definition-release-notes?" + \
               "requestVersion=" + signature_version
    r = requests.get(base_url)
    release_date = ""
    for line in r.text.split("\n"):
        if "releaseDate_0" in line:
            release_date = re.sub("\r","",re.sub("^ *","",re.sub("<[^>]*>","",line)))
    return(release_date)

top_20_signature_versions = get_top_20_signature_versions()
for signature_version in top_20_signature_versions:
    release_date = get_release_date(signature_version)
    print(signature_version + " - " + release_date)

Output:

1.349.657.0 - 9/13/2021 12:59:12 PM
1.349.655.0 - 9/13/2021 10:45:47 AM
1.349.651.0 - 9/13/2021 8:35:52 AM
1.349.647.0 - 9/13/2021 6:25:58 AM
1.349.643.0 - 9/13/2021 4:09:46 AM
...

By analyzing this mapping, you can, for example, get the maximum version that was 2 days ago, and assume that all versions more than it are normal, and those that are less than it are outdated.

...

import time
import datetime

def release_date_to_ts(date_string):
    return (time.mktime(datetime.datetime.strptime(str(date_string), '%m/%d/%Y %I:%M:%S %p').timetuple()))

def ts_to_human(ts):
    return(
        datetime.datetime.fromtimestamp(
            int(ts)
        ).strftime('%Y-%m-%d %H:%M:%S')
    )

def get_current_ts():
    return(int(time.time()))

def get_max_2_days_ago_sifnature_version():
    top_20_signature_versions = get_top_20_signature_versions()
    for signature_version in top_20_signature_versions:
        release_date = get_release_date(signature_version)
        ts = release_date_to_ts(release_date)
        if ts > get_current_ts() - 3*24*60*60 and ts < get_current_ts() - 2*24*60*60:
            return signature_version

print(get_max_2_days_ago_sifnature_version())

Output:

1.349.554.0

The use of this information about the latest versions of the Defender for Endpoint engine and signatures depends on your corporate policy.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.