← All hunts high TLP:CLEAR Part 1 of 2

Cloud Identity Takeover and DevOps Enumeration

An adversary hijacked a cloud identity using self-service password reset to perform automated discovery across Azure DevOps repositories and harvest Kubernetes configuration files.

Based on research by Microsoft 2026-09-30 13 steps · 4 queries T1078 T1087 T1552.001 T1566

Brief

Background

Microsoft recently published "Beyond source code: A path to the keys to the kingdom", detailing an adversary tracked as Storm-3068. This group targets development and DevOps environments by first hijacking identities through self-service password resets (SSPR). Once they gain access, they perform automated enumeration of Azure DevOps resources and harvest credentials from developer workstations.

Scoping the Environment

The hunt begins by identifying high-value targets within the fleet. We use software inventory data to list hosts running cloud management and container tools like kubectl, docker, and the Azure CLI. This creates a focused list of developer workstations, ensuring that subsequent file-level queries only run on hosts where Kubernetes configurations are likely to exist. Using software inventory instead of static host lists ensures the hunt remains effective as the engineering team grows.

Finding the Lead

The first behavioral check looks for self-service password resets in identity logs. We treat an SSPR event as a low-cost lead. While many resets are legitimate, Storm-3068 uses this mechanism to take control of accounts. The hunt identifies these events over a 14-day window to provide the starting point for the investigation. We focus specifically on successful resets that result in a password change.

Gating the Investigation

Because querying cloud API activity and file access across an enterprise is resource-intensive, this hunt uses a decision gate. An analyst or an automated agent reviews the SSPR events for suspicious patterns, such as resets from unusual locations or those performed by unexpected actors. The hunt only proceeds to the expensive phases if the identity takeover appears plausible. This prevents unnecessary volume in DevOps and endpoint logs.

Correlating DevOps Discovery

Once a lead is qualified, the hunt pivots into Azure DevOps audit logs. We look for the suspect account performing high-volume enumeration of repositories, pipelines, and projects. The query identifies accounts that touch more than five distinct DevOps objects in a short period. This threshold suggests automated mapping rather than typical developer work. We group results by operation and user to highlight the most aggressive discovery behaviors.

Checking for Credential Harvesting

In parallel with the cloud API check, the hunt examines file activity on the previously scoped developer hosts. We specifically look for the suspect user accessing Kubernetes configuration files, such as those named "kubeconfig" or "credentials". Finding a recently reset account suddenly enumerating DevOps repositories and touching K8s configs on a workstation provides a strong indicator of a Storm-3068 compromise. This step joins the identity lead with physical host activity.

Blind Spots

This hunt has two primary limitations. First, it lacks visibility into the specific MFA method registered during an SSPR event; an attacker registering a new device looks very similar to a legitimate user in basic logs. Second, while we see that a file was accessed or a commit was made, we cannot see the actual secrets within the file content. These require manual verification of the repository history.

Running the Hunt

This hunt is packaged as an open hunt.md playbook. You can import it into Huntbase or any compatible runtime to execute the gated flow. The design ensures you only spend your query budget on deep investigations when a clear identity lead exists. The playbook is self-contained and handles the pivots between identity, cloud, and endpoint surfaces automatically.

Beyond source code: A path to the keys to the kingdom: https://www.microsoft.com/en-us/security/blog/2026/09/29/beyond-source-code-a-path-to-the-keys-to-the-kingdom/

In this series

