AiTM Kit Device PRT Enrollment

DRS EnrollmentServer/device call with non-native UA (axios/1.15.2 not Dsreg) → Entra ID audit event → device-bound PRT surviving revokeSignInSessions

Persistence T1098.005 Detection difficulty: LOW Prevalence: MEDIUM

After capturing a victim's session via AiTM proxy, the Tycoon 2FA kit registers a synthetic device with Microsoft Device Registration Service (DRS) to obtain a Primary Refresh Token (PRT) bound to a kit-controlled device object. This PRT survives the standard IR playbook of revokeSignInSessions because the device is a separate Entra ID principal - its PRT remains valid indefinitely unless the device object is explicitly deleted first. The enrollment call always uses the kit's Node.js HTTP client (axios/1.15.2), not the native Entra ID join clients (Dsreg, DeviceRegistrationClient, Dalvik) - a binary distinction exploitable as a high-fidelity detection. Critical IR finding: the correct containment sequence is atomically disable account → enumerate and DELETE all registered devices → THEN revoke sign-in sessions → reset password. Reversing steps 2 and 3 leaves the device-PRT valid and allows the kit to continue brokering fresh access tokens via HMAC-SHA256 signed PRT assertions.

Attack Chokepoints 2 invariant stages

Each stage is an invariant condition the attacker must satisfy, regardless of tool, variant, or threat actor. Detection at any stage breaks the chain.

1 DRS Enrollment with Non-Native User Agent
  • Kit has already captured victim's session via AiTM relay (Tier 1 success)
  • Kit holds a Microsoft Authentication Broker refresh token for the victim
  • Victim tenant allows device registration (not restricted to Entra ID joined devices only)
  • Kit has network access to enterpriseregistration.windows.net (DRS endpoint)
Input Kit has captured MAB refresh token; proceeds to register synthetic device for PRT persistence
Chokepoint The attacker MUST call the DRS EnrollmentServer/device endpoint to register a device - this is the only path to a device-bound PRT in Entra ID. The enrollment always generates an audit event in Entra ID AuditLogs. The kit's HTTP client (axios/1.15.2) is the user agent on this call, not the native clients (Dsreg / DeviceRegistrationClient / Dalvik) used by legitimate join workflows.
Observable Entra ID AuditLogs: operationName == "Add registered users to device" AND resultType == "0" (success) AND userAgent NOT IN [Dsreg, DeviceRegistrationClient, Dalvik, Windows-AzureAD-Authentication-Provider]. The non-native UA is the binary invariant - legitimate device enrollment from managed endpoints never uses axios or node.
Why unavoidable
DRS EnrollmentServer/device is the only API path to obtain a device-bound PRT in Entra ID. There is no alternative enrollment mechanism. The audit event is generated server-side by Microsoft's DRS - the attacker cannot suppress it. The kit cannot use native Dsreg/DeviceRegistrationClient because those are Windows OS components that would require code execution on a managed endpoint, defeating the purpose of the cloud-side AiTM kit.
  • Entra ID AuditLogs (AuditLogs table in Sentinel; filter ActivityDisplayName "Add registered users to device")
  • Entra ID Sign-in Logs (cross-reference UPN for preceding AiTM sign-in within 5-15 min)
