CounterCraft Deception Director
CounterCraft Deception Solution detects advanced adversaries. Automate counterintelligence campaigns to discover targeted attacks with real-time active response.
Deception & Breach Simulation · CounterCraft Deception Director
Details
| ID | CounterCraft Deception Director |
|---|---|
| Provider | CounterCraft |
| Category | Deception & Breach Simulation |
| From Version | 5.0.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
Overview
CounterCraft Deception Solution detects advanced adversaries. Automate counterintelligence campaigns to discover targeted attacks with real-time active response.
This integration was integrated and tested with version 2.5.13 of CounterCraft Deception Director
CounterCraft Deception Director Playbook
Use Cases
- Query IOCs (objects) in your Deception Director
- Retrieve events from your deception campaigns
- Retrieve configuration from your Deception Director
- Retrieve alerts (notifications) from your Deception Director
- Create new deception campaigns
- Create new deception hosts
- Operate your campaigns, hosts, services and breadcrumbs
Prerequisites
You need to obtain the following Deception Director information.
- Server URL
- API Key
- Secret Key
In order to obtain the API Key and the Secret Key you need to go to the user settings in the Deception Director
and copy both or generate a new pair if they are not already generated.
Configure CounterCraft Deception Director on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for CounterCraft Deception Director.
- Click Add instance to create and configure a new integration instance.
- Name: a textual name for the integration instance.
- Deception Director Domain or IP Address: for example, https://192.168.1.1
- Fetch incidents: if you select this option, your notifications in the Deception Director will be created as Cortex XSOAR incidents.
- Incident type
- API Key for Deception Director connection: paste your API Key.
- Secret Key for Deception Director connection: paste your Secret Key.
- Ignore SSL Warnings: in case the SSL certificate is self-signed.
- Use system proxy settings: in case you need to connect through a proxy.
- Click Test to validate the URLs, token, and connection.
Fetched Incidents Data
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.
- countercraft-list-campaigns
- countercraft-list-hosts
- countercraft-list-services
- countercraft-list-breadcrumbs
- countercraft-get-object
- countercraft-get-events
- countercraft-create-campaign
- countercraft-list-dsns
- countercraft-list-providers
- countercraft-create-host-machine
- countercraft-list-incidents
- countercraft-manage-campaign
- countercraft-manage-host
- countercraft-manage-service
- countercraft-manage-breadcrumb
1. countercraft-list-campaigns
List all deception campaigns
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to list only the campaigns you have access to.
Base Command
countercraft-list-campaigns
Input
| Argument Name | Description | Required |
|---|---|---|
| name | Campaign Name | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Campaign.ID | number | Campaign ID |
| CounterCraft.Campaign.Name | string | Campaign Name |
| CounterCraft.Campaign.Description | string | Campaign Description |
| CounterCraft.Campaign.StatusCode | string | Campaign Status |
Command Example
!countercraft-list-campaigns
Human Readable Output
| ID | Name | Description | StatusCode |
|---|---|---|---|
| 1 | AntiPhishing | Gather intelligence from phishers | ACTIVE |
| 2 | External recoinassance | Collect pre-attack evidence | ACTIVE |
| 3 | Internal lateral movement | Detect lateral movement | ACTIVE |
| 4 | DMZ | DMZ activity | ACTIVE |
| 5 | VIP | VIP mobile protection | ACTIVE |
2. countercraft-list-hosts
Lists all deception hosts
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to list only the hosts you have access to.
Base Command
countercraft-list-hosts
Input
| Argument Name | Description | Required |
|---|---|---|
| campaign_id | Campaign ID | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Host.ID | number | Host Id |
| CounterCraft.Host.Name | string | Host Name |
| CounterCraft.Host.Description | string | Host Description |
| CounterCraft.Host.StatusCode | string | Host Status |
| CounterCraft.Host.TypeCode | string | Host Type |
Command Example
!countercraft-list-hosts campaign_id=2
Human Readable Output
| ID | Name | Description | StatusCode | TypeCode |
|---|---|---|---|---|
| 1 | Ubuntu Web | Wordpress | ACTIVE | MACHINE |
| 2 | Azure Windows 2019 | RDP with breadcrumbs | ACTIVE | MACHINE |
| 3 | Office365 tenant | Office365 with domain name | ACTIVE | CLOUD_ENTITY |
| 4 | Apache Struts | Vulnerable Apache Struts | ACTIVE | MACHINE |
| 5 | CFO | CFO persona | ACTIVE | IDENTITY |
3. countercraft-list-services
List services currently deployed on deception hosts
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to list only the services you have access to.
Base Command
countercraft-list-services
Input
| Argument Name | Description | Required |
|---|---|---|
| host_id | Host Id | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Service.ID | number | Service ID |
| CounterCraft.Service.Name | string | Service Name |
| CounterCraft.Service.Description | string | Service.Description |
| CounterCraft.Service.StatusCode | string | Service Status |
| CounterCraft.Service.TypeCode | string | Service Type |
Command Example
!countercraft-list-services host_id=1
Human Readable Output
| ID | Name | Description | StatusCode | TypeCode |
|---|---|---|---|---|
| 1 | Operating system | User events | ACTIVE | SYSTEM |
| 2 | WebApp | Web application | ACTIVE | WEB_SERVER |
| 8 | Tailored Service | Anonymous FTP | ACTIVE | FTP_SERVER |
| 9 | Phishing Sinkhole | Sinkhole | ACTIVE | SMTP_SERVER |
4. countercraft-list-breadcrumbs
List breadcrumbs in a campaign
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to list only the breadcrumbs you have access to.
Base Command
countercraft-list-breadcrumbs
Input
| Argument Name | Description | Required |
|---|---|---|
| campaign_id | Campaign ID | Optional |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Breadcrumb.ID | number | Breadcrumb ID |
| CounterCraft.Breadcrumb.Name | string | Breadcrumb Name |
| CounterCraft.Breadcrumb.Description | string | Breadcrumb Description |
| CounterCraft.Breadcrumb.StatusCode | string | Breadcrumb Status |
| CounterCraft.Breadcrumb.TypeCode | string | Breadcrumb Type |
Command Example
!countercraft-list-breadcrumbs campaign_id=1
Human Readable Output
| ID | Name | Description | StatusCode | TypeCode |
|---|---|---|---|---|
| 1 | Fake document | ACTIVE | DOCUMENT | |
| 2 | Mobile App | ACTIVE | MOBILE_APP | |
| 3 | SSL Certificate | ACTIVE | SSL_CERTIFICATE | |
| 4 | LinkedIn_persona | ACTIVE | HONEYTOKEN |
5. countercraft-get-object
Get information about an object (IoC)
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to list only the objects you have access to.
Base Command
countercraft-get-object
Input
| Argument Name | Description | Required |
|---|---|---|
| value | Object value | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Object.ID | number | Object ID |
| CounterCraft.Object.Value | string | Object value |
| CounterCraft.Object.Hits | number | Object hits |
| CounterCraft.Object.Score | number | Object score |
| CounterCraft.Object.TypeCode | string | Object type |
| CounterCraft.Object.FirstSeen | date | Object first seen |
| CounterCraft.Object.LastSeen | date | Object last seen |
| CounterCraft.Object.EventsCount | number | Object events count |
| CounterCraft.Object.Tags | string | Object tags |
Command Example
!countercraft-get-object value=root
Human Readable Output
| Id | 852 |
| Value | root |
| EventsCount | 7 |
| TypeCode | CC_USERNAME |
| Score | 0 |
| FirstSeen | Wed Jan 29 12:33:34 2020 |
| LastSeen | Wed Jan 29 12:53:19 2020 |
| Tags |
6. countercraft-get-events
Get full list of Events
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to list only the objects you have access to.
Base Command
countercraft-get-events
Input
| Argument Name | Description | Required |
|---|---|---|
| criteria | Search criteria | Required |
| max_results | Maximum number of results | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Event.ID | number | Event id |
| CounterCraft.Event.CampaignName | string | Campaign name |
| CounterCraft.Event.CategoryCode | string | Category Code |
| CounterCraft.Event.EventDate | date | Event date |
| CounterCraft.Event.HostName | string | Host name |
| CounterCraft.Event.ServiceName | string | Service name |
| CounterCraft.Event.TypeCode | string | Type |
| CounterCraft.Event.Score | number | Score |
| CounterCraft.Event.Tags | string | Tags |
| CounterCraft.Events.Data | unknown | Data |
Command Example
!countercraft-get-events criteria="type_code:ValidAuth" max_results="1"
Human Readable Output
| Id | 45 | |
| Campaignname | External recoinassance | |
| Hostname | Azure | Windows |
| Servicename | OS Logs (Azure | Windows) |
| Eventdate | Thu Jan 30 08:11:01 2020 | |
| Score | 100 | |
| Typecode | ValidAuth | |
| Data | event: ValidAuth subject: A session was reconnected to a Window Station event_id: 4778 … | |
| Tags | attack.T1078 |
7. countercraft-create-campaign
Create a new deception campaign
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You can only create campaigns if you have the role ARCHITECT.
Base Command
countercraft-create-campaign
Input
| Argument Name | Description | Required |
|---|---|---|
| name | Campaign name | Required |
| description | Campaign description | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Campaign.ID | number | Campaign ID |
| CounterCraft.Campaign.Name | string | Name |
| CounterCraft.Campaign.Description | string | Description |
| CounterCraft.Campaign.StatusCode | string | Status Code |
Command Example
!countercraft-create-campaign name="TestCampaign" description="Test Description"
Human Readable Output
| Id | 5 |
| Name | TestCampaign |
| Description | Test Description |
| StatusCode | DESIGN |
8. countercraft-list-dsns
List Deception Support Nodes (DSNs)
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You can only create campaigns if you have the role ARCHITECT.
Base Command
countercraft-list-dsns
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.DSN.ID | number | ID |
| CounterCraft.DSN.Name | string | Name |
| CounterCraft.DSN.Description | string | Description |
| CounterCraft.DSN.Hostname | string | Hostname |
| CounterCraft.DSN.Port | number | Port |
Command Example
!countercraft-list-dsns
Human Readable Output
| Id | 1 |
| Name | Local DSN |
| Description | Local DSN in the intranet |
| Hostname | 192.168.1.2 |
| Port | 4567 |
9. countercraft-list-providers
List providers (providers for hosts or services i.e. AWS or Office365)
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to list only the providers you have access to.
Base Command
countercraft-list-providers
Input
| Argument Name | Description | Required |
| — | — | — |
Context Output
| Path | Type | Description |
|---|---|---|
| ID | number | ID |
| CounterCraft.Provider.Name | string | Name |
| CounterCraft.Provider.Description | string | Description |
| CounterCraft.Provider.TypeCode | string | Type |
| CounterCraft.Provider.StatusCode | string | Status |
Command Example
!countercraft-list-providers
Human Readable Output
| ID | Name | Description | StatusCode | TypeCode |
|---|---|---|---|---|
| 1 | Splunk | Internal Splunk | HEALTHY | SPLUNK_PROVIDER |
| 3 | Signal | Signal notifications | HEALTHY | SIGNAL_PROVIDER |
| 4 | Office365 | Office365 Tenant | HEALTHY | OFFICE365_PROVIDER |
| 5 | AWS | AWS EC2 | HEALTHY | AWS_PROVIDER |
10. countercraft-create-host-machine
Deploy a new deception host
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to create a host if you are MANAGER in a campaign.
Base Command
countercraft-create-host-machine
Input
| Argument Name | Description | Required |
|---|---|---|
| name | Name | Required |
| description | Description | Optional |
| provider_id | Provider | Required |
| deception_support_node_id | Deception Support Node ID | Required |
| campaign_id | Campaign | Required |
| os_family | Operating System | Required |
| ip_address | IP Address | Required |
| port | Port | Required |
| username | Username | Required |
| password | Password | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Host.Id | number | Host ID |
Command Example
!countercraft-create-host-machine campaign_id=2 deception_support_node_id=1 os_family=linux ip_address=192.168.1.2 port=22 name="Test host" description="Test Description" username="ubuntu" password="ubuntu provider_id=1"
Human Readable Output
| Id | 8 |
| Name | Test Host |
| Description | Test Description |
| StatusCode | DESIGN |
| TypeCode | MACHINE |
11. countercraft-list-incidents
List all incidents currently active
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to list only the incidents you have access to.
Base Command
countercraft-list-incidents
Input
| Argument Name | Description | Required |
|---|---|---|
| campaign_id | Campaign ID | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Incident.ID | number | Incident ID |
| CounterCraft.Incident.Name | string | Name |
| CounterCraft.Incident.Description | string | Description |
| CounterCraft.Incident.StatusCode | string | Status |
| CounterCraft.Incident.TLPCode | string | TLP code |
Command Example
!countercraft-list-incidents campaign_id=1
Human Readable Output
| ID | Name | Description | StatusCode | TLPCode | Tags |
|---|---|---|---|---|---|
| 1 | APT incident | State-sponsored | OPEN | AMBER | |
| 2 | Internal Fraud | SWIFT apps | CLOSED | AMBER |
12. countercraft-manage-campaign
Manage Campaign parameters
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to manage only the campaigns you have access to.
Base Command
countercraft-manage-campaign
Input
| Argument Name | Description | Required |
|---|---|---|
| campaign_id | Campaign ID | Required |
| operation | Operation | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Campaign.Message | string | Result message |
| CounterCraft.Campaign.ID | number | Campaign ID |
Command Example
!countercraft-manage-campaign campaign_id=5 operation=activate
Human Readable Output
| Id | 5 |
| Message | Campaign is currently in state: PAUSED. Action activate discarded |
13. countercraft-manage-host
Manage a deception host
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to manage only the hosts you have access to.
Base Command
countercraft-manage-host
Input
| Argument Name | Description | Required |
|---|---|---|
| host_id | Host ID | Required |
| operation | Operation | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Host.Message | string | Result message |
| CounterCraft.Host.ID | number | Host ID |
Command Example
!countercraft-manage-campaign host_id=5 operation=activate
Human Readable Output
| Id | 5 |
| Message | Host is currently in state: PAUSED. Action activate discarded |
14. countercraft-manage-service
Manage a deception service
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to manage only the services you have access to.
Base Command
countercraft-manage-service
Input
| Argument Name | Description | Required |
|---|---|---|
| service_id | Service ID | Required |
| operation | Operation | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Service.Message | string | Result message |
| CounterCraft.Service.ID | number | Service ID |
Command Example
!countercraft-manage-campaign service_id=5 operation=activate
Human Readable Output
| Id | 5 |
| Message | Service is currently in state: PAUSED. Action activate discarded |
15. countercraft-manage-breadcrumb
Manage breadcrumb
Required Permissions
Any interaction will be based on your permissions on the Deception Director. Please consult
your Deception Director administrator in you have any questions.
You will be able to manage only the breadcrumbs you have access to.
Base Command
countercraft-manage-breadcrumb
Input
| Argument Name | Description | Required |
|---|---|---|
| breadcrumb_id | Breadcrumb ID | Required |
| operation | Operation | Required |
Context Output
| Path | Type | Description |
|---|---|---|
| CounterCraft.Breadcrumb.Message | string | Result message |
| CounterCraft.Breadcrumb.ID | number | Breadcrumb ID |
Command Example
!countercraft-manage-campaign breadcrumb_id=5 operation=activate
Human Readable Output
| Id | 5 |
| Message | Breadcrumb is currently in state: PAUSED. Action activate discarded |
Additional Information
Please check the Deception Director user manual for more guidance on how to use and deploy campaigns.
Configuration parameters
server— Deception Director Domain or IP Address (required)api_key— API Key for Deception Director connection (required)secret_key— Secret Key for Deception Director connection (required)insecure— Trust any certificate (not secure)proxy— Use system proxy settingsisFetch— Fetch incidentsincidentType— Incident typeincidentFetchInterval— Incidents Fetch Interval
Commands (15)
-
countercraft-create-campaignCreate a new deception campaign.
-
countercraft-create-host-machineDeploy a new deception host.
-
countercraft-get-eventsGet full list of Events.
-
countercraft-get-objectGet information about an object (IoC).
-
countercraft-list-breadcrumbsList breadcrumbs in a campaign.
-
countercraft-list-campaignsList all deception campaigns.
-
countercraft-list-dsnsList Deception Support Nodes (DSNs).
-
countercraft-list-hostsLists all deception hosts.
-
countercraft-list-incidentsList all incidents currently active.
-
countercraft-list-providersList providers (providers for hosts or services i.e. AWS or Office365).
-
countercraft-list-servicesList services currently deployed on deception hosts.
-
countercraft-manage-breadcrumbManage breadcrumb.
-
countercraft-manage-campaignManage Campaign parameters.
-
countercraft-manage-hostManage a deception host.
-
countercraft-manage-serviceManage a deception service.
import demistomock as demisto from CommonServerPython import * """ IMPORTS """ import binascii import hmac import json import time import urllib.parse import uuid from datetime import datetime from hashlib import sha1 import requests import urllib3 # Disable insecure warnings urllib3.disable_warnings() """ GLOBALS""" X_API_KEY_AUTHENTICATION_HEADER_PREFIX = "X-API-Key-Auth-" SERVER = demisto.params().get("server").rstrip("/") + "/api" API_KEY = demisto.params().get("api_key") SECRET_KEY = demisto.params().get("secret_key") VERIFY_CERTIFICATE = not demisto.params().get("insecure", False) PROXY = demisto.params().get("proxy", False) FETCH_DELTA = "24 hours" DEF_HEADERS = {"Accept": "application/json", "Content-Type": "application/json"} ERR_DICT = { 400: "Bad request. Please check your arguments and Deception Director API manual", 401: "User does not have the right permission", 404: "Entity not found. Please make sure the entity does exist", 500: "Bad request. Please check your arguments and Deception Director API manual", } CAMPAIGN_FIELDS = { "id": "ID", "name": "Name", "description": "Description", "status_code": "StatusCode", } DSN_FIELDS = { "id": "ID", "name": "Name", "description": "Description", "hostname": "Hostname", "port": "Port", } HOST_FIELDS = { "id": "ID", "name": "Name", "description": "Description", "type_code": "TypeCode", "status_code": "StatusCode", } SERVICE_FIELDS = { "id": "ID", "name": "Name", "description": "Description", "type_code": "TypeCode", "status_code": "StatusCode", } BREADCRUMB_FIELDS = { "id": "ID", "name": "Name", "description": "Description", "type_code": "TypeCode", "status_code": "StatusCode", } PROVIDER_FIELDS = { "id": "ID", "name": "Name", "description": "Description", "type_code": "TypeCode", "status_code": "StatusCode", } INCIDENT_FIELDS = { "id": "ID", "name": "Name", "description": "Description", "status_code": "StatusCode", "tlp_code": "TLPCode", } OBJECT_FIELDS = { "id": "ID", "value": "Value", "hits": "Hits", "score": "Score", "type_code": "TypeCode", "first_seen": "FirstSeen", "last_seen": "LastSeen", "events_count": "EventsCount", "tags": "Tags", } EVENT_FIELDS = { "id": "ID", "campaign_name": "CampaignName", "category_code": "CategoryCode", "host_name": "HostName", "service_name": "ServiceName", "event_date": "EventDate", "score": "Score", "type_code": "TypeCode", "data": "Data", "tags": "Tags", } """ HELPERS """ def sign(secret_key, data): """ @param $secret_key the secret key to use for the HMAC-SHA digesting @param $data the string to sign @return string base64 encoding of the HMAC-SHA1 hash of the data parameter using {@code secret_key} as cipher key. """ sha1_hash = hmac.new(secret_key.encode(), data.encode(), sha1) return binascii.b2a_base64(sha1_hash.digest())[:-1] # strip \n from base64 string result def get_signature(request_method, request_headers, path, query_string, private_key): """ Calculate the authentication headers to be sent with a request to the API @param $request_method the HTTP method (GET, POST, etc.) @param $path the urlencoded string including the path (from the first forward slash) and the parameters @param $x_headers HTTP headers specific to CounterCraft API @return array a map with the Authorization and Date headers needed to sign a Latch API request """ x_headers = {k: v for k, v in request_headers.items() if k.lower().startswith(X_API_KEY_AUTHENTICATION_HEADER_PREFIX.lower())} string_to_sign = ( request_method.upper().strip() + "\n" + get_serialized_headers(x_headers) + "\n" + (path if query_string.strip() == b"" else f"{path}?{query_string.strip().decode('utf - 8')}") ) return sign(private_key, string_to_sign).decode("utf-8") def get_serialized_headers(x_headers): """ Generate a string ready to be signed based on HTTP headers received @param $x_headers a non neccesarily ordered map (array without duplicates) of the HTTP headers to be ordered. @return string The serialized headers, an empty string if no headers are passed """ res = {} # type: Dict[str, List] for k, v in x_headers.items(): if k.strip().lower() not in res: res[k.lower()] = [] res[k.strip().lower()].append(v.strip()) return "\n".join(f"{k}:{v}" for k, v in sorted({k: ",".join(v) for k, v in res.items()}.items())) def http_request(request_method, path, data={}, params=""): """ Send an HTTP request @param $request_method the request method GET, POST, etc. @param $path the HTTP path @param $data the data included in the POST request @param $params the URL params to be included @return string A dict containing the response in JSON format or an error """ headers = {X_API_KEY_AUTHENTICATION_HEADER_PREFIX + "Date": time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())} headers.update(DEF_HEADERS) signature = get_signature( request_method, headers, path, urllib.parse.urlencode(params).encode("utf-8"), SECRET_KEY, ) headers["Authorization"] = f"APIKey {API_KEY}:{signature}" url = SERVER + path proxies = None if PROXY: proxies = handle_proxy() res = requests.request( request_method, url, data=json.dumps(data), params=params, headers=headers, verify=VERIFY_CERTIFICATE, proxies=proxies ) if res.status_code not in [200, 201, 204]: demisto.debug(f"Error doing the HTTP query. We got a {res.status_code}: {res.text}") return_error(ERR_DICT[res.status_code]) try: res_json = res.json() return res_json except Exception as ex: demisto.debug(str(ex)) return_error(str(ex)) def return_host_standard_context(host): host_standard_context = {} if host["type_code"] == "MACHINE": host_standard_context["ID"] = host["uuid"] host_standard_context["IP"] = host["data"]["ip_address"] if "ansible_facts" in host["data"]: host_standard_context["Domain"] = host["data"]["ansible_facts"]["ansible_domain"] host_standard_context["Hostname"] = host["data"]["ansible_facts"]["ansible_hostname"] host_standard_context["BIOSVersion"] = host["data"]["ansible_facts"]["ansible_bios_version"] host_standard_context["Memory"] = host["data"]["ansible_facts"]["ansible_memtotal_mb"] host_standard_context["Model"] = host["data"]["ansible_facts"]["ansible_product_name"] host_standard_context["OS"] = host["data"]["ansible_facts"]["ansible_os_family"] host_standard_context["OSVersion"] = host["data"]["ansible_facts"]["ansible_distribution_version"] host_standard_context["Processor"] = " ".join(host["data"]["ansible_facts"]["ansible_processor"]) host_standard_context["Processors"] = len(host["data"]["ansible_facts"]["ansible_processor"]) return host_standard_context def return_entry_results(title, content, human_readable, context, headers): """ Generic function that receives a result json, and turns it into an entryObject @param $title the table title @param $content the object contents in JSON format @param $human_readable human readable data for the table @param $context the entry context @param $headers table headers @return dict the entryObject """ if len(content) == 0: return_outputs( readable_output="There is no output result", outputs=context, raw_response=content, ) return if headers: if isinstance(headers, str): headers = headers.split(",") else: if isinstance(content, dict): headers = list(set(headers).intersection(set(content.keys()))) readable_output = tableToMarkdown( title, human_readable, headers, lambda h: h.title().replace("_", " ").replace(".", ":"), ) return_outputs(readable_output=readable_output, outputs=context, raw_response=content) """ COMMANDS """ def test_module_command(): http_request("GET", "/campaigns") demisto.results("ok") def list_dsns_command(): """ Retrieve all deception support nodes """ res = http_request("GET", "/deception_support_nodes") new_dsns = [] for o in res["data"]: new_dsns.append({new_key: o.get(old_key, None) for old_key, new_key in DSN_FIELDS.items()}) context = createContext(new_dsns, removeNull=True) return_entry_results( "Deception Support Node", res["data"], new_dsns, {"CounterCraft.DSN(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Name", "Description", "Hostname", "Port"], ) def list_providers_command(): """ Retrieve all providers """ res = http_request("GET", "/providers") new_providers = [] for o in res["data"]: new_providers.append({new_key: o.get(old_key, None) for old_key, new_key in PROVIDER_FIELDS.items()}) context = createContext(new_providers, removeNull=True) return_entry_results( "Providers", res["data"], new_providers, {"CounterCraft.Provider(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Name", "Description", "TypeCode", "StatusCode"], ) def list_campaigns_command(): """ Retrieve all campaigns """ name = demisto.args().get("name") if name is not None: criteria = {"criteria": f'name:"{name}"'} else: criteria = {} res = http_request("GET", "/campaigns", params=criteria) new_campaigns = [] for o in res["data"]: new_campaigns.append({new_key: o.get(old_key, None) for old_key, new_key in CAMPAIGN_FIELDS.items()}) context = createContext(new_campaigns, removeNull=True) return_entry_results( "Campaigns", res["data"], new_campaigns, {"CounterCraft.Campaign(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Name", "Description", "StatusCode"], ) def list_hosts_command(): """ Retrieve all hosts """ campaign_id = demisto.args().get("campaign_id") if campaign_id is not None: criteria = {"criteria": "campaigns.id:" + campaign_id} else: criteria = {} res = http_request("GET", "/hosts", params=criteria) new_hosts = [] for o in res["data"]: new_hosts.append({new_key: o.get(old_key, None) for old_key, new_key in HOST_FIELDS.items()}) context = createContext(new_hosts, removeNull=True) contextHosts = createContext([return_host_standard_context(x) for x in res["data"]], removeNull=True) return_entry_results( "Hosts", res["data"], new_hosts, {"CounterCraft.Host(val.ID && val.ID === obj.ID)": context, "Host(val.IP && val.IP === obj.IP)": contextHosts}, headers=["ID", "Name", "Description", "StatusCode", "TypeCode"], ) def list_services_command(): """ Retrieve all services """ host_id = demisto.args().get("host_id") if host_id is not None: criteria = {"criteria": "hosts.id:" + host_id} else: criteria = {} res = http_request("GET", "/services", params=criteria) new_services = [] for o in res["data"]: new_services.append({new_key: o.get(old_key, None) for old_key, new_key in SERVICE_FIELDS.items()}) context = createContext(new_services, removeNull=True) return_entry_results( "Services", res["data"], new_services, {"CounterCraft.Service(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Name", "Description", "StatusCode", "TypeCode"], ) def list_breadcrumbs_command(): """ Retrieve all breadcrumbs """ campaign_id = demisto.args().get("campaign_id") if campaign_id is not None: criteria = {"criteria": "campaigns.id:" + campaign_id} else: criteria = {} res = http_request("GET", "/breadcrumbs", params=criteria) new_breadcrumbs = [] for o in res["data"]: new_breadcrumbs.append({new_key: o.get(old_key, None) for old_key, new_key in BREADCRUMB_FIELDS.items()}) context = createContext(new_breadcrumbs, removeNull=True) return_entry_results( "Breadcrumbs", res["data"], new_breadcrumbs, {"CounterCraft.Breadcrumb(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Name", "Description", "StatusCode", "TypeCode"], ) def list_incidents_command(): """ Retrieve all incidents """ campaign_id = demisto.args().get("campaign_id") if campaign_id is not None: criteria = {"criteria": "campaigns.id:" + campaign_id} else: criteria = {} res = http_request("GET", "/incidents", params=criteria) new_incidents = [] for o in res["data"]: new_incidents.append({new_key: o.get(old_key, None) for old_key, new_key in INCIDENT_FIELDS.items()}) context = createContext(new_incidents, removeNull=True) return_entry_results( "Incidents", res["data"], new_incidents, {"CounterCraft.Incident(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Name", "Description", "StatusCode", "TLPCode", "Tags"], ) def get_object_command(): """ Retrieve all objects """ value = demisto.args().get("value") if value is not None: criteria = {"criteria": "objects.value:" + value} else: criteria = {} res = http_request("GET", "/objects", params=criteria) for entry in res["data"]: entry["first_seen"] = formatEpochDate(entry["first_seen"]) entry["last_seen"] = formatEpochDate(entry["last_seen"]) new_objects = [] for o in res["data"]: new_objects.append({new_key: o.get(old_key, None) for old_key, new_key in OBJECT_FIELDS.items()}) context = createContext(new_objects, removeNull=True) return_entry_results( "Objects", res["data"], new_objects, {"CounterCraft.Object(val.ID && val.ID === obj.ID)": context}, headers=[ "ID", "Value", "Hits", "EventsCount", "TypeCode", "Score", "FirstSeen", "LastSeen", "Tags", ], ) def get_events_command(): """ Retrieve all events """ sfilter = demisto.args().get("criteria") per_page = demisto.args().get("max_results") criteria = { "criteria": sfilter, "order": "-event_date", "page": 1, "per_page": per_page, } res = http_request("GET", "/events", params=criteria) for entry in res["data"]: entry["event_date"] = formatEpochDate(entry["event_date"]) new_events = [] for o in res["data"]: new_events.append({new_key: o.get(old_key, None) for old_key, new_key in EVENT_FIELDS.items()}) context = createContext(new_events, removeNull=True) return_entry_results( "Events", res["data"], new_events, {"CounterCraft.Event(val.ID && val.ID === obj.ID)": context}, headers=[ "ID", "CampaignName", "CategoryCode", "HostName", "ServiceName", "EventDate", "Score", "TypeCode", "Data", "Tags", ], ) def list_notifications(last_fetched): """ Retrieve all Notifications """ criteria = { "criteria": f'plugin_code:CONSOLE AND notifications.ctime:["{last_fetched}" TO *]', "order": "-ctime", "with_stats": True, } res = http_request("GET", "/notifications", params=criteria) return res["data"] def create_campaign_command(): """ Create a campaign """ name = demisto.args().get("name") description = demisto.args().get("description") data = {"name": name, "description": description} res = http_request("POST", "/campaigns", data=data) campaign = {new_key: res.get(old_key, None) for old_key, new_key in CAMPAIGN_FIELDS.items()} context = createContext(campaign, removeNull=True) return_entry_results( "Campaign", res, campaign, {"CounterCraft.Campaign(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Name", "Description", "StatusCode"], ) def manage_campaign_command(): """ Operate a campaign """ campaign_id = demisto.args().get("campaign_id") operation = demisto.args().get("operation") data = {"action": operation} res = http_request("PATCH", "/campaigns/" + campaign_id, data=data) message = [{"ID": campaign_id, "Message": res["message"]}] context = createContext(message, removeNull=True) return_entry_results( "Campaign Management", res, message, {"CounterCraft.Campaign(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Message"], ) def create_host_machine_command(): """ Create a host of type MACHINE """ name = demisto.args().get("name") description = demisto.args().get("description") provider_id = demisto.args().get("provider_id") type_code = "MACHINE" deception_support_node_id = demisto.args().get("deception_support_node_id") campaign_id = demisto.args().get("campaign_id") ip_address = demisto.args().get("ip_address") port = demisto.args().get("port") username = demisto.args().get("username") password = demisto.args().get("password") host_data = { "ip_address": ip_address, "port": port, "username": username, "password": password, "os_family_usr": "linux", } _uuid = str(uuid.uuid4()) data = { "name": name, "description": description, "provider_id": provider_id, "deception_support_node_id": deception_support_node_id, "campaign_id": campaign_id, "type_code": type_code, "uuid": _uuid, "data": host_data, } res = http_request("POST", "/hosts", data=data) host = {new_key: res.get(old_key, None) for old_key, new_key in HOST_FIELDS.items()} context = createContext(host, removeNull=True) return_entry_results( "Hosts", res, host, {"CounterCraft.Host(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Name", "Description", "StatusCode", "TypeCode"], ) def manage_host_command(): """ Operate a host """ host_id = demisto.args().get("host_id") operation = demisto.args().get("operation") data = {"action": operation} res = http_request("PATCH", "/hosts/" + host_id, data=data) message = {"ID": host_id, "Message": res["message"]} context = createContext(message, removeNull=True) return_entry_results( "Host Management", res, message, {"CounterCraft.Host(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Message"], ) def manage_service_command(): """ Operate a service """ service_id = demisto.args().get("service_id") operation = demisto.args().get("operation") data = {"action": operation} res = http_request("PATCH", "/services/" + service_id, data=data) message = {"ID": service_id, "Message": res["message"]} context = createContext(message, removeNull=True) return_entry_results( "Service Management", res, message, {"CounterCraft.Service(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Message"], ) def manage_breadcrumb_command(): """ Operate a breadcrumb """ breadcrumb_id = demisto.args().get("breadcrumb_id") operation = demisto.args().get("operation") data = {"action": operation} res = http_request("PATCH", "/breadcrumbs/" + breadcrumb_id, data=data) message = {"ID": breadcrumb_id, "Message": res["message"]} context = createContext(message, removeNull=True) return_entry_results( "Breadcrumb Management", res, message, {"CounterCraft.Breadcrumb(val.ID && val.ID === obj.ID)": context}, headers=["ID", "Message"], ) def fetch_incidents_command(): """ Fetch incidents (user notifications) """ last_run = demisto.getLastRun() if not last_run: last_run = {} if "time" not in last_run: # get timestamp in seconds timestamp, _ = parse_date_range(FETCH_DELTA, to_timestamp=True) timestamp /= 1000 else: timestamp = last_run["time"] max_timestamp = timestamp # All alerts retrieved from get_alerts are newer than last_fetch and are in a chronological order notifications = list_notifications(timestamp) incidents = [] for notification in notifications: if int(notification["ctime"]) > timestamp: incidents.append( { "name": notification["data"]["subject"], "occurred": datetime.utcfromtimestamp(int(notification["ctime"])).strftime("%Y-%m-%dT%H:%M:%SZ"), "details": notification["data"]["html"], "rawJSON": json.dumps(notification), } ) if int(notification["ctime"]) > max_timestamp: max_timestamp = int(notification["ctime"]) demisto.incidents(incidents) demisto.setLastRun({"time": max_timestamp}) def main(): try: if demisto.command() == "test-module": test_module_command() elif demisto.command() == "countercraft-list-providers": list_providers_command() elif demisto.command() == "countercraft-list-dsns": list_dsns_command() elif demisto.command() == "countercraft-list-campaigns": list_campaigns_command() elif demisto.command() == "countercraft-list-hosts": list_hosts_command() elif demisto.command() == "countercraft-list-services": list_services_command() elif demisto.command() == "countercraft-list-breadcrumbs": list_breadcrumbs_command() elif demisto.command() == "countercraft-list-incidents": list_incidents_command() elif demisto.command() == "countercraft-get-object": get_object_command() elif demisto.command() == "countercraft-get-events": get_events_command() elif demisto.command() == "countercraft-create-campaign": create_campaign_command() elif demisto.command() == "countercraft-create-host-machine": create_host_machine_command() elif demisto.command() == "countercraft-manage-campaign": manage_campaign_command() elif demisto.command() == "countercraft-manage-host": manage_host_command() elif demisto.command() == "countercraft-manage-service": manage_service_command() elif demisto.command() == "countercraft-manage-breadcrumb": manage_breadcrumb_command() elif demisto.command() == "fetch-incidents": fetch_incidents_command() except Exception as e: return_error(f"Unable to perform command : {demisto.command()}, Reason: {e}") if __name__ in ("__main__", "builtin", "builtins"): main()