Steps

  1. Scope developer infrastructure

    Query · scoping

    Identify hosts that run cloud and Kubernetes management tools to focus endpoint file activity checks.

    reads hb_software_inventorysql
    SELECT DISTINCT device_hostname FROM hb_software_inventory WHERE instr(',' || '{{dev_tool_packages}}' || ',', ',' || LOWER(package_name) || ',') > 0

    What a hit looks like. A list of hostnames belonging to developers or administrators. Silence means no relevant tools are installed.

  2. Identify suspicious password resets

    Query · baseline

    Find accounts that performed a self-service password reset as a lead for identity takeover.

    reads hb_account_changesql
    SELECT user_name, actor_user_name, time, activity_name FROM hb_account_change WHERE activity_id = 4 AND provider = 'm365' AND time >= datetime('now', '-{{lookback_days}} days')

    What a hit looks like. A list of accounts that reset their passwords. Silence proves no SSPR activity occurred in the window.

  3. Analyze password reset patterns

    Agent triage

    Evaluate whether the account reset looks like a potential Storm-3068 takeover.

  4. Gate on suspected identity takeover

    Decision

    Open the expensive DevOps and file investigation only when the identity lead is real.

  5. Azure DevOps resource enumeration

    Query · detection candidate

    Identify accounts performing automated discovery across many repositories or pipelines.

    reads hb_cloud_api_activitysql
    SELECT actor_user_name, api_operation, COUNT(DISTINCT resource_name) AS res_count, MIN(time) AS first_seen FROM hb_cloud_api_activity WHERE provider = 'm365' AND (LOWER(api_operation) LIKE '%repository%' OR LOWER(api_operation) LIKE '%pipeline%' OR LOWER(api_operation) LIKE '%project%') AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY actor_user_name, api_operation HAVING res_count > 5

    What a hit looks like. An account mapping more than 5 DevOps objects in the window. Silence means no high-volume enumeration was detected.

  6. Kubernetes credential harvesting

    Query · enrichment

    Find file activity involving Kubernetes configuration files on scoped developer hosts.

    reads hb_file_activitysql
    SELECT device_hostname, actor_user_name, file_path, file_name, time FROM hb_file_activity WHERE instr(',' || '{{kubeconfig_files}}' || ',', ',' || LOWER(file_name) || ',') > 0 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')

    What a hit looks like. Access events on kubeconfig files, particularly by the account identified in the lead. Silence means no such files were touched.

  7. Weigh the intrusion chain

    Agent triage

    Correlate identity takeover, DevOps enumeration, and credential harvesting into a single verdict.

  8. Route remediation

    Decision

    Isolate the compromised identity or escalate for manual review.

  9. Isolate compromised identity

    Response action

    Immediately contain the threat by disabling the hijacked account and revoking tokens.

  10. Analyst review

    Analyst task

    Review the findings for indeterminate or unavailable data cases.

  11. Secrets and Git history review

    Analyst task

    Examine the content of Git commits to confirm if secrets were exfiltrated.

  12. Close out hunt

    Analyst task

    Finalize the hunt by documenting findings and tuning notes.

Coverage

Scenario coverage

StageCoveredHow, or why not
Identity Takeover via SSPR
T1566 · T1078
Yes sspr-lead, analyze-sspr-lead
Azure DevOps Environment Discovery
T1087 · T1018
Yes devops-enumeration-check
Kubernetes Credential Harvesting
T1552.001
Yes kubeconfig-access-check
Malicious Pipeline Deployment
T1059 · T1190
Out of scope Belongs to another part of the 'Beyond source code: A path to the keys to the kingdom' series.
RMM and Tunneling Tooling
T1219 · T1572
Out of scope Belongs to another part of the 'Beyond source code: A path to the keys to the kingdom' series.

Blind spots

  • Needs hb_account_change with mfa_method column. Legitimate SSPR followed by MFA registration may be indistinguishable from attacker takeover without method-level auditing. It would answer what specific authentication method was registered by the actor.
  • Needs Git version history content auditing. Audit logs show the commit and filename but not the sensitive content, requiring manual review. It would answer whether the files committed to the repository actually contained valid secrets.

Parameters & data

Parameters

ParameterTypeDefaultWhat it is
dev_tool_packageslist[string]kubectl, azure-cli, docker, helmPackage names indicating potential developer or cloud management infrastructure.
kubeconfig_fileslist[string]kubeconfig, config, credentialsFilenames associated with Kubernetes cluster configuration.
lookback_daysnumber14Days of history to examine.
scope_hostslist[host]—Hosts identified as developer workstations or cloud management endpoints; paste results from the scoping query here.

Telemetry

SourceCategoryTelemetry
Endpoint telemetry (hb_ surfaces)endpointendpoint
Identity / sign-in telemetryidentityidentity

Source

Download hunt.md Definition (JSON) An open hunt.md file; it runs anywhere that reads the format.
---
analysis: "A simple rule detects a password reset, but this hunt connects that reset\
  \ to automated Azure DevOps resource mapping and Kubernetes credential harvesting\u2014\
  a chain no single-surface rule can see."
