Quantcast
API

Using the Automox API: Missing Patches

By July 3, 2018 No Comments
Missing Patches

Today I will continue to share information about using the Automox API. This post will show you how to get a list of uninstalled patches from your systems using the API.

There are a number of API features to use which can be found here: https://docs.automox.com/api/. In this example, we will go over using the software_version API call using python and we will create a graph of missing packages using Plotly. Be sure to log in to the console, navigate to Settings, and you’ll find the API Key at the bottom of the page.

The Software_version API is structured as shown below:

[
{
“server_count”: 0,
“severity_low_count”: 0,
“severity_medium_count”: 0,
“severity_high_count”: 0,
“severity_other_count”: 0,
“size”: 0,
“results”: [
{
“id”: 0,
“software_id”: 0,
“name”: “string”,
“display_name”: “string”,
“version”: “string”,
“os_family”: “string”,
“severity”: “string”,
“server_count”: 0,
“approvals”: [
{
“id”: 0,
“software_version_id”: 0,
“policy_id”: 0,
“manual_approval”: true,
“manual_approval_time”: “2018-06-25T16:32:31.508Z”,
“auto_approval”: true,
“server_count”: 0
}
],
“pending_update”: {},
“total_count”: 0,
“total_server_count”: 0,
“total_severity_low_count”: 0,
“total_severity_medium_count”: 0,
“total_severity_high_count”: 0,
“total_severity_other_count”: 0
}
] }
]

While this call gives you a lot of information, we will only look at patches missing from systems and the number of systems missing that patch. This can be caused, for example, because a machine was turned off for the weekend.

A simple method to get a list would be to just display to the screen as shown below:

for item in pageText[‘results’]:
serverName=item[‘display_name’] for item2 in item[‘pending_update’]:
missingCount=item2[‘server_count’] print( serverName + ” ” + str(missingCount))

The output would be:

joes-MacBook-Pro:AMX-API joe$ ./software.py  –dump
2018-05 Cumulative Update for Windows 10 Version 1803 for x64-based Systems (KB4103721) 1
2018-05 Cumulative Update for Windows 10 Version 1803 for x64-based Systems (KB4103721) 1
2018-06 Security Monthly Quality Rollup for Windows Server 2008 R2(KB4284826) 1
Adobe Acrobat Reader DC 1
Adobe Reader (32-bit) 1
Command Line Tools (macOS High Sierra version 10.13) for Xcode 1
Definition Update for Windows Defender Antivirus – KB2267602 1
Definition Update for Windows Defender Antivirus – KB2267602 1
Digital Camera RAW Compatibility Update 1
Firefox 1
Google Chrome 1
Google Chrome 1
iTunes 1
kernel-tools.x86_64 1
kernel.x86_64 1
macOS High Sierra 10.13.3 Update Combo 1
macOS High Sierra 10.13.5 Update 1
Microsoft Office 365 – en-us 1
Remote Desktop Client Update 1
Safari 1
Security Update 2018-003 1
system-release.noarch 1

You may want a CSV or a table as an output. That can be done easily with pretty tables and writing a file.

As usual, you would want to perform imports of required modules:

import json
import requests
from prettytable import PrettyTable
import argparse
from datetime import datetime

I am a huge, HUGE fan of using ArgParse to pass in things like your API Key and switches. It is just easier than parsing sysargv options:

parser = argparse.ArgumentParser(description=’Automox API Example’)
parser.add_argument(‘–limit’, help=”Limit results to X”, type=int)
parser.add_argument(‘–csv’, help=”Output as CSV”,  action=”store_true”)
parser.add_argument(‘–table’, help=”Output as table”,  action=”store_true”)
parser.add_argument(‘–graph’, help=”Output as table”,  action=”store_true”)
parser.add_argument(‘–dump’, help=”Dump data & exit”,action=”store_true”)
parser.add_argument(‘apiKey’, help=”API Key “, type=str)
parser.add_argument(‘orgId’, help=”AMX Org ID “, type=str)
args=parser.parse_args()

Then, you would request the JSON using requests and loop through the data. Note that I am creating a table or writing a CSV based on the user arg:

