Kaspersky Security Center 11 API: getting information about hosts and installed products

I spent a lot of time last week working with the new API of Kaspersky Security Center 11. KSC is the administration console for Kaspersky Endpoint Protection products. And it has some pretty interesting features besides the antivirus/antimalware, for example, vulnerability and patch management. So, the possible integrations with other security systems might be quite useful.

Kaspersky SC 11 openAPI

A fully functional API was firstly presented in this latest version of KSC. It’s is documented pretty well, but in some strange way. In fact, the documentation is one huge .chm file that lists the classes, methods of these classes and data structures with brief descriptions. It’s not a cookbook that gives a solution for the problem. In fact, you will need to guess which methods of which classes should be used to solve your particular task.

For the first task, I decided to export the versions of Kaspersky products installed on the hosts. It is useful to control the endpoint protection process: whether all the necessary agents and products were installed on the hosts or not (and why not).

So, I need to:

  1. Get all groups (of hosts).
  2. Make search requests to get the hosts in each group.
  3. Get various parameters for each host, for example installed products (agent, endpoint security, etc.) and the date of last antivirus scan.

Authentication

First of all, some authentication is required to work with Kaspersky Security Center. There are several authentication methods supported in KSC 11. I decided to use the login and password of the local KSC user (technical account). The idea is to send a POST request to /api/v1.0/login with a login and password (encoded in base64 ) in the header.

#!/usr/bin/python
# -*- coding: utf-8 -*-

import requests
import base64
import json
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

ksc_server = "https://ksc.corporation.com:13299"
url = ksc_server + "/api/v1.0/login"
user = "automation_user"
password = "Password123"

user = base64.b64encode(user.encode('utf-8')).decode("utf-8")
password = base64.b64encode(password.encode('utf-8')).decode("utf-8")

session = requests.Session()

auth_headers = {
    'Authorization': 'KSCBasic user="' + user + '", pass="' + password + '", internal="1"',
    'Content-Type': 'application/json',
}

data = {}

response = session.post(url=url, headers=auth_headers, data=data, verify=False)
print(response.status_code)

Output:
200

If the status code is 200, everything is fine, and this session can be used for all further requests. I create a session object with requests and not just calling requests.post because I want to use all the authentication magic it does during the login for my other requests. I haven’t found easily if they set some specific token in headers, specific cookies or something else. 🙂 They don’t document it and I am not very interested to research it. With sessions it works fine.

Getting groups

Then I decided to get all the host groups using FindGroups method in HostGroup class. You can set some filter to search for some particular group. If the filter is empty, the method will simply return all the groups. Well, not actual groups, but strAccessor which you can then use to to get the results of a search request.

url = ksc_server + "/api/v1.0/HostGroup.FindGroups"
common_headers = {
    'Content-Type': 'application/json',
}
data = {"wstrFilter": "", "vecFieldsToReturn": ['id', 'name'], "lMaxLifeTime": 100}
response = session.post(url=url, headers=common_headers, data=json.dumps(data), verify=False)
strAccessor = json.loads(response.text)['strAccessor']
print(strAccessor)

Output:
x7_atWQNHLKjJh84E4QuK1

Getting search results

For getting the results (groups, hosts, etc.) of a search request by strAccessor I used GetItemsChunk method of ChunkAccessor class and some simple pagination. How big the step of pagination should be? It seem that there are no particular limits, It has “int” type, so basically it can be pretty big. I set it 100000.

def get_search_results(strAccessor):
    url = ksc_server + "/api/v1.0/ChunkAccessor.GetItemsCount"
    common_headers = {
        'Content-Type': 'application/json',
    }
    data = {"strAccessor": strAccessor}
    response = session.post(url=url, headers=common_headers, data=json.dumps(data), verify=False)
    items_count = json.loads(response.text)['PxgRetVal']
    start = 0
    step = 100000
    results = list()
    while start < items_count:
        url = ksc_server + "/api/v1.0/ChunkAccessor.GetItemsChunk"
        data = {"strAccessor": strAccessor, "nStart": 0, "nCount": items_count}
        response = session.post(url=url, headers=common_headers, data=json.dumps(data), verify=False)
        results += json.loads(response.text)['pChunk']['KLCSP_ITERATOR_ARRAY']
        start += step
    return (results)

So, to get the host groups I run:

groups  = get_search_results(strAccessor = strAccessor)
for group in groups:
    print(str(group['value']['id'])  + " - " + group['value']['name'])

Output:

4 – OURORGANIZATION
5 – IMPORTANTServers
9 – OTHERSEravers
10 – NewYork
11 – Area51

Getting hosts from the group

I will make a search request once again, but now with FindHosts method of HostGroup class. KSC 11 has an advanced search query language. Here is some simple filter to search for hosts with some particular group id:

group_id = 5