blind_spots:
- id: mfa-registration-visibility
  question: what specific authentication method was registered by the actor
  requires: hb_account_change with mfa_method column
  risk: Legitimate SSPR followed by MFA registration may be indistinguishable from
    attacker takeover without method-level auditing.
  stage: identity-compromise-sspr
- id: git-content-visibility
  question: whether the files committed to the repository actually contained valid
    secrets
  requires: Git version history content auditing
  risk: Audit logs show the commit and filename but not the sensitive content, requiring
    manual review.
  stage: azure-devops-enumeration
coverage:
- stage: identity-compromise-sspr
  status: covered
  steps:
  - sspr-lead
  - analyze-sspr-lead
- stage: azure-devops-enumeration
  status: covered
  steps:
  - devops-enumeration-check
- stage: credential-harvesting-exfiltration
  status: covered
  steps:
  - kubeconfig-access-check
- reason: 'Belongs to another part of the ''\u200b\u200bBeyond source code: A path
    to the keys to the kingdom'' series.'
  stage: malicious-pipeline-execution
  status: out_of_scope
- reason: 'Belongs to another part of the ''\u200b\u200bBeyond source code: A path
    to the keys to the kingdom'' series.'
  stage: remote-access-tooling
  status: out_of_scope
guardrails:
  claims: no_unsupported
  evidence: citation_required
  missing_data: not_benign
  telemetry: untrusted
hunt:
  applicability: campaign-specific
  handoff: promote-to-detection
  justification: Hijacked cloud identities provide a path to production environments
    that bypasses malware detection; monitoring SSPR and discovery activity is vital
    for supply chain protection.
  methodology: model-assisted
  trigger: intel-report
hypothesis: An adversary hijacked a cloud identity using self-service password reset
  to perform automated discovery across Azure DevOps repositories and harvest Kubernetes
  configuration files.
labels:
- hunt
- attack.t1566
- attack.t1078
- attack.t1087
- attack.t1552.001
- command and control
- credential access
- discovery
- execution
- initial access
name: Cloud Identity Takeover and DevOps Enumeration
parameters:
  dev_tool_packages:
    default:
    - kubectl
    - azure-cli
    - docker
    - helm
    description: Package names indicating potential developer or cloud management
      infrastructure.
    type: list[string]
  kubeconfig_files:
    default:
    - kubeconfig
    - config
    - credentials
    description: Filenames associated with Kubernetes cluster configuration.
    from:
      kind: article
      observed: '2026-09-29'
      ref: msrc-beyond-source-code
    type: list[string]
  lookback_days:
    default: '14'
    description: Days of history to examine.
    type: number
  scope_hosts:
    default: []
    description: Hosts identified as developer workstations or cloud management endpoints;
      paste results from the scoping query here.
    type: list[host]
provenance:
  authors:
  - name: Huntbase hunt generation
    org: huntbase.io
  generated:
    by: huntbase-hunt-generation
    from: https://www.microsoft.com/en-us/security/blog/2026/09/29/beyond-source-code-a-path-to-the-keys-to-the-kingdom/
    gates:
    - dry-run
    - lint
    model: hb_google/gemini-3-flash-preview
rationale: The hunt uses a software inventory query to focus file activity checks
  on hosts with developer tools, then uses SSPR activity as a cheap lead to justify
  deeper DevOps log analysis.
references:
- name: 'Beyond source code: A path to the keys to the kingdom'
  url: https://www.microsoft.com/en-us/security/blog/2026/09/29/beyond-source-code-a-path-to-the-keys-to-the-kingdom/
related:
- hunt: remote-access-tooling-in-dev-pipelines
  reason: The execution of Atera and Chisel within build agents is a separate stage
    of the campaign focusing on compute telemetry.
  relation: out-of-scope-alternative