if args.csv:
filename=datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’) + “-noncomp.csv”
fh=open(filename, “w+”)

table=PrettyTable([“Package”,  “Machines to be Upgraded”])
for item in pageText[‘results’]:
packageName=item[‘display_name’] for item2 in item[‘pending_update’]:
missingCount=item2[‘server_count’] if args.table:
table.add_row([packageName, missingCount])
if args.csv:
fh.write(“\”{}\” , {}”.format(packageName, missingCount))
if args.graph:
xData.append(packageName)
yData.append(missingCount)

if args.limit:
if i >= args.limit:
break
i+=1

This will create a table or CSV which you can open in Excel, etc.:


If you wanted to carry this further and create graph from this data, you would import Plotly and graph using just a few lines of code:

if args.graph:
plotly.offline.plot({
“data”:[plotly.graph_objs.Bar(x=xData, y=yData)],
“layout”:plotly.graph_objs.Layout(title=”Missing Package Count”,
xaxis=dict(title=”Package Name”),
yaxis=dict(title=”Count”))})

The graph would look like the one below, which is not terribly interesting, as I am only missing one patch each on a single system:

The script start to finish looks like this:

#!/usr/bin/env python3
import json
import requests
from prettytable import PrettyTable
import argparse
from datetime import datetime
parser = argparse.ArgumentParser(description=’Automox API Example’)
parser.add_argument(‘–limit’, help=”Limit results to X”, type=int)
parser.add_argument(‘–csv’, help=”Output as CSV”,  action=”store_true”)
parser.add_argument(‘–table’, help=”Output as table”,  action=”store_true”)
parser.add_argument(‘–graph’, help=”Output as table”,  action=”store_true”)
parser.add_argument(‘–dump’, help=”Dump data & exit”,  action=”store_true”)
parser.add_argument(‘apiKey’, help=”API Key “, type=str)
parser.add_argument(‘orgId’, help=”AMX Org ID “, type=str)
args=parser.parse_args()

baseUrl=”https://console.automox.com/api/software_version?api_key=”
url=baseUrl + args.apiKey  + “&o=” + args.orgId + “&pendingUpdate=1”

i=0
pageText=requests.get(url).json()

if args.graph:
try:
import plotly
import plotly.graph_objs as go
xData=[] yData=[] except:
print(“ERROR: Plotly not installed, try pip3 install plotly”)
quit()

if args.dump:
print(pageText)
for item in pageText[‘results’]:
serverName=item[‘display_name’] for item2 in item[‘pending_update’]:
missingCount=item2[‘server_count’] print( serverName + ” ” + str(missingCount))
quit()

if args.csv:
filename=datetime.now().strftime(‘%Y-%m-%d %H:%M:%S’) + “-noncomp.csv”
fh=open(filename, “w+”)

table=PrettyTable([“Package”,  “Machines to be Upgraded”])
for item in pageText[‘results’]:
packageName=item[‘display_name’] for item2 in item[‘pending_update’]:
missingCount=item2[‘server_count’] if args.table:
table.add_row([packageName, missingCount])
if args.csv:
fh.write(“\”{}\” , {}”.format(packageName, missingCount))
if args.graph:
xData.append(packageName)
yData.append(missingCount)

if args.limit:
if i >= args.limit:
break
i+=1

if args.table:
print(table)
if args.csv:
fh.close()
if args.graph:
plotly.offline.plot({
“data”:[plotly.graph_objs.Bar(x=xData, y=yData)],
“layout”:plotly.graph_objs.Layout(title=”Missing Package Count”,
xaxis=dict(title=”Package Name”),
yaxis=dict(title=”Count”))})

Now that you have begun to dive deeper in to the Automox API you can continue to automate features of your patch management. If you have any questions, feel free to contact support@automox.com.

Joe McManus

Author Joe McManus

Joe is a Senior Cyber Security Researcher at CERT and a Professor at the University of Colorado College of Engineering where he teaches graduate courses in information security and forensics. Recently, Joe was the Director of Security at SolidFire, (acquired by NetApp [NTAP]). He is an avid cyclist, climber and leads the Automox security team.

More posts by Joe McManus

Leave a Reply