Query Defender ATP for Vulnerabilities: Part 1

Vulnerability Management is HARD

Understanding patch compliance is mission critical task for all organizations regardless of their size or affiliation. What a lot of organizations struggle with is the different between vulnerabilities and patch compliance. As a result a lot of companies end up buying expensive third party tools to scan their environment for vulnerabilities, which happens to include your base Microsoft Patches.

These vulnerability scans result in providing a huge sometimes seemingly unconquerable list for the IT department. Fortunately a large number of these vulnerabilities can be mitigated using a third party patch management solution like PatchMyPC.

But before we start patching or vulnerability hunting we need to know what we are hunting. Fortunately the Defender ATP portal can make the initial vulnerability discovery easy.

In this blog post the following items will be covered:

  • Building an Authentication Token for Defender
  • Querying the Defender for Endpoint API for vulnerabilities using PowerShell
  • Turning that Data into a consumable CSV Report

There will be future blog posts to cover the following:

  • Creating a security key with minimal read permissions for the API
  • Converting Vulnerability data into PowerBI Reports
  • Querying the same data using KQL

The above planned posts will be updated and linked as they are written.

This blog post assumes you already have an app registration, and an API secret key in Azure AD – or have the knowledge to create one. If you do not – a new post on how to create one is scheduled for next week.

Asking the right question

Before we get into automation lets jump into how we can test out different API endpoints to find out what data we would get back. Whenever I do data analytics, or build a report the first thing I do is try to make sure I’m asking, and answering the right question. If you navigate with me to securitycenter.Microsoft.com we can find to query the endpoint API without any type of automation. This way we can ensure we have the right question before we waste a bunch of time on other things.

Once you click on the API Explorer you’ll be taken to a webpage that showcase different ways you can query the defender API to retrieve data. I would encourage you to use the API Explorer to test different endpoints and see what data is sent back and how. While the overall documentation for the website is pretty good, it’s confusing especially if API’s are not something you play with on a regular basis.

You can use these queries to get a good idea of how the API works simply click one of the samples, for this example I’m going to chose “get 10 Vulnerabilities by machines” and then click the “run query” button. Once you click the run query button if you have any machines with vulnerabilities in the environment you’ll get a return back!

This alone gives us a bunch of data that’s useful. We know the CVE-ID we know the machine ID and we can use that to find the referenced machine we need to go patch. However, this format this style isn’t very helpful and having to log into a web portal like this is probably not something we can expect a manager to do on a daily basis.

Fortunately we can take the URL – presented to us above and use it with PowerShell to automate the data grab, and make it present only the data we are interested in.

For this we will be using the URL:

https://api-us.securitycenter.windows.com/api/vulnerabilities/machinesVulnerabilities?

Asking the Right Question – With PowerShell

PowerShell has a couple of different ways to query an API. However before we can even get started with querying the API we need to build our bearer token which will authenticate us to ASK questions. Fortunately this is pretty well documented on the Microsoft Website.

NOTE: Everything above was theory and info, USE CAUTION on the lines below. When you query the API you could potentially pull sensitive (vulnerabilties) – information – and a LARGE amount of information. MAKE SURE YOU TEST before you drown your machine in data.

https://docs.microsoft.com/en-us/windows/security/threat-protection/microsoft-defender-atp/api-hello-world#step-2—get-a-token-using-the-app-and-use-this-token-to-access-the-api

$tenantId = '' ### Paste your tenant ID here
$appId = '' ### Paste your Application ID here
$appSecret = '' ### Paste your Application secret here
    
#NOTE: Build the auth response token to the Windows Security Center API (Basically establishing a logged in session)
$resourceAppIdUri = 'https://api.securitycenter.windows.com'
$oAuthUri = "https://login.windows.net/$TenantId/oauth2/token"
$authBody = [Ordered] @{
     resource = "$resourceAppIdUri"
     client_id = "$appId"
     client_secret = "$appSecret"
     grant_type = 'client_credentials'
}
$authResponse = Invoke-RestMethod -Method Post -Uri $oAuthUri -Body $authBody -ErrorAction Stop
#NOTE: This returns your access token, and below we pull out the token that will be used as the authorization token going forward. 
$token = $authResponse.access_token

The above token object will then be used to query the API and get data back. Once we have the token (You can validate it by running $token in the command line) we can use a simple invoke command to get some data back and translate it from JSON.

First we build our header. Note how we specific the content type, and the bearer token.

    $headers = @{ 
        'Content-Type' = 'application/json'
        Accept = 'application/json'
        Authorization = "Bearer $token" 
    }

Then we invoke the URL that we stole, I mean borrowed from the API Explorer.

    $url = "https://api-us.securitycenter.windows.com/api/vulnerabilities/machinesVulnerabilities?"
    $response = Invoke-WebRequest -Method Get -Uri $url -Headers $headers -ErrorAction Stop
    $Data = $response.Content | ConvertFrom-Json 
    $Data.value

Now I’m skipping some steps up above, but essentially we are just taking the response content we received and convert it from JSON into a PSCustomObject. When we look at the values stored in the custom object we get:

Behold our vulnerabilities! – Now there are only a few more things we need to do here. Most managers won’t exactly approve of being handed an obscure “MachineID” as a way to start fixing issues. Fortunately the API has a way for us to retrieve that information as well.

https://api-us.securitycenter.windows.com/api/machines

