Thinkst Canary

By presenting itself as an apparently benign and legitimate service(s), the Canary draws the attention of unwanted activity. When someone trips one of the Canary's triggers, an alert is sent to notify the responsible parties so that action can be taken before valubale systems in your network are compromised.

Deception & Breach Simulation · Thinkst Canary

Details

IDThinkst Canary
ProviderThinkst Canary
CategoryDeception & Breach Simulation
From Version5.0.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

By presenting itself as an apparently benign and legitimate service(s), the canary draws the attention of unwanted activity. When someone trips one of the Canary’s triggers, an alert is sent to notify the responsible parties so that action can be taken before valuable systems in your network are compromised.

Use Cases

  • Fetch alerts from CanaryTools as incidents in Cortex XSOAR and acknowledge them.
  • Get information about all registered Canaries.
  • Get information about Canary Tokens.
  • Add IP addresses to allow list.

Configure Thinkst Canary on Cortex XSOAR

For information about how to get your Authentication Token, see the Canary documentation.

  1. Navigate to Settings > Integrations > Servers & Services.
  2. Search for Thinkst Canary.
  3. Click Add instance to create and configure a new integration instance.
    • Name: A textual name for the integration instance.
    • Fetch incidents: Should the instance fetch incidents or not
    • Incident type: Choose a type for Canary Tools incidents
    • Canary Server URL
    • API auth token
    • Trust any certificate (not secure)
    • Use system proxy settings
  4. Click Test to validate the URLs, token, and connection.

Commands

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook. After you successfully execute a command, a DBot message appears in the War Room with the command details.

1. List all registered Canaries


Lists all registered Canaries.

Base Command

canarytools-list-canaries

Input

There are no input arguments for this command.

Context Output
Path Type Description
CanaryTools.Device.ID string Device ID
CanaryTools.Device.Name string Device name
CanaryTools.Device.Description string Device description
CanaryTools.Device.Address string Device IP address
CanaryTools.Device.Status boolean Device status - Live (True/False)
CanaryTools.Device.Location string Device location
CanaryTools.Device.Version string Device version
CanaryTools.Device.LastSeen date Device last seen time
CanaryTools.Device.LastUpdated date Device last updated time

 

Command Example
!canarytools-list-canaries
Context Example
{  
   "CanaryTools":{  
      "Device":{  
         "Status":true,
         "Description":"Lab",
         "LastUpdated":"2019-02-24 16:46:01 UTC+0000",
         "Version":"2.1.2",
         "Location":"Lab",
         "Address":"192.168.1.43",
         "Name":"VirtualCanary-1",
         "ID":"0002f07cb32d45b1",
         "LastSeen":"Sun Feb 24 2019 16:46:01 GMT+0000 (UTC)"
      }
   }
}
Human Readable Output

image

2. List all Canary tokens


Lists all Canary tokens.

Base Command

canarytools-list-tokens

Input

There are no input arguments for this command.

Context Output
Path Type Description
CanaryTools.Tokens.CanaryToken string Canary Token
CanaryTools.Tokens.CreatedTime date Token Created time
CanaryTools.Tokens.Status boolean Token status - Enabled (True / False)
CanaryTools.Tokens.Kind string Token Kind
CanaryTools.Tokens.Triggered number Token triggered count
CanaryTools.Tokens.DocName string Token document name (If the token is from type document)
CanaryTools.Tokens.TokenURL string Token URL (How the token is presented)

 

Command Example
!canarytools-list-tokens
Context Example
{  
   "CanaryTools":{  
      "Tokens":[  
         {  
            "Status":true,
            "Kind":"doc-msword",
            "Triggered":16,
            "TokenURL":"http://e71ed0532685.o3n.io/cdn/ows8bhzvpx9ro3nz32r5vb3pa/logo.gif",
            "DocName":"token.docx",
            "CanaryToken":"ows8bhzvpx9ro3nz32t5vb3pa",
            "CreatedTime":"2019-01-23 15:51:31 (UTC)"
         }
      ]
   }
}
Human Readable Output

image

3. Check if an IP address is on allow list


Checks whether a given IP address and port are on allow list.

Base Command

canarytools-check-whitelist

Input
Argument Name Description Required
ip IP address Required
port Destination port Optional

 

Context Output
Path Type Description
CanaryTools.IP.Address string IP address
CanaryTools.IP.Port string Destination port for the IP allow list
CanaryTools.IP.Whitelisted boolean Is the IP address on allow list (true/false)

 

Command Example
!canarytools-check-whitelist ip=1.1.1.1 port=45
Context Example
{  
   "CanaryTools":{  
      "IP":{  
         "Whitelisted":"True",
         "Port":"45",
         "Address":"1.1.1.1"
      }
   }
}
Human Readable Output

image

4. Add an IP address to the allow list


Adds an IP address to the allow list in Canary.

Base Command

canarytools-whitelist-ip

Input
Argument Name Description Required
ip IP address to allow list Required
port Destination port to allow list Optional

 

