{
  "hunt": {
    "meta": {
      "tlp": "clear",
      "hunt": {
        "handoff": "promote-to-detection",
        "trigger": "intel-report",
        "methodology": "model-assisted",
        "applicability": "campaign-specific",
        "justification": "EvilTokens has compromised over 10,000 organizations by automating the capture of session tokens. Identifying the interaction phase prevents unauthorized account access."
      },
      "name": "EvilTokens Client-Side Phishing Interaction",
      "type": "investigation",
      "labels": [
        "hunt",
        "attack.t1566.001",
        "attack.t1566.002",
        "attack.t1115",
        "attack.t1041"
      ],
      "series": {
        "slug": "unmasking-eviltokens-getting-to-the-root-of-device-code-phishing",
        "index": 1,
        "title": "Unmasking EvilTokens: Getting to the root of device code phishing",
        "total": 2
      },
      "related": [
        {
          "hunt": "unauthorized-device-code-authentication",
          "reason": "This hunt detects the interaction; a follow-on hunt is needed to identify successful, unauthorized sign-ins in the authentication logs.",
          "relation": "follows"
        }
      ],
      "targets": {
        "web": {
          "name": "Web server / proxy logs",
          "category": "siem",
          "telemetry": [
            "network"
          ]
        },
        "hunter": {
          "name": "Hunt agent",
          "agent": true
        },
        "analyst": {
          "name": "Tier-2 analyst",
          "role": "analyst"
        },
        "endpoint": {
          "name": "Endpoint telemetry (hb_ surfaces)",
          "category": "endpoint",
          "telemetry": [
            "endpoint"
          ]
        }
      },
      "analysis": "A standard rule might flag a known malicious URL; this hunt uses behavioral timing (the 3-5 second ping interval) and cross-surface correlation (file touch vs. network redirect) to find zero-day phishing infrastructure.",
      "coverage": [
        {
          "stage": "initial-access-ai-phishing",
          "steps": [
            "suspicious-lure-files"
          ],
          "status": "covered"
        },
        {
          "stage": "device-code-polling-interaction",
          "steps": [
            "background-state-polling",
            "device-login-navigation"
          ],
          "status": "covered"
        },
        {
          "stage": "unauthorized-device-code-authentication",
          "reason": "Belongs to another part of the 'Unmasking EvilTokens: Getting to the root of device code phishing' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "malicious-inbox-rule-persistence",
          "reason": "Belongs to another part of the 'Unmasking EvilTokens: Getting to the root of device code phishing' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "rogue-device-registration",
          "reason": "Belongs to another part of the 'Unmasking EvilTokens: Getting to the root of device code phishing' series.",
          "status": "out_of_scope"
        },
        {
          "stage": "graph-api-reconnaissance-and-collection",
          "reason": "Belongs to another part of the 'Unmasking EvilTokens: Getting to the root of device code phishing' series.",
          "status": "out_of_scope"
        }
      ],
      "scenario": {
        "stages": [
          {
            "name": "AI-Tailored Phishing Delivery",
            "slug": "initial-access-ai-phishing",
            "tactic": "initial-access",
            "techniques": [
              "T1566"
            ],
            "observables": [
              "PDF attachments",
              "HTML file attachments",
              "Invoices or RFP themes",
              "High-pressure lures like 'Action Required: Password Expiration'",
              "Short-lived polling node URLs"
            ]
          },
          {
            "name": "Client-Side Device Code Interaction",
            "slug": "device-code-polling-interaction",
            "tactic": "execution",
            "techniques": [
              "T1115"
            ],
            "observables": [
              "checkStatus() function",
              "setInterval polling every 3-5 seconds",
              "Pings to /state endpoint",
              "Automated clipboard copy of device codes",
              "Redirects to microsoft.com/devicelogin",
              "Buttons labeled 'Continue with Microsoft' or 'Copy Code'"
            ]
          },
          {
            "name": "MFA Bypass via Device Code Flow",
            "slug": "unauthorized-device-code-authentication",
            "tactic": "credential-access",
            "observables": [
              "Sign-in to 'Device Registration Service' resource",
              "OAuth device code flow authentications from anomalous IPs",
              "Authentication sessions decoupled from the originating lure session"
            ]
          },
          {
            "name": "Concealed Communication via Inbox Rules",
            "slug": "malicious-inbox-rule-persistence",
            "tactic": "persistence",
            "observables": [
              "Creation of new inbox rules designed to hide communications",
              "Rules filtering for specific keywords or alerting to Telegram"
            ]
          },
          {
            "name": "Long-Term Persistence via Device Registration",
            "slug": "rogue-device-registration",
            "tactic": "persistence",
            "observables": [
              "Registration of new devices within 10 minutes of breach",
              "Generation of Primary Refresh Tokens (PRT) for unauthorized devices"
            ]
          },
          {
            "name": "Graph API Recon and Email Exfiltration",
            "slug": "graph-api-reconnaissance-and-collection",
            "tactic": "collection",
            "techniques": [
              "T1041"
            ],
            "observables": [
              "Microsoft Graph API calls for organizational structure mapping",
              "Bulk email exfiltration using stolen tokens",
              "AI-assisted analysis of compromised inbox content"
            ]
          }
        ],
        "summary": "EvilTokens is a PhaaS platform operated by Storm-2992 that facilitates device code phishing via AI-tailored lures and automated polling infrastructure. The attack steals OAuth tokens to bypass MFA, enabling threat actors to maintain persistence through malicious inbox rules, register new devices, and exfiltrate email data via the Microsoft Graph API."
      },
      "severity": "high",
      "rationale": "Focus on endpoints with active users in the last 14 days. Prioritize users with roles often targeted by BEC lures, such as finance or HR.",
      "guardrails": {
        "claims": "no_unsupported",
        "evidence": "citation_required",
        "telemetry": "untrusted",
        "missing_data": "not_benign"
      },
      "hypothesis": "An intruder has delivered an AI-tailored phishing lure that, when opened, initiates high-frequency background polling to a malicious Node.js endpoint while redirecting the user to the Microsoft device login portal.",
      "parameters": {
        "scope_hosts": {
          "type": "list[host]",
          "default": [],
          "description": "Specific hostnames to scope the hunt; leave empty for fleet-wide."
        },
        "lookback_days": {
          "type": "number",
          "default": "14",
          "description": "Days of history to examine."
        },
        "lure_extensions": {
          "from": {
            "ref": "msrc-blog-eviltokens",
            "kind": "article",
            "observed": "2026-09-22"
          },
          "type": "list[string]",
          "default": [
            ".html",
            ".htm",
            ".pdf"
          ],
          "description": "Common lure file extensions associated with EvilTokens campaigns."
        },
        "trusted_domains": {
          "from": {
            "ref": "https://www.microsoft.com/en-us/security/blog/2026/09/22/unmasking-eviltokens-getting-to-the-root-of-device-code-phishing/",
            "kind": "article",
            "observed": "2026-09-22"
          },
          "type": "list[domain]",
          "default": [
            "microsoft.com",
            "google.com",
            "bing.com",
            "apple.com",
            "linkedin.com"
          ],
          "description": "Known-good domains to exclude from background polling results."
        }
      },
      "provenance": {
        "authors": [
          {
            "org": "huntbase.io",
            "name": "Huntbase hunt generation"
          }
        ],
        "generated": {
          "by": "huntbase-hunt-generation",
          "from": "https://www.microsoft.com/en-us/security/blog/2026/09/22/unmasking-eviltokens-getting-to-the-root-of-device-code-phishing/",
          "gates": [
            "dry-run",
            "lint"
          ],
          "model": "hb_google/gemini-3-flash-preview"
        }
      },
      "references": [
        {
          "url": "https://www.microsoft.com/en-us/security/blog/2026/09/22/unmasking-eviltokens-getting-to-the-root-of-device-code-phishing/",
          "name": "MSRC - Unmasking EvilTokens: Getting to the root of device code phishing"
        }
      ],
      "blind_spots": [
        {
          "id": "encrypted-http-visibility",
          "risk": "If the network source only logs the hostname (via SNI) and not the path, the polling pattern cannot be confirmed, potentially hiding the automated nature of the phishing page.",
          "stage": "device-code-polling-interaction",
          "question": "Can we see the URL path /state within encrypted HTTPS traffic?",
          "requires": "TLS interception or EDR browser-extension visibility"
        },
        {
          "id": "memory-only-lures",
          "risk": "EvilTokens can function purely via a link in a browser; if the user never downloads the PDF or HTML lure, the hb_file_activity step will return no rows.",
          "stage": "initial-access-ai-phishing",
          "question": "Does the hunt miss lures that never touch the disk?",
          "requires": "hb_http_activity"
        }
      ]
    },
    "name": "EvilTokens Client-Side Phishing Interaction",
    "description": "EvilTokens is a PhaaS platform that specializes in device code phishing. This hunt identifies the early stages of an attack by detecting the characteristic 3-5 second polling interval of the background script ('checkStatus') and the concurrent user redirection to microsoft.com/devicelogin. By correlating these behaviors with the presence of suspicious invoice or RFP-themed lure files, we can identify sessions that are actively being phished before token theft is completed. This behavioral approach bypasses rotating domains and AI-generated lures that evade standard signature-based controls."
  },
  "nodes": [
    {
      "id": "hypothesis",
      "type": "hypothesis",
      "label": "Hypothesis",
      "config": {
        "tags": [],
        "series": {
          "slug": "unmasking-eviltokens-getting-to-the-root-of-device-code-phishing",
          "index": 1,
          "title": "Unmasking EvilTokens: Getting to the root of device code phishing",
          "total": 2
        },
        "coverage": [
          {
            "stage": "initial-access-ai-phishing",
            "steps": [
              "suspicious-lure-files"
            ],
            "status": "covered"
          },
          {
            "stage": "device-code-polling-interaction",
            "steps": [
              "background-state-polling",
              "device-login-navigation"
            ],
            "status": "covered"
          },
          {
            "stage": "unauthorized-device-code-authentication",
            "reason": "Belongs to another part of the 'Unmasking EvilTokens: Getting to the root of device code phishing' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "malicious-inbox-rule-persistence",
            "reason": "Belongs to another part of the 'Unmasking EvilTokens: Getting to the root of device code phishing' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "rogue-device-registration",
            "reason": "Belongs to another part of the 'Unmasking EvilTokens: Getting to the root of device code phishing' series.",
            "status": "out_of_scope"
          },
          {
            "stage": "graph-api-reconnaissance-and-collection",
            "reason": "Belongs to another part of the 'Unmasking EvilTokens: Getting to the root of device code phishing' series.",
            "status": "out_of_scope"
          }
        ],
        "rationale": "An intruder has delivered an AI-tailored phishing lure that, when opened, initiates high-frequency background polling to a malicious Node.js endpoint while redirecting the user to the Microsoft device login portal.",
        "blind_spots": [
          {
            "id": "encrypted-http-visibility",
            "risk": "If the network source only logs the hostname (via SNI) and not the path, the polling pattern cannot be confirmed, potentially hiding the automated nature of the phishing page.",
            "stage": "device-code-polling-interaction",
            "question": "Can we see the URL path /state within encrypted HTTPS traffic?",
            "requires": "TLS interception or EDR browser-extension visibility"
          },
          {
            "id": "memory-only-lures",
            "risk": "EvilTokens can function purely via a link in a browser; if the user never downloads the PDF or HTML lure, the hb_file_activity step will return no rows.",
            "stage": "initial-access-ai-phishing",
            "question": "Does the hunt miss lures that never touch the disk?",
            "requires": "hb_http_activity"
          }
        ],
        "scoping_notes": "Focus on endpoints with active users in the last 14 days. Prioritize users with roles often targeted by BEC lures, such as finance or HR.",
        "beyond_detection": "A standard rule might flag a known malicious URL; this hunt uses behavioral timing (the 3-5 second ping interval) and cross-surface correlation (file touch vs. network redirect) to find zero-day phishing infrastructure."
      }
    },
    {
      "id": "suspicious-lure-files",
      "type": "query",
      "label": "Suspicious Lure File Creation",
      "config": {
        "dsl": "sqlite",
        "role": "scoping",
        "source": "endpoint",
        "content": "SELECT device_hostname, actor_user_name, file_path, file_name, time FROM hb_file_activity WHERE activity_id = 1 AND (LOWER(file_path) LIKE '%\\\\downloads\\\\%' OR LOWER(file_path) LIKE '%\\\\desktop\\\\%') AND (instr(',' || '{{lure_extensions}}' || ',', ',' || LOWER(SUBSTR(file_name, -4)) || ',') > 0 OR instr(',' || '{{lure_extensions}}' || ',', ',' || LOWER(SUBSTR(file_name, -5)) || ',') > 0) AND (LOWER(file_name) LIKE '%invoice%' OR LOWER(file_name) LIKE '%rfp%' OR LOWER(file_name) LIKE '%proposal%' OR LOWER(file_name) LIKE '%expiration%' OR LOWER(file_name) LIKE '%compensation%' OR LOWER(file_name) LIKE '%benefits%') AND time >= datetime('now', '-{{lookback_days}} days')",
        "surface": "hb_file_activity",
        "description": "Identify the creation of potential phishing lures in user-writable paths with themes mentioned in the EvilTokens research.",
        "expected_signal": "Rows mapping lures to hosts. Silence is not evidence of absence as lures can be hosted purely in-browser without a file download."
      },
      "parents": [
        {
          "id": "hypothesis"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "scoping",
        "label": "Suspicious Lure File Creation",
        "reads": [
          "device_hostname",
          "actor_user_name",
          "file_path",
          "file_name",
          "time"
        ],
        "source": "hb_file_activity",
        "target": "endpoint",
        "content": "SELECT device_hostname, actor_user_name, file_path, file_name, time FROM hb_file_activity WHERE activity_id = 1 AND (LOWER(file_path) LIKE '%\\\\downloads\\\\%' OR LOWER(file_path) LIKE '%\\\\desktop\\\\%') AND (instr(',' || '{{lure_extensions}}' || ',', ',' || LOWER(SUBSTR(file_name, -4)) || ',') > 0 OR instr(',' || '{{lure_extensions}}' || ',', ',' || LOWER(SUBSTR(file_name, -5)) || ',') > 0) AND (LOWER(file_name) LIKE '%invoice%' OR LOWER(file_name) LIKE '%rfp%' OR LOWER(file_name) LIKE '%proposal%' OR LOWER(file_name) LIKE '%expiration%' OR LOWER(file_name) LIKE '%compensation%' OR LOWER(file_name) LIKE '%benefits%') AND time >= datetime('now', '-{{lookback_days}} days')",
        "silence": "not_evidence_of_absence",
        "expected": "Rows mapping lures to hosts. Silence is not evidence of absence as lures can be hosted purely in-browser without a file download.",
        "verified": "dry-run",
        "verified_at": "2026-09-23"
      }
    },
    {
      "id": "background-state-polling",
      "type": "query",
      "label": "High-Frequency Background Polling",
      "config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "source": "web",
        "content": "SELECT device_hostname, src_endpoint_ip, url_hostname, COUNT(*) as request_count, MIN(time) as first_ping, MAX(time) as last_ping FROM hb_http_activity WHERE url_path LIKE '%/state%' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND instr(',' || '{{trusted_domains}}' || ',', ',' || LOWER(url_hostname) || ',') = 0 AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY device_hostname, src_endpoint_ip, url_hostname HAVING request_count > 10 AND (strftime('%s', MAX(time)) - strftime('%s', MIN(time))) / request_count <= 6",
        "surface": "hb_http_activity",
        "description": "Identify the EvilTokens checkStatus polling behavior, which pings a /state endpoint every 3-5 seconds.",
        "expected_signal": "A single host making frequent requests to a non-Microsoft domain with a consistent sub-6-second gap. This indicates the phishing kit loop."
      },
      "parents": [
        {
          "id": "suspicious-lure-files"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "detection-candidate",
        "label": "High-Frequency Background Polling",
        "reads": [
          "device_hostname",
          "src_endpoint_ip",
          "url_hostname",
          "url_path",
          "time"
        ],
        "source": "hb_http_activity",
        "target": "web",
        "content": "SELECT device_hostname, src_endpoint_ip, url_hostname, COUNT(*) as request_count, MIN(time) as first_ping, MAX(time) as last_ping FROM hb_http_activity WHERE url_path LIKE '%/state%' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND instr(',' || '{{trusted_domains}}' || ',', ',' || LOWER(url_hostname) || ',') = 0 AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY device_hostname, src_endpoint_ip, url_hostname HAVING request_count > 10 AND (strftime('%s', MAX(time)) - strftime('%s', MIN(time))) / request_count <= 6",
        "silence": "not_evidence_of_absence",
        "expected": "A single host making frequent requests to a non-Microsoft domain with a consistent sub-6-second gap. This indicates the phishing kit loop.",
        "verified": "dry-run",
        "prevalence": {
          "by": "device_hostname",
          "key": [
            "url_hostname"
          ],
          "rare_below": 3
        },
        "verified_at": "2026-09-23"
      }
    },
    {
      "id": "device-login-navigation",
      "type": "query",
      "label": "Navigation to Microsoft Device Login",
      "config": {
        "dsl": "sqlite",
        "role": "enrichment",
        "source": "web",
        "content": "SELECT device_hostname, actor_user_name, url_hostname, url_path, referrer, time FROM hb_http_activity WHERE url_hostname = 'microsoft.com' AND url_path LIKE '/devicelogin%' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')",
        "surface": "hb_http_activity",
        "description": "Confirm the user navigated to the legitimate device login portal, which is the destination for the EvilTokens redirect.",
        "expected_signal": "A visit to the device login page. While legitimate, its timing must be weighed against lure delivery and polling behavior."
      },
      "parents": [
        {
          "id": "suspicious-lure-files"
        }
      ],
      "primitive_config": {
        "dsl": "sqlite",
        "role": "enrichment",
        "label": "Navigation to Microsoft Device Login",
        "reads": [
          "device_hostname",
          "actor_user_name",
          "url_hostname",
          "url_path",
          "referrer",
          "time"
        ],
        "source": "hb_http_activity",
        "target": "web",
        "content": "SELECT device_hostname, actor_user_name, url_hostname, url_path, referrer, time FROM hb_http_activity WHERE url_hostname = 'microsoft.com' AND url_path LIKE '/devicelogin%' AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')",
        "silence": "not_evidence_of_absence",
        "expected": "A visit to the device login page. While legitimate, its timing must be weighed against lure delivery and polling behavior.",
        "verified": "dry-run",
        "verified_at": "2026-09-23"
      }
    },
    {
      "id": "triage-interaction",
      "type": "analytic",
      "label": "Triage Phishing Interaction",
      "config": {
        "cite": "required",
        "tools": [
          "endpoint",
          "web"
        ],
        "context": [
          "suspicious-lure-files",
          "background-state-polling",
          "device-login-navigation"
        ],
        "objective": "Determine if any single host shows a combination of suspicious lure creation, consistent sub-6-second background polling to a non-trusted domain, and a visit to microsoft.com/devicelogin.",
        "description": "Correlate lure files, background polling, and device login redirection per host to determine if a successful phishing interaction occurred.",
        "max_iterations": 4,
        "expected_signal": "A host-by-host verdict on whether an EvilTokens interaction occurred.",
        "success_criteria": "A per-host verdict of malicious, suspicious, or benign with cited rows for any host showing concurrent polling and redirection."
      },
      "parents": [
        {
          "id": "background-state-polling",
          "kind": "merge"
        },
        {
          "id": "device-login-navigation",
          "kind": "merge"
        }
      ]
    },
    {
      "id": "route-on-verdict",
      "type": "checkpoint",
      "label": "Route on Interaction Verdict",
      "config": {
        "fuzzy": true,
        "judge": "hunter",
        "question": "the triage-interaction verdict is malicious for at least one host",
        "condition": "the triage-interaction verdict is malicious for at least one host",
        "blind_spot": "encrypted-http-visibility",
        "confidence": "high",
        "description": "Direct confirmed beachhead hosts to containment.",
        "checkpoint_type": "mandatory"
      },
      "parents": [
        {
          "id": "triage-interaction"
        }
      ]
    },
    {
      "id": "isolate-host",
      "type": "action",
      "label": "Isolate Affected Host",
      "config": {
        "target": "endpoint",
        "description": "Isolate the host to prevent further interaction with the PhaaS infrastructure or token capture.",
        "instructions": "Isolate the endpoint. Revoke all active sessions for the identified user in Azure AD/M365 and force a password reset immediately.",
        "action_approval": "required"
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "on_supports"
        }
      ]
    },
    {
      "id": "manual-analyst-review",
      "type": "task",
      "label": "Manual Analyst Review",
      "config": {
        "assignee": "analyst",
        "description": "Confirm the findings and identify the specific polling domain for blocklisting.",
        "instructions": "Review the url_hostname identified in the polling step. Use whois or threat intelligence to confirm Node.js infrastructure. Check for other hosts visiting the same domain."
      },
      "parents": [
        {
          "id": "route-on-verdict",
          "branch": "default"
        },
        {
          "id": "route-on-verdict",
          "branch": "on_unavailable"
        },
        {
          "id": "route-on-verdict",
          "branch": "on_refutes"
        },
        {
          "id": "isolate-host"
        }
      ]
    },
    {
      "id": "close-out",
      "type": "task",
      "label": "Hunt Close-Out",
      "config": {
        "assignee": "analyst",
        "description": "Finalize documentation and consider promoting the polling logic to a standing detection rule.",
        "instructions": "Record the results. If the polling query produced high-fidelity results without false positives, promote it to a standing detection candidate."
      },
      "parents": [
        {
          "id": "manual-analyst-review"
        }
      ]
    }
  ]
}