ipinfo_v2

Use the IPinfo.io API to get data about an IP address.

Data Enrichment & Threat Intelligence · Ipinfo

Details

IDipinfo_v2
ProviderIPinfo
CategoryData Enrichment & Threat Intelligence
From Version5.5.0
Docker Imagedemisto/python3:3.12.13.10116658
Supported ModulesAgentix XSIAM

README

Use the IPinfo.io API to get data about an IP address.

Differences from IPinfo (v1)

  • The ip-field command has been removed on v2: all outputs are available by running ip.
  • IPinfo v2 Allows setting source reliability.
  • IPinfo v2 Enriches data with IP-hostname relationships.

Configure IPinfo v2 in Cortex

Parameter Description Required
API Token The API key to use for the connection. False
Source Reliability Reliability of the source providing the intelligence data. True
Base URL   True
Trust any certificate (not secure)   False
Use system proxy settings   False

Commands

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

ip


Check IP reputation (when information is available, returns a JSON with details). Uses all configured Threat Intelligence feeds.

Base Command

ip

Input

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

Context Output

Path Type Description
IPinfo.IP.Address String The IP address.
IPinfo.IP.Hostname String The IP hostname.
IPinfo.IP.ASN String The IP ASN.
IPinfo.IP.ASOwner String The IP AS owner.
IPinfo.IP.Organization.Name String The IP organization name (Only available in some IPinfo.io plans).
IPinfo.IP.Organization.Type String The IP organization type (Only available in some IPinfo.io plans).
IPinfo.IP.Geo.Location String The IP geographic location (coordinates as lat:lon).
IPinfo.IP.Geo.Country String The IP country.
IPinfo.IP.Geo.Description String The IP location as <City, Region, Postal Code, Country>.
IPinfo.IP.Registrar.Abuse.Address String The physical address registered for receiving abuse reports for the IP. (Only available in some IPinfo.io plans).
IPinfo.IP.Registrar.Abuse.Country String The country where abuse reports are received for the IP. (Only available in some IPinfo.io plans).
IPinfo.IP.Registrar.Abuse.Email String The email address for abuse reports provided by the IP. (Only available in some IPinfo.io plans).
IPinfo.IP.Registrar.Abuse.Name String The name of the abuse report handler received for the IP. (Only available in some IPinfo.io plans).
IPinfo.IP.Registrar.Abuse.Network String The IP range relevant for abuse inquiries provided for the IP. (Only available in some IPinfo.io plans).
IP.Address String The IP address.
IP.Hostname String The IP hostname.
IP.ASN String The IP ASN.
IP.Tags String Tags related the IP use (hosting, proxy, tor, vpn).
IP.FeedRelatedIndicators.value String Names of indicators associated with the IP.
IP.FeedRelatedIndicators.type String Types of indicators associated with the IP.
IP.Relationships.EntityA string The source of the relationship.
IP.Relationships.EntityB string The destination of the relationship.
IP.Relationships.Relationship string The name of the relationship.
IP.Relationships.EntityAType string The type of the source of the relationship.
IP.Relationships.EntityBType string The type of the destination of the relationship.
IP.Geo.Location String The IP geographic location (coordinates as lat:lon)
IP.Geo.Country String The IP country.
IP.Geo.Description String The IP location as <City, Region, Postal Code, Country>.
IP.Organization.Name String The organization of the IP.
IP.Organization.Type String The organization type of the IP.
DBotScore.Indicator String The indicator that was tested.
DBotScore.Score Number The actual score.
DBotScore.Reliability String How reliable the score is (for example, “C - fairly reliable”).
DBotScore.Type String The indicator type.
DBotScore.Vendor String The vendor used to calculate the score.

Command example

!ip ip=1.1.1.1

Context Example