Context Output
Path Type Description
CanaryTools.IP.Address string IP address
CanaryTools.IP.Port string Destination port for the IP allow list
CanaryTools.IP.Whitelisted boolean Is the IP address on allow list (True/False)

 

Command Example
!canarytools-whitelist-ip ip=2.2.2.2 port=21
Context Example
{  
   "CanaryTools":{  
      "IP":{  
         "Whitelisted":"True",
         "Port":"21",
         "Address":"2.2.2.2"
      }
   }
}
Human Readable Output

image

5. Edit an alert status


Edits the status for an alert in Canary Tools.

Base Command

canarytools-edit-alert-status

Input
Argument Name Description Required
alertID Alert ID (e.g., incident:canarytoken:d6fe0ae4dfd36cc3cc6d9d4f::1548593719) Required
status Required status for the alert (Acknowledge, Unacknowledge) Required

 

Context Output
Path Type Description
CanaryTools.Alert.ID string Alert ID
CanaryTools.Alert.Status string Alert status

 

Command Example
!canarytools-edit-alert-status alertID=incident:canarytoken:d6fe0ae4dfd36cc3cc6d9d4f::1548593719 status=Acknowledge
Context Example
{  
   "CanaryTools":{  
      "Alert":{  
         "Status":"Acknowledge",
         "ID":"incident:canarytoken:d6fe0ae4dfd36cc3cc6d9d4f::1548593719"
      }
   }
}
Human Readable Output

image

6. Get a Canary Token file


Fetches a Canary Token file from the Canary Tools server.

Base Command

canarytools-get-token

Input
Argument Name Description Required
token Canary Token Required

 

Context Output
Path Type Description
CanaryTools.Tokens.CanaryToken string Canary Token
File.Size string File Size
File.SHA1 string File SHA-1
File.SHA256 string File SHA-256
File.Name string File name
File.SSDeep string File SSDeep
File.EntryID string File EntryID
File.Info string File info
File.Type string File type
File.MD5 string File MD5
File.Extension string File extension

 

Command Example
!canarytools-get-token token=wpdkr30rx9naixdsijxdbd5ab
Context Example
{  
   "CanaryTools":{  
      "Tokens":"wpdkr30rx9naixdsijxwbd5ab"
   },
   "File":{  
      "Info":"image/jpeg",
      "SHA1":"9719f38b13a9ab79469987a1ba495939c8577c54",
      "Name":"affinity-photo-161120170952.jpg",
      "Extension":"jpg",
      "Size":240804,
      "EntryID":"163@f7519b11-9105-4dd4-8036-fe2790f28ca6",
      "SSDeep":"6144:IIgHoF7QKgKBiWlCdA1xQe5S55nXxlQIqwUAlP:InIFHriAGALT2Xd5P",
      "SHA256":"cacf61ee474920578bd0e6f000b65fa40b313b912a8611b930414a7b9ae1d49b",
      "Type":"ASCII text, with very long lines, with no line terminators\n",
      "MD5":"c499c7f42eae921c974a4e71cb4a4cc5"
   }
}
Human Readable Output

image