scenario:
  stages:
  - name: Identity Takeover via SSPR
    observables:
    - self-service password reset activity
    - registration of new authentication methods
    - MFA registration bypass
    slug: identity-compromise-sspr
    tactic: initial-access
    techniques:
    - T1566
    - T1078
  - name: Azure DevOps Environment Discovery
    observables:
    - enumeration of repositories, projects, pipelines, and deployment environments
    - automated scripts mapping cloud resources
    slug: azure-devops-enumeration
    tactic: discovery
    techniques:
    - T1087
    - T1018
  - name: Malicious Pipeline Deployment
    observables:
    - creation of malicious pipeline
    - deployment of kube agent
    - modification of pipeline scripts
    - execution of pipeline jobs to collect kubeconfig files
    slug: malicious-pipeline-execution
    tactic: execution
    techniques:
    - T1059
    - T1190
  - name: RMM and Tunneling Tooling
    observables:
    - Atera remote management agent installation
    - Chisel tunneling utility download
    - chisel commands establishing reverse tunnel to external IP
    slug: remote-access-tooling
    tactic: command-and-control
    techniques:
    - T1219
    - T1572
  - name: Kubernetes Credential Harvesting
    observables:
    - harvesting of kubeconfig files
    - addition of stolen kubeconfig files to a Git repository
    - Git version history modification
    slug: credential-harvesting-exfiltration
    tactic: credential-access
    techniques:
    - T1552.001
  summary: Storm-3068 compromised a user identity through a self-service password
    reset and registered their own MFA to gain persistent access. The actor pivoted
    to Azure DevOps to enumerate repositories and pipelines, then created a malicious
    pipeline to harvest Kubernetes credentials (kubeconfig) and establish remote access
    via Atera and Chisel protocol tunneling.
series:
  index: 1
  slug: beyond-source-code-a-path-to-the-keys-to-the-kingdom
  title: "\u200B\u200BBeyond source code: A path to the keys to the kingdom"
  total: 2
severity: high
targets:
  analyst:
    name: Tier-2 analyst
    role: analyst
  endpoint:
    category: endpoint
    name: Endpoint telemetry (hb_ surfaces)
    telemetry:
    - endpoint
  hunter:
    agent: true
    name: Hunt agent
  identity:
    category: identity
    name: Identity / sign-in telemetry
    telemetry:
    - identity
tlp: clear
type: investigation
---


# Cloud Identity Takeover and DevOps Enumeration

Storm-3068 has been observed compromising accounts through self-service password reset (SSPR) to bypass traditional credentials. This hunt identifies the initial takeover in identity logs and then pivots into Azure DevOps audit logs to find high-volume enumeration of repositories and pipelines. Finally, it checks for the access of Kubernetes configuration files on developer workstations. The gated flow ensures that expensive cloud API and file-level queries only run when a suspicious account reset is detected, while the scoping step identifies critical developer infrastructure.

## scoping-dev-infrastructure
<!-- Scope developer infrastructure -->
Identify hosts that run cloud and Kubernetes management tools to focus endpoint file activity checks.

```sqlite target=endpoint role=scoping params=(dev_tool_packages=dev_tool_packages)
~~~yaml
expected: A list of hostnames belonging to developers or administrators. Silence means
  no relevant tools are installed.
reads:
- package_name
- device_hostname
silence: not_evidence_of_absence
source: hb_software_inventory
verified: dry-run
verified_at: '2026-09-30'
~~~
SELECT DISTINCT device_hostname FROM hb_software_inventory WHERE instr(',' || '{{dev_tool_packages}}' || ',', ',' || LOWER(package_name) || ',') > 0
```

## sspr-lead
<!-- Identify suspicious password resets -->
Find accounts that performed a self-service password reset as a lead for identity takeover.

```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days)
~~~yaml
expected: A list of accounts that reset their passwords. Silence proves no SSPR activity
  occurred in the window.
reads:
- user_name
- actor_user_name
- time
- activity_id
- provider
silence: evidence_of_absence
source: hb_account_change
verified: dry-run
verified_at: '2026-09-30'
~~~
SELECT user_name, actor_user_name, time, activity_name FROM hb_account_change WHERE activity_id = 4 AND provider = 'm365' AND time >= datetime('now', '-{{lookback_days}} days')
```

## analyze-sspr-lead
<!-- Analyze password reset patterns -->
```agent target=hunter
cite: required
context:
- sspr-lead
max_iterations: 3
objective: Determine if any account reset was performed by an unusual actor or exhibits
  patterns of identity hijacking.
success_criteria: A per-user verdict of suspicious for any identity with irregular
  reset patterns.
tools:
- endpoint
```