url = ksc_server + "/api/v1.0/HostGroup.FindHosts"
common_headers = {
    'Content-Type': 'application/json',
}
data = {"wstrFilter": "(KLHST_WKS_GROUPID = " + str(group_id) + ")",
        "vecFieldsToReturn": ['KLHST_WKS_FQDN', 'KLHST_WKS_HOSTNAME'], "lMaxLifeTime": 100}
response = session.post(url=url, headers=common_headers, data=json.dumps(data), verify=False)
if 'strAccessor' in json.loads(response.text):
    strAccessor = json.loads(response.text)['strAccessor']
    hosts = get_search_results(strAccessor)
    for host in hosts:
        print(host['value']['KLHST_WKS_FQDN'] + " - " +  host['value']['KLHST_WKS_HOSTNAME'])

Output:

aw-plbd01 – d8017ba2-sdfr-40c5-9058-51661dbe10ba
aw-plbd02 – b414bdd9-2dfd-414e-a876-79bd0e9fa666
df-gty01 – 5318a567-6efg-4257-8942-08948b483d81
df-gty02 – 261bf509-1345-4ac1-a1ab-a1da180fe235

KLHST_WKS_HOSTNAME – is the host identifier in Kaspersky SC and it can be used to retrieve data associated with the host. I don’t know why Kaspersky call it “hostname”, I find it pretty it’s confusing, but still.

Getting products installed on the host

So, now for each host_id I can use GetHostProducts method of HostGroup class to get information about Kaspersky products installed on the host:

host_id = "261bf509-1345-4ac1-a1ab-a1da180fe235"

url = ksc_server + "/api/v1.0/HostGroup.GetHostProducts"
common_headers = {
    'Content-Type': 'application/json',
}
data = {"strHostName": host_id}
response = session.post(url=url, headers=common_headers, data=json.dumps(data), verify=False)
product_data = json.loads(response.text)['PxgRetVal']
products = dict()
for product in product_data:
    major_ver = list(product_data[product]['value'].keys())[0]
    if 'DisplayName' in product_data[product]['value'][major_ver]['value']:
        name = product_data[product]['value'][major_ver]['value']['DisplayName']
    else:
        name = product
    products[name] = dict()
    if 'ProdVersion' in product_data[product]['value'][major_ver]['value']:
        products[name]['version'] = product_data[product]['value'][major_ver]['value']['ProdVersion']
    else:
        products[name]['version'] = major_ver
    if 'LastUpdateTime' in product_data[product]['value'][major_ver]['value']:
        products[name]['last_update'] = product_data[product]['value'][major_ver]['value']['LastUpdateTime'][
            'value']
    else:
        products[name]['last_update'] = "n/a"
    print(name)
    print(products[name])

Output:
Kaspersky Security 10.1 для Windows Server
{‘version’: u’10.1.0.622′, ‘last_update’: u’2019-06-19T09:17:21Z’}
Агент администрирования Kaspersky Security Center 10
{‘version’: u’10.5.1781′, ‘last_update’: u’2018-04-12T14:50:26Z’}

The second product is in fact a Network Agent. Unfortunately I have KSC with Russian locale and don’t know how exactly Kaspersky call this component in English.

Getting information about the host

And finally, I will show the GetHostInfo method of HostGroup class that returns information about the host. For example, I get the OS version (KLHST_WKS_OS_NAME), the last time when full antivirus scan was performed on the host (KLHST_WKS_LAST_FULLSCAN) and the number of detected viruses (KLHST_WKS_VIRUS_COUNT):

host_id = "261bf509-1345-4ac1-a1ab-a1da180fe235"
url = ksc_server + "/api/v1.0/HostGroup.GetHostInfo"
common_headers = {
    'Content-Type': 'application/json',
}
data = {"strHostName":host_id, "pFields2Return":['KLHST_WKS_OS_NAME', 'KLHST_WKS_LAST_FULLSCAN', 'KLHST_WKS_VIRUS_COUNT']}

response = session.post(url=url, headers=common_headers, data=json.dumps(data), verify=False)
print(json.loads(response.text)['PxgRetVal']['KLHST_WKS_OS_NAME'])
print(json.loads(response.text)['PxgRetVal']['KLHST_WKS_LAST_FULLSCAN'])
print(json.loads(response.text)['PxgRetVal']['KLHST_WKS_VIRUS_COUNT'])

Output:
Microsoft Windows Server 2012 R2
{u’type’: u’datetime’, u’value’: u’2019-07-13T20:05:23Z’}
{u’type’: u’long’, u’value’: 0}

That’s it. I hope someone will find it useful. The next task, that I want to solve with Kaspersky SC 11 API will be probably related to it’s vulnerability and patch management features.

Leave a Reply

Your email address will not be published. Required fields are marked *

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