← Back to blog

Threat Intelligence in Sentinel: MDTI and custom Feeds

This blog post explains how Security Teams can integrate threat intelligence feeds into KQL-based detections; from Microsoft Defender Threat Intelligence (MDTI) to open source feeds.


What are Threat Intelligence Feeds?

A threat intelligence feed is a continuously updated stream of indicators of compromise (IOCs). If an adversary is known to operate from a specific IP or domain, any connection to that infrastructure is worth investigating.


ti

Microsoft Defender Threat Intelligence

MDTI is Microsoft's commercial threat intelligence platform, built on the infrastructure acquired from RiskIQ in 2021. It aggregates passive DNS, WHOIS, SSL certificate data, web crawls, and Microsoft's own telemetry across billions of endpoints, emails, and cloud workloads.

The new MDTI tables

If you have used Sentinel before, you might know the ThreatIntelligenceIndicator table. As of July 2025, Microsoft moved all threat intelligence into two new tables:

  • ThreatIntelIndicators — the main table, holding the core indicators (IPs, domains, hashes)
  • ThreatIntelObjects — the context table, holding rich details like threat actor attribution, malware relationships, and campaign associations

For Sentinel users, MDTI indicators are automatically synchronized into the ThreatIntelIndicators table when the MDTI data connector is enabled. This means no external calls or ingestion pipelines are needed.

Using MDTI tables in KQL

These new indicators follow the STIX 2.1 pattern format, which is what makes the query slightly more advanced than a simple lookup. Extracting usable IP addresses and URLs requires parsing that structure first.

let TI_Indicators = materialize(
    ThreatIntelIndicators
    | extend Pattern = tostring(parse_json(Data).pattern)
    | extend TI_IndicatorTypes = tostring(parse_json(Data).indicator_types)
    | where TI_IndicatorTypes != '["Botnet"]'
    | extend TI_IP = iff(Pattern contains "network-traffic", extract(@"src_ref\.value = '([^']+)'", 1, Pattern), "")
    | extend TI_URL = iff(Pattern contains "url", extract(@"url:value = '([^']+)'", 1, Pattern), "")
    | where isnotempty(TI_IP) or isnotempty(TI_URL)
    | project TI_IP, TI_URL);
let TI_IPs  = TI_Indicators | where isnotempty(TI_IP)  | distinct TI_IP;
let TI_URLs = TI_Indicators | where isnotempty(TI_URL) | distinct TI_URL;
DeviceNetworkEvents
| where ActionType in ("ConnectionSuccess", "InboundConnectionAccepted")
| where RemoteIP in (TI_IPs) or (isnotempty(RemoteUrl) and RemoteUrl in (TI_URLs))
| project TimeGenerated, DeviceName, InitiatingProcessFileName, RemoteIP, RemoteUrl, RemotePort, ActionType

Adding your own indicators

MDTI is powerful, but it does not know about the phishing domain that started targeting your executives this morning, or the specific IP list you just received from your industry ISAC. This is where you add your own intelligence.

In Sentinel, navigate to Threat Management > Threat Intelligence and click + New > New TI object. This creates a STIX object directly in ThreatIntelIndicators, which your existing analytics rules will pick up automatically.

mdti


Open Source Feeds: C2IntelFeeds

Sentinel can also benefit from layering additional feed sources. C2IntelFeeds by drb-ra is an open source option specifically focused on C2 infrastructure (Cobalt Strike, Sliver, Havoc, Brute Ratel, and similar frameworks).

The feed is built from a combination of honeypots, passive DNS observation, certificate transparency logs, and community submissions. It is updated daily and hosted as raw CSV files on GitHub, which makes it directly queryable from KQL via externaldata() without any ingestion or preprocessing step.

let C2IPs = materialize(
    externaldata(ip: string, ioc: string)
    [@"https://raw.githubusercontent.com/drb-ra/C2IntelFeeds/refs/heads/master/feeds/IPC2s.csv"]
    with (format="csv", ignoreFirstRecord=true)
    | distinct ip);
let C2Domains = materialize(
    externaldata(domain: string, ioc: string)
    [@"https://raw.githubusercontent.com/drb-ra/C2IntelFeeds/refs/heads/master/feeds/domainC2s-filter-abused.csv"]
    with (format="csv", ignoreFirstRecord=true)
    | distinct domain);
DeviceNetworkEvents
| where ActionType in ("ConnectionSuccess", "InboundConnectionAccepted")
| extend CleanUrl = tostring(parse_url(RemoteUrl).Host)
| extend CleanUrl = iff(isempty(CleanUrl), RemoteUrl, CleanUrl)
| where RemoteIP in (C2IPs) or (isnotempty(CleanUrl) and CleanUrl in (C2Domains))
| project DeviceName, InitiatingProcessFileName, RemoteIP, RemoteUrl, CleanUrl, RemotePort, ActionType

Switching from IPC2s.csv to IPC2s-90day.csv extends the lookback window at the cost of more entries and a slightly higher false positive rate. For threat hunting rather than real-time alerting, the 90-day feed is the better choice.


Which feed to use

MDTI and C2IntelFeeds answer different questions. MDTI tells you whether a connection matches Microsoft's broader threat landscape with attribution and confidence context attached. C2IntelFeeds tells you whether a connection matches recently observed C2 infrastructure from a community-maintained, framework-specific source. Running both in parallel is in my opinion the most practical approach, as they overlap partially but cover different intelligence gaps.