## gate-on-suspected-takeover
<!-- Gate on suspected identity takeover -->
if~: "the analyze-sspr-lead verdict is suspicious for at least one account" (confidence: high, judge=hunter)
then: → expensive-investigation
indeterminate: → analyst-review
unavailable: → analyst-review (blind_spot: mfa-registration-visibility)
else: → close-out

## expensive-investigation
<!-- Parallel DevOps and credential check -->
parallel:
- → devops-enumeration-check
- → kubeconfig-access-check
join: → triage-intrusion-chain

## devops-enumeration-check
<!-- Azure DevOps resource enumeration -->
Identify accounts performing automated discovery across many repositories or pipelines.

```sqlite target=endpoint role=detection-candidate params=(lookback_days=lookback_days)
~~~yaml
baseline:
  compare: first_seen
  window: '{{lookback_days}}d'
expected: An account mapping more than 5 DevOps objects in the window. Silence means
  no high-volume enumeration was detected.
prevalence:
  by: actor_user_name
  key:
  - api_operation
  rare_below: 5
reads:
- actor_user_name
- api_operation
- resource_name
- time
- provider
silence: not_evidence_of_absence
source: hb_cloud_api_activity
verified: dry-run
verified_at: '2026-09-30'
~~~
SELECT actor_user_name, api_operation, COUNT(DISTINCT resource_name) AS res_count, MIN(time) AS first_seen FROM hb_cloud_api_activity WHERE provider = 'm365' AND (LOWER(api_operation) LIKE '%repository%' OR LOWER(api_operation) LIKE '%pipeline%' OR LOWER(api_operation) LIKE '%project%') AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY actor_user_name, api_operation HAVING res_count > 5
```

## kubeconfig-access-check
<!-- Kubernetes credential harvesting -->
Find file activity involving Kubernetes configuration files on scoped developer hosts.

```sqlite target=endpoint role=enrichment params=(lookback_days=lookback_days, scope_hosts=scope_hosts, kubeconfig_files=kubeconfig_files)
~~~yaml
expected: Access events on kubeconfig files, particularly by the account identified
  in the lead. Silence means no such files were touched.
reads:
- device_hostname
- actor_user_name
- file_name
- file_path
- time
silence: not_evidence_of_absence
source: hb_file_activity
verified: dry-run
verified_at: '2026-09-30'
~~~
SELECT device_hostname, actor_user_name, file_path, file_name, time FROM hb_file_activity WHERE instr(',' || '{{kubeconfig_files}}' || ',', ',' || LOWER(file_name) || ',') > 0 AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```

## triage-intrusion-chain
<!-- Weigh the intrusion chain -->
```agent target=hunter
cite: required
context:
- analyze-sspr-lead
- devops-enumeration-check
- kubeconfig-access-check
max_iterations: 6
objective: Review the suspected identity reset and determine if it was followed by
  automated resource discovery and sensitive file harvesting.
success_criteria: A per-host and per-user verdict of malicious | suspicious | benign
  citing specific API operations and file paths.
tools:
- endpoint
```

## route-remediation
<!-- Route remediation -->
if~: "the triage-intrusion-chain verdict is malicious for at least one host and user" (confidence: high, judge=hunter)
then: → isolate-identity
indeterminate: → analyst-review
unavailable: → analyst-review (blind_spot: git-content-visibility)
else: → close-out

## isolate-identity
<!-- Isolate compromised identity -->
```action target=identity
~~~yaml
approval: required
~~~
Disable the compromised user account in Entra ID, revoke all active Refresh Tokens, and reset the password.
```
→ secrets-rotation-review

## analyst-review
<!-- Analyst review -->
```manual target=analyst
Manually review the cited rows and determine if the SSPR activity and subsequent DevOps calls constitute an intrusion. Rotate credentials for any confirmed account compromise.
```
→ secrets-rotation-review

## secrets-rotation-review
<!-- Secrets and Git history review -->
```manual target=analyst
Review the Git version history for the repositories identified in the DevOps enumeration. Specifically look for commits containing kubeconfig data and rotate all cluster credentials found within.
```
→ close-out

## close-out
<!-- Close out hunt -->
```manual target=analyst
Record the findings, document the remediation steps taken, and update the detection tuning notes for SSPR activity.
```
→ end

Run it

Take this hunt into your environment.

Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.

Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, then reviewed by a person.