View rule →
IR team has executed revokeSignInSessions (but NOT delete...
2 PRT Persistence Post-Revocation
Input IR team has executed revokeSignInSessions (but NOT deleted device objects first)
Chokepoint The attacker MUST use the PRT + session key to sign per-request HMAC-SHA256 assertions for ongoing oauth2/token calls - these generate non-interactive sign-in records with incomingTokenType: primaryRefreshToken even after revokeSignInSessions has been executed.
Observable Microsoft Graph Activity Logs or Entra ID NonInteractiveUserSignInLogs: incomingTokenType == "primaryRefreshToken" from same UPN AFTER a revokeSignInSessions event. This confirms device-PRT is still active and device objects were not deleted before session revocation.
Why unavoidable
PRT token grants always log incomingTokenType in Microsoft's audit infrastructure. The attacker cannot obtain fresh access tokens without making oauth2/token calls that generate log entries. The signal is generated server-side by Microsoft.
  • Microsoft Graph Activity Logs (incomingTokenType field)
  • Entra ID NonInteractiveUserSignInLogs
Bypass risk: If IR deletes device objects before revoking sessions (correct order), the PRT is invalidated and this stage never fires. This stage is a post-IR-failure indicator, not a preventive detection.
View rule →

Variations 1 variant tracked

Tools and methods that exploit this chokepoint. The list grows. The chokepoint doesn't change.

Tycoon 2FA - Device PRT Enrollment Chain 2023-Q3 Active
Full API call chain documented by Elastic Security Labs. Five steps: (1) Resource-swap MAB refresh token for DRS access token at oauth2/token (resource=urn:ms-drs:enterpriseregistration.windows.net). (2) POST EnrollmentServer/device with PKCS#10 CSR + synthetic device metadata. (3) Build RS256 JWT signed with kit-generated device private key. (4) POST jwt-bearer grant to obtain PRT + encrypted session key. (5) Sign per-request HMAC-SHA256 PRT assertions for ongoing access. User agent on enrollment call: axios/1.15.2.
Audit event fires on Step 2 (DRS enrollment). Kit user agent (axios/1.15.2) is the binary distinguishing signal vs. legitimate Dsreg/DeviceRegistrationClient. PRT obtained in Step 4 survives revokeSignInSessions - device must be deleted first.
# Step 1: Resource-swap MAB refresh token for DRS access token
# User-Agent: axios/1.15.2
POST https://login.microsoftonline.com/common/oauth2/token
  grant_type=refresh_token
  refresh_token=<stolen_refresh_token>
  resource=urn:ms-drs:enterpriseregistration.windows.net
  client_id=29d9ed98-a469-4536-ade2-f981bc1d605e

# Step 2: Register synthetic device
# User-Agent: axios/1.15.2
POST https://enterpriseregistration.windows.net/EnrollmentServer/device
  {"CertificateRequest":{"Type":"pkcs10","Data":"<base64_PKCS10_CSR>"},
   "DeviceDisplayName":"<fabricated>","OSVersion":"<fabricated>"}

# Step 3-4: JWT bearer grant → obtain PRT
POST https://login.microsoftonline.com/common/oauth2/token
  grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
  assertion=<RS256_JWT_signed_with_device_key>

# Step 5: Ongoing - sign PRT assertions with HMAC-SHA256 session key
  • Entra ID AuditLogs: operationName "Add registered users to device" resultType "0" userAgent "axios/1.15.2"
  • Entra ID Device objects: fabricated DeviceDisplayName, OSVersion on registeredDevices/ownedDevices
  • Graph Activity Logs: incomingTokenType "primaryRefreshToken" from cloud-VPS ASN post-revokeSignInSessions
Same chokepoint: Kit captures session → axios enrolls DRS device (audit event) → PRT obtained → survives revokeSignInSessions → ongoing non-interactive sign-ins
Source: www.elastic.co →

Detection Strategy

Rules organized by the chokepoint stage they detect. Each stage has one or more rules at different maturity levels.

1 DRS Enrollment with Non-Native User Agent
2 PRT Persistence Post-Revocation
Device registration with user agent NOT in the native Entra ID join client se...
Hunt
Goal
Device registration with user agent NOT in the native Entra ID join client set. High-fidelity due to binary distinction between kit and native clients.
Log Sources
  • Entra ID AuditLogs
FP Rate
Low (custom MDM tools with non-standard UAs; document and exclude)
Use Case
Active hunting for device-PRT persistence. High-fidelity due to binary UA distinction. False positives limited to bespoke MDM automation.
operationName == "Add registered users to device" AND resultType == "0" AND userAgent NOT contains_any [Dsreg, DeviceRegistrationClient, Dalvik, Windows-AzureAD-Authentication-Provider]. Correlate with preceding AiTM sign-in from same UPN within 5-15 minutes (Node.js UA on OfficeHome/Auth Broker).
Sigma Rule - Hunt Level
title: Entra ID Device Registration with Non-Native User Agent (AiTM PRT Persistence)
id: f6a7b8c9-d0e1-4f2a-3b4c-5d6e7f8a9b0c
status: experimental
description: >
  Detects Entra ID device registration audit events where the initiating user agent
  is not a native OS registration client. Tycoon 2FA's kit performs DRS
  EnrollmentServer/device enrollment using axios/1.15.2, not the expected OS agents
  (Dsreg, DeviceRegistrationClient, Dalvik, Windows-AzureAD-Authentication-Provider).
  A registered device from a non-native UA obtains a PRT that survives
  revokeSignInSessions, enabling persistent M365 access after IR response.
  VERIFY field names (UserAgent vs. InitiatedBy.app.displayName or AdditionalDetails)
  against lab AuditLogs data before production deployment.
references:
  - https://www.elastic.co/security-labs/tycoon-2fa-aitm-detection-engineering
author: '@iimp0ster'
date: 2026-05-27
tags:
  - attack.persistence
  - attack.t1098.005
  - detection.maturity.hunt
logsource:
  product: azure
  service: auditlogs
detection:
  selection:
    OperationName: 'Add registered users to device'
    ResultType: '0'
  filter_native_ua:
    # Native OS registration clients — these are expected
    UserAgent|contains:
      - 'Dsreg'
      - 'DeviceRegistrationClient'
      - 'Dalvik'
      - 'Windows-AzureAD-Authentication-Provider'
  filter_known_automation:
    # PENDING: Add documented automation service accounts or Intune pipeline identities
    # InitiatedBy|contains:
    #   - 'svc-intune@tenant.onmicrosoft.com'
    InitiatedBy: ''  # placeholder — remove and populate before production
  condition: selection and not filter_native_ua and not filter_known_automation
falsepositives:
  - Third-party MDM solutions using custom HTTP clients for device enrollment
  - Custom Intune enrollment wrappers with non-standard UA strings
  - Vendor tooling not yet in the native UA filter
level: high
Device registration with non-native UA correlated with preceding AiTM sign-in...
Analyst
Goal
Device registration with non-native UA correlated with preceding AiTM sign-in signal from same UPN within 15 minutes.
Log Sources
  • Entra ID AuditLogs
  • Entra ID Sign-in Logs (for AiTM correlation)
FP Rate
Very Low
Use Case
SOC alerting. Immediate IR escalation. Trigger automated response: disable account → delete registered devices → revoke sessions → reset password.
operationName == "Add registered users to device" AND resultType == "0" AND userAgent NOT contains_any [Dsreg, DeviceRegistrationClient, Dalvik] AND same UPN has preceding sign-in with Node.js UA within 15 minutes. Correlated alert: high confidence AiTM device-PRT persistence chain.
Correlation required: This rule covers one stage only. See the description for SIEM-side join guidance.
Sigma Rule - Analyst Level
title: Entra ID AiTM Device PRT Enrollment — Non-Native UA Correlated with Preceding AiTM Sign-In
id: a7b8c9d0-e1f2-4a3b-4c5d-6e7f8a9b0c1d
status: experimental
description: >
  Detects a synthetic Entra ID device registration (non-native UA) within 15 minutes
  of a preceding AiTM kit relay sign-in (Node.js UA on OfficeHome/Auth Broker) for the
  same UPN. This correlation confirms the Tycoon 2FA persistence chain: kit relay steals
  session token → kit immediately enrolls synthetic device using DRS API with axios UA →
  device-bound PRT acquired → persists through revokeSignInSessions. IR teams MUST delete
  registered devices BEFORE revoking sessions; skipping device deletion leaves the PRT
  active and allows immediate re-authentication.
  NOTE: This rule requires correlation across two log sources (AuditLogs + SigninLogs) --
  implement as KQL join or SIEM correlation rule.
  KQL join (Sentinel):
    let AiTMSignIn = SigninLogs
      | where ResultType == "0"
        and UserAgent has_any ("node","axios","undici","node-fetch")
        and AppDisplayName has_any ("OfficeHome","Microsoft Authentication Broker")
      | project AiTMTime = TimeGenerated, UserPrincipalName;
    AuditLogs
    | where OperationName == "Add registered users to device" and ResultType == "0"
    | where not(UserAgent has_any ("Dsreg","DeviceRegistrationClient","Dalvik"))
    | extend UPN = tostring(InitiatedBy.user.userPrincipalName)
    | join kind=inner AiTMSignIn on $left.UPN == $right.UserPrincipalName
    | where TimeGenerated between (AiTMTime .. AiTMTime + 15m)
references:
  - https://www.elastic.co/security-labs/tycoon-2fa-aitm-detection-engineering
author: '@iimp0ster'
date: 2026-05-27
tags:
  - attack.persistence
  - attack.t1098.005
  - detection.maturity.analyst
logsource:
  product: azure
  service: auditlogs
detection:
  # This rule requires correlation across AuditLogs and SigninLogs — use the KQL join above.
  # Sigma stub fires on device registration with non-native UA; full correlation requires SIEM.
  selection_device_registration:
    OperationName: 'Add registered users to device'
    ResultType: '0'
  filter_native_ua:
    UserAgent|contains:
      - 'Dsreg'
      - 'DeviceRegistrationClient'
      - 'Dalvik'
      - 'Windows-AzureAD-Authentication-Provider'
  # CORRELATION: same UPN in SigninLogs with Node.js UA within 900s preceding -- KQL required
  condition: selection_device_registration and not filter_native_ua
falsepositives:
  - Automated device enrollment workflows triggered by sign-in event (document and exclude)
  - Split-session scenarios where Node.js tooling and device enrollment coincidentally overlap
level: high