Details
| ID | Vectra v2 |
|---|---|
| Provider | Vectra AI |
| Category | Network Security |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.11.4819260 |
| Supported Modules | Agentix XSIAM |
README
Vectra is a detection product that alerts on suspicious network behavior. It can recognize certain known attacks and suspicious interactions on the network level (e.g. Reverse Shell, Port Scans, etc)
Detailed Description
-
Vectra API is available for administrators and developers to integrate Vectra's breach detection data into their applications. Vectra provides access to security event data, platform configuration, and health information via URI paths.
- Tested with API version 2.1.
- c_score and t_score fields changed to certainty and threat accordingly
Fetch Incidents
You can Fetch only Detections with Greater/Equal Certainty score and Threat score
Configure Vectra v2 on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for Vectra v2.
-
Click Add instance to create and configure a new integration instance.
- Name: a textual name for the integration instance.
- Server URL (e.g. https://192.168.0.1)
- API Token
- First fetch time range ( , e.g., 1 hour, 30 minutes)</strong></li>
- Fetch only Detections with greater/equal Certainty score
- Fetch only Detections with greater/equal Threat score
- Fetch only Detections with matching State
- The number of results returned in each fetch
- Fetch incidents
- Trust any certificate (not secure)
- Incident type
- Use system proxy settings </ul> </li>
- Click Test to validate the new instance. </ol>
- vectra-get-detections: vectra-get-detections
- vectra-get-hosts: vectra-get-hosts
- vectra-triage: vectra-triage
- vectra-get-host-by-id: vectra-get-host-by-id
- vectra-get-detection-by-id: vectra-get-detection-by-id
- vectra-get-users: vectra-get-users
- vectra-get-proxies: vectra-get-proxies
- vectra-get-threatfeed: vectra-get-threatfeed
- vectra-search: vectra-search
Commands
You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook. After you successfully execute a command, a DBot message appears in the War Room with the command details.
1. vectra-get-detections
Detection objects contain all the information related to security events detected on the network
Base Command
vectra-get-detectionsInput
Argument Name Description Required detection_id Filter by detection ID Optional fields Filters objects listed Optional page Page number. Possible values are a positive integer or last Optional page_size Page size. Possible values are a positive integer or all Optional ordering Orders records by last timestamp, threat score and certainty score. The default out sorts threat and certainty score in ascending order. Scores can sorted in descending order by prepending the query with “minus” symbol Optional min_id >= the id provided Optional max_id <= the id provided Optional state filter by state: active, inactive, ignored, ignored for all Optional type_vname filter by the detection type (verbose name) Optional category filter by the detection category Optional src_ip filter by source (ip address) Optional threat_score filter by threat score Optional threat_score_gte filter by threat score >= the score provided Optional certainty_score filter by certainty score Optional certainty_score_gte filter by certainty score >= the score provided Optional last_timestamp filter by last timestamp Optional host_id filter by id of the host object a detection is attributed to Optional tags filter by a tag or a comma-separated list of tags Optional destination filter by destination in the detection detail set Optional proto filter by the protocol in the detection detail set Optional destination_port filter by the destination port in the detection detail set Optional inbound_ip filter by the inbound_ip in the relayed comm set Optional inbound_proto filter by the inbound_proto in the relayed comm set Optional inbound_port filter by the inbound_port in the relayed comm set Optional inbound_dns filter by the inbound_dns in the relayed comm set Optional outbound_ip filter by the outbound_ip in the relayed comm set Optional outbound_proto filter by the outbound_proto in the relayed comm set Optional outbound_port filter by the outbound_port in the relayed comm set Optional outbound_dns filter by the outbound_dns in the relayed_comm_set Optional dns_ip filter by the dns_ip in the dns_set Optional dns_request filter by the dns_request in the dns_set Optional resp_code filter by the resp_code in the dns_set Optional resp filter by the resp in the dns_set Optional Context Output
Path Type Description Vectra.Detection.Category String The category of the vname attack detected Vectra.Detection.TargetsKeyAsset Boolean Flag indicating if the host has a detection targeting a key asset Vectra.Detection.CertaintyScore Number The current certainty score correlated to this host Vectra.Detection.ID Number Object ID Vectra.Detection.FirstTimestamp String The timestamp when the event was first detected Vectra.Detection.LastTimestamp String The timestamp when the event was last detected Vectra.Detection.State String The state of the detection Vectra.Detection.Threat_Score Number The threat score attributed to the detection Vectra.Detection.SourceIP String The source IP address of the host attributed to the security event Vectra.Detection.SourceAccount Unknown A dictionary with fields that describe the Account the detection is from Command Example
!vectra-get-detections certainty_score_gte=20Context Example
{ "Vectra.Detection": [ { "Category": "LATERAL MOVEMENT", "CertaintyScore": 22, "Detection": "Automated Replication", "DetectionCategory": "LATERAL MOVEMENT", "DetectionType": "Automated Replication", "FirstTimestamp": "2019-10-02T22:05:34Z", "ID": 3, "LastTimestamp": "2019-10-02T22:12:39Z", "SourceHost": { "certainty": 0, "groups": [], "id": 9, "ip": "0.0.0.0", "is_key_asset": false, "name": "sandbox", "threat": 0, "url": "" }, "SourceIP": "0.0.0.0", "State": "inactive", "TargetsKeyAsset": false, "ThreatScore": 22 }, { "Category": "RECONNAISSANCE", "CertaintyScore": 80, "Detection": "Port Sweep", "DetectionCategory": "RECONNAISSANCE", "DetectionType": "Port Sweep", "FirstTimestamp": "2019-10-02T22:38:58Z", "ID": 5, "LastTimestamp": "2019-10-02T22:54:49Z", "SourceHost": { "certainty": 27, "groups": [], "id": 11, "ip": "0.0.0.0", "is_key_asset": false, "name": "Robert-MBP", "threat": 11, "url": "" }, "SourceIP": "0.0.0.0", "State": "active", "TargetsKeyAsset": false, "ThreatScore": 60 } ] }Human Readable Output
Detection table (Showing Page 1 out of 1)
id category src_ip threat certainty state detection detection_category detection_type first_timestamp tags targets_key_asset type_vname 3 LATERAL MOVEMENT 0.0.0.0 22 22 inactive Automated Replication LATERAL MOVEMENT Automated Replication 2019-10-02T22:05:34Z false 5 RECONNAISSANCE 0.0.0.0 60 80 active Port Sweep RECONNAISSANCE Port Sweep 2019-10-02T22:38:58Z false 2. vectra-get-hosts
Host information includes data that correlates the host data to detected security events
Base Command
vectra-get-hostsInput
Argument Name Description Required host_id Filter by host ID Optional fields Filters objects listed Optional page Page number. Possible values are a positive integer or last Optional page_size Page size. Possible values are a positive integer or all Optional ordering Orders records by last timestamp, threat score and certainty score. The default out sorts threat and certainty score in ascending order. Scores can sorted in descending order by prepending the query with “minus” symbol Optional name filter by name Optional state filter by state: active, inactive, suspended, ignored, ignored for all Optional last_source filter by last_source (ip address) Optional threat_score filter by threat score Optional threat_score_gte filter by threat score >= the score provided Optional certainty_score filter by certainty score Optional certainty_score_gte filter by certainty score >= the score provided Optional last_detection_timestamp filter by last_detection_timestamp Optional tags filter by a tag or a comma-separated list of tags (returns hosts that contain any of the tags specified), e.g.tags=baz | tags=foo,bar" Optional key_assest filter by key asset: True, False Optional mac_address filter by mac address Optional Context Output
Path Type Description Vectra.Host.Name String The learned hostname Vectra.Host.TargetsKeyAsset Boolean Flag indicating if the host has a detection targeting a key asset Vectra.Host.CertaintyScore Number The current certainty score correlated to this host Vectra.Host.DetectionID String List of Detections for Account Vectra.Host.KeyAsset Boolean Flag indicating if the host is a key asset Vectra.Host.State String The state of this host Vectra.Host.Threat_Score Number The current threat score correlated to this host Vectra.Host.LastDetection String Last detection activity from this host (Timestamp format: YYYY-MM-DD HH-MM-SS GMT) Vectra.Host.IP String Last source IP associated with this host Vectra.Host.ID Number ID of the Host Command Example
!vectra-get-hosts threat_score_gte=20Context Example
{ "Vectra.Host": [ { "ActiveTraffic": false, "CertaintyScore": 45, "DetectionID": [ "22", "23", "37", "42", "61", "62", "63", "64" ], "HostLuid": "duOUtBa4", "ID": 57, "IP": "0.0.0.0", "KeyAsset": true, "LastDetection": "2019-10-07T05:37:12Z", "LastDetectionTimestamp": "2019-10-07T05:37:12Z", "LastModified": "2019-10-03T07:04:06Z", "LastSource": "0.0.0.0", "Name": "leroy_brown", "Note": null, "OwnerName": "lbrown", "Severity": "low", "State": "active", "Tags": [], "ThreatScore": 34 }, { "ActiveTraffic": false, "CertaintyScore": 32, "DetectionID": [ "53", "56", "60" ], "HostLuid": "dwGUtBaK", "ID": 103, "IP": "0.0.0.0", "KeyAsset": false, "LastDetection": "2019-10-04T19:24:04Z", "LastDetectionTimestamp": "2019-10-04T19:24:04Z", "LastModified": "2019-10-04T12:40:38Z", "LastSource": "0.0.0.0", "Name": "winfs06r3u17", "Note": null, "OwnerName": null, "Severity": "low", "State": "active", "Tags": [], "ThreatScore": 22 } ] }Human Readable Output
Hosts table (Showing Page 1 out of 1)
id name state threat certainty last_source url assigned_to owner_name first_timestamp tags note 7 BThomas-Win7 active 23 33 0.0.0.0 demist.com/api/v2/hosts/7 bthomas 11 Robert-MBP active 25 46 0.0.0.0 demist.com/api/v2/hosts/11 rwilliams 3. vectra-triage
The rules branch can be used to retrieve a listing of configured Triage rules
Base Command
vectra-triageInput
There are no input arguments for this command.Context Output
Path Type Description Vectra.Rule.ID Unknown The record id Vectra.Rule.SmartCategory Unknown Custom Triage label used to recategorize specified detections Vectra.Rule.Description Unknown Name of Triage filter Vectra.Rule.Type Unknown Original detection type Vectra.Rule.Category Unknown Original detection category Vectra.Rule.Created Unknown The timestamp when this Triage filter was created Vectra.Rule.LastUpdate Unknown The timestamp when this Triage filter was triggered Vectra.Rule.Host.ID Unknown Host(s) that this Triage filter applies to Vectra.Rule.IP Unknown Host IP Vectra.Rule.Priority Unknown Used in ordering execution of Triage filters Vectra.Rule.Remote.IP Unknown Destination IP where this Triage filter will be applied to Vectra.Rule.Remote.Protocol Unknown Destination protocol where this Triage filter will be applied to Vectra.Rule.Remote.Port Unknown Destination port where this Triage filter will be applied to Vectra.Rule.Remote.DNS Unknown Destination FQDN where this Triage filter will apply to Vectra.Rule.Remote.Kerberos.Account Unknown Kerberos Account Vectra.Rule.Remote.Kerberos.Service Unknown Kerberos Service Command Example
!vectra-triageContext Example
No output examples
Human Readable Output
Couldn't find any results
4. vectra-get-host-by-id
Get host by id
Base Command
vectra-get-host-by-idInput
Argument Name Description Required host_id The id of the required host (Can get from vectra-get-hosts) Required Context Output
There are no context output for this command.Command Example
!vectra-get-host-by-id host_id=11Context Example
{ "Vectra.Host": [ { "CertaintyScore": 27, "Hostname": "Robert-MBP", "ID": 11, "IP": "0.0.0.0", "KeyAsset": false, "LastDetection": "2019-10-03T01:10:43Z", "State": "active", "TargetsKeyAsset": false, "ThreatScore": 11 } ] }Human Readable Output
Search results table
id threat certainty state first_timestamp 11 25 46 active 5. vectra-get-detection-by-id
Get detections by detetction id
Base Command
vectra-get-detection-by-idInput
Argument Name Description Required detection_id The id of the required detection (Can get from vectra-get-detections) Required
</p>6. vectra-get-users
Retrieves the current list of Users
Base Command
vectra-get-usersInput
Argument Name Description Required username Filter by username Optional role Filter by role Optional account_type Filter by account type (Local, Special, Limited Time Link, LDAP, TACACS) Optional authentication_profile Filter by authentication profile (LDAP or TACACS only) Optional last_login_gte Filters for User’s that have logged in since the given timestamp Optional Context Output
There are no context output for this command.Command Example
</p>!vectra-get-users7. vectra-get-proxies
Retrieves the current list of proxy IP addresses, or just one by Proxy ID
Base Command
vectra-get-proxiesInput
Argument Name Description Required proxy_id The id of the Proxy object Optional Context Output
Path Type Description Vectra.Proxy.Source String Whether the proxy was auto detected by Cognito or was added by user Vectra.Proxy.ID String The ID of the Proxy Vectra.Proxy.Source String Whether the proxy was auto detected by Cognito or was added by user Vectra.Proxy.ConsidersProxy String Whether to consider the object as a proxy or not Vectra.Proxy.Address String The proxy IP address Command Example
!vectra-get-proxiesContext Example
{}Human Readable Output
Couldn't find any results
8. vectra-get-threatfeed
Retrieves the current list of all ThreatFeeds, or just one by ThreatFeed ID
Base Command
vectra-get-threatfeedInput
Argument Name Description Required threatfeed_id The id of the ThreatFeed object Optional Context Output
Path Type Description Vectra.ThreatFeed.Category String The category in which the detection will fire if a match is observed with any indicator in the ThreatFeed Vectra.ThreatFeed.Name String The name of the ThreatFeed Vectra.ThreatFeed.Certainty String The default certainty to use for indicators in the STIX file Vectra.ThreatFeed.IndicatorType String The default indicatorType to use for the observables in the STIX file Vectra.ThreatFeed.Duration Number The default duration for which indicators in the ThreatFeed are valid Vectra.ThreatFeed.ID String The ID of the ThreatFeed Command Example
!vectra-get-threatfeedContext Example
{ "Vectra.ThreatFeed": [ { "Category": "exfil", "Certainty": "Medium", "Duration": 14, "ID": "50f897f3c9bdc606472e8d72348c3263", "IndicatorType": "Exfiltration", "Name": "Suspicious Domains" } ] }Human Readable Output
Rules table
id name certainty category duration indicatorType 50f897f3c9bdc606472e8d72348c3263 Suspicious Domains Medium exfil 14 Exfiltration 9. vectra-search
Advanced search on hosts and detections
Base Command
vectra-searchInput
Argument Name Description Required query_string The query that needs to be performed, e.g. `host.threat:>=50 and host.certainty:>=50` will find all hosts in the critical quadrant. `host.owner_name:bob` will find hosts with probable owner that contains the phrase “bob” in it. Required search_type The type of search to preform, can be either Hosts or Detections Required page_size Number of results returned per page. the default page_size is 50, max 5000 Optional Context Output
There are no context output for this command.Command Example
!vectra-search query_string="host.threat:>=20 and host.certainty:>=20" search_type=hostsContext Example
{ "Vectra.Host": [ { "Certainty_Score": 33, "Hostname": "BThomas-Win7", "ID": 7, "IP": "0.0.0.0", "KeyAsset": true, "LastDetection": "2019-10-03T05:56:31Z", "State": "active", "TargetsKeyAsset": false, "Threat_Score": 23 }, { "Certainty_Score": 46, "Hostname": "Robert-MBP", "ID": 11, "IP": "0.0.0.0", "KeyAsset": false, "LastDetection": "2019-10-03T01:10:43Z", "State": "active", "TargetsKeyAsset": false, "Threat_Score": 25 } ] }Human Readable Output
Search results table
# Replacement Commands Vectra has developed a new XSOAR content pack to replace the legacy Cortex XSOAR content pack. This new content pack includes commands that replace and extend the functionality of existing commands. The following table outlines which commands should be used for updating existing integrations. | Legacy Command | Replacement Command | |------------------------------|---------------------------------------------------| | `vectra-detections` | `vectra-search-detections` | | `vectra-get-detections` | `vectra-search-detections` | | `vectra-get-detection-by-id` | `vectra-detection-describe` | | `vectra-hosts` | `vectra-search-hosts` | | `vectra-get-hosts` | `vectra-search-hosts` | | `vectra-get-host-by-id` | `vectra-host-describe` | | `vectra-get-users` | `vectra-search-users` | | `vectra-search` | `vectra-search-hosts`, `vectra-search-detections` | # New Commands In addition to the replacement commands, new functionality is included with the current content pack. The following table outlines the new functionality provided. | Command | Command Description | |--------------------------------|-------------------------------------------------------------------| | `vectra-search-accounts` | Returns a list of Account objects | | `vectra-search-assignments` | Return a list of assignments | | `vectra-search-outcomes` | Returns a list of assignment outcomes | | `vectra-account-describe` | Returns a single Account details | | `vectra-account-add-tags` | Add tags to an Account | | `vectra-host-del-tags` | Delete tags from an Host | | `vectra-detection-get-pcap` | Returns a Detection's PCAP file (if available) | | `vectra-detection-markasfixed` | Marks/Unmarks a Detection as fixed by providing the Detection ID | | `vectra-detection-add-tags` | Add tags to a Detection | | `vectra-detection-del-tags` | Delete tags from a Detection | | `vectra-outcome-describe` | Returns a single outcome details | | `vectra-outcome-create` | Creates a new assignment outcome | | `vectra-assignment-describe` | Returns a single assignment details | | `vectra-assignment-assign` | Assigns an Account/Host entity to a Vectra User for investigation | | `vectra-assignment-resolve` | Resolves an assignment by selecting resolution scheme |id threat certainty state first_timestamp 7 23 33 active 11 25 46 active
Configuration parameters
server— Server URL (e.g., https://192.168.0.1) (required)token— API Token (required)first_fetch_time— First fetch time range (<number> <time unit>, e.g., 1 hour, 30 minutes)c_score_gte— Fetch only Detections with greater/equal Certainty scoret_score_gte— Fetch Detections with a threat score greater than or equal tostate— Fetch only Detections with matching Statefetch_size— The number of results returned in each fetchisFetch— Fetch incidentsinsecure— Trust any certificate (not secure)incidentType— Incident typeproxy— Use system proxy settings
Commands (9)
-
vectra-get-detection-by-idDeprecatedReturns detections by detection ID.
-
vectra-get-detectionsDeprecatedReturns a list of detection objects, which contain all the information related to security events detected on the network.
-
vectra-get-host-by-idDeprecatedReturns a host by ID.
-
vectra-get-hostsDeprecatedReturns host information, which includes data that correlates the host data to detected security events.
-
vectra-get-proxiesDeprecatedRetrieves the current list of proxy IP addresses, or single proxy IP address by proxy ID.
-
vectra-get-threatfeedDeprecatedRetrieves the current list of all ThreatFeeds, or single ThreatFeed by ThreatFeed ID.
-
vectra-get-usersDeprecatedRetrieves the current list of Users.
-
vectra-searchDeprecatedRuns an advanced search on hosts and detections.
-
vectra-triageDeprecatedThe rules branch can be used to retrieve a listing of configured Triage rules.
from CommonServerPython import * # IMPORTS # import json import requests import urllib3 from typing import Dict, List, Union # Disable insecure warnings urllib3.disable_warnings() # CONSTANTS # MAX_FETCH_SIZE = 50 DATE_FORMAT = "%Y-%m-%dT%H%M" # 2019-09-01T1012 PARAMS_KEYS = { "threat_score": "t_score", "threat_score_gte": "t_score_gte", "certainty_score": "c_score", "certainty_score_gte": "c_score_gte", "destination_port": "dst_port" } # HELPER FUNCTIONS # def create_incident_from_detection(detection: dict): """ converts a detection object to an Incident object """ labels = [] for key, value in detection.items(): labels.append({'type': key, 'value': json.dumps(value)}) return { "name": f'Detection from Vectra with ID: {detection.get("id")}', "labels": labels, "rawJSON": json.dumps(detection) } def calc_pages(total_count: int, this_count: int): """ preforms ciel operation to find the total number of pages """ return -(-total_count // this_count) # minus minus so the floor will become ceiling def max_timestamp(timestamp1: str, timestamp2: str) -> str: """ returns the older timestamp """ date1 = parse_date_string(timestamp1, date_format=DATE_FORMAT) date2 = parse_date_string(timestamp2, date_format=DATE_FORMAT) return timestamp1 if date1 > date2 else timestamp2 def update_vectra_params(kwargs: dict) -> dict: """ updates keys to match Vectra's syntax """ return {PARAMS_KEYS.get(key, key): value for key, value in kwargs.items()} class Client: def __init__(self, vectra_url: str, api_token: str, verify: bool, proxy: dict, fetch_size: int, first_fetch: str, t_score_gte: int, c_score_gte: int, state: str): """ :param vectra_url: IP or hostname of Vectra brain (ex https://www.example.com) - required :param api_token: API token for authentication when using API v2* :param verify: Boolean, controls whether we verify the server's TLS certificate :param proxy: Dictionary mapping protocol to the URL of the proxy. :param fetch_size: Max number of incidents to fetch in each cycle :param first_fetch: Fetch only Detections newer than this date :param c_score_gte: Fetch only Detections with greater/equal Certainty score :param t_score_gte: Fetch only Detections with greater/equal Threat score :param state: Fetch only Detections with matching State (e.g., active, inactive, ignored) """ self.state = state self.t_score_gte = t_score_gte self.c_score_gte = c_score_gte self.fetch_size = fetch_size self.headers = {'Authorization': f'Token {api_token}'} self.base_url = vectra_url + '/api/v2.1/' self.verify = verify self.proxies = proxy self.first_fetch = first_fetch def http_request(self, method='GET', url_suffix='', params=None, data=None) -> Dict: """ Generic HTTP request to Vectra API. :param method: Request's method e.g., 'GET', 'POST', 'PATCH' :param url_suffix: The URL's suffix, usually indicates the API command :param params: Command parameters :param data: Other data to send the request with :return: .json() of the response if exists """ full_url = self.base_url + url_suffix try: res = requests.request( method=method, url=full_url, headers=self.headers, params=params, data=data, verify=self.verify, proxies=self.proxies, ) except requests.exceptions.ConnectTimeout: raise Exception('Connection Timeout Error - potential reasons might be that the Server URL parameter is' ' incorrect or that the Server is not accessible from your host.') except requests.exceptions.SSLError: raise Exception('SSL Certificate Verification Failed \nTry selecting \'Trust any certificate\'') except requests.exceptions.ConnectionError: raise Exception(f'Failed to connect to - {self.base_url} \nPlease check the URL') if not res.ok: raise ValueError(f'Error in API call to Vectra [{res.status_code:d}]. Reason: {res.text}') try: return res.json() except Exception: raise ValueError(f"Failed to parse http response to JSON format. Original response body: \n{res.text}") def fetch_incidents(self, last_run: Dict): """ Fetches Detections from Vectra into Demisto Incidents :param last_run: Integration's last run """ last_timestamp: str = last_run.get('last_timestamp', self.first_fetch) # type: ignore query_string = f'detection.threat:>={self.t_score_gte}' query_string += f' and detection.certainty:>={self.c_score_gte}' query_string += f' and detection.last_timestamp:>{last_timestamp}' # format: "%Y-%m-%dT%H%M" query_string += f' and detection.state:{self.state}' if self.state != 'all' else '' demisto.info(f'\n\nQuery String:\n{query_string}\n\n') params = { 'query_string': query_string, 'page_size': self.fetch_size, 'page': 1, 'order_field': 'last_timestamp' } raw_response = self.http_request(params=params, url_suffix='search/detections') # type: ignore demisto.info("\n\n Queried Successfully\n\n") # Detections -> Incidents, if exists incidents = [] if 'results' in raw_response: res: Union[List[Dict], Dict] = raw_response.get('results') # type: ignore detections: List[Dict] = [res] if not isinstance(res, List) \ else sorted(res, key=lambda h: h.get('id')) # type: ignore try: for detection in detections: incidents.append(create_incident_from_detection(detection)) # type: ignore # format from response: %Y-%m-%dT%H:%M:%SZ response_last_timestamp = datetime.strptime(detection.get('last_timestamp'), # type: ignore "%Y-%m-%dT%H:%M:%SZ").strftime("%Y-%m-%dT%H%M") last_timestamp = max_timestamp(last_timestamp, response_last_timestamp) # type: ignore if incidents: last_run = {'last_timestamp': last_timestamp} except ValueError: raise demisto.info(f"Last run is:\n {last_run}") return last_run, incidents def get_detections_command(client: Client, **kwargs): """ Detection objects contain all the information related to security events detected on the network. :QUERY PARAMETERS: :keyword fields: Filters objects listed :keyword page: Page number. Possible values are a positive integer or last :keyword page_size: Possible values are a positive integer or all :keyword ordering: Orders records by last timestamp, threat score and certainty score. Default is ascending order. Scores can sorted in descending order by prepending the query with “minus” symbol :keyword min_id: >= the id provided :keyword max_id: <= the id provided :keyword state: Filter by state: active, inactive, ignored, ignored for all :keyword type_vname: Filter by the detection type (verbose name) :keyword category: Filter by the detection category :keyword src_ip: Filter by source (ip address) :keyword threat_score: Filter by threat score :keyword threat_score_gte: Filter by threat score >= the score provided :keyword certainty_score: Filter by certainty score :keyword certainty_score_gte: Filter by certainty score >= the score provided :keyword last_timestamp: Filter by last timestamp :keyword host_id: Filter by id of the host object a detection is attributed to :keyword tags: Filter by a tag or a comma-separated list of tags :keyword destination: Filter by destination in the detection detail set :keyword proto: Filter by the protocol in the detection detail set :keyword destination_port: Filter by the destination port in the detection detail set :keyword inbound_ip: Filter by the inbound_ip in the relayed comm set :keyword inbound_proto: Filter by the inbound_proto in the relayed comm set :keyword inbound_port: Filter by the inbound_port in the relayed comm set :keyword inbound_dns: Filter by the inbound_dns in the relayed comm set :keyword outbound_ip: Filter by the outbound_ip in the relayed comm set :keyword outbound_proto: Filter by the outbound_proto in the relayed comm set :keyword outbound_port: Filter by the outbound_port in the relayed comm set :keyword outbound_dns: Filter by the outbound_dns in the relayed_comm_set :keyword dns_ip: Filter by the dns_ip in the dns_set :keyword dns_request: Filter by the dns_request in the dns_set :keyword resp_code: Filter by the resp_code in the dns_set :keyword resp: Filter by the resp in the dns_set """ raw_response = client.http_request(params=update_vectra_params(kwargs), url_suffix='detections') count = raw_response.get('count') if count == 0: return "Couldn't find any results", {}, raw_response res = raw_response.get('results') # type: ignore dets: List[Dict] = [res] if not isinstance(res, List) else sorted(res, key=lambda h: h.get('id')) # type: ignore headers = ['id', 'category', 'src_ip', 'threat', 'certainty', 'state', 'detection', 'detection_category', 'detection_type', 'first_timestamp', 'tags', 'targets_key_asset', 'type_vname'] pages = calc_pages(this_count=len(res), total_count=count) # type: ignore readable_output = tableToMarkdown( name=f'Detection table (Showing Page {kwargs.get("page", 1)} out of {pages})', t=dets, headers=headers ) if 'detection_id' in kwargs: if 'summary' in dets[0]: # type: ignore summary = dets[0].get('summary') # type: ignore if summary: readable_output += '\n' + tableToMarkdown(name='Summary', t=summary[0], headers=summary[0].keys()) if 'relayed_comm_set' in dets[0]: # type: ignore relayed_comm_set: List = dets[0].get('relayed_comm_set') # type: ignore if not isinstance(relayed_comm_set, list): relayed_comm_set = [relayed_comm_set] if len(relayed_comm_set) > 0 and relayed_comm_set[0]: wanted_keys = relayed_comm_set[0].keys().remove('url') readable_output += '\n' + tableToMarkdown(name='Relayed Comm Set', t=relayed_comm_set[0], headers=wanted_keys) context = [] for detection in dets: context.append(createContext( { 'ID': detection.get('id'), 'TypeVName': detection.get('type_vname'), 'Category': detection.get('category'), 'SourceIP': detection.get('src_ip'), 'SourceAccount': detection.get('src_account'), 'SourceHost': detection.get('src_host'), 'Description': detection.get('description'), 'Detection': detection.get('detection'), 'DetectionCategory': detection.get('detection_category'), 'DetectionType': detection.get('detection_type'), 'HasActiveTraffic': detection.get('has_active_traffic'), 'Note': detection.get('note'), 'TriageRuleID': detection.get('triage_rule_id'), 'ThreatScore': detection.get('threat'), 'CertaintyScore': detection.get('certainty'), 'TargetsKeyAsset': detection.get('targets_key_asset'), 'FirstTimestamp': detection.get('first_timestamp'), 'LastTimestamp': detection.get('last_timestamp'), 'Tags': detection.get('tags'), 'HostID': detection.get('host', '').split('/')[-1] if 'host' in detection else None }, removeNull=True) ) outputs = {'Vectra.Detection(val.ID==obj.ID)': context} return readable_output, outputs, raw_response def get_hosts_command(client: Client, **kwargs): """ Host information includes data that correlates the host data to detected security events. :QUERY PARAMETERS: :keyword host_id: Filter by host ID :keyword fields: Filters objects listed :keyword page: Page number. Possible values are a positive integer or last :keyword page_size: Page size. Possible values are a positive integer or all :keyword ordering: Orders records by last timestamp, threat score and certainty score. The default out sorts threat and certainty score in ascending order. Scores can sorted in descending order by prepending the query with “minus” symbol :keyword name: Filter by name :keyword state: Filter by state: active, inactive, suspended, ignored, ignored4all :keyword last_source: Filter by last_source (ip address) :keyword threat_score: Filter by threat score :keyword threat_score_gte: Filter by threat score >= the score provided :keyword certainty_score: Filter by certainty score :keyword certainty_score_gte: Filter by certainty score >= the score provided :keyword last_detection_timestamp: Filter by last_detection_timestamp :keyword tags: comma-separated list of tags, e.g., tags=baz | tags=foo,bar" :keyword key_assest: Filter by key asset: True, False :keyword mac_address: Filter by mac address """ raw_response = client.http_request(params=update_vectra_params(kwargs), url_suffix='hosts') count = raw_response.get('count') if count == 0: return "Couldn't find any results", {}, raw_response res: List[Dict] = raw_response.get('results') # type: ignore hosts: List[Dict] = [res] if not isinstance(res, List) else sorted(res, key=lambda h: h.get('id')) # type: ignore for host in hosts: if 'detection_set' in host: host['detection_ids'] = [host.split('/')[-1] for host in host.get('detection_set')] # type: ignore headers = ['id', 'name', 'state', 'threat', 'certainty', 'last_source', 'url', 'assigned_to', 'owner_name', 'first_timestamp', 'tags', 'note'] pages = calc_pages(this_count=len(res), total_count=count) # type: ignore readable_output = tableToMarkdown( name=f'Hosts table (Showing Page {kwargs.get("page", 1)} out of {pages})', t=hosts, headers=headers ) context = [] for host in hosts: context.append( { 'ID': host.get('id'), 'Name': host.get('name'), 'LastDetection': host.get('last_detection_timestamp'), 'DetectionID': host.get('detection_ids'), 'KeyAsset': host.get('key_asset'), 'State': host.get('state'), 'IP': host.get('last_source'), 'Note': host.get('note'), 'ThreatScore': host.get('threat'), 'CertaintyScore': host.get('certainty'), 'HostLuid': host.get('host_luid'), 'LastDetectionTimestamp': host.get('last_detection_timestamp'), 'LastModified': host.get('last_modified'), 'OwnerName': host.get('owner_name'), 'Severity': host.get('severity'), 'LastSource': host.get('last_source'), 'Tags': host.get('tags'), 'ActiveTraffic': host.get('active_traffic') } ) outputs = {'Vectra.Host(val.ID==obj.ID)': context} return readable_output, outputs, raw_response def get_users_command(client: Client, **kwargs): """ User information includes all data corresponding to user accounts :QUERY PARAMETERS: :keyword username: Filter by username :keyword role: Filter by role :keyword account_type: Filter by account type (Local, Special, Limited Time Link, LDAP, TACACS) :keyword authentication_profile: Filter by authentication profile (LDAP or TACACS only) :keyword last_login_gte: Filters for User's that have logged in since the given timestamp """ raw_response = client.http_request(params=kwargs, url_suffix='users') count = raw_response.get('count') if count == 0: return "Couldn't find any results", {}, raw_response res: List[Dict] = raw_response.get('results') # type: ignore users: List[Dict] = [res] if not isinstance(res, List) else sorted(res, key=lambda h: h.get('id')) # type: ignore headers = ['id', 'last_login', 'username', 'email', 'account_type', 'authentication_profile', 'role'] pages = calc_pages(this_count=len(res), total_count=count) # type: ignore readable_output = tableToMarkdown( name=f'Users table (Showing Page {kwargs.get("page", 1)} out of {pages})', t=users, headers=headers ) context = [] for user in users: context.append( { 'ID': user.get('id'), 'UserName': user.get('username'), 'LastLogin': user.get('last_login'), 'Email': user.get('email'), 'AccountType': user.get('account_type'), 'AuthenticationProfile': user.get('authentication_profile'), 'Role': user.get('role'), } ) outputs = {'Vectra.User(val.ID==obj.ID)': context} return readable_output, outputs, raw_response def search_command(client: Client, search_type: str, **kwargs): """ The search API endpoint allows users to perform advanced search against hosts and detections :param client: Vectra Client :QUERY PARAMETERS: :param search_type: The type of search to preform, can be either Hosts or Detections :keyword query_string: Query that needs to be performed :keyword page_size: Number of results returned per page. the default page_size is 50, max 5000 """ raw_response = client.http_request(params=kwargs, url_suffix=f'search/{search_type}') count = raw_response.get('count') if count == 0: return "Couldn't find any results", {}, raw_response res: List[Dict] = raw_response.get('results') # type: ignore res: List[Dict] = [res] if not isinstance(res, List) else sorted(res, key=lambda h: h.get('id')) # type: ignore headers = ['id', 'threat', 'certainty', 'state', 'first_timestamp'] readable_output = tableToMarkdown(name='Search results table', t=res, headers=headers) context = [] for r in res: context.append(createContext( { 'ID': r.get('id'), 'Hostname': r.get('name'), 'LastDetection': r.get('last_detection_timestamp'), 'DetectionID': r.get('detection_ids'), 'ThreatScore': r.get('threat'), 'CertaintyScore': r.get('certainty'), 'KeyAsset': r.get('key_asset'), 'IP': r.get('last_source'), 'TypeVName': r.get('type_vname'), 'Category': r.get('category'), 'SrcIP': r.get('src_ip'), 'State': r.get('state'), 'TargetsKeyAsset': r.get('targets_key_asset'), 'FirstTimestamp': r.get('first_timestamp'), 'LastTimestamp': r.get('last_timestamp'), 'Tags': r.get('tags'), 'HostID': r.get('host', '').split('/')[-1] if 'host' in r else None }, removeNull=True) ) path = 'Host' if search_type == 'hosts' else 'Detection' outputs = {f'Vectra.{path}(val.ID==obj.ID)': context} return readable_output, outputs, raw_response def get_triage_command(client: Client): """ The rules branch can be used to retrieve a listing of configured Triage rules """ raw_response = client.http_request(url_suffix='rules') count = raw_response.get('count') if count == 0: return "Couldn't find any results", {}, raw_response res: List[Dict] = raw_response.get('results') # type: ignore rules: List[Dict] = [res] if not isinstance(res, List) else sorted(res, key=lambda h: h.get('name')) # type: ignore headers = ['id', 'enabled', 'created_timestamp', 'is_whitelist', 'priority', 'active_detections', 'total_detections', 'template', 'detection_category', 'triage_category', 'detection'] readable_output = tableToMarkdown(name='Rules table', t=rules, headers=headers) context = [] for rule in rules: temp = { 'ID': rule.get('name'), 'SmartCategory': rule.get('smart_category'), 'Description': rule.get('description'), 'Type': rule.get('type_vname'), 'Category': rule.get('category'), 'Created': rule.get('created_timestamp'), 'LastUpdate': rule.get('last_timestamp'), 'Host': rule.get('host'), 'IP': rule.get('ip'), 'Priority': rule.get('priority'), 'Remote': [ { 'IP': rule.get('remote1_ip'), 'Protocol': rule.get('remote1_proto'), 'Port': rule.get('remote1_port'), 'DNS': rule.get('remote1_dns') }, { 'IP': rule.get('remote2_ip'), 'Protocol': rule.get('remote2_proto'), 'Port': rule.get('remote2_port'), 'DNS': rule.get('remote2_dns') } ] } # type: ignore kerberos = { 'Account': rule.get('remote1_kerb_account'), 'Service': rule.get('remote1_kerb_service') } if kerberos['Account'] or kerberos['Service']: temp['Remote'] = {'Kerberos': kerberos} # type: ignore remove_nulls_from_dictionary(temp) context.append(temp) outputs = {'Vectra.Rule(val.ID==obj.ID)': context} return readable_output, outputs, raw_response def get_proxies_command(client: Client, proxy_id: int = None): """ The proxies API can be used to manage proxy IP addresses (internal or external) in Cognito. The API can be used to retrieve the current list of proxy IP addresses or to create new proxy objects in Cognito. :param proxy_id: The id of the Proxy object. :param client: Vectra Client """ raw_response = client.http_request(url_suffix=f'proxies/{proxy_id}' if proxy_id else 'proxies') count = demisto.get(raw_response, 'meta.count') if count == 0: return "Couldn't find any results", {}, raw_response res = raw_response.get('proxies') # type: ignore proxies: List[Dict] = [res] if not isinstance(res, List) else sorted(res, key=lambda h: h.get('id')) # type: ignore headers = ['id', 'source', 'considersProxy', 'address'] readable_output = tableToMarkdown(name='Rules table', t=proxies, headers=headers) context = [] for proxy in proxies: context.append(createContext( { 'ID': proxy.get('id'), 'Source': proxy.get('source'), 'ConsidersProxy': proxy.get('considersProxy'), 'Address': proxy.get('address'), }, removeNull=True) ) outputs = {'Vectra.Proxy(val.ID==obj.ID)': context} return readable_output, outputs, raw_response def get_threatfeed_command(client: Client, threatfeed_id: int = None): """ Retrieves the current list of threatFeed objects already configured in the system :param threatfeed_id: The id of the ThreatFeed object. :param client: Vectra Client """ raw_response = client.http_request(url_suffix=f'threatFeeds/{threatfeed_id}' if threatfeed_id else 'threatFeeds') count = demisto.get(raw_response, 'meta.count') if count == 0: return "Couldn't find any results", {}, raw_response res = raw_response.get('threatFeeds') # type: ignore feeds: List[Dict] = [res] if not isinstance(res, List) else sorted(res, key=lambda h: h.get('id')) # type: ignore for feed in feeds: feed.update(feed.get('defaults')) # type: ignore headers = ['id', 'name', 'certainty', 'category', 'duration', 'indicatorType'] readable_output = tableToMarkdown(name='Rules table', t=feeds, headers=headers) context = [] for feed in feeds: context.append(createContext( { 'ID': feed.get('id'), 'Name': feed.get('name'), 'Duration': feed.get('duration'), 'Category': feed.get('category'), 'Certainty': feed.get('certainty'), 'Data': feed.get('data'), 'IndicatorType': feed.get('indicatorType'), }, removeNull=True) ) outputs = {'Vectra.ThreatFeed(val.ID==obj.ID)': context} return readable_output, outputs, raw_response def module_test(client: Client, last_run: dict): """ Performs basic tests to insure API connection, and to test integration's parameters """ client.fetch_incidents(last_run=last_run) # will handle any bad request/bad api token return 'ok' # COMMANDS MANAGER / SWITCH PANEL # def main(): api_token = demisto.getParam('token') # Remove trailing slash to prevent wrong URL path to service server_url = demisto.getParam('server').rstrip('/') # Fetch only detections that have greater or equal Certainty and Threat scores c_score_gte, t_score_gte = int(demisto.params().get('c_score_gte', 0)), int(demisto.params().get('t_score_gte', 0)) state = demisto.params().get('state') # How many time before the first fetch to retrieve incidents first_fetch_time = demisto.params().get('first_fetch_time', '7 days') first_fetch, _ = parse_date_range(first_fetch_time, date_format=DATE_FORMAT) # Remove proxy if not set to true in params proxies = handle_proxy() fetch_size = int(demisto.params().get('fetch_size', 20)) verify_certificate = not demisto.params().get('insecure', False) LOG(f'Command being called is {demisto.command()}') try: # create a new Client instance client = Client( vectra_url=server_url, verify=verify_certificate, api_token=api_token, proxy=proxies, fetch_size=max(0, min(fetch_size, MAX_FETCH_SIZE)), c_score_gte=c_score_gte, t_score_gte=t_score_gte, first_fetch=first_fetch, state=state ) # execute the current command if demisto.command() == 'test-module': results = module_test(client, last_run=demisto.getLastRun()) demisto.results(results) elif demisto.command() == 'fetch-incidents': next_run, incidents = client.fetch_incidents(last_run=demisto.getLastRun()) demisto.setLastRun(next_run) demisto.incidents(incidents) elif demisto.command() == 'vectra-get-detections': return_outputs(*get_detections_command(client, **demisto.args())) elif demisto.command() == 'vectra-get-users': return_outputs(*get_users_command(client, **demisto.args())) elif demisto.command() == 'vectra-get-hosts': return_outputs(*get_hosts_command(client, **demisto.args())) elif demisto.command() == 'vectra-get-proxies': return_outputs(*get_proxies_command(client, demisto.getArg('proxy_id'))) elif demisto.command() == 'vectra-get-threatfeed': return_outputs(*get_threatfeed_command(client, demisto.getArg('threatfeed_id'))) elif demisto.command() == 'vectra-search': return_outputs(*search_command(client, **demisto.args())) elif demisto.command() == 'vectra-triage': return_outputs(*get_triage_command(client)) elif demisto.command() == 'vectra-get-host-by-id': query_string = f'host.id:{demisto.args().get("host_id")}' return_outputs(*search_command(client, search_type='hosts', query_string=query_string)) elif demisto.command() == 'vectra-get-detection-by-id': query_string = f'detection.id:{demisto.args().get("detection_id")}' return_outputs(*search_command(client, search_type='detections', query_string=query_string)) # Log exceptions except Exception as ex: if demisto.command() == 'fetch-incidents': LOG(str(ex)) raise else: return_error(str(ex)) if __name__ in ['__main__', 'builtin', 'builtins']: main()