TeamCymru

Team Cymru provides various service options dedicated to mapping IP numbers to BGP prefixes and ASNs. Each of the services is based on the same BGP feeds from 50+ BGP peers and is updated at 4-hour intervals.

Data Enrichment & Threat Intelligence · Team Cymru

Details

IDTeamCymru
ProviderAudax Private Equity
CategoryData Enrichment & Threat Intelligence
From Version6.5.0
Docker Imagedemisto/vendors-sdk:1.0.0.10120494
Supported ModulesAgentix XSIAM

README

Team Cymru provides various service options dedicated to mapping IP numbers to BGP prefixes and ASNs. Each of the services is based on the same BGP feeds from 50+ BGP peers and is updated at 4-hour intervals.
This integration was integrated and tested with version 1.0 of TeamCymru

Configure Team Cymru in Cortex

Parameter Required  
Use system proxy settings False  
Proxy URL Supports socks4/socks5/http connect proxies (e.g., socks5h://host:1080). False
Source Reliability Reliability of the source providing the intelligence data. 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


Checks the reputation of an IP address.

Base Command

ip

Input

Argument Name Description Required
ip An IPv4 address to query, e.g., 1.1.1.1. Required

Context Output

Path Type Description
IP.Address String IP address.
IP.ASN String The autonomous system name for the IP address, for example: “AS8948”.
IP.ASOwner String The autonomous system owner of the IP address.
IP.Geo.Country String The country in which the IP address is located.
IP.Registrar.Abuse.Network String The network of the contact for reporting abuse.
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.
DBotScore.Reliability String Reliability of the source providing the intelligence data.
TeamCymru.IP.Address String The IP address.
TeamCymru.IP.ASN String The IP ASN.
TeamCymru.IP.ASOwner String The IP AS owner.
TeamCymru.IP.Geo.Country String The IP country.
TeamCymru.IP.Registrar.Abuse.Network String The IP range relevant for abuse inquiries provided for the IP.

Command example

!ip ip=1.1.1.1

Context Example

{
    "DBotScore": {
        "Indicator": "1.1.1.1",
        "Score": 0,
        "Type": "ip",
        "Vendor": "TeamCymru"
    },
    "IP": {
        "ASN": "13335",
        "ASOwner": "CLOUDFLARENET, US",
        "Address": "1.1.1.1",
        "Geo": {
            "Country": "AU"
        },
        "Registrar": {
            "Abuse": {
                "Network": "1.1.1.0/24"
            }
        }
    },
    "TeamCymru": {
        "IP": {
            "ASN": "13335",
            "ASOwner": "CLOUDFLARENET, US",
            "Address": "1.1.1.1",
            "Geo": {
                "Country": "AU"
            },
            "Registrar": {
                "Abuse": {
                    "Network": "1.1.1.0/24"
                }
            }
        }
    }
}

Human Readable Output

Team Cymru results for 1.1.1.1

IP ASN Organization Country Range
1.1.1.1 13335 CLOUDFLARENET, US AU 1.1.1.0/24

cymru-bulk-whois


Checks the reputation of a CSV list of IPv4 addresses within a file.
Note: Results for queries exceeding 10,000 IPs may take more than a minute given a moderately sized Internet link.

Base Command

cymru-bulk-whois

Input

Argument Name Description Required
entry_id The file’s War Room entry ID. Required
delimiter Delimiter by which the content of the file is separated.
Eg: “ , “ , “ : “, “ ; “. Default is ,.
Optional

Context Output

Path Type Description
IP.Address String IP address.
IP.ASN String The autonomous system name for the IP address, for example: “AS8948”.
IP.ASOwner String The autonomous system owner of the IP address.
IP.Geo.Country String The country in which the IP address is located.
IP.Registrar.Abuse.Network String The network of the contact for reporting abuse.
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.
DBotScore.Reliability String Reliability of the source providing the intelligence data.
TeamCymru.IP.Address String The IP address.
TeamCymru.IP.ASN String The IP ASN.
TeamCymru.IP.ASOwner String The IP AS owner.
TeamCymru.IP.Geo.Country String The IP country.
TeamCymru.IP.Registrar.Abuse.Network String The IP range relevant for abuse inquiries provided for the IP.

Command example

!cymru-bulk-whois entry_id=${File.EntryID}

Troubleshooting

  • In case of a problem with the proxy configuration, validate that the given proxy is working with the Whois content pack.

Configuration parameters

  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • proxy_url — Proxy URL
  • integration_reliability — Source Reliability

Commands (2)

  • cymru-bulk-whois

    Checks the reputation of a CSV list of IPv4 addresses within a file. Note: Results for queries exceeding 10,000 IPs may take more than a minute given a moderately sized Internet link.

  • ip

    Checks the reputation of an IP address.

"""TeamCymru for Cortex XSOAR - Unit Tests file"""

import json

import demistomock as demisto
import pytest
import TeamCymru
from TeamCymru import CymruClient

"""GLOBALS"""

client = CymruClient()
MOCK_ENTRY_ID = "@123"
MOCK_BULK_LIST = (
    '1.1.1.1, b, 2.2.2, n, 3.3.3.3,2001:0db8:85a3:0000:0000:8a2e:0370:7334,a,"8.8.8.8",4.4.4.4, 1.1.2.2, 6,6.6.6.6, 1.1.2.2'
)
MOCK_IPS_LIST = [
    "1.1.1.1",
    "b",
    "2.2.2",
    "n",
    "3.3.3.3",
    "2001:0db8:85a3:0000:0000:8a2e:0370:7334",
    "a",
    "8.8.8.8",
    "4.4.4.4",
    "1.1.2.2",
    "6",
    "6.6.6.6",
    "1.1.2.2",
]
MOCK_INVALID_IPS = ["b", "2.2.2", "n", "2001:0db8:85a3:0000:0000:8a2e:0370:7334", "a", "6"]
MOCK_VALID_IPS = ["1.1.1.1", "3.3.3.3", "8.8.8.8", "4.4.4.4", "1.1.2.2", "6.6.6.6", "1.1.2.2"]
MOCK_FILE_RES = {
    "id": "test_id",
    "path": "test_data/test_ips_file.csv",
    "name": "test_ips_file.csv",
}
DEFAULT_RELIABILITY = "B - Usually reliable"


def load_test_data(json_path):
    with open(json_path) as f:
        return json.load(f)


@pytest.mark.parametrize(
    "args, expected_error",
    [({"ip": None}, "IP not specified"), ({"ip": "172.16.0"}, "The given IP address: 172.16.0 is not valid")],
)
def test_ip_command_invalid_ip(args, expected_error):
    """
    Given:
        - Invalid IP
    When:
        - Running the IP command
    Then:
        - Raise ValueError with the expected value
    """
    from TeamCymru import ip_command

    with pytest.raises(ValueError, match=expected_error):
        ip_command(client, args, reliability=DEFAULT_RELIABILITY)


def test_ip_command(mocker):
    """
    Given:
        - Command arguments: ip ip = 8.8.8.8 (valid IPv4)
    When:
        - Running the IP command
    Then:
        - Validate the output compared to the mock output
    """
    from TeamCymru import ip_command

    mock_arg = {"ip": "8.8.8.8"}
    test_data = load_test_data("test_data/test_ip_command.json")
    return_value = test_data.get("ip_command_response")
    mocker.patch.object(CymruClient, "lookup", return_value=return_value)
    response = ip_command(client, mock_arg, reliability=DEFAULT_RELIABILITY)
    mock_outputs = test_data.get("mock_output")
    mock_readable_outputs = test_data.get("mock_readable")
    assert mock_outputs == response[0].outputs
    assert mock_readable_outputs == response[0].readable_output
    assert response[0].indicator
    assert response[0].indicator.dbot_score.reliability == DEFAULT_RELIABILITY


@pytest.mark.parametrize(
    "reliability",
    [
        "A+ - 3rd party enrichment",
        "A - Completely reliable",
        "B - Usually reliable",
        "C - Fairly reliable",
        "D - Not usually reliable",
        "E - Unreliable",
        "F - Reliability cannot be judged",
    ],
)
def test_ip_different_reliability(mocker, reliability):
    """
    Given:
        - Different source reliability param
    When:
        - Running ip command
    Then:
        - Ensure the reliability specified is returned.
    """
    from TeamCymru import ip_command

    mock_arg = {"ip": "8.8.8.8"}
    test_data = load_test_data("test_data/test_ip_command.json")
    return_value = test_data.get("ip_command_response")
    mocker.patch.object(CymruClient, "lookup", return_value=return_value)
    response = ip_command(client, mock_arg, reliability=reliability)
    assert response[0].indicator.dbot_score.reliability == reliability


def test_ip_command_with_list(mocker):
    """
    Given:
        - List of IP addresses
    When:
        - Running the IP command
    Then:
        - Verify support list of IPs
        - Verify the result is as expected and returns the expected warning
    """
    from TeamCymru import ip_command

    mock_arg = {"ip": MOCK_BULK_LIST}
    test_data = load_test_data("test_data/test_cymru_bulk_whois_command.json")
    return_value = test_data.get("cymru_bulk_whois_command_response")
    mocker.patch.object(CymruClient, "lookupmany_dict", return_value=return_value)
    warning = mocker.patch.object(TeamCymru, "return_warning")
    mock_outputs = test_data.get("mock_output")
    mock_readable_outputs = test_data.get("mock_readable")

    response = ip_command(client, mock_arg, reliability=DEFAULT_RELIABILITY)
    assert warning.call_args[0][0] == test_data.get("warning_message")
    assert warning.call_args[1] == {"exit": False}
    for i, res in enumerate(response):
        assert mock_outputs[i] == res.outputs
        assert res.indicator
        assert mock_readable_outputs[i] == res.readable_output


def test_cymru_bulk_whois_command_with_file(mocker):
    """
    Given:
        - File of IP addresses
    When:
        - Running the cymru_bulk_whois command
    Then:
        - Verify support file of IPs
        - Verify the result is as expected
    """
    from TeamCymru import cymru_bulk_whois_command

    mock_arg = {"entry_id": MOCK_ENTRY_ID}
    test_data = load_test_data("test_data/test_cymru_bulk_whois_command.json")
    return_value = test_data.get("cymru_bulk_whois_command_response")
    mocker.patch.object(CymruClient, "lookupmany_dict", return_value=return_value)

    mocker.patch.object(demisto, "getFilePath", return_value=MOCK_FILE_RES)
    mock_outputs = test_data.get("mock_output")
    mock_readable_outputs = test_data.get("mock_readable")

    response = cymru_bulk_whois_command(client, mock_arg, reliability=DEFAULT_RELIABILITY)

    for i, res in enumerate(response):
        assert mock_outputs[i] == res.outputs
        assert res.indicator
        assert mock_readable_outputs[i] == res.readable_output


@pytest.mark.parametrize(
    "args, expected_error",
    [({"entry_id": MOCK_ENTRY_ID}, "No file was found for given entry_id"), ({}, "No entry_id specified.")],
)
def test_cymru_bulk_whois_invalid_bulk(args, expected_error, mocker):
    """
    Given:
        - Invalid given argument
    When:
        - Running the cymru-bulk-whois command
    Then:
        - Raise ValueError with the expected value
    """
    from TeamCymru import cymru_bulk_whois_command

    mocker.patch.object(demisto, "getFilePath", return_value=None)
    with pytest.raises(ValueError, match=expected_error):
        cymru_bulk_whois_command(client, args, reliability=DEFAULT_RELIABILITY)


def test_team_cymru_parse_file():
    """
    Given:
        - get_file_path_res, dict: Object contains file ID, path and name
    When:
        - Running the parse_file function
    Then:
        - Return list of the elements in the file without spaces
    """
    from TeamCymru import parse_file

    mock_arg = {
        "id": "test_id",
        "path": "test_data/test_ips_file.csv",
        "name": "test_ips_file.csv",
    }
    assert parse_file(mock_arg) == MOCK_IPS_LIST


def test_team_cymru_validate_ip_addresses():
    """
    Given:
        - Ips list
    When:
        - Running the validate_ip_addresses function
    Then:
        - Returns two list of invalid and valid IPv4 addresses
    """
    from TeamCymru import validate_ip_addresses

    invalid_ip_addresses, valid_ip_addresses = validate_ip_addresses(MOCK_IPS_LIST)
    assert invalid_ip_addresses == MOCK_INVALID_IPS
    assert valid_ip_addresses == MOCK_VALID_IPS


def test_team_cymru_parse_ip_result():
    """
    Given:
        - The function arguments: ip, ip_data
    When:
        - Running the parse_ip_result function
    Then:
        - Validate the returned value (commandResult) compared to the mock output
    """
    from CommonServerPython import Common
    from TeamCymru import parse_ip_result

    test_data = load_test_data("test_data/test_ip_command.json")
    ip_data = test_data.get("ip_command_response")
    ip = "8.8.8.8"
    mock_entry_context = test_data.get("mock_output")
    mock_readable = test_data.get("mock_readable")
    command_result = parse_ip_result(ip, ip_data, reliability=DEFAULT_RELIABILITY)

    assert command_result.outputs == mock_entry_context
    assert command_result.readable_output == mock_readable
    assert command_result.indicator
    assert command_result.raw_response == ip_data
    assert isinstance(command_result.indicator, Common.IP)


def test_empty_command_result(mocker):
    """
    Given:
        - Valid ip address, running the ip_command and cymru_bulk_whois_command functions
    When:
        - team_cymru_ip, team_cymru_bulk_whois functions return None
    Then:
        - Verify the functions doesn't fail and returns empty list
    """
    from TeamCymru import cymru_bulk_whois_command, ip_command

    mocker.patch.object(CymruClient, "lookup", return_value=None)
    result = ip_command(client, {"ip": "1.1.1.1"}, reliability=DEFAULT_RELIABILITY)
    assert not result
    mocker.patch.object(CymruClient, "lookupmany_dict", return_value=None)
    mocker.patch.object(demisto, "getFilePath", return_value=MOCK_FILE_RES)
    result = cymru_bulk_whois_command(client, {"entry_id": MOCK_ENTRY_ID}, reliability=DEFAULT_RELIABILITY)
    assert not result


def assert_results_ok():
    assert demisto.results.call_count == 1
    # call_args is tuple (args list, kwargs). we only need the first one
    results = demisto.results.call_args[0]
    assert len(results) == 1
    assert results[0] == "ok"


def test_test_command(mocker):
    mocker.patch.object(demisto, "results")
    mocker.patch.object(demisto, "command", return_value="test-module")
    return_value = load_test_data("test_data/test_ip_command.json").get("ip_command_response")
    mocker.patch.object(CymruClient, "lookup", return_value=return_value)
    TeamCymru.main()
    assert_results_ok()