Use API to List All Systems That Have or Need a Particular CVE Installed

You’ll need to set the following before running the script:

$apiKey = 'YOUR_API_KEY' - in your console, go to Settings->API and select the API key. Note that the API key is per admin user, so you and another admin in your console will have different API keys.

$orgID = 'YOUR_ORG_ID' - put in your Org ID which can be found by looking at the URL on the dashboard and selecting the value after the “?o=”: [https://console.automox.com/dashboard?o=1234]. In this example the Org ID is 1234.

$cve = 'DESIRED_CVE' - Set the CVE

You can also modify $filepath if you want to change the file name or save it in a different location. Keep in mind the script will overwrite a previously generated file if it exists.

# Set these -----------------

$apiKey = 'YOUR_API_KEY'
$orgID = 'YOUR_ORG_ID'
$cve = 'CVE-2020-0689'
$filepath = 'C:\Temp\cve.csv'

# ---------------------------

$page = 0
$limit = 500
$servers = @()

$apiInstance = 'https://console.automox.com/api/'
$apiTable = 'servers'

Set-Content $filepath -Value "Computer,display_name,version,installed,$cve"

while($true) {

    $orgAndKey = "?o=$orgID&api_key=$apiKey&l=$limit&p=$page"

    # Put components together
    $uri = $apiInstance + $apiTable + $orgAndKey
    
    $resp = (Invoke-WebRequest -Method GET -Uri $uri -UseBasicParsing).Content | ConvertFrom-Json | Select-Object results

    $servers += $resp.results
    $page += 1

    if($resp.results.count -lt $limit) {
        break
    }
}

$servers = $servers | Sort-Object name

# Check each server for software
foreach ($server in $servers) {

    $serverID = $server.id
    $serverName = $server.name
    
    $orgAndKey = "/$serverID/packages?o=$orgID"

    # Put components together
    $getURI = $apiInstance + $apiTable + $orgAndKey

    $headers = @{ "Authorization" = "Bearer $apiKey" }
    $response = (Invoke-WebRequest -Method Get -Uri $getURI -Headers $headers).Content | ConvertFrom-Json

    $response | Where-Object {$_.cves -Contains $cve} | Select-Object @{label=”Computer”; Expression= {"$serverName"}},Display_Name,Version,Installed `
              | Sort-Object Display_Name | Export-Csv -Path $filepath -NoTypeInformation -Append -Force

}
1 Like

@Tony - any special account and/or access to run as? I am getting this error while simulating this script.

Invoke-WebRequest : The remote server returned an error: (403) Forbidden.
At line:26 char:14

  • … $resp = (Invoke-WebRequest -Method GET -Uri $uri -UseBasicParsing) …
  •             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebReques
      t], WebException
    • FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCo
      mmand

Hi Justin. Did you set your API key (not access key) in the script along with your orgID? Are you running Powershell as admin? Also if you do a get-executionpolicy, does is say something other than “unrestricted”? If so, do a: set-executionpolicy unrestricted

@Tony - check out above screenshot running with API key and as admin, the execution’s policy is unrestricted

Can you try generating another API key for yourself and trying that one?

same error message

Running PowerShell as administrator should only be necessary to make changes to local workstation. Messing with Execution Policy of unrestricted isn’t recommended either. If you want to see if ExecutionPolicy is a problem, then at least limit the scope to the process like Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope Process.

I’ve successfully executed Tony’s code. I did change my filepath variable to $filepath = "$env:temp\cve.csv" to keep the results in my user space.

Justin’s PowerShell error is complaining about the remote server response. That can include any proxy servers in between you and Automox. In my early days of learning Automox API, I did get ‘unauthorized’ errors when I used an expired token.

@cgreggo - what do you suggest I change to be able to run this script successfully?

Tony,

Can we jump on a quick WebEx to help me with this script?

Justin

@Tony @cgreggo - =/ I know what I did wrong! HA! I reviewed the code again and realized in the org ID - i pasted the entire URL instead of just the Org ID!

I am able to run the script! thanks for your help

2 Likes

Ah-ha! Always an adventure when computers do exactly what we tell them to do instead of what we want them to do.

1 Like

Any suggestions how to run this in an environment where the limit of 500 is causing to not show all results?

Run it as is. Tony’s code has a pagination loop already. Each loop grabs 500 servers, filters to the ones with matching CVE, and appends the matches to the CSV. Then next run through the loop advances to the next page (servers 501-1000 and so on).

1 Like

And if you do need pagination for any of your other scripts, here’s some sample code to show how to set that up:

Mind if I chime in? If I were to guess based on @Maikel ‘s response I think he meant that the pagination loop isn’t working as expected.

I understand what you are trying to say, but I can’t verify it.
You can reproduce this by lowering the $limit variable to 5 and placing a write-host inside the server pagination loop. In this iteration of the script it will only display/iterate once.

This is due to the fact that the pagination is stopped when the response length is less than the limit.
The response length is evaluated by the following line:

if($response.count -lt $limit) {
        break
    } 

However this part is not working as expected because the $response variable has never been defined till this point. An undefined variable with the count method, results in a 0 which is less than the limit, so the pagination immediately stops.
You can test this by entering a random variable with the count method in a Powershell window, for example: $test123.count .

Besides the pagination for the server endpoint, I also see that there is no pagination implemented for the packages endpoint. I don’t know how likely the chances are for a device to contain more than 500 packages, but the chances are there ¯\(ツ) .

I’m not that well versed in PowerShell, but the below iteration of the script should take pagination into consideration for both the server as packages endpoint:

Summary
# Set these -----------------

$apiKey = 'YOUR_API_KEY'
$orgID = 'YOUR_ORG_ID'
$cve = 'CVE-2020-0689'
$filepath = 'C:\Temp\cve.csv'

# ---------------------------

$page = 0
$limit = 500
$servers = @()

$apiInstance = 'https://console.automox.com/api/'
$apiTable = 'servers'

Set-Content $filepath -Value "Computer,display_name,version,installed"

while($true) {

    $orgAndKey = "?o=$orgID&api_key=$apiKey&l=$limit&p=$page"

    # Put components together
    $uri = $apiInstance + $apiTable + $orgAndKey
    
    $resp = (Invoke-WebRequest -Method GET -Uri $uri -UseBasicParsing).Content | ConvertFrom-Json | Select-Object results

    $servers += $resp.results
    $page += 1

    if($resp.results.Count -lt $limit) {
        break
    }
}

$servers = $servers | Sort-Object name

# Check each server for software
foreach ($server in $servers) {
    $page = 0
    $serverID = $server.id
    $serverName = $server.name
    $packages = @()
    
    while($true) {

    $orgAndKey = "/$serverID/packages?o=$orgID&l=$limit&p=$page"

    # Put components together
    $getURI = $apiInstance + $apiTable + $orgAndKey

    $headers = @{ "Authorization" = "Bearer $apiKey" }
    $response = (Invoke-WebRequest -Method Get -Uri $getURI -Headers $headers).Content | ConvertFrom-Json

    $packages += $response
    $page += 1

    if($response.Count -lt $limit) {
        break
    }

    }
    $packages | Where-Object {$_.cves -Contains $cve} | Select-Object @{label=”Computer”; Expression= {"$serverName"}},Display_Name,Version,Installed `
              | Sort-Object Display_Name | Export-Csv -Path $filepath -NoTypeInformation -Append -Force
}
2 Likes

I think you did find a bug! The first loop while/do is collecting results in $resp but measuring count in $response. I did not experience this bug because I had slipped in an -or $page -gt 4 when testing as I did not want to wait for it to get through my entire org. So my loop exited successfully when others would have only run once because like you said, $response doesn’t exist so it’s automatically less that the $limit and breaks out of the loop.

I modified the if statement to look at the $resp contents instead and successfully collected all the servers in my org into $servers.

if($resp.results.count -lt $limit) {
    break
}

Maybe the package collection still needs a pagination loop if a server has more packages than what the API allows to be collected in one GET request.

2 Likes