{
    "DBotScore": {
        "Indicator": "1.1.1.1",
        "Reliability": "C - Fairly reliable",
        "Score": 0,
        "Type": "ip",
        "Vendor": "ipinfo_v2"
    },
    "IP": {
        "ASN": "AS13335",
        "Address": "1.1.1.1",
        "FeedRelatedIndicators": [
            {
                "description": "Hostname",
                "type": "URL",
                "value": "one.one.one.one"
            },
            {
                "description": "AS domain",
                "type": "Domain",
                "value": "cloudflare.com"
            },
            {
                "description": "Company domain",
                "type": "Domain",
                "value": "cloudflare.com"
            }
        ],
        "Geo": {
            "Country": "US",
            "Description": "Los Angeles, California, 90076, US",
            "Location": "34.0522:-118.2437"
        },
        "Hostname": "one.one.one.one",
        "Organization": {
            "Name": "APNIC and Cloudflare DNS Resolver project",
            "Type": "hosting"
        },
        "Relationships": [
            {
                "EntityA": "1.1.1.1",
                "EntityAType": "IP",
                "EntityB": "one.one.one.one",
                "EntityBType": "Domain",
                "Relationship": "resolves-to"
            }
        ],
        "Tags": "hosting"
    },
    "IPinfo": {
        "IP": {
            "ASN": "AS13335",
            "ASOwner": "Cloudflare, Inc.",
            "Address": "1.1.1.1",
            "Geo": {
                "Country": "US",
                "Description": "Los Angeles, California, 90076, US",
                "Location": "34.0522,-118.2437"
            },
            "Hostname": "one.one.one.one",
            "Organization": {
                "Name": "APNIC and Cloudflare DNS Resolver project",
                "Type": "hosting"
            },
            "Registrar": {
                "Abuse": {
                    "Address": "PO Box 3646, South Brisbane, QLD 4101, Australia",
                    "Country": "AU",
                    "Email": "test",
                    "Name": "APNIC RESEARCH",
                    "Network": "1.1.1.0/24",
                    "Phone": "+61-7-3858-3188"
                }
            },
            "Tags": [
                "hosting"
            ]
        }
    }
}

Human Readable Output

IPinfo results for 1.1.1.1

anycast city country hostname ip loc org postal readme region timezone
true Miami US one.one.one.one 1.1.1.1 25.7867,-80.1800 AS13335 Cloudflare, Inc. 33132 https    

Configuration parameters

  • credentials
  • integrationReliability — Source Reliability (required)
  • base_url — Base URL (required)
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (1)

  • ip

    Check IP reputation (when information is available, returns a JSON with details). Uses all configured Threat Intelligence feeds.

from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import
from CommonServerUserPython import *  # noqa

import traceback
from typing import Any

BRAND_NAME = "IPinfo"  # matches context output path for faster caching


class Client(BaseClient):
    def __init__(self, api_key: str, base_url: str, verify_certificate: bool, proxy: bool, reliability: str):
        """
        Client to use in the IPinfo integration. Uses BaseClient
        """
        super().__init__(base_url=base_url, proxy=proxy, verify=verify_certificate)
        time_sensitive = is_time_sensitive()
        demisto.debug(f"{time_sensitive=}")

        self.timeout = 2 if time_sensitive else 20
        self.api_key = api_key
        self.reliability = reliability

    def ipinfo_ip(self, ip: str) -> dict[str, Any]:
        return self.http_request(ip)

    def http_request(self, ip: str) -> dict[str, Any]:
        """constructs url with token (if existent), then returns request"""
        return self._http_request(
            method="GET", url_suffix=f"{ip}/json", params=assign_params(token=self.api_key), timeout=self.timeout
        )


def test_module(client: Client) -> str:
    """Tests IPinfo by sending a query on 8.8.8.8"""
    client.ipinfo_ip("8.8.8.8")
    return "ok"  # on any failure, an exception is raised


def ipinfo_ip_command(client: Client, ip: str) -> List[List[CommandResults]]:
    ip_lists = argToList(ip)
    ip_results = []

    for ip in ip_lists:
        response = client.ipinfo_ip(ip)
        ip_results.append(parse_results(ip, response, client.reliability))

    return ip_results