The above uri will get us our machine information that means we can re-run our above code with one small change and we can get the data we want back!

    $machineURL = "https://api-us.securitycenter.windows.com/api/machines"
    $machineResponse = Invoke-WebRequest -Method Get -Uri $machineURL -Headers $headers -ErrorAction Stop
    $machineData = $machineResponse.Content | ConvertFrom-Json 
    $machineData.value

This will return back the ObjectID and the DNS name of the machine, then it’s just some matching games in PowerShell and we’ve got the vulnerabilities combined with useful information!

An important NOTE: You could do all of this data analysis in PowerBI – and simply absorb the two outputs and skip this step. However if you just want a nifty CSV – this works great.

    $endingData = New-Object -TypeName System.Collections.Generic.List[PsObject]
    foreach($machine in $machineData.value){
        $MachineVulnerabilitylist = $vulnerabilityData.value | Where-object {$_.MachineID -eq $machine.ID}
        foreach($vulnerability in $MachineVulnerabilitylist){
            $machineDataHash = [ordered]@{
                MachineName = $machine.computerDnsName
                MachineID = $machine.id
                lastSeen = $machine.lastSeen
                OSPlatform = $machine.osPlatform
                version = $machine.version
                agentVersion = $machine.agentVersion
                osBuild = $machine.osBuild
                isaadJoined = $machine.isAadJoined
                lastIpAddress = $machine.lastIpAddress
                lastExternalIpAddress = $machine.lastExternalIpAddress
                healthstatus = $machine.healthStatus
                CVE = $vulnerability.cveID
                productName = $vulnerability.productName
                productVendor = $vulnerability.productVendor
                productVersion = $vulnerability.Version
                fixingKBID = $(if($null -eq $vulnerability.fixingKBID){$vulnerability.fixingKbId};if($null -ne $vulnerability.fixingKBID){"KB$($vulnerability.FixingKBID)"})
            }
            $vuln = New-Object -typename PSobject -property $machineDataHash
            $endingData.Add($vuln) | Out-Null
        }
    }

This then produces data that looks like:

Again please Note: This data can be merged in PowerBI instead

So what does this look like all together and how accurate or useful is it? Mashing the code together is pretty easy below as to how accurate it is? The missing KB’s I have found to be very accurate I have not dug into the details of how exact the vulnerability data is or how fast the data refreshes. I imagine with pretty fast though with the EDR implications.

$tenantId = '' ### Paste your tenant ID here
$appId = '' ### Paste your Application ID here
$appSecret = '' ### Paste your Application secret here
$resourceAppIdUri = 'https://api.securitycenter.windows.com'
$oAuthUri = "https://login.windows.net/$TenantId/oauth2/token"
$authBody = [Ordered] @{
    resource      = "$resourceAppIdUri"
    client_id     = "$appId"
    client_secret = "$appSecret"
    grant_type    = 'client_credentials'
}
$authResponse = Invoke-RestMethod -Method Post -Uri $oAuthUri -Body $authBody -ErrorAction Stop
#NOTE: This returns your access token, and below we pull out the token that will be used as the authorization token going forward. 
$token = $authResponse.access_token
    
#NOTE: Build our headers to ensure we can access the information.
$headers = @{ 
    'Content-Type' = 'application/json'
    Accept         = 'application/json'
    Authorization  = "Bearer $token" 
}
$vulnerabilityUrl = "https://api-us.securitycenter.windows.com/api/vulnerabilities/machinesVulnerabilities?"
$vulnerabilityResponse = Invoke-WebRequest -Method Get -Uri $vulnerabilityUrl -Headers $headers -ErrorAction Stop
$vulnerabilityData = $vulnerabilityResponse.Content | ConvertFrom-Json 
    
   
$machineURL = "https://api-us.securitycenter.windows.com/api/machines"
$machineResponse = Invoke-WebRequest -Method Get -Uri $machineURL -Headers $headers -ErrorAction Stop
$machineData = $machineResponse.Content | ConvertFrom-Json 
    
$endingData = New-Object -TypeName System.Collections.Generic.List[PsObject]
foreach ($machine in $machineData.value) {
    $MachineVulnerabilitylist = $vulnerabilityData.value | Where-object { $_.MachineID -eq $machine.ID }
    foreach ($vulnerability in $MachineVulnerabilitylist) {
        $machineDataHash = [ordered]@{
            MachineName           = $machine.computerDnsName
            MachineID             = $machine.id
            lastSeen              = $machine.lastSeen
            OSPlatform            = $machine.osPlatform
            version               = $machine.version
            agentVersion          = $machine.agentVersion
            osBuild               = $machine.osBuild
            isaadJoined           = $machine.isAadJoined
            lastIpAddress         = $machine.lastIpAddress
            lastExternalIpAddress = $machine.lastExternalIpAddress
            healthstatus          = $machine.healthStatus
            CVE                   = $vulnerability.cveID
            productName           = $vulnerability.productName
            productVendor         = $vulnerability.productVendor
            productVersion        = $vulnerability.Version
            fixingKBID            = $(if ($null -eq $vulnerability.fixingKBID) { $vulnerability.fixingKbId }; if ($null -ne $vulnerability.fixingKBID) { "KB$($vulnerability.FixingKBID)" })
        }
        $vuln = New-Object -typename PSobject -property $machineDataHash
        $endingData.Add($vuln) | Out-Null
    }
}

I am still testing how fast it takes to do a remediation of something like – installing an update before it shows up in the defender portal as no longer a vulnerability.

I hope this was helpful and happy patching.

Leave a Reply

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

%d bloggers like this: