Back to Articles

Fingerprinting Microsoft 365 Defenses: How check_mdi Detects Defender for Identity Without Authentication

[ View on GitHub ]

Fingerprinting Microsoft 365 Defenses: How check_mdi Detects Defender for Identity Without Authentication

Hook

Microsoft’s own infrastructure tells attackers whether advanced threat detection is watching them—all without requiring a single credential or triggering an authentication event.

Context

In the modern enterprise landscape, Microsoft 365 has become the backbone of organizational productivity, with over 345 million paid seats worldwide. As organizations migrate to the cloud, understanding their security posture has become critical for both defenders conducting audits and red teams simulating realistic attack scenarios. Microsoft Defender for Identity (MDI), formerly Azure ATP, sits as a key defensive layer—monitoring on-premises Active Directory signals and cloud authentication patterns to detect suspicious behavior. For penetration testers, knowing whether MDI is deployed fundamentally changes attack strategies: it means behavioral anomalies will be flagged, lateral movement techniques will be scrutinized, and credential theft attempts may trigger alerts.

Traditionally, discovering an organization’s security stack required either insider knowledge or post-compromise enumeration that risked detection. The check_mdi tool changes this calculus by exploiting a quirk in Microsoft’s architecture: certain endpoints used for legitimate service integration inadvertently reveal tenant configuration details to unauthenticated requests. Built on research by thalpius and the broader offensive security community, this Python script demonstrates how publicly accessible Microsoft infrastructure can be interrogated to map an organization’s defensive capabilities before the first credential is ever submitted. It’s reconnaissance that lives entirely in the passive-to-active gray zone, using the same endpoints that legitimate applications query during OAuth flows and service discovery.

Technical Insight

Microsoft Public APIs

Domain exists

Invalid

Managed/Federated

Tenant ID extracted

Not found

200/403

404

Domain Input

Domain Validation

getuserrealm.srf

Parse

NameSpaceType

Exit: Invalid Domain

Tenant Name Retrieval

AutoDiscover endpoint

MDI Detection

Probe MDI endpoints

Exit: No Tenant

Check HTTP

Response

Exit: MDI Deployed

Exit: No MDI

System architecture — auto-generated

At its core, check_mdi performs a three-stage interrogation of Microsoft’s public API surface. The script’s elegance lies in its exploitation of endpoints designed for service discovery and federation—infrastructure Microsoft maintains for legitimate integration scenarios but that also leak valuable operational security information.

The first stage validates whether a domain is actually federated with Microsoft 365. The script queries the https://login.microsoftonline.com/getuserrealm.srf endpoint, which Microsoft uses during authentication flows to determine whether a user should be redirected to an on-premises federation server or authenticated directly in the cloud. By sending a GET request with a domain parameter, you receive a JSON response revealing the tenant’s namespace type, federation configuration, and authentication URL—all without presenting credentials:

import requests

def check_domain(domain):
    url = f"https://login.microsoftonline.com/getuserrealm.srf?login=user@{domain}&xml=1"
    response = requests.get(url)
    if response.status_code == 200:
        # Parse response to determine if domain is managed/federated
        if "NameSpaceType" in response.text:
            if "Managed" in response.text:
                return True, "managed"
            elif "Federated" in response.text:
                return True, "federated"
    return False, None

This seemingly innocuous endpoint serves a critical business function—enabling seamless single sign-on experiences—but it simultaneously provides reconnaissance gold. The response tells an attacker whether the organization uses cloud-only authentication (easier to target with password spray attacks) or federated authentication (requiring different tactics).

The second stage extracts the tenant name by querying OpenID configuration endpoints. Microsoft publishes these for OAuth integration, and they contain the tenant GUID and human-readable tenant name. The script parses https://login.microsoftonline.com/{domain}/.well-known/openid-configuration to extract identifiers that map domains to specific Azure AD tenants. This matters because large organizations often have multiple domains, and understanding the canonical tenant name helps attackers correlate information from different OSINT sources.

The third and most distinctive stage detects MDI deployment by probing MDI-specific endpoints. Microsoft Defender for Identity maintains cloud-based infrastructure that MDI sensors communicate with, and certain URL patterns reveal whether an organization has provisioned an MDI instance. The script checks for the existence of tenant-specific MDI workspace URLs:

def check_mdi(tenant_name):
    # MDI instances follow a predictable URL pattern
    mdi_url = f"https://{tenant_name}sensorapi.atp.azure.com"
    try:
        response = requests.get(mdi_url, timeout=5, allow_redirects=False)
        # A specific response pattern indicates MDI is provisioned
        if response.status_code in [302, 401, 403]:
            return True
        return False
    except requests.exceptions.RequestException:
        return False

The detection logic relies on HTTP status codes rather than content. An MDI instance that exists will return authentication challenges or redirects, while non-existent endpoints typically timeout or return generic 404 responses. This behavioral fingerprinting approach is more resilient than content-based parsing, since Microsoft frequently updates UI elements but rarely changes fundamental HTTP semantics.

What makes this reconnaissance valuable is its position in the kill chain. Traditional enumeration tools like enum4linux or ldapsearch require network access to internal resources. Cloud-based tools like AADInternals or ROADtools offer more comprehensive data but often require at least basic authentication. check_mdi operates entirely from an external network position with zero credentials, making it ideal for the earliest reconnaissance phases. An attacker can build a target list of organizations, validate their M365 presence, and understand defensive posture before ever touching corporate infrastructure.

The script’s simplicity is both strength and limitation. With under 200 lines of Python, it’s trivial to audit, modify, and integrate into existing reconnaissance workflows. Red teamers can embed this check into automated OSINT pipelines, security researchers can adapt it for large-scale studies of MDI adoption rates, and penetration testers can quickly triage targets. The lack of dependencies beyond the requests library means it runs anywhere Python exists, from Kali Linux to AWS Lambda functions.

Gotcha

The tool’s focused scope means it won’t replace comprehensive enumeration frameworks in most scenarios. check_mdi tells you whether MDI exists, but nothing about how it’s configured. An organization might have MDI deployed but with minimal sensor coverage, misconfigured alert policies, or understaffed security operations that never act on alerts. The binary yes/no answer doesn’t reveal detection maturity, which varies wildly between organizations. You’ll get false confidence if you assume MDI presence automatically means sophisticated defense—many organizations purchase licenses for compliance checkboxes without operationalizing the tooling.

Detection methods may also degrade over time. Microsoft could change endpoint URLs, modify authentication flows to reduce information leakage, or implement rate limiting that blocks reconnaissance attempts. The script includes no built-in evasion, throttling, or user-agent rotation, meaning bulk scanning could trigger cloud-based threat intelligence feeds. Organizations with mature security programs may monitor for unusual patterns of requests to authentication endpoints, especially if you’re checking hundreds of domains from a single source IP. The technique works today because it exploits architectural decisions Microsoft made for interoperability, but those decisions could change as Microsoft hardens their public API surface against reconnaissance. Unlike tools that exploit fundamental protocol weaknesses, check_mdi depends on implementation details that exist at Microsoft’s discretion.

Verdict

Use if: You’re conducting red team reconnaissance and need to understand defensive capabilities before engaging targets, you’re a security researcher studying M365 security adoption patterns across organizations, you need a lightweight check to integrate into automated OSINT pipelines, or you’re in the earliest phases of penetration testing with zero credentials and want to prioritize targets. Skip if: You need comprehensive M365 enumeration beyond just MDI detection (use AADInternals or o365recon instead), you’re performing post-compromise enumeration where you already have credentials and access to richer data sources, you require production-grade tooling with extensive error handling and logging for compliance reporting, or you need to understand MDI configuration depth rather than just presence. This is a tactical reconnaissance probe, not a strategic assessment platform—use it for what it does well and chain it with other tools for complete visibility.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/expl0itabl3-check-mdi.svg)](https://starlog.is/api/badge-click/developer-tools/expl0itabl3-check-mdi)