def parse_results(ip: str, raw_result: dict[str, Any], reliability: str) -> List[CommandResults]:
    command_results: List[CommandResults] = []

    # default values
    asn = as_owner = None
    feed_related_indicators: List[Common.FeedRelatedIndicators] = []
    relationships: list[EntityRelationship] = []
    if not raw_result:
        return command_results

    hostname = str(raw_result.get("hostname", ""))
    hostname_indicator_type = FeedIndicatorType.URL if urlRegex.find(hostname) else FeedIndicatorType.Domain
    feed_related_indicators.append(Common.FeedRelatedIndicators(hostname, hostname_indicator_type, "Hostname"))

    relationships.append(
        EntityRelationship(
            name=EntityRelationship.Relationships.RESOLVES_TO,
            entity_a=ip,
            entity_a_type=FeedIndicatorType.IP,
            entity_b=hostname,
            entity_b_type=FeedIndicatorType.Domain,
            brand=BRAND_NAME,
            source_reliability=reliability,
        )
    )

    if "org" in raw_result:
        org = raw_result.get("org", "")
        if " " in org:
            org_parts = org.split(" ")
            asn, as_owner = org_parts[0], " ".join(org_parts[1:])

    # example of a field only available on paid accounts
    if "asn" in raw_result:
        asn = demisto.get(raw_result, "asn.asn")
        as_owner = demisto.get(raw_result, "asn.name")
        as_domain = demisto.get(raw_result, "asn.domain")

        if as_domain:
            feed_related_indicators.append(Common.FeedRelatedIndicators(as_domain, FeedIndicatorType.Domain, "AS domain"))

    organization = (
        {"Name": demisto.get(raw_result, "company.name"), "Type": demisto.get(raw_result, "company.type")}
        if "company" in raw_result
        else None
    )

    company_domain = demisto.get(raw_result, "company.domain")
    if company_domain is not None:
        feed_related_indicators.append(Common.FeedRelatedIndicators(company_domain, FeedIndicatorType.Domain, "Company domain"))

    abuse = (
        {
            "Address": demisto.get(raw_result, "abuse.address"),
            "Country": demisto.get(raw_result, "abuse.country"),
            "Name": demisto.get(raw_result, "abuse.name"),
            "Network": demisto.get(raw_result, "abuse.network"),
            "Phone": demisto.get(raw_result, "abuse.phone"),
            "Email": demisto.get(raw_result, "abuse.email"),
        }
        if "abuse" in raw_result
        else None
    )

    tags = []
    for tag_path, tag_name in (
        ("privacy.hosting", "hosting"),
        ("privacy.proxy", "proxy"),
        ("privacy.tor", "tor"),
        ("privacy.vpn", "vpn"),
    ):
        if demisto.get(raw_result, tag_path):
            tags.append(tag_name)

    city = raw_result.get("city", "")
    region = raw_result.get("region", "")
    postal = raw_result.get("postal", "")
    country = raw_result.get("country", "")

    description = ", ".join(filter(None, [city, region, postal, country]))

    # parses geolocation
    lat = lon = None
    loc = raw_result.get("loc", "")  # empty string as default on purpose,
    if "," in loc:
        coordinates = loc.split(",")
        lat, lon = float(coordinates[0]), float(coordinates[1])

    entry_context = {
        "Address": raw_result.get("ip"),
        "Hostname": hostname,  # type: ignore
        "ASN": asn,
        "ASOwner": as_owner,
        "Tags": tags,  # type: ignore
        "Organization": organization,
        "Geo": {"Location": loc, "Country": country, "Description": description},  # type: ignore
        "Registrar": {"Abuse": abuse} if abuse else None,
    }

    outputs_key_field = "Address"  # marks the ip address

    if DBotScoreReliability.is_valid_type(reliability):
        dbot_reliability = DBotScoreReliability.get_dbot_score_reliability_from_str(reliability)
    else:
        raise Exception("Please provide a valid value for the Source Reliability parameter.")

    indicator = Common.IP(
        ip=ip,
        dbot_score=Common.DBotScore(
            indicator=ip,
            indicator_type=DBotScoreType.IP,
            reliability=dbot_reliability,
            integration_name=BRAND_NAME,
            score=Common.DBotScore.NONE,
        ),
        asn=asn,
        hostname=hostname,
        feed_related_indicators=feed_related_indicators,
        geo_latitude=str(lat) if lat else None,
        geo_longitude=str(lon) if lon else None,
        geo_description=description or None,
        geo_country=country,
        tags=",".join(tags),
        organization_name=organization.get("Name") if organization else None,
        organization_type=organization.get("Type") if organization else None,
        relationships=relationships,
    )

    if lat and lon:
        raw_result.update({"lat": lat, "lng": lon})
        map_output = CommandResults(
            raw_response={"lat": lat, "lng": lon},
            entry_type=EntryType.MAP_ENTRY_TYPE,
            outputs_key_field=outputs_key_field,
            indicator=indicator,
        )
        command_results.append(map_output)

    # do not change the order of the calls for CommandResults due to an issue where the ip command would not
    # present all of the information returned from the API.
    command_results.append(
        CommandResults(
            readable_output=tableToMarkdown(f"IPinfo results for {ip}", raw_result),
            raw_response=raw_result,
            outputs_prefix="IPinfo.IP",
            outputs=entry_context,
            outputs_key_field=outputs_key_field,
            indicator=indicator,
            relationships=relationships,
        )
    )

    return command_results


def main() -> None:
    """main function, parses params and runs command functions"""

    params = demisto.params()
    args = demisto.args()
    command = demisto.command()

    proxy = params.get("proxy") or False
    api_key = demisto.get(params, "credentials.password") or ""
    insecure = params.get("insecure") or False
    base_url = params.get("base_url") or "https://ipinfo.io"
    reliability = params.get("integrationReliability")

    demisto.debug(f"Command being called is {command}")

    try:
        client = Client(api_key=api_key, verify_certificate=not insecure, proxy=proxy, base_url=base_url, reliability=reliability)

        if command == "test-module":
            return_results(test_module(client))

        elif command == "ip":
            ip_command = ipinfo_ip_command(client, **args)
            return_results(ip_command)
        else:
            raise NotImplementedError(f"command {command} is not supported")

    # Log exceptions and return errors
    except Exception as e:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f"Failed to execute {command} command.\nError:\n{e!s}")


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