Smokescreen IllusionBLACK

Smokescreen IllusionBLACK is a deception-based threat defense platform designed to accurately and efficiently detect targeted threats including reconnaissance, lateral movement, malware-less attacks, social engineering, Man-in-the-Middle attacks, and ransomware in real-time.

Deception & Breach Simulation · Smokescreen IllusionBLACK

Details

IDSmokescreen IllusionBLACK
ProviderZscaler
CategoryDeception & Breach Simulation
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.12042988
Supported ModulesAgentix XSIAM

README

Smokescreen IllusionBLACK is a deception-based threat defense platform designed to accurately and efficiently detect targeted threats including reconnaissance, lateral movement, malware-less attacks, social engineering, Man-in-the-Middle attacks, and ransomware in real-time.
This integration was integrated and tested with version v3.10.7.4 of Smokescreen IllusionBLACK

Permissions

Configure Smokescreen IllusionBLACK in Cortex

Parameter Description Required
url Server URL (e.g. https://example.net) True
client_id IllusionBLACK API Client Id True
token IllusionBLACK External API Token True
insecure Trust any certificate (not secure) False
proxy Use system proxy settings False
first_fetch First fetch time for fetching incidents (2 days, 3 weeks, etc) 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.

illusionblack-get-ad-decoys


Gets a list of Active Directory decoys.

Base Command

illusionblack-get-ad-decoys

Input

There are no input arguments for this command.

Context Output
Path Type Description
IllusionBlack.AdDecoy Unknown IllusionBLACK AD Decoy users.
Command Example

##### Context Example

{
“IllusionBlack”: {
“AdDecoy”: {
“first_name”: “felix”,
“last_name”: “hunt”,
“ou”: “mumbai”,
“state”: “added”,
“user_name”: “sqladmin”
}
}
}


##### Human Readable Output

### IllusionBLACK AD Decoys

|First Name|Last Name|Ou|State|User Name|
|---|---|---|---|---|
| felix | hunt | mumbai | added | sqladmin |

### illusionblack-get-network-decoys

***
Gets a list of Network decoys.

##### Base Command

`illusionblack-get-network-decoys`

##### Input

There are no input arguments for this command.

##### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| IllusionBlack.NetworkDecoy | Unknown | IllusionBLACK Network decoys. |

##### Command Example

```!illusionblack-get-network-decoys```

##### Context Example

{
“IllusionBlack”: {
“NetworkDecoy”: [
{
“ip”: “10.20.23.61”,
“mac”: “d0:43:1e:cd:cb:c2”,
“name”: “CTX-BACKUPS31”,
“services”: “web”
},
{
“ip”: “10.20.23.64”,
“mac”: “a0:48:1c:ee:08:38”,
“name”: “GCP-CYBERARK”,
“services”: “web”
},
{
“ip”: “10.20.23.63”,
“mac”: “00:fd:45:fa:6f:4d”,
“name”: “NEW-XEN”,
“services”: “web”
},
{
“ip”: “10.20.23.65”,
“mac”: “14:b3:1f:08:84:6d”,
“name”: “PRIM-CYBERARK”,
“services”: “web, shares”
},
{
“ip”: “10.20.23.62”,
“mac”: “20:a6:cd:00:6e:70”,
“name”: “SAP44”,
“services”: “web, shares”
},
{
“ip”: “10.20.23.60”,
“mac”: “90:b1:1c:73:64:fc”,
“name”: “ARCOSNEW”,
“services”: “web”
}
]
}
}


##### Human Readable Output

### IllusionBLACK Network Decoys

|Ip|Mac|Name|Services|
|---|---|---|---|
| 10.20.23.61 | d0:43:1e:cd:cb:c2 | CTX-BACKUPS31 | web |
| 10.20.23.64 | a0:48:1c:ee:08:38 | GCP-CYBERARK | web |
| 10.20.23.63 | 00:fd:45:fa:6f:4d | NEW-XEN | web |
| 10.20.23.65 | 14:b3:1f:08:84:6d | PRIM-CYBERARK | web, shares |
| 10.20.23.62 | 20:a6:cd:00:6e:70 | SAP44 | web, shares |
| 10.20.23.60 | 90:b1:1c:73:64:fc | ARCOSNEW | web |

### illusionblack-get-ti-decoys

***
Gets a list of Threat Intel decoys.

##### Base Command

`illusionblack-get-ti-decoys`

##### Input

There are no input arguments for this command.

##### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| IllusionBlack.TIDecoy | Unknown | IllusionBLACK TI Decoys. |

##### Command Example

```!illusionblack-get-ti-decoys```

##### Context Example

{
“IllusionBlack”: {
“TIDecoy”: {
“dataset”: “Finacle”,
“ip”: “10.20.23.67”,
“name”: “dev.smokescreen.io”,
“server_type”: “nginx/1.14.0 (Ubuntu)”
}
}
}


##### Human Readable Output

### IllusionBLACK TI Decoys

|Dataset|Ip|Name|Server Type|
|---|---|---|---|
| Finacle | 10.20.23.67 | dev.smokescreen.io | nginx/1.14.0 (Ubuntu) |

### illusionblack-is-host-decoy

***
Checks if a host or IP address is a network decoy.

##### Base Command

`illusionblack-is-host-decoy`

##### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| host | Hostname or IP address to check. | Required |

##### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| IllusionBlack.IsHostDecoy.Host | String | The IP address or hostname submitted to IllusionBLACK to check. |
| IllusionBlack.IsHostDecoy.Value | Boolean | The boolean value whether the host is a decoy or not. |

##### Command Example

```!illusionblack-is-host-decoy host="SAP44"```

##### Context Example

{
“IllusionBlack”: {
“IsHostDecoy”: {
“Host”: “SAP44”,
“Value”: true
}
}
}


##### Human Readable Output

True

### illusionblack-is-user-decoy

***
Checks if an Active Directory user is a decoy.

##### Base Command

`illusionblack-is-user-decoy`

##### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| user | Active Directory user name to check. | Required |

##### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| IllusionBlack.IsUserDecoy.User | String | The AD username submitted to IllusionBLACK to check. |
| IllusionBlack.IsUserDecoy.Value | Boolean | The boolean value whether the user is a decoy or not. |

##### Command Example

```!illusionblack-is-user-decoy user="sqladmin"```

##### Context Example

{
“IllusionBlack”: {
“IsUserDecoy”: {
“User”: “sqladmin”,
“Value”: true
}
}
}


##### Human Readable Output

True

### illusionblack-is-subdomain-decoy

***
Checks if a subdomain is a Threat Intel decoy.

##### Base Command

`illusionblack-is-subdomain-decoy`

##### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| subdomain | Subdomain to check. For example: dec.smokescreen.io. | Required |

##### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| IllusionBlack.IsSubdomainDecoy.Subdomain | String | The subdomain submitted to IllusionBLACK to check. |
| IllusionBlack.IsSubdomainDecoy.Value | Boolean | The boolean value whether the subdomain is a decoy or not. |

##### Command Example

```!illusionblack-is-subdomain-decoy subdomain="experience.illusionblack.com"```

##### Context Example

{
“IllusionBlack”: {
“IsSubdomainDecoy”: {
“Subdomain”: “experience.illusionblack.com”,
“Value”: false
}
}
}


##### Human Readable Output

False

### illusionblack-get-events

***
Gets events from IllusionBLACK.

##### Base Command

`illusionblack-get-events`

##### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| limit | Number of events. It can be between 1 and 1000. | Optional |
| query | IllusionBLACK query. For example: "attacker.ip is \"1.2.3.4\"" | Optional |
| from | ISO 8601 formatted date string. | Optional |
| to | ISO 8601 formatted date string. | Optional |

##### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| IllusionBlack.Event | Unknown | IllusionBLACK Events. |

##### Command Example

```!illusionblack-get-events limit=3```

##### Context Example

{
“IllusionBlack”: {
“Event”: [
{
“attacker.id”: “ADMIN-PC-001\admin”,
“attacker.name”: “ADMIN-PC-001\admin”,
“attacker.score”: 175,
“attacker.threat_parse_ids”: [
“lm_file_open”,
“lm_file_active_monitoring”
],
“decoy.appliance.id”: “cmc”,
“decoy.appliance.name”: “CMC”,
“decoy.client.id”: “experience”,
“decoy.client.name”: “experience”,
“decoy.group”: “Endpoint”,
“decoy.id”: “endpoint:admin-pc-001”,
“decoy.name”: “admin-pc-001”,
“decoy.type”: “endpoint”,
“file.name”: “C:\Users\admin\Desktop\passwords\Passwords.xlsx”,
“file.operation”: “67”,
“file.operation_string”: “Read”,
“file.process.command_line”: “"C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe" -executionpolicy bypass”,
“file.process.domain_name”: “ADMIN-PC-001”,
“file.process.exit_code”: -1,
“file.process.id”: “10228”,
“file.process.image_name”: “powershell.exe”,
“file.process.user_name”: “admin”,
“file.process.user_sid”: “S-1-5-21-399445878-2258755057-882339928-1000”,
“file.thread_id”: “8588”,
“id”: “2020-02-25T09:49:15Z-76c99a22-03b9-439e-8638-37306c2d8e7f”,
“kill_chain_phase”: “Data Theft”,
“mitre_ids”: [
“T1005”
],
“record_type”: “event”,
“severity”: “high”,
“sub_type”: “file”,
“threat_parse_ids”: [
“lm_file_open”
],
“timestamp”: “2020-04-12T08:57:01Z”,
“type”: “endpoint”,
“whitelisted”: false
},
{
“attacker.id”: “NT AUTHORITY\SYSTEM”,
“attacker.name”: “NT AUTHORITY\SYSTEM”,
“attacker.score”: 250,
“attacker.threat_parse_ids”: [
“filetheft_unattend”,
“lm_file_active_monitoring”,
“lm_file_open”
],
“decoy.appliance.id”: “cmc”,
“decoy.appliance.name”: “CMC”,
“decoy.client.id”: “experience”,
“decoy.client.name”: “experience”,
“decoy.group”: “Endpoint”,
“decoy.id”: “endpoint:admin-pc-001”,
“decoy.name”: “admin-pc-001”,
“decoy.type”: “endpoint”,
“file.name”: “C:\Users\admin\Desktop\passwords\Passwords.xlsx”,
“file.operation”: “67”,
“file.operation_string”: “Read”,
“file.process.command_line”: “”,
“file.process.domain_name”: “NT AUTHORITY”,
“file.process.exit_code”: -1,
“file.process.id”: “2824”,
“file.process.image_name”: “MsMpEng.exe”,
“file.process.user_name”: “SYSTEM”,
“file.process.user_sid”: “S-1-5-18”,
“file.thread_id”: “724”,
“id”: “2020-02-25T09:49:15Z-0950f80f-7571-4382-b4b8-5e04c160c4c0”,
“kill_chain_phase”: “Data Theft”,
“mitre_ids”: [
“T1005”
],
“record_type”: “event”,
“severity”: “high”,
“sub_type”: “file”,
“threat_parse_ids”: [
“lm_file_open”
],
“timestamp”: “2020-04-12T08:57:01Z”,
“type”: “endpoint”,
“whitelisted”: false
},
{
“attacker.id”: “ADMIN-PC-001\admin”,
“attacker.name”: “ADMIN-PC-001\admin”,
“attacker.score”: 175,
“attacker.threat_parse_ids”: [
“lm_file_open”,
“lm_file_active_monitoring”
],
“decoy.appliance.id”: “cmc”,
“decoy.appliance.name”: “CMC”,
“decoy.client.id”: “experience”,
“decoy.client.name”: “experience”,
“decoy.group”: “Endpoint”,
“decoy.id”: “endpoint:admin-pc-001”,
“decoy.name”: “admin-pc-001”,
“decoy.type”: “endpoint”,
“file.name”: “C:\Users\admin\Desktop\docs\vulnerability assessment report\vulnerability assessment report.xlsx”,
“file.operation”: “65”,
“file.operation_string”: “Cleanup”,
“file.process.command_line”: “"C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe" -executionpolicy bypass”,
“file.process.domain_name”: “ADMIN-PC-001”,
“file.process.exit_code”: -1,
“file.process.id”: “10228”,
“file.process.image_name”: “powershell.exe”,
“file.process.user_name”: “admin”,
“file.process.user_sid”: “S-1-5-21-399445878-2258755057-882339928-1000”,
“file.thread_id”: “0”,
“id”: “2020-02-25T09:45:48Z-fa248a98-bc8a-4275-93c7-e63ff1ee8d34”,
“kill_chain_phase”: “Data Theft”,
“mitre_ids”: [
“T1005”
],
“record_type”: “event”,
“severity”: “high”,
“sub_type”: “file”,
“threat_parse_ids”: [
“lm_file_active_monitoring”
],
“timestamp”: “2020-04-12T08:53:20Z”,
“type”: “endpoint”,
“whitelisted”: false
}
]
}
}


##### Human Readable Output

### IllusionBLACK Events

|attacker.id|attacker.name|attacker.score|attacker.threat_parse_ids|decoy.appliance.id|decoy.appliance.name|decoy.client.id|decoy.client.name|decoy.group|decoy.id|decoy.name|decoy.type|file.name|file.operation|file.operation_string|file.process.command_line|file.process.domain_name|file.process.exit_code|file.process.id|file.process.image_name|file.process.user_name|file.process.user_sid|file.thread_id|id|kill_chain_phase|mitre_ids|record_type|severity|sub_type|threat_parse_ids|timestamp|type|whitelisted|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ADMIN-PC-001\admin | ADMIN-PC-001\admin | 175 | lm_file_open,<br/>lm_file_active_monitoring | cmc | CMC | experience | experience | Endpoint | endpoint:admin-pc-001 | admin-pc-001 | endpoint | C:\Users\admin\Desktop\passwords\Passwords.xlsx | 67 | Read | "C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe" -executionpolicy bypass | ADMIN-PC-001 | -1 | 10228 | powershell.exe | admin | S-1-5-21-399445878-2258755057-882339928-1000 | 8588 | 2020-02-25T09:49:15Z-76c99a22-03b9-439e-8638-37306c2d8e7f | Data Theft | T1005 | event | high | file | lm_file_open | 2020-04-12T08:57:01Z | endpoint | false |
| NT AUTHORITY\SYSTEM | NT AUTHORITY\SYSTEM | 250 | filetheft_unattend,<br/>lm_file_active_monitoring,<br/>lm_file_open | cmc | CMC | experience | experience | Endpoint | endpoint:admin-pc-001 | admin-pc-001 | endpoint | C:\Users\admin\Desktop\passwords\Passwords.xlsx | 67 | Read |  | NT AUTHORITY | -1 | 2824 | MsMpEng.exe | SYSTEM | S-1-5-18 | 724 | 2020-02-25T09:49:15Z-0950f80f-7571-4382-b4b8-5e04c160c4c0 | Data Theft | T1005 | event | high | file | lm_file_open | 2020-04-12T08:57:01Z | endpoint | false |
| ADMIN-PC-001\admin | ADMIN-PC-001\admin | 175 | lm_file_open,<br/>lm_file_active_monitoring | cmc | CMC | experience | experience | Endpoint | endpoint:admin-pc-001 | admin-pc-001 | endpoint | C:\Users\admin\Desktop\docs\vulnerability assessment report\vulnerability assessment report.xlsx | 65 | Cleanup | "C:\WINDOWS\System32\WindowsPowerShell\v1.0\powershell.exe" -executionpolicy bypass | ADMIN-PC-001 | -1 | 10228 | powershell.exe | admin | S-1-5-21-399445878-2258755057-882339928-1000 | 0 | 2020-02-25T09:45:48Z-fa248a98-bc8a-4275-93c7-e63ff1ee8d34 | Data Theft | T1005 | event | high | file | lm_file_active_monitoring | 2020-04-12T08:53:20Z | endpoint | false |

### illusionblack-get-event-by-id

***
Gets a single event by the event ID.

##### Base Command

`illusionblack-get-event-by-id`

##### Input

| **Argument Name** | **Description** | **Required** |
| --- | --- | --- |
| id | IllusionBLACK Event ID. | Required |

##### Context Output

| **Path** | **Type** | **Description** |
| --- | --- | --- |
| IllusionBlack.Event | Unknown | IllusionBLACK Single Event. |
| IllusionBlack.Event.attacker.id | Unknown | IllusionBLACK Event Attacker ID. |
| IllusionBlack.Event.decoy.id | Unknown | IllusionBLACK Event Decoy ID. |
| IllusionBlack.Event.id | Unknown | IllusionBLACK Event ID. |
| IllusionBlack.Event.severity | Unknown | IllusionBLACK Event Severity. |
| IllusionBlack.Event.type | Unknown | IllusionBLACK Event Attack Type. |

##### Command Example

```!illusionblack-get-event-by-id id="2020-02-25T09:49:15Z-0950f80f-7571-4382-b4b8-5e04c160c4c0"```

##### Context Example

{
“IllusionBlack”: {
“Event”: {
“attacker.id”: “NT AUTHORITY\SYSTEM”,
“attacker.name”: “NT AUTHORITY\SYSTEM”,
“attacker.score”: 250,
“attacker.threat_parse_ids”: [
“filetheft_unattend”,
“lm_file_active_monitoring”,
“lm_file_open”
],
“decoy.appliance.id”: “cmc”,
“decoy.appliance.name”: “CMC”,
“decoy.client.id”: “experience”,
“decoy.client.name”: “experience”,
“decoy.group”: “Endpoint”,
“decoy.id”: “endpoint:admin-pc-001”,
“decoy.name”: “admin-pc-001”,
“decoy.type”: “endpoint”,
“file.name”: “C:\Users\admin\Desktop\passwords\Passwords.xlsx”,
“file.operation”: “67”,
“file.operation_string”: “Read”,
“file.process.command_line”: “”,
“file.process.domain_name”: “NT AUTHORITY”,
“file.process.exit_code”: -1,
“file.process.id”: “2824”,
“file.process.image_name”: “MsMpEng.exe”,
“file.process.user_name”: “SYSTEM”,
“file.process.user_sid”: “S-1-5-18”,
“file.thread_id”: “724”,
“id”: “2020-02-25T09:49:15Z-0950f80f-7571-4382-b4b8-5e04c160c4c0”,
“kill_chain_phase”: “Data Theft”,
“mitre_ids”: [
“T1005”
],
“record_type”: “event”,
“severity”: “high”,
“sub_type”: “file”,
“threat_parse_ids”: [
“lm_file_open”
],
“timestamp”: “2020-04-12T08:57:01Z”,
“type”: “endpoint”,
“whitelisted”: false
}
}
}
```

Human Readable Output

IllusionBLACK Single Event

attacker.id attacker.name attacker.score attacker.threat_parse_ids decoy.appliance.id decoy.appliance.name decoy.client.id decoy.client.name decoy.group decoy.id decoy.name decoy.type file.name file.operation file.operation_string file.process.command_line file.process.domain_name file.process.exit_code file.process.id file.process.image_name file.process.user_name file.process.user_sid file.thread_id id kill_chain_phase mitre_ids record_type severity sub_type threat_parse_ids timestamp type whitelisted
NT AUTHORITY\SYSTEM NT AUTHORITY\SYSTEM 250 filetheft_unattend,
lm_file_active_monitoring,
lm_file_open
cmc CMC experience experience Endpoint endpoint:admin-pc-001 admin-pc-001 endpoint C:\Users\admin\Desktop\passwords\Passwords.xlsx 67 Read   NT AUTHORITY -1 2824 MsMpEng.exe SYSTEM S-1-5-18 724 2020-02-25T09:49:15Z-0950f80f-7571-4382-b4b8-5e04c160c4c0 Data Theft T1005 event high file lm_file_open 2020-04-12T08:57:01Z endpoint false

Configuration parameters

  • url — Server URL (e.g. https://example.net) (required)
  • client_id — IllusionBLACK API Client Id (required)
  • token — IllusionBLACK External API Token (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • first_fetch — First fetch time for fetching incidents (2 days, 3 weeks, etc)
  • isFetch — Fetch incidents
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval

Commands (8)

  • illusionblack-get-ad-decoys

    Gets a list of Active Directory decoys.

  • illusionblack-get-event-by-id

    Gets a single event by the event ID.

  • illusionblack-get-events

    Gets events from IllusionBLACK.

  • illusionblack-get-network-decoys

    Gets a list of Network decoys.

  • illusionblack-get-ti-decoys

    Gets a list of Threat Intel decoys.

  • illusionblack-is-host-decoy

    Checks if a host or IP address is a network decoy.

  • illusionblack-is-subdomain-decoy

    Checks if a subdomain is a Threat Intel decoy.

  • illusionblack-is-user-decoy

    Checks if an Active Directory user is a decoy.

import json
from datetime import UTC, datetime
from typing import Any

import demistomock as demisto

# Disable insecure warnings
import urllib3
from CommonServerPython import *

urllib3.disable_warnings()


class Client(BaseClient):
    """
    Client will implement the service API, and should not contain any Demisto logic.
    Should only do requests and return data.
    """

    def __init__(self, base_url, verify, client_id, token, proxy):
        """
        Constructor which adds the authentication headers required by IllusionBLACK external API
        Args:
            base_url: IllusionBLACK URL. For example: https://experience.illusionblack.com
            verify: Allow insecure SSL
            client_id:
            token:
            proxy:
        """
        headers = {"x-client-id": client_id, "x-client-auth": token}
        super().__init__(base_url=base_url, verify=verify, proxy=proxy, headers=headers)

    def ping(self):
        """
        Initiates a HTTP Request to IllusionBLACK test endpoint /ping
        """
        response = self._http_request(method="GET", url_suffix="/ping", ok_codes=(200,))
        return response.get("message", "error")

    def get_ad_decoys(self):
        """
        Gets a list of Active Directory (AD) user decoys from IllusionBLACK
        Returns: A tuple containing the response in human readable, context data and raw response formats
        """
        response = self._http_request(method="GET", url_suffix="/decoy/users", ok_codes=(200,))
        users = response["items"]
        return (
            tableToMarkdown(
                "IllusionBLACK AD Decoys", users, headerTransform=lambda s: " ".join([w.capitalize() for w in s.split("_")])
            ),
            {"IllusionBlack.AdDecoy(val.user_name==obj.user_name)": users},
            users,
        )

    def get_network_decoys(self):
        """
        Gets a list of network decoys from IllusionBLACK and enriches the data with the list of services enabled.
        Returns:A tuple containing the response in human readable, context data and raw response formats
        """
        response = self._http_request(method="GET", url_suffix="/decoy/hosts", ok_codes=(200,))
        hosts: list = response["items"]
        for h in hosts:
            h["services"] = ", ".join(h["services"])
        return (
            tableToMarkdown("IllusionBLACK Network Decoys", hosts, headerTransform=lambda s: s.capitalize()),
            {"IllusionBlack.NetworkDecoy(val.name==obj.name)": hosts},
            hosts,
        )

    def get_ti_decoys(self):
        """
        Gets a list of Threat Intelligence decoys from IllusionBLACK.
        Returns: A tuple containing the response in human readable, context data and raw response formats
        """
        response = self._http_request(method="GET", url_suffix="/decoy/recon", ok_codes=(200,))
        recon_decoys = response["items"]
        return (
            tableToMarkdown(
                "IllusionBLACK TI Decoys",
                recon_decoys,
                headerTransform=lambda s: " ".join([w.capitalize() for w in s.split("_")]),
            ),
            {"IllusionBlack.TIDecoy(val.name==obj.name)": recon_decoys},
            recon_decoys,
        )

    def is_host_decoy(self, host):
        """
        Checks if the host is an IllusionBLACK network decoy
        Args:
            host: The name of the entity For example: SMB-12
        Returns: True if host is a decoy else False
        """
        response = self._http_request(method="GET", url_suffix="/decoy/hosts", ok_codes=(200,))
        hosts: list = response["items"]
        for decoy_host in hosts:
            if host == decoy_host["name"]:
                return "True", {"IllusionBlack.IsHostDecoy": {"Host": host, "Value": True}}
        return "False", {"IllusionBlack.IsHostDecoy": {"Host": host, "Value": False}}

    def is_user_decoy(self, user):
        """
        Checks if the user is an IllusionBLACK AD user decoy
        Args:
            user: The user name of the AD user to check
        Returns: True if user is a decoy else False
        """
        response = self._http_request(method="GET", url_suffix="/decoy/users", ok_codes=(200,))
        users: list = response["items"]
        for decoy_user in users:
            if user.lower() == decoy_user["user_name"]:
                return "True", {"IllusionBlack.IsUserDecoy": {"User": user, "Value": True}}
            return "False", {"IllusionBlack.IsUserDecoy": {"User": user, "Value": False}}
        return None

    def is_subdomain_decoy(self, subdomain):
        """
        Checks if the subdomain is an IllusionBLACK TI decoy
        Args:
            subdomain: The subdomain to check. For example: experience.illusionblack.com
        Returns: True if subdomain is a decoy else False
        """
        response = self._http_request(method="GET", url_suffix="/decoy/recon", ok_codes=(200,))
        ti_decoys: list = response["items"]
        for ti_decoy in ti_decoys:
            if subdomain == ti_decoy["name"]:
                return "True", {"IllusionBlack.IsSubdomainDecoy": {"Subdomain": subdomain, "Value": True}}
            return "False", {"IllusionBlack.IsSubdomainDecoy": {"Subdomain": subdomain, "Value": False}}
        return None

    def get_events(self, limit=None, query=None, from_time=None, to_time=None):
        """
        Gets Events and corresponding Threat Parse data from IllusionBLACK based on the filtering parameters.
        Args:
            limit: Number of events to return per API call. Defaults to 10.
            query: IllusionBLACK orchestrate engine query string. Refer to IllusionBLACK doc for reference.
            from_time: ISO-8601 formatted datetime string of the starting time in the filter
            to_time: ISO-8601 formatted datetime string of the ending time in the filter
        Returns: A tuple with raw events and threat parse data corresponding to the events
        """
        raw_events, raw_threat_parse, offset = [], {}, 0  # type: ignore
        while True:
            response = self._http_request(
                method="GET",
                url_suffix="/events",
                params={"limit": limit, "expfilter": query, "from": from_time, "to": to_time, "offset": offset},
                ok_codes=(200,),
            )
            meta: dict = response["meta"]
            amount = meta["paging"]["amount"]

            raw_events.extend(response["events"])
            for tp in response.get("threat_parse", {}):
                tp_id = tp["id"]
                if tp_id not in raw_threat_parse:
                    tp.pop("id", None)
                    raw_threat_parse[tp_id] = tp

            offset += 1000
            if amount < 1000:
                break
        return raw_events, raw_threat_parse


def test_module(client):
    """
    Returning "ok" indicates that the integration works like it is supposed to. Connection to the service is successful.
    Args:
        client: IllusionBLACK client
    Returns:
        "ok" if test passed, anything else will fail the test.
    """
    try:
        message = client.ping()
        if message == "pong":
            return "ok"
    except DemistoException as e:
        if e.args[0] == "Error in API call [401] - Unauthorized":
            return_error("Failed to connect to IllusionBLACK. External API Token or Client Id might be invalid.")
        else:
            raise e


def convert_to_demisto_severity(ib_severity="medium", tp_score_based=False, score=0):
    """
    Converts the IllusionBLACK Threat Parse score for an attacker to demisto incident severity
    Args:
        ib_severity: IllusionBLACK severity. Some events do not have threat parse score.
        tp_score_based: If score is based on Threat Parse cumulative score
        score: The cumulative Threat Parse score
    Returns: The demisto incident severity ranging from 1 to 4
    """
    severity = 1
    if tp_score_based:
        severity = score // 25
        severity = max(severity, 1)
        severity = min(severity, 4)
    else:
        if ib_severity == "low":
            severity = 2
        elif ib_severity == "medium":
            severity = 3
        elif ib_severity == "high":
            severity = 4
    return severity


def process_events(events, threat_parse):
    """
    Converts raw events and raw threat parse to demisto incidents based on common parameters.
    Args:
        events: Raw events from IllusionBLACK
        threat_parse: Raw Threat Parse from IllusionBLACK
    Returns: A list of raw incidents with data pertinent to demisto incident format.
    """
    raw_incident_data: dict[str, Any] = {}

    for event in events:
        attacker_id = event.get("attacker.id", "")
        decoy_id = event.get("decoy.id", "")
        attack_type = event.get("type", "")
        ib_severity = event.get("severity")

        incident_id = "-".join(filter(None, [attacker_id, decoy_id, attack_type])).rstrip("-")
        title = f"{attack_type} activity by {attacker_id} on {decoy_id} decoy"
        tps = event.get("threat_parse_ids", [])

        score, is_tp = 0, False
        for tp in tps:
            is_tp = True
            score += threat_parse[tp]["score"]
        severity = convert_to_demisto_severity(ib_severity=ib_severity, tp_score_based=is_tp, score=score)
        raw_incident = raw_incident_data.setdefault(
            incident_id,
            {
                "events": [],
                "threat_parse_ids": [],
                "title": "",
                "severity": 1,
                "attack_type": "illusionblack_event",
                "attacker_id": "",
                "decoy_id": "",
                "source": "IllusionBLACK",
            },
        )
        raw_incident["events"].append(event["id"])
        raw_incident["threat_parse_ids"].extend(tps)
        raw_incident["threat_parse_ids"] = list(set(raw_incident["threat_parse_ids"]))
        raw_incident["title"] = title
        raw_incident["severity"] = severity
        raw_incident["attack_type"] = attack_type
        raw_incident["attacker_id"] = attacker_id
        raw_incident["decoy_id"] = decoy_id

    return raw_incident_data


def create_incident(raw_incident):
    """
    Creates a demisto incident from a raw incident.
    Args:
        raw_incident: The data in the raw incident processed from raw events and Threat Parse from IllusionBLACK
    Returns: Demisto incident dict
    """
    demisto.info(f"Severity is {raw_incident['severity']}")
    return {"name": raw_incident["title"], "severity": raw_incident["severity"], "rawJSON": json.dumps(raw_incident)}


def fetch_incidents(first_fetch, client):
    """
    Automated fetching of incidents from IllusionBLACK. For first run 2 days is the fixed duration for events.
    Args:
        first_fetch: For first fetch the timespan to consider to fetch incidents. Example: 2 days, 5 weeks etc
        client: IllusionBLACK client
    Returns: Demisto Incidents
    """
    now = datetime.now(tz=UTC)
    demisto.info(f"IllusionBLACK: Fetching incidents at {now}")
    demisto_last_run = demisto.getLastRun()
    if "last_run" in demisto_last_run:
        last_run = datetime.fromisoformat(demisto_last_run["last_run"])
    else:
        last_run, _ = parse_date_range(first_fetch)
        last_run = last_run.replace(tzinfo=UTC)
    if now - last_run < timedelta(minutes=5):
        return []
    from_time = last_run.replace(microsecond=0).isoformat()
    to_time = now.replace(microsecond=0).isoformat()
    demisto.debug(f"IllusionBLACK: Getting raw events from {from_time} to {to_time}")
    events, all_threat_parse = client.get_events(limit=1000, from_time=from_time, to_time=to_time)
    raw_incidents = process_events(events, all_threat_parse)
    incidents = []

    for _incident_id, raw_incident in raw_incidents.items():
        incidents.append(create_incident(raw_incident))
    demisto.setLastRun({"last_run": to_time})
    return incidents


def main():
    client_id = demisto.params().get("client_id")
    token = demisto.params().get("token")
    base_url = urljoin(demisto.params()["url"], "/apiv1")
    verify_certificate = not demisto.params().get("insecure", False)
    proxy = demisto.params().get("proxy", False)

    LOG(f"Command being called is {demisto.command()}")
    try:
        client = Client(base_url=base_url, verify=verify_certificate, client_id=client_id, token=token, proxy=proxy)

        if demisto.command() == "test-module":
            # This is the call made when pressing the integration Test button.
            result = test_module(client)
            demisto.results(result)
        elif demisto.command() == "illusionblack-get-ad-decoys":
            return_outputs(*client.get_ad_decoys())
        elif demisto.command() == "illusionblack-get-network-decoys":
            return_outputs(*client.get_network_decoys())
        elif demisto.command() == "illusionblack-get-ti-decoys":
            return_outputs(*client.get_ti_decoys())
        elif demisto.command() == "illusionblack-is-host-decoy":
            return_outputs(*client.is_host_decoy(demisto.args()["host"]))
        elif demisto.command() == "illusionblack-is-user-decoy":
            return_outputs(*client.is_user_decoy(demisto.args()["user"]))
        elif demisto.command() == "illusionblack-is-subdomain-decoy":
            return_outputs(*client.is_subdomain_decoy(demisto.args()["subdomain"]))
        elif demisto.command() == "illusionblack-get-events":
            args = demisto.args()
            events, _ = client.get_events(args.get("limit"), args.get("query"), args.get("from"), args.get("to"))
            return_outputs(
                tableToMarkdown("IllusionBLACK Events", events), {"IllusionBlack.Event(val.id==obj.id)": events}, events
            )
        elif demisto.command() == "illusionblack-get-event-by-id":
            events, _ = client.get_events(query=f"id == \"{demisto.args()['id']}\"")
            if len(events) != 1:
                return_error("Invalid event ID")
            event = events[0]
            return_outputs(
                tableToMarkdown("IllusionBLACK Single Event", event), {"IllusionBlack.Event(val.id==obj.id)": event}, event
            )
        elif demisto.command() == "fetch-incidents":
            demisto.incidents(fetch_incidents(demisto.params().get("first_fetch", "2 days"), client=client))

    # Log exceptions
    except Exception as e:
        return_error(f"Failed to execute {demisto.command()} command. Error: {e!s}")


if __name__ in ("__main__", "builtins"):
    main()