Details
| ID | BotDefender |
|---|---|
| Provider | HUMAN |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Gathers PerimeterX related data
Configure BotDefender in Cortex
| Parameter | Description | Required |
|---|---|---|
| url | Server URL (e.g. https://example.net) | True |
| apikey | API Key | True |
| incidentType | Incident type | False |
| insecure | Trust any certificate (not secure) | False |
| proxy | Use system proxy settings | 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.
ip
Gets the PerimeterX DBotScore decision for a particular IP
Base Command
ip
Input
| Argument Name | Description | Required |
|---|---|---|
| ip | The custom parameter value or IP address for which the report is requested | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| IP.Address | String | Bad IP address |
| IP.Malicious.Vendor | String | For malicious IPs, the vendor that made the decision |
| IP.Malicious.Description | String | For malicious IPs, the reason that the vendor made the decision |
| DBotScore.Indicator | String | The indicator that was tested |
| DBotScore.Type | String | The indicator type |
| DBotScore.Vendor | String | The vendor used to calculate the score |
| DBotScore.Score | Number | The actual score |
Command Example
!ip ip="5.79.76.181"
Context Example
{
"DBotScore": {
"Indicator": "5.79.76.181",
"Score": 3,
"Type": "ip",
"Vendor": "PerimeterX"
},
"IP": {
"Address": "5.79.76.181",
"Malicious": {
"Description": "Something random from PerimeterX for now enjoy!",
"Vendor": "PerimeterX"
}
},
"PerimeterX": {
"catpchaSolves": 200,
"ipClassifications": [
{
"class": "Bad Reputation",
"name": "Bad Reputation"
},
{
"class": "SharedIPs",
"name": "Shared IPs"
},
{
"class": "DataCenter",
"name": "TAG DCIP"
}
],
"max_risk_score": 100,
"pageTypeDistributions": [
{
"count": 1228,
"pageType": "Login"
},
{
"count": 739,
"pageType": "Scraping"
},
{
"count": 139,
"pageType": "Checkout"
}
],
"topBlockedURLPaths": [
{
"blockedURLPath": "/",
"count": 1404
},
{
"blockedURLPath": "/cgi-bin/way-board.cgi",
"count": 702
},
{
"blockedURLPath": "/loginok/light.cgi",
"count": 702
}
],
"topIncidentTypes": [
{
"count": 2106,
"incidentType": "Spoof"
},
{
"count": 702,
"incidentType": "Bot Behavior"
}
],
"topURLPaths": [
{
"count": 3315,
"urlPath": "/favicon.ico"
},
{
"count": 3253,
"urlPath": "/favicon.png"
},
{
"count": 3212,
"urlPath": "/"
},
{
"count": 1228,
"urlPath": "/loginok/light.cgi"
},
{
"count": 1222,
"urlPath": "/cgi-bin/way-board.cgi"
},
{
"count": 205,
"urlPath": "/phpmyadmin/"
},
{
"count": 139,
"urlPath": "-"
},
{
"count": 82,
"urlPath": "/images/icons/favicon.ico"
},
{
"count": 48,
"urlPath": "/test.php"
}
],
"topUserAgents": [
{
"count": 84,
"userAgentName": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36"
},
{
"count": 80,
"userAgentName": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36"
},
{
"count": 78,
"userAgentName": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.170 Safari/537.36 OPR/53.0.2907.99"
},
{
"count": 76,
"userAgentName": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36 OPR/54.0.2952.64 (Edition Yx)"
},
{
"count": 72,
"userAgentName": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36 OPR/54.0.2952.51"
},
{
"count": 72,
"userAgentName": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36"
},
{
"count": 72,
"userAgentName": "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36"
},
{
"count": 72,
"userAgentName": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36"
},
{
"count": 72,
"userAgentName": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36 Kinza/4.7.2"
},
{
"count": 72,
"userAgentName": "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.79 Safari/537.36"
}
],
"trafficOverTime": []
}
}
Human Readable Output
[<IP object at 0x7f31335e0e80>]
Configuration parameters
url— Server URL (e.g. https://example.net) (required)apikey— API Key (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settingsdbotBadThreshold— DBot Scoring - Bad Score Threshold (required)dbotSuspiciousThreshold— DBot Scoring - Suspicious Score Threshold (required)dbotGoodThreshold— DBot Scoring - Good Score Threshold (required)integrationReliability— Source ReliabilityfeedExpirationPolicy—feedExpirationInterval—
Commands (1)
-
ipGets the PerimeterX DBotScore decision for a particular IP.
from CommonServerPython import * # noqa: E402 lgtm [py/polluting-import] # CONSTANTS DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ" class pXScoring: """ Class to handle all current and future scoring for PerimeterX objects """ @staticmethod def dbotscore_from_risk(risk_score: int, thresholds: dict[str, Any]) -> int: """ Create the DBotScore structure first using the Common.DBotScore class. :type risk_score: ``int`` :param risk_score: PerimeterX provided risk score for the IP Address :return: Returns the relevant DBotScore value for the provided PerimeterX risk score :rtype: ``int`` """ if risk_score > thresholds["bad_threshold"]: dbot_score = Common.DBotScore.BAD # bad elif risk_score > thresholds["suspicious_threshold"]: dbot_score = Common.DBotScore.SUSPICIOUS # suspicious elif risk_score > thresholds["good_threshold"]: dbot_score = Common.DBotScore.GOOD # good else: dbot_score = Common.DBotScore.NONE # unknown return dbot_score @staticmethod def get_ip_score(ip_address: str, risk_score: int, thresholds: dict[str, Any]): """ Create the DBotScore structure first using the Common.DBotScore class. :type ip_address: ``str`` :param ip_address: IP Address to be used as the indicator for this entry :type risk_score: ``int`` :param risk_score: PerimeterX provided risk sco1re for the IP Address :return: Updated List of Common IP Addresses with this entry appended :rtype: ``List[Common.IP]`` """ # create the DBotScore object and populate it with the needed values dbot_score = Common.DBotScore( indicator=ip_address, indicator_type=DBotScoreType.IP, integration_name="PerimeterX", score=pXScoring.dbotscore_from_risk(risk_score, thresholds), malicious_description="High risk score indicates high probability that the requests from the IP are malicious ", reliability=demisto.params().get("integrationReliability"), ) # Create the IP Standard Context structure using Common.IP and add # dbot_score to it. return Common.IP( ip=ip_address, # asn=ip_data.get('asn'), dbot_score=dbot_score, ) class Client(BaseClient): """ Client will implement the service API, and should not contain any Demisto logic. Should only do requests and return data. """ def test_api_connection(self, api_key: str) -> dict[str, Any]: """ Makes a call to the status API path to confirm the proper URL and Authorization token were provided """ headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} return self._http_request(method="GET", full_url=f"{self._base_url}?search=ip:1.1.1.1&tops=path", headers=headers) def post_investigate_by_ip(self, ip_type: str, ip_address: str, api_key: str) -> dict[str, Any]: """ Query the PerimeterX API to get the relevant details regarding the provided IP within a particular customer's own data :type ip_address: ``str`` :param ip_address: IP Address to be used as the indicator for this entry :type ip_type: ``str`` :param ip_type: The type of IP address we will be querying (true_ip or socket_ip) :return: The JSON response body from the PerimeterX API :rtype: ``Dict[str, Any]`` """ headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} request_params: dict[str, Any] = {} if ip_type: request_params["ip_type"] = ip_type if ip_address: request_params["ip_address"] = f"search=ip:{ip_address}" return self._http_request( method="GET", full_url=f'{self._base_url}?{request_params["ip_address"]}&tops=user-agent,path,socket_ip_classification', headers=headers, ) def post_investigate_by_name(self, name_type: str, name: str) -> dict[str, Any]: """ THIS IS NOT CURRENTLY IMPLEMENTED Query the PerimeterX API to get the relevant details regarding the provided name within a particular customer's own data :type name: ``str`` :param name: name to be used as the indicator for this entry :type name_type: ``str`` :param name_type: The type of name we will be querying (domain or param) :return: The JSON response body from the PerimeterX API :rtype: ``Dict[str, Any]`` """ request_params: dict[str, Any] = {} if name_type: request_params["name_type"] = name_type if name: request_params["name"] = name return self._http_request(method="POST", full_url=self._base_url, json_data=request_params) def test_module(client: Client, api_key): """ Returning 'ok' indicates that the integration works like it is supposed to. Connection to the service is successful. Args: client: PerimeterX client Returns: 'ok' if test passed, anything else will fail the test. """ try: result = client.test_api_connection(api_key) if result["success"]: return "ok" else: return "Connection to api failed: " + result["errors"] except DemistoException as de: return "Connection to api failed with exception: " + de.message def perimeterx_get_investigate_details( client: Client, args: dict[str, Any], thresholds: dict[str, Any], api_key: str ) -> CommandResults: """ Collect the required details to query the PerimeterX API to get the relevant details regarding the provided search term within a particular customer's own data :type search_term: ``str`` :param search_term: This is the entry that we'll be querying for against the PerimeterX API :type search_type: ``str`` :param search_type: The type of query that will be run against the PerimeterX API :return: A ``CommandResults`` object that is then passed to ``return_results``, that contains Domains :rtype: ``CommandResults`` """ # Check for the field to query for search_type = args.get("search_type", None) if not search_type: raise ValueError("No search_type specified") # Check to make sure we have a query term search_term = args.get("search_term", None) if not search_term: raise ValueError("No search_term specified") # Check for an IP based investigation supported_ip_search_types = ["true_ip", "socket_ip"] if search_type in supported_ip_search_types: """ Run an IP based search if the search type is one supported by the IP types """ result = client.post_investigate_by_ip(ip_type=search_type, ip_address=search_term, api_key=api_key) indicator = pXScoring.get_ip_score(ip_address=search_term, risk_score=result["max_risk_score"], thresholds=thresholds) return CommandResults(outputs_prefix="PerimeterX", outputs_key_field="", outputs=result, indicator=indicator) elif search_type == "name": """ THIS IS NOT CURRENTLY IMPLEMENTED Run a name based search if the search type is name """ result = client.post_investigate_by_name(name_type=search_type, name=search_term) else: """ Generate an error because the search type is not supported """ raise ValueError("Invalid search_type provided") return CommandResults(outputs_prefix="PerimeterX", outputs_key_field="", outputs=result) def ip(client: Client, args, thresholds: dict[str, Any], api_key): """ Collect the details to run an IP Reputation query against the PerimeterX API :type ip: ``str`` :param ip: Results will be provided for list of IPs :return: A ``CommandResults`` object that is then passed to ``return_results``, that contains Domains :rtype: ``CommandResults`` """ # Check to make sure we have a query term ip_list = argToList(args.get("ip")) results = [] for ip_address in ip_list: result = client.post_investigate_by_ip(ip_type="true_ip", ip_address=ip_address, api_key=api_key) indicator = pXScoring.get_ip_score(ip_address=ip_address, risk_score=result["max_risk_score"], thresholds=thresholds) readable_output = f"{indicator}" cr = CommandResults( readable_output=readable_output, outputs_prefix="PerimeterX", outputs_key_field="", outputs=result, indicator=indicator, ) results.append(cr) return results def main(): """ PARSE AND VALIDATE INTEGRATION PARAMS """ params = demisto.params() api_key = params.get("apikey") # get the service API url base_url = urljoin(params["url"], "/v1/bot-defender/") verify_certificate = not params.get("insecure", False) proxy = params.get("proxy", False) # get the DBot Thresholds thresholds = { "good_threshold": int(params.get("dbotGoodThreshold")), "suspicious_threshold": int(params.get("dbotSuspiciousThreshold")), "bad_threshold": int(params.get("dbotBadThreshold")), "unknown_threshold": 0, } command = demisto.command() LOG(f"Command being called is {command}") try: headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} client = Client(base_url=base_url, verify=verify_certificate, headers=headers, proxy=proxy) if command == "test-module": # This is the call made when pressing the integration Test button. result = test_module(client, api_key) demisto.results(result) elif command == "ip": return_results(ip(client, demisto.args(), thresholds=thresholds, api_key=api_key)) elif command == "perimeterx_get_investigate_details": return_results( perimeterx_get_investigate_details(client=client, args=demisto.args(), thresholds=thresholds, api_key=api_key) ) # Log exceptions except Exception as e: return_error(f"Failed to execute {demisto.command()} command. Error: {str(e)}") if __name__ in ("__main__", "__builtin__", "builtins"): main()