Configuration parameters

  • server — Canary Server URL (e.g., https://***.canary.tools) (required)
  • auth_token — API Authentication Token
  • authentication_token
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • isFetch — Fetch incidents
  • fetchDelta — First fetch time (<number> <time unit>, e.g., 12 hours, 7 days, 3 months, 1 year)
  • incidentType — Incident type
  • incidentFetchInterval — Incidents Fetch Interval

Commands (6)

  • canarytools-check-whitelist

    Checks whether a given IP address and port are on allow list.

  • canarytools-edit-alert-status

    Edits the status for an alert in Canary Tools.

  • canarytools-get-token

    Fetches a Canary Token file from the Canary Tools server.

  • canarytools-list-canaries

    Lists all registered Canaries.

  • canarytools-list-tokens

    Lists all Canary tokens.

  • canarytools-whitelist-ip

    Adds an IP address to the allow list in Canary.

import demistomock as demisto
import pytest

MOCK_PARAMS = {
    "access-key": "fake_access_key",
    "secret-key": "fake_access_key",
    "server": "http://123-fake-api.com/",
    "unsecure": True,
    "proxy": True,
    "authentication_token": {"password": 1},
}


def test_fetch_incidents(mocker, requests_mock):
    """
    Given: An existing last run time.
    When:  Running a fetch incidents command normally (not a first run).
    Then:  The last run time object should increment by 1 second.
           2020-01-07-04:58:18 -> 2020-01-07-04:58:19
    """
    mocker.patch.object(demisto, "params", return_value=MOCK_PARAMS)
    mocker.patch.object(demisto, "getLastRun", return_value={"time": "2020-07-01-04:58:18"})
    mocker.patch.object(demisto, "setLastRun")
    requests_mock.get(
        "http://123-fake-api.com/api/v1/incidents/unacknowledged?newer_than=2020-07-01-04%3A58%3A18",
        json={"incidents": [{"description": {"created": 1593579498}}]},
    )
    from ThinkstCanary import fetch_incidents_command

    fetch_incidents_command()
    assert demisto.setLastRun.call_args[0][0]["time"] == "2020-07-01-04:58:19"


def test_check_whitelist_command_not_whitelisted(mocker):
    """
    Given: An IP to check
    When:  Running check_whitelist_command.
    Then:  The IP should not be ignored (not in the whitelist).
    """
    ip_to_check = "1.2.3.4"
    mocker.patch.object(demisto, "results")
    mocker.patch.object(demisto, "params", return_value=MOCK_PARAMS)
    mocker.patch.object(demisto, "args", return_value={"ip": ip_to_check})

    import ThinkstCanary

    mocker.patch.object(ThinkstCanary, "check_whitelist", return_value={"is_ip_ignored": False, "is_whitelist_enabled": True})
    ThinkstCanary.check_whitelist_command()
    assert demisto.results.call_args_list[0][0][0].get("HumanReadable") == "The IP address 1.2.3.4:Any is not Whitelisted"


def test_check_whitelist_commands_whitelisted(mocker):
    """
    Given: An already whitelisted IP to check
    When:  Inserting IP to whitelist (whitelist_ip_command) and checking if it is whitelisted (check_whitelist_command).
    Then:  The IP should be ignored (in the whitelist), and an appropriate message to the user should be prompted.
    """
    ip_to_whitelist = "1.2.3.4"
    mocker.patch.object(demisto, "results")
    mocker.patch.object(demisto, "params", return_value=MOCK_PARAMS)
    mocker.patch.object(demisto, "args", return_value={"ip": ip_to_whitelist})

    import ThinkstCanary

    mocker.patch.object(ThinkstCanary, "whitelist_ip", return_value={"message": "Whitelist added", "result": "success"})
    mocker.patch.object(ThinkstCanary, "check_whitelist", return_value={"is_ip_ignored": True, "is_whitelist_enabled": True})
    ThinkstCanary.whitelist_ip_command()
    ThinkstCanary.check_whitelist_command()
    assert demisto.results.call_args_list[1][0][0].get("HumanReadable") == "The IP address 1.2.3.4:Any is Whitelisted"


@pytest.mark.parametrize(
    "arg_status, return_status, expected_result",
    [
        ("Acknowledge", "acknowledged", "The Alert alert_id was acknowledged"),
        ("Unacknowledge", "unacknowledged", "The Alert alert_id was unacknowledged"),
    ],
)
def test_alert_status_command(mocker, arg_status, return_status, expected_result):
    """
    Given: An alert id to check.
    When:  Running alert_status_command.
    Then:  ensure the expected result returned.
    """

    mocker.patch.object(demisto, "results")
    mocker.patch.object(demisto, "params", return_value=MOCK_PARAMS)
    mocker.patch.object(demisto, "args", return_value={"alert_id": "alert_id", "status": arg_status})

    import ThinkstCanary

    mocker.patch.object(ThinkstCanary, "http_request", return_value={"action": return_status})
    ThinkstCanary.alert_status_command()
    assert expected_result in demisto.results.call_args_list[0][0][0].get("HumanReadable")


def test_list_canaries_command(mocker):
    """
    Given: demisto params.
    When:  Running list_canaries_command.
    Then:  ensure the expected result returned.
    """

    mocker.patch.object(demisto, "results")
    mocker.patch.object(demisto, "params", return_value=MOCK_PARAMS)

    import ThinkstCanary

    mocker.patch.object(
        ThinkstCanary,
        "http_request",
        return_value={
            "devices": [
                {
                    "description": "Description",
                    "id": "ID",
                    "ip_address": "Address",
                    "last_seen": "LastSeen",
                    "live": "Status",
                    "location": "Location",
                    "name": "Name",
                    "updated_std": "LastUpdated",
                    "version": "Version",
                }
            ]
        },
    )
    ThinkstCanary.list_canaries_command()
    assert "Canary Devices" in demisto.results.call_args_list[0][0][0].get("HumanReadable")


def test_list_tokens_command(mocker):
    """
    Given: demisto params.
    When:  Running list_tokens_command.
    Then:  ensure the expected result returned.
    """

    mocker.patch.object(demisto, "results")
    mocker.patch.object(demisto, "params", return_value=MOCK_PARAMS)

    import ThinkstCanary

    mocker.patch.object(
        ThinkstCanary,
        "http_request",
        return_value={
            "tokens": [
                {
                    "canarytoken": "CanaryToken",
                    "created_printable": "CreatedTime",
                    "enabled": "Enabled",
                    "kind": "Kind",
                    "triggered_count": "Triggered",
                    "doc_name": "DocName",
                    "url": "TokenURL",
                }
            ]
        },
    )
    ThinkstCanary.list_tokens_command()
    assert "Canary Tools Tokens" in demisto.results.call_args_list[0][0][0].get("HumanReadable")