Dragos Worldview
Custom integration designed to pull in reports from the Dragos Worldview API as incidents.
Data Enrichment & Threat Intelligence · Dragos Worldview
Details
| ID | Dragos Worldview |
|---|---|
| Provider | Dragos |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 6.2.0 |
| Docker Image | demisto/python3:3.12.8.3296088 |
| Supported Modules | Agentix XSIAM |
README
Custom integration designed to pull in reports from the Dragos Worldview API as incidents
This integration was integrated and tested with version 1.0 of Dragos Worldview
Configure Dragos Worldview in Cortex
| Parameter | Description | Required |
|---|---|---|
| Server URL (e.g. https://portal.dragos.com) | The Dragos server URL | True |
| API Token | The API token | True |
| API Key | The key for the API Token | True |
| First fetch time | The first time to run a fetch request | False |
| Trust any certificate (not secure) | If true trust any certicicate | False |
| Use system proxy settings | If true use system proxy settings | False |
| Incidents Fetch Interval | How often to fetch incidents | False |
| Traffic Light Protocol Color | The Traffic Light Protocol (TLP) designation to apply to indicators | False |
| Fetch incidents | If true fetch incidents in a feed | False |
| Incident type | The incident type | False |
| Fetch Limit | The fetch limit | False |
Commands
You can execute these commands from the 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.
dragos-get-indicators
Get Indicators from the Dragos WorldView API
Base Command
dragos-get-indicators
Input
| Argument Name | Description | Required | |
|---|---|---|---|
| exclude_suspect_domain | Exclude indicators that are only associated with Suspect Domain Reports (API default false). | Optional | FContext |
| page | Page number to start at (API default 1). | Optional | |
| page_size | Page size (API default 500) (must be less than 1001). | Optional | |
| serial | Report serial number to get indicators from, if no serial number provided command will retrieve all indicators from the last 48 hours. | Optional | |
| tags | List of tags to search for indicators. | Optional | |
| type | Search for indicators of a specific type. | Optional | |
| updated_after | UTC timestamp in YYYY-mm-dd (optionally with HH:mm:ss) to filter to recent indicators (default is within the last 48 hours). | Optional | |
| value | Search for indicators that match a specific value. | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| Dragos.Indicators.activity_groups | Unknown | A list of activity groups. |
| Dragos.Indicators.attack_techniques | Unknown | A list of attack techniques. |
| Dragos.Indicators.category | String | The Dragos Indicator’s category. |
| Dragos.Indicators.comment | String | The Dragos Indicator’s comment. |
| Dragos.Indicators.confidence | String | The Dragos Indicator’s confidence. |
| Dragos.Indicators.first_seen | String | The first time the Indicator was seen in Dragos (yyyy-mm-ddThh:mm:ss.sssZ). |
| Dragos.Indicators.ics_attack_techniques | Unknown | A list of ics attack techniques. |
| Dragos.Indicators.indicator_id | Number | The Dragos Indicator’s id. |
| Dragos.Indicators.indicator_type | String | The Dragos Indicator’s type. |
| Dragos.Indicators.kill_chain | String | The Dragos Indicator’s kill chain. |
| Dragos.Indicators.kill_chains | Unknown | A list of kill chains. |
| Dragos.Indicators.last_seen | String | The last time the Indicator was seen in Dragos (yyyy-mm-ddThh:mm:ss.sssZ). |
| Dragos.Indicators.pre_attack_techniques | Unknown | A list of pre-attack techniques. |
| Dragos.Indicators.products | Unknown | A list of dictionaries, usually containing the serial numbers of related Dragos reports. |
| Dragos.Indicators.products.serial | Unknown | The serial numbers of related Dragos reports. |
| Dragos.Indicators.severity | String | The Dragos Indicator’s severity. |
| Dragos.Indicators.status | String | The Dragos Indicator’s status. |
| Dragos.Indicators.threat_groups | Unknown | A list of threat groups. |
| Dragos.Indicators.updated_at | String | The last time the Indicator was updated in Dragos (yyyy-mm-ddThh:mm:ss.sssZ). |
| Dragos.Indicators.uuid | String | The Dragos Indicator’s uuid. |
| Dragos.Indicators.value | String | The Dragos Indicator’s value. |
Command Example
!dragos-get-indicators exclude_suspect_domain=false page=1 page_size=500 serial=DOM-2023-37 tags=test type=domain updated_after=2023-12-31 value=example.com
Human Readable Output
Dragos Indicators
| activity_groups | attack_techniques | category | comment | confidence | first_seen | ics_attack_techniques | id | indicator_type | kill_chain | kill_chains | last_seen | pre_attack_techniques | products | severity | status | threat_groups | updated_at | uuid | value |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| moderate | 2018-04-06T00:00:00.000Z | 0000 | domain | 2023-09-12T19:37:31.000Z | {‘serial’: ‘DOM-2023-37’} | released | 2024-09-12T21:31:51.000Z | example.com |
dragos-get-full-report
Get the report file from the given serial number
Base Command
dragos-get-full-report
Input
| Argument Name | Description | Required |
|---|---|---|
| serial | Serial number for the report to retrieve. | Required |
Context Output
There is no context output for this command.
Command Example
!dragos-get-full-report serial=DOM-2023-37
Human Readable Output
dragos-get-ioc-csv
Get csv file with indicators from a given report
Base Command
dragos-get-ioc-csv
Input
| Argument Name | Description | Required |
|---|---|---|
| serial | Serial number of the report from which to get the file. | Required |
Context Output
There is no context output for this command.
Command Example
!dragos-get-ioc-csv serial=DOM-2023-37
Human Readable Output
dragos-get-stix2
Get the stix2 json bundle of indicators from a given report
Base Command
dragos-get-stix2
Input
| Argument Name | Description | Required |
|---|---|---|
| serial | Serial number of the report from which to retrieve the file. | Required |
Context Output
There is no context output for this command.
Command Example
!dragos-get-stix2 serial=DOM-2023-37
Human Readable Output
Configuration parameters
url— Server URL (e.g. https://portal.dragos.com) (required)credential_token— API Tokencredential_key— API Keyapitoken— API Tokenapikey— API Keyfirst_fetch— First fetch timeinsecure— Trust any certificate (not secure)proxy— Use system proxy settingsincidentFetchInterval— Incidents Fetch Intervaltlp_color— Traffic Light Protocol ColorisFetch— Fetch incidentsincidentType— Incident typemax_fetch— Fetch Limit
Commands (4)
-
dragos-get-full-reportGet the report file from the given serial number.
-
dragos-get-indicatorsGet Indicators from the Dragos WorldView API, if no arguments are provided the command will retrieve all indicators from the last 48 hours.
-
dragos-get-ioc-csvGet csv file with indicators from a given report.
-
dragos-get-stix2Get the stix2 json bundle of indicators from a given report.
import json from collections.abc import Callable from datetime import datetime, timedelta from typing import Any import dateparser import demistomock as demisto # noqa: F401 import urllib3 from CommonServerPython import * # noqa: F401 """Dragos Worldview Integration for XSOAR.""" # flake8: noqa: F402,F405 lgtm STATUS_TO_RETRY = [500, 501, 502, 503, 504] # disable insecure warnings urllib3.disable_warnings() # pylint:disable=no-member class Client(BaseClient): def whoami(self) -> dict[str, Any]: """Test that API works.""" return self._http_request( method="get", url_suffix="indicators?page=1&page_size=1&serial%5B%5D=AA-2022-24", timeout=60, ) def api_request(self, suffix: str, response_type: str = "json"): return self._http_request( method="get", url_suffix=suffix, timeout=30, retries=3, status_list_to_retry=STATUS_TO_RETRY, resp_type=response_type ) def get_report(client: Client, args: dict[str, Any]) -> dict[str, Any]: serial = args.get("serial") if serial is None: api_query = "" else: api_query = "products/" + serial + "/report" file = client.api_request(api_query, "content") file_entry = fileResult(filename="report.pdf", data=file) return file_entry def get_csv(client: Client, args: dict[str, Any]) -> dict[str, Any]: serial = args.get("serial") if serial is None: api_query = "" else: api_query = "products/" + serial + "/csv" file = client.api_request(api_query, "content") file_entry = fileResult(filename="indicators.csv", data=file) return file_entry def get_stix(client: Client, args: dict[str, Any]) -> dict[str, Any]: serial = args.get("serial") if serial is None: api_query = "" else: api_query = "products/" + serial + "/stix2" file = client.api_request(api_query, "content") file_entry = fileResult(filename="indicators.stix2.json", data=file) return file_entry def get_indicators(client: Client, args: dict[str, Any]) -> CommandResults: exclude_suspect_domain = argToBoolean(args.get("exclude_suspect_domain", False)) page = args.get("page") page_size = args.get("page_size") updated_after = args.get("updated_after") value = args.get("value") indicator_type = args.get("type") serials = argToList(args.get("serial")) tags = argToList(args.get("tags")) # The arguments page, page_size and exclude_suspect_domain have an API default of 1, 500 and false respectively, # and do not need to be included in the query unless changed query_list = [] if page: query_list.append("page=" + page) if exclude_suspect_domain: query_list.append("exclude_suspect_domain=" + str(exclude_suspect_domain).lower()) if page_size: query_list.append("page_size=" + page_size) if updated_after: query_list.append("updated_after=" + updated_after.replace(":", "%3A")) if value: query_list.append("value=" + value) if indicator_type: query_list.append("type=" + indicator_type) for serial in serials: query_list.append("serial%5B%5D=" + serial) for tag in tags: query_list.append("tags%5B%5D=" + tag) # If any arguments were submitted then run the relevent query, # else return all indicators from the last 48 hours if query_list: query_string = "&".join(query_list) api_query = f"indicators?{query_string}" else: time = str(datetime.now() - timedelta(hours=48)) time = time.replace(":", "%3A") api_query = f"indicators?updated_after={time}" raw_response = client.api_request(api_query) data = raw_response["indicators"] page_number = 2 if not page else int(page) + 1 if page: query_list.pop(0) query_string = "&".join(query_list) full_response = raw_response # If there are still more dragos pages (ie more indicators) than was returned by # the intial query, iterate through the remaining pages and add all unique indicators # to the return data while int(raw_response["total_pages"]) > int(raw_response["page"]): if query_list: api_query = f"indicators?page={page_number}&{query_string}" else: api_query = f"indicators?page={page_number}&updated_after={time}" page_number += 1 raw_response = client.api_request(api_query) new_data = raw_response["indicators"] data.extend(new_data) for item in new_data: if item not in full_response["indicators"]: full_response["indicators"].append(item) results = CommandResults( outputs_prefix="Dragos.Indicators", outputs_key_field="indicator_id", outputs=data, readable_output=tableToMarkdown("Dragos Indicators", data), raw_response=full_response, ) return results def fetch_incidents(client: Client, last_run: dict, first_fetch: str) -> tuple[list, dict]: if last_run == {}: last_fetch = dateparser.parse(first_fetch) else: last_fetch = last_run.get("time") last_fetch = dateparser.parse(str(last_fetch)) max_time = last_fetch api_query = "products?released_after=" api_query = api_query + str(max_time) api_query = api_query.replace(":", "%3A") api_query = api_query.replace(" ", "%20") incident_data = client.api_request(api_query) incidents = [] items = incident_data["products"] for item in items: item["updated_at"] = item["updated_at"][:-5] incident_time = dateparser.parse(item["updated_at"]) incident = { "name": item["title"], "occurred": incident_time.strftime("%Y-%m-%dT%H:%M:%SZ"), # type: ignore "rawJSON": json.dumps(item), } incidents.append(incident) if incident_time > max_time: # type: ignore max_time = incident_time next_run = {"time": max_time.strftime("%Y-%m-%dT%H:%M:%S")} # type: ignore incidents.reverse() return incidents, next_run def main() -> None: """Main method used to run actions.""" try: demisto_params = demisto.params() base_url = demisto_params.get("url", "").rstrip("/") base_url = base_url + "/api/v1" verify_ssl = not demisto_params.get("insecure", False) proxy = demisto_params.get("proxy", False) api_token = demisto_params.get("credential_token", {}).get("password") or demisto_params.get("apitoken") if not api_token: return_error("Please provide a valid API token") api_key = demisto_params.get("credential_key", {}).get("password") or demisto_params.get("apikey") if not api_key: return_error("Please provide a valid API key") headers = { "accept": "*/*", "API-TOKEN": api_token, "API-SECRET": api_key, } client = Client(base_url=base_url, verify=verify_ssl, headers=headers, proxy=proxy) commands: dict[str, Callable] = { "dragos-get-indicators": get_indicators, "dragos-get-full-report": get_report, "dragos-get-ioc-csv": get_csv, "dragos-get-stix2": get_stix, } command = demisto.command() if command == "test-module": try: client.whoami() return_results("ok") except Exception as err: message = str(err) try: error = json.loads(str(err).split("\n")[1]) if "fail" in error.get("result", {}).get("status", ""): message = error.get("result", {})["message"] except Exception: message = ( "Unknown error. Please verify that the API" f" URL, Token and Key are correctly configured. RAW Error: {err}" ) raise DemistoException(f"Failed due to - {message}") elif command == "fetch-incidents": first_fetch = demisto_params.get("first_fetch", "24 hours").strip() incidents, next_run = fetch_incidents(client=client, last_run=demisto.getLastRun(), first_fetch=first_fetch) demisto.setLastRun(next_run) demisto.incidents(incidents) elif command in commands: return_results(commands[command](client, demisto.args())) except Exception as e: return_error(f"Failed to execute {demisto.command()} command. Error: {e!s}") if __name__ in ("__main__", "__builtin__", "builtins"): main()