Office 365 Feed
The Office 365 IP Address and URL web service is a read-only API provided by Microsoft to expose the URLs and IPs used by Office 365. The Office 365 Feed integration fetches indicators from the service, with which you can create a list (allow list, block list, EDL, etc.) for your SIEM or firewall service to ingest and apply to its policy rules.
Data Enrichment & Threat Intelligence · Office 365 Feed · Feed
Details
| ID | Office 365 Feed |
|---|---|
| Provider | Microsoft |
| Category | Data Enrichment & Threat Intelligence |
| From Version | 5.5.0 |
| Docker Image | demisto/python3:3.12.13.10116658 |
| Supported Modules | Agentix XSIAM |
README
The Office 365 IP Address and URL web service is a read-only API provided by Microsoft to expose the URLs and IPs used by Office 365. The Office 365 Feed integration fetches indicators from the service, with which you can create a list (allow list, block list, EDL, etc.) for your SIEM or firewall service to ingest and apply to its policy rules.
Configure Office 365 Feed on Cortex XSOAR
- Navigate to Settings > Integrations > Servers & Services.
- Search for Office 365 Feed.
-
Click Add instance to create and configure a new integration instance.
Parameter Description Example Name A meaningful name for the integration instance. Office 365 Feed_worldwide_exchange Fetch indicators Select this option if you want this integration instance to fetch indicators from the Office 365 feed. N/A Regions The regions from which to fetch indicators. Supports multi-select. For all regions, you need to select each region. Services The services for which to fetch indicators. Supports multi-select. For all services, select the “All” option. Sharepoint, Exchange Indicator Reputation This reputation will be applied to all indicators fetched from this integration instance. Good Source Reliability The reliability of the source providing the intelligence data, which affects how this indicator’s fields and reputation are populated. A - Completely reliable Traffic Light Protocol Color The Traffic Light Protocol (TLP) designation to apply to indicators fetched from the feed. More information about the protocol can be found at https://us-cert.cisa.gov/tlp N/A feedExpirationPolicy The method by which to expire indicators from this integration instance. When removed from the feed feedExpirationInterval Feed Fetch Interval How often to fetch indicators from this integration instance. You can specify the interval in days, hours, or minutes. 30 minutes Bypass exclusion list When selected, the exclusion list is ignored for indicators from this feed. This means that if an indicator from this feed is on the exclusion list, the indicator might still be added to the system. N/A Enrichment Excluded Select this option to exclude the fetched indicators from the enrichment process. False Trust any certificate (not secure) When selected, certificates are not checked. N/A Use system proxy settings Runs the integration instance using the proxy server (HTTP or HTTPS) that you defined in the server configuration. False - Click Test to validate the URLs 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.
Get indicators from the feed
Gets indicators from the feed.
Base Command
office365-get-indicators
Input
| Argument Name | Description | Required |
|---|---|---|
| limit | The maximum number of results to return. The default value is 10. | Optional |
| indicator_type | The indicator type. Can be “IPs”, “URLs”, or “Both”. The default value is “IPs”. | Optional |
Context Output
There is no context output for this command.
Command Example
!office365-get-indicators limit=”5”
Human Readable Output
Indicators from Office 365 Feed
| value | type |
|---|---|
| 0.0.0.0/0 | CIDR |
| 0.0.0.0/0 | CIDR |
| 0.0.0.0/0 | CIDR |
| 0.0.0.0/0 | CIDR |
| 0.0.0.0/0 | CIDR |
| 0.0.0.0/0 | CIDR |
Configuration parameters
feed— Fetch indicatorscategory— Categoryregions— Regions (required)services— Services (required)allow_germany— Allow GermanyfeedReputation— Indicator ReputationfeedReliability— Source Reliability (required)tlp_color— Traffic Light Protocol ColorfeedExpirationPolicy—feedExpirationInterval—feedFetchInterval— Feed Fetch IntervalfeedTags— TagsfeedBypassExclusionList— Bypass exclusion listenrichmentExcluded— Enrichment Excludedinsecure— Trust any certificate (not secure)proxy— Use system proxy settings
Commands (1)
-
office365-get-indicatorsGets indicators from the feed.
import uuid from collections.abc import Callable from typing import Any import urllib3 from CommonServerPython import * # disable insecure warnings urllib3.disable_warnings() INTEGRATION_NAME = "Office 365" GERMANY = "Germany" ALL_REGIONS_LIST = ["Worldwide", "China", "USGovDoD", "USGovGCCHigh"] ALL_CATEGORY_LIST = ["Optimize", "Allow", "Default"] def build_region_or_category_list(param_list: list, all_config_list: list, allow_germany: bool = False) -> list: """Builds the region or category list for the feed. If the param_list includes 'All', it will add all the items from the 'all_config_list' to the list, and remove the string all. Args: allow_germany: In some cases, Germany endpoints can throw a 400 error, by default we exclude Germany from All param_list: list of regions or categories provided by integration configuration all_config_list: list of all the regions or categories, to be added if All is chosen Returns: list of regions or categories """ if allow_germany: param_list.append(GERMANY) if "All" in param_list: param_list.remove("All") return list(set(param_list + all_config_list)) return param_list def build_urls_dict(regions_list: list, services_list: list, unique_id) -> list[dict[str, Any]]: """Builds a URL dictionary with the relevant data for each service Args: regions_list: list of regions services_list: list of services unique_id: unique uuid Returns: URLs services list """ urls_list = [] for region in regions_list: for service in services_list: if service == "All": url = f"https://endpoints.office.com/endpoints/{region}?ClientRequestId={unique_id}" else: url = f"https://endpoints.office.com/endpoints/{region}?ServiceAreas={service}&ClientRequestId={unique_id}" urls_list.append({"Region": region, "Service": service, "FeedURL": url}) return urls_list class Client: """ Client to use in the Office 365 Feed integration. Overrides BaseClient. Office 365 IP address and URL web service announcement: https://docs.microsoft.com/en-us/office365/enterprise/managing-office-365-endpoints?redirectSourcePath=%252fen-us%252farticle%252fmanaging-office-365-endpoints-99cab9d4-ef59-4207-9f2b-3728eb46bf9a#webservice https://techcommunity.microsoft.com/t5/Office-365-Blog/Announcing-Office-365-endpoint-categories-and-Office-365-IP/ba-p/177638 """ def __init__( self, urls_list: list, category_list: list, insecure: bool = False, tags: list | None = None, tlp_color: str | None = None ): """ Implements class for Office 365 feeds. :param urls_list: List of url, regions and service of each service. :param insecure: boolean, if *false* feed HTTPS server certificate is verified. Default: *false* :param tlp_color: Traffic Light Protocol color. """ self._urls_list: list[dict] = urls_list self._verify: bool = insecure self.tags = [] if tags is None else tags self.tlp_color = tlp_color self._proxies = handle_proxy(proxy_param_name="proxy", checkbox_default_value=False) self.category_list = category_list def build_iterator(self) -> list: """Retrieves all entries from the feed. Returns: A list of objects, containing the indicators. """ result = [] for feed_obj in self._urls_list: feed_url = feed_obj.get("FeedURL", "") region = feed_obj.get("Region") service = feed_obj.get("Service") try: response = requests.get( url=feed_url, verify=self._verify, proxies=self._proxies, ) response.raise_for_status() data = response.json() # filter empty entries and category param, add metadata indicators = [i for i in data if ("ips" in i or "urls" in i) and i.get("category") in self.category_list] for i in indicators: # add relevant fields of services i.update({"Region": region, "Service": service, "FeedURL": feed_url}) result.extend(indicators) except requests.exceptions.SSLError as err: demisto.debug(str(err)) raise Exception( f"Connection error in the API call to {INTEGRATION_NAME}.\nCheck your not secure parameter.\n\n{err}" ) except requests.ConnectionError as err: demisto.debug(str(err)) raise Exception( f"Connection error in the API call to {INTEGRATION_NAME}.\nCheck your Server URL parameter.\n\n{err}" ) except requests.exceptions.HTTPError as err: demisto.debug(f"Got an error from {feed_url} while fetching indicators {err!s} ") if err.response.status_code == 503: raise Exception(f"The service located at {feed_url} is unavailable while fetching indicators {err!s} ") elif err.response.status_code == 400 and region == GERMANY: raise Exception( "The service returned a 400 status code, this could possibly be due to the Germany" " endpoint being unavailable. Please exclude Germany from All using the parameter" " Allow Germany." ) else: raise Exception(f"HTTP error in the API call to {INTEGRATION_NAME}.\n\n{err}") except ValueError as err: demisto.debug(str(err)) raise ValueError(f"Could not parse returned data to Json. \n\nError massage: {err}") return result @staticmethod def check_indicator_type(indicator): """Checks the indicator type. The indicator type can be classified as one of the following values: CIDR, IPv6CIDR, IP, IPv6 or Domain. Args: indicator: indicator value Returns: The type of the indicator """ is_ip_indicator = FeedIndicatorType.ip_to_indicator_type(indicator) if is_ip_indicator: return is_ip_indicator elif "*" in indicator: return FeedIndicatorType.DomainGlob # domain else: return FeedIndicatorType.Domain def test_module(client: Client, *_) -> tuple[str, dict[Any, Any], dict[Any, Any]]: """Builds the iterator to check that the feed is accessible. Args: client: Client object. Returns: Outputs. """ client.build_iterator() return "ok", {}, {} def fetch_indicators(client: Client, indicator_type_lower: str, limit: int = -1, enrichment_excluded: bool = False) -> list[dict]: """Retrieves indicators from the feed Args: client: Client object with request indicator_type_lower: indicator type limit: limit the results Returns: Indicators. """ iterator = client.build_iterator() # filter indicator_type specific entries if indicator_type_lower != "both": iterator = [i for i in iterator if indicator_type_lower in i] indicators = [] if limit > 0: iterator = iterator[:limit] for item in iterator: if indicator_type_lower == "both": values = item.get("ips", []) + item.get("urls", []) else: values = item.get(indicator_type_lower) if values: for value in values: type_ = Client.check_indicator_type(value) raw_data = { "value": value, "type": type_, } for key, val in item.items(): if key not in ["ips", "urls"]: raw_data.update({key: val}) indicator_mapping_fields = {"port": argToList(item.get("tcpPorts", "")), "service": item.get("serviceArea", "")} if item.get("expressRoute"): indicator_mapping_fields["office365expressroute"] = item.get("expressRoute") if item.get("category"): indicator_mapping_fields["office365category"] = item.get("category") if item.get("required"): indicator_mapping_fields["office365required"] = item.get("required") if item.get("notes"): indicator_mapping_fields["description"] = item.get("notes") indicator_mapping_fields["tags"] = client.tags if client.tlp_color: indicator_mapping_fields["trafficlightprotocol"] = client.tlp_color indicator_obj = { "value": value, "type": type_, "rawJSON": raw_data, "fields": indicator_mapping_fields, } if enrichment_excluded: indicator_obj["enrichmentExcluded"] = enrichment_excluded indicators.append(indicator_obj) return indicators def get_indicators_command( client: Client, args: dict[str, str], enrichment_excluded: bool = False ) -> tuple[str, dict[Any, Any], dict[Any, Any]]: """Wrapper for retrieving indicators from the feed to the war-room. Args: client: Client object with request args: demisto.args() Returns: Outputs. """ indicator_type = str(args.get("indicator_type")) indicator_type_lower = indicator_type.lower() limit = int(demisto.args().get("limit")) if "limit" in demisto.args() else 10 indicators = fetch_indicators(client, indicator_type_lower, limit, enrichment_excluded) human_readable = tableToMarkdown("Indicators from Office 365 Feed:", indicators, headers=["value", "type"], removeNull=True) return human_readable, {}, {"raw_response": indicators} def fetch_indicators_command(client: Client, enrichment_excluded: bool = False) -> list[dict]: """Wrapper for fetching indicators from the feed to the Indicators tab. Args: client: Client object with request Returns: Indicators. """ indicators = fetch_indicators(client, "both", enrichment_excluded=enrichment_excluded) return indicators def main(): """ PARSE AND VALIDATE INTEGRATION PARAMS """ params = demisto.params() unique_id = str(uuid.uuid4()) regions_list = build_region_or_category_list( argToList(params.get("regions")), ALL_REGIONS_LIST, allow_germany=params.get("allow_germany") ) services_list = argToList(params.get("services")) category_list = build_region_or_category_list(argToList(params.get("category", ["All"])), ALL_CATEGORY_LIST) urls_list = build_urls_dict(regions_list, services_list, unique_id) use_ssl = not params.get("insecure", False) tags = argToList(params.get("feedTags")) tlp_color = params.get("tlp_color") enrichment_excluded = demisto.params().get("enrichmentExcluded", False) command = demisto.command() demisto.info(f"Command being called is {command}") try: client = Client(urls_list, category_list, use_ssl, tags, tlp_color) commands: dict[str, Callable[[Client, dict[str, str]], tuple[str, dict[Any, Any], dict[Any, Any]]]] = { "test-module": test_module, "office365-get-indicators": get_indicators_command, } if command in commands: return_outputs(*commands[command](client, demisto.args())) elif command == "fetch-indicators": indicators = fetch_indicators_command(client, enrichment_excluded) for iter_ in batch(indicators, batch_size=2000): demisto.createIndicators(iter_) else: raise NotImplementedError(f"Command {command} is not implemented.") except Exception as err: err_msg = f"Error in {INTEGRATION_NAME} Integration. [{err}]" return_error(err_msg) if __name__ in ["__main__", "builtin", "builtins"]: main()