{
  "technique_id": "T1526",
  "name": "Cloud Service Discovery",
  "priority": "high",
  "status": "draft",
  "version": "0.1.0",
  "last_reviewed": "2026-07-23",
  "generated_by": "SOC Response Atlas by Basyrix",
  "tactics": [
    "Discovery"
  ],
  "platforms": [
    "IaaS",
    "Identity Provider",
    "Office Suite",
    "SaaS"
  ],
  "summary": "An adversary may attempt to enumerate the cloud services running on a system after gaining access. These methods can differ from platform-as-a-service (PaaS), to infrastructure-as-a-service (IaaS), or software-as-a-service (SaaS). Many services exist throughout the various cloud providers and can include Continuous Integration and Continuous Delivery (CI/CD), Lambda Functions, Entra ID, etc...",
  "soc_recommendation": "Investigate Cloud Service Discovery activity in the context of Discovery: confirm scope, affected host/identity, and whether it matches expected administrative behaviour before deciding this is benign.",
  "d3fend_mappings": [
    {
      "id": "D3-RC",
      "name": "Restore Configuration",
      "relationship": "restore",
      "practical_action": "Use Restore Configuration to recover affected systems or data after containment.",
      "tooling": [
        "Defender for Endpoint"
      ]
    },
    {
      "id": "D3-CI",
      "name": "Configuration Inventory",
      "relationship": "model",
      "practical_action": "Use Configuration Inventory to establish a baseline that makes this technique's deviations easier to spot.",
      "tooling": [
        "Defender for Endpoint"
      ]
    }
  ],
  "investigation_steps": {
    "microsoft": [
      "Review Defender for Endpoint / Defender XDR alerts and timeline for the affected host or identity.",
      "Check Sentinel analytics rules and incidents correlated with this technique.",
      "Review Entra ID sign-in and audit logs if the technique involves an identity or cloud resource."
    ],
    "generic": [
      "Confirm whether the observed cloud service discovery activity matches expected administrative or application behaviour.",
      "Identify the host, account, or resource where the activity occurred and its business criticality.",
      "Check for related alerts before and after this activity to reconstruct the broader intrusion timeline.",
      "Real detection reference: \"Suspicious VM Instance Creation Activity Detected\" -- This detection identifies high-severity alerts across various Microsoft security products, including Microsoft Defender XDR and Microsoft Entra ID, and correlates them with instances of Google Cloud VM creation. It focuses on instances where VMs were created within a short timeframe of high-severity alerts, potentially indicating suspicious activity.'"
    ]
  },
  "evidence_to_collect": [
    "Host or resource affected",
    "Account or identity involved",
    "Timestamp of the activity",
    "Related process, file, or network artifact",
    "Any preceding or follow-on alerts"
  ],
  "response_actions": [
    {
      "name": "Contain the affected host or account",
      "category": "Containment",
      "risk": "Medium",
      "automation_safe": false,
      "approval_required": true,
      "tool": "Defender for Endpoint / Entra ID",
      "notes": "Requires approval -- confirm malicious intent before isolating a host or disabling an account."
    },
    {
      "name": "Collect and preserve evidence",
      "category": "Investigation",
      "risk": "Low",
      "automation_safe": true,
      "approval_required": false,
      "tool": "Defender for Endpoint",
      "notes": "Safe -- read-only evidence collection."
    }
  ],
  "queries": {
    "kql": [
      {
        "name": "1cc0ba27-c5ca-411a-a779-fbc89e26be83 — Suspicious VM Instance Creation Activity Detected",
        "description": "This detection identifies high-severity alerts across various Microsoft security products, including Microsoft Defender XDR and Microsoft Entra ID, and correlates them with instances of Google Cloud VM creation. It focuses on instances where VMs were created within a short timeframe of high-severity alerts, potentially indicating suspicious activity.' (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed).)",
        "query": "// Filter alerts from specific Microsoft security products with medium and high severity\nSecurityAlert \n| where ProductName in (\"Microsoft 365 Defender\", \"Azure Active Directory\", \"Microsoft Defender Advanced Threat Protection\", \"Microsoft Cloud App Security\", \"Azure Active Directory Identity Protection\", \"Microsoft Defender ATP\")\n| where AlertSeverity has_any (\"Medium\", \"High\")\n// Parse JSON entities and extend AlertTimeGenerated\n| extend Entities = parse_json(Entities), AlertTimeGenerated=TimeGenerated\n// Extract and process IP entities\n| mv-apply Entity = Entities on \n    ( \n    where Entity.Type == 'ip' \n    | extend EntityIp = tostring(Entity.Address) \n    ) \n// Extract and process account entities\n| mv-apply Entity = Entities on \n    ( \n    where Entity.Type == 'account' \n    | extend AccountObjectId = tostring(Entity.AadUserId)\n    )\n// Filter out records with empty EntityIp\n| where isnotempty(EntityIp)\n// Summarize data and create sets of entities and system alert IDs\n| summarize Entitys=make_set(Entity), SystemAlertIds=make_set(SystemAlertId)\n    by \n    AlertName,\n    ProductName,\n    AlertSeverity,\n    EntityIp,\n    Tactics,\n    Techniques,\n    ProviderName,\n    AlertTime= bin(AlertTimeGenerated, 1d),\n    AccountObjectId\n// Join with GCPAuditLogs for VM instance creation\n| join kind=inner (\n    GCPAuditLogs\n    | where ServiceName == \"compute.googleapis.com\" and MethodName endswith \"instances.insert\"\n    | extend\n        GCPUserUPN= tostring(parse_json(AuthenticationInfo).principalEmail),\n        GCPUserIp = tostring(parse_json(RequestMetadata).callerIp),\n        GCPUserUA= tostring(parse_json(RequestMetadata).callerSuppliedUserAgent),\n        VMStatus =  tostring(parse_json(Response).status),\n        VMOperation=tostring(parse_json(Response).operationType),\n        VMName= tostring(parse_json(Request).name),\n        VMType = tostring(split(parse_json(Request).machineType, \"/\")[-1])\n    | where GCPUserUPN !has \"gserviceaccount.com\"\n    | where VMOperation == \"insert\" and isnotempty(GCPUserIp) and GCPUserIp != \"private\"\n    | project\n        GCPOperationTime=TimeGenerated,\n        VMName,\n        VMStatus,\n        MethodName,\n        GCPUserUPN,\n        ProjectId,\n        GCPUserIp,\n        GCPUserUA,\n        VMOperation,\n        VMType\n    )\n    on $left.EntityIp == $right.GCPUserIp \n// Join with IdentityInfo to enrich user identity details\n| join kind=inner (IdentityInfo \n    | distinct AccountObjectId, AccountUPN, JobTitle\n    )\n    on AccountObjectId \n// Calculate the time difference between the alert and VM creation for further analysis\n| extend TimeDiff= datetime_diff('day', AlertTime, GCPOperationTime),Name = split(GCPUserUPN, \"@\")[0], UPNSuffix = split(GCPUserUPN, \"@\")[1]"
      },
      {
        "name": "75647b58-bcc8-4eb5-9658-46698d3fa153 — AWSCloudTrail - SSM document is publicly exposed",
        "description": "Detects an AWS Systems Manager (SSM) document that has been made publicly accessible, which could lead to sensitive information exposure. Verify the document configurations and confirm the change was authorized. (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed).)",
        "query": "AWSCloudTrail\n| where  EventName == \"ModifyDocumentPermission\" and isempty(ErrorCode) and isempty(ErrorMessage)\n| where todynamic(parse_json(RequestParameters).[\"accountIdsToAdd\"]) == '[\"all\"]'\n| extend UserIdentityArn = iif(isempty(UserIdentityArn), tostring(parse_json(Resources)[0].ARN), UserIdentityArn)\n| extend UserName = tostring(split(UserIdentityArn, '/')[-1])\n| extend AccountName = case( UserIdentityPrincipalid == \"Anonymous\", \"Anonymous\", isempty(UserIdentityUserName), UserName, UserIdentityUserName)\n| extend AccountName = iif(AccountName contains \"@\", tostring(split(AccountName, '@', 0)[0]), AccountName),\n  AccountUPNSuffix = iif(AccountName contains \"@\", tostring(split(AccountName, '@', 1)[0]), \"\")  \n| project TimeGenerated, EventName, EventTypeName, UserIdentityAccountId, UserIdentityPrincipalid, UserAgent,UserIdentityUserName, SessionMfaAuthenticated, RecipientAccountId, AccountName, AccountUPNSuffix, SourceIpAddress, AWSRegion, EventSource, AdditionalEventData, RequestParameters, ResponseElements, UserIdentityArn"
      },
      {
        "name": "11650b85-d8cc-49c4-8c04-a8a739635983 — Dataverse - Honeypot instance activity",
        "description": "Identifies activities in a predefined Honeypot Dataverse instance. Alerts when either sign-in to the Honeypot is detected or when monitored Dataverse tables in the Honeypot are accessed. Note: Requires a dedicated Honeypot Dataverse instance in Power Platform with auditing enabled. (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed).)",
        "query": "let honeypot_dataverse_instances = dynamic([\"https://myinstance.crm.dynamics.com/\"]);\nlet honeypot_authorized_users = dynamic([\"scanner@mydomain.com\"]);\nlet monitored_dataverse_entities = dynamic([\"contact\", \"account\", \"opportunity\", \"lead\", \"competitor\"]);\nlet query_frequency = 1h;\nDataverseActivity\n| where TimeGenerated >= ago(query_frequency)\n| where InstanceUrl in (honeypot_dataverse_instances)\n| where UserId !in (honeypot_authorized_users)\n| where UserId !endswith \"@onmicrosoft.com\"\n    and UserId != \"Unknown\"\n    and isnotempty(ClientIp)\n| where Message in (\"UserSignIn\") or EntityName in (monitored_dataverse_entities)\n| summarize\n    TimeStart = min(TimeGenerated),\n    TimeEnd = max(TimeGenerated),\n    Entities = make_set(EntityName, 10),\n    Messages = make_set(Message, 10)\n    by UserId, ClientIp, InstanceUrl\n| extend Severity = iif(array_length(set_difference(Messages, dynamic([\"UserSignIn\"]))) > 0, \"Medium\", \"Low\")\n| extend CloudAppId = int(32780)\n| extend AccountName = tostring(split(UserId, '@')[0])\n| extend UPNSuffix = tostring(split(UserId, '@')[1])\n| project\n    TimeStart,\n    TimeEnd,\n    UserId,\n    ClientIp,\n    InstanceUrl,\n    Messages,\n    Entities,\n    Severity,\n    CloudAppId,\n    AccountName,\n    UPNSuffix"
      }
    ],
    "spl": [],
    "esql": [
      {
        "name": "a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7 — AWS Lateral Movement from Kubernetes SA via AssumeRoleWithWebIdentity",
        "description": "(ESQL) Detects when credentials issued through `AssumeRoleWithWebIdentity` for a Kubernetes service account identity are later used for several distinct AWS control-plane actions on the same session access key... (Source: Elastic's official detection-rules repository (Elastic License v2).)",
        "query": "FROM logs-aws.cloudtrail-*\n| WHERE (event.action == \"AssumeRoleWithWebIdentity\" AND user.name like \"system:serviceaccount:*\")\n  // S3 PutObject/GetObject is too  common in legit pod SA behavior \n  OR (event.action IN (\"ListBuckets\", \"DescribeInstances\", \"GetCallerIdentity\",\n    \"ListUsers\", \"ListRoles\", \"ListAttachedRolePolicies\", \"GetRolePolicy\",\n    \"GetSecretValue\", \"ListSecrets\",\n    \"GetParameters\", \"DescribeParameters\", \"ListKeys\", \"Decrypt\",\n    \"ListFunctions\", \"GetAuthorizationToken\",\n    \"SendCommand\", \"StartSession\",\n    \"CreateUser\", \"CreateAccessKey\", \"AttachRolePolicy\", \"CreateRole\",\n    \"PutRolePolicy\", \"UpdateAssumeRolePolicy\",\n    \"UpdateFunctionCode\", \"UpdateFunctionConfiguration\", \"ModifyInstanceAttribute\",\n    \"StopLogging\", \"DeleteTrail\")\n    AND aws.cloudtrail.user_identity.type == \"AssumedRole\")\n| GROK aws.cloudtrail.response_elements \"accessKeyId=%{NOTSPACE:issued_key_id},\"\n| EVAL access_key = COALESCE(issued_key_id, aws.cloudtrail.user_identity.access_key_id)\n| EVAL is_assume = CASE(event.action == \"AssumeRoleWithWebIdentity\", 1, 0)\n| EVAL is_post_exploit = CASE(event.action != \"AssumeRoleWithWebIdentity\", 1, 0)\n| EVAL phase = CASE(\n    event.action == \"AssumeRoleWithWebIdentity\", \"initial_access\",\n    event.action IN (\"ListBuckets\", \"DescribeInstances\", \"ListUsers\", \"ListRoles\",\n      \"GetCallerIdentity\", \"ListAttachedRolePolicies\", \"GetRolePolicy\",\n      \"ListFunctions\"), \"recon\",\n    event.action IN (\"GetSecretValue\", \"ListSecrets\", \"GetParameters\",\n      \"GetAuthorizationToken\", \"Decrypt\"), \"credential_access\",\n    event.action IN (\"SendCommand\", \"StartSession\"), \"lateral_movement\",\n    event.action IN (\"CreateUser\", \"CreateAccessKey\", \"AttachRolePolicy\",\n      \"CreateRole\", \"PutRolePolicy\", \"UpdateAssumeRolePolicy\",\n      \"UpdateFunctionCode\", \"UpdateFunctionConfiguration\",\n      \"ModifyInstanceAttribute\"), \"persistence\",\n    event.action IN (\"StopLogging\", \"DeleteTrail\"), \"defense_evasion\"\n  )\n| STATS \n    Esql.assume_count = SUM(is_assume),\n    Esql.post_exploit_count = COUNT_DISTINCT(event.action),\n    Esql.attack_phases = VALUES(phase),\n    Esql.event_action_values = VALUES(event.action),\n    Esql.source_ip_values = VALUES(source.ip),\n    Esql.source_as_organization_name_values = VALUES(source.as.organization.name),\n    Esql.user_name_values = VALUES(user.name),\n    Esql.user_agent_original_values = VALUES(user_agent.original),\n    Esql.cloud_account_id_values = VALUES(cloud.account.id),\n    Esql.data_stream_namespace_values = VALUES(data_stream.namespace),\n    Esql.first_seen = MIN(@timestamp),\n    Esql.last_seen = MAX(@timestamp),\n    Esql.total_calls = COUNT(*)\n  BY access_key\n| WHERE access_key is not null and Esql.assume_count >= 1 AND Esql.post_exploit_count >= 3\n| EVAL aws.cloudtrail.user_identity.access_key_id = MV_FIRST(access_key)\n| KEEP aws.cloudtrail.user_identity.access_key_id, Esql.*"
      },
      {
        "name": "80aa6cca-b343-457b-877e-5877cd71a1f8 — Azure AD Graph Potential Enumeration (ROADrecon)",
        "description": "(ESQL) Detects an Azure AD Graph (graph.windows.net) burst from a user-agent identifying as \"aiohttp\" (the default HTTP library used by ROADrecon's \"gather\" command) where a single calling identity issues many requests in a short window. ROADrecon walks every interesting directory object type via aiohttp, producing a large volume of requests from one user / source IP / UA triple... (Source: Elastic's official detection-rules repository (Elastic License v2).)",
        "query": "from logs-azure.aadgraphactivitylogs-* metadata _id, _version, _index\n\n| where data_stream.dataset == \"azure.aadgraphactivitylogs\"\n  and to_lower(user_agent.original) like \"*aiohttp*\"\n\n| eval Esql.target_endpoints = case(\n    url.path like \"*/eligibleRoleAssignments*\", \"eligibleRoleAssignments\",\n    url.path like \"*/roleAssignments*\",         \"roleAssignments\",\n    url.path like \"*/users*\",                   \"users\",\n    url.path like \"*/groups*\",                  \"groups\",\n    url.path like \"*/servicePrincipals*\",       \"servicePrincipals\",\n    url.path like \"*/applications*\",            \"applications\",\n    url.path like \"*/devices*\",                 \"devices\",\n    url.path like \"*/directoryRoles*\",          \"directoryRoles\",\n    url.path like \"*/roleDefinitions*\",         \"roleDefinitions\",\n    url.path like \"*/administrativeUnits*\",     \"administrativeUnits\",\n    url.path like \"*/contacts*\",                \"contacts\",\n    url.path like \"*/oauth2PermissionGrants*\",  \"oauth2PermissionGrants\",\n    url.path like \"*/authorizationPolicy*\",     \"authorizationPolicy\",\n    url.path like \"*/settings*\",                \"settings\",\n    url.path like \"*/policies*\",                \"policies\",\n    url.path like \"*/tenantDetails*\",           \"tenantDetails\",\n    \"other\"\n  )\n| where Esql.target_endpoints != \"other\"\n\n| eval Esql.time_window = date_trunc(1 minutes, @timestamp)\n\n| stats\n    Esql.request_count                = count(*),\n    Esql.distinct_endpoints           = count_distinct(Esql.target_endpoints),\n    Esql.api_versions                 = values(azure.aadgraphactivitylogs.properties.api_version),\n    Esql.app_ids                      = values(azure.aadgraphactivitylogs.properties.app_id),\n    Esql.user_agent                   = values(user_agent.original),\n    Esql.http_methods                 = values(http.request.method),\n    Esql.status_codes                 = values(http.response.status_code),\n    Esql.source_ips                   = values(source.ip),\n    Esql.source_asn_orgs              = values(source.`as`.organization.name),\n    Esql.source_countries             = values(source.geo.country_name),\n    Esql.actor_types                  = values(azure.aadgraphactivitylogs.properties.actor_type),\n    Esql.client_auth_methods          = values(azure.aadgraphactivitylogs.properties.client_auth_method),\n    Esql.session_ids                  = values(azure.aadgraphactivitylogs.properties.session_id),\n    Esql.sign_in_activity_ids         = values(azure.aadgraphactivitylogs.properties.sign_in_activity_id),\n    Esql.scopes                       = values(azure.aadgraphactivitylogs.properties.scopes),\n    Esql.first_seen                   = min(@timestamp),\n    Esql.last_seen                    = max(@timestamp)\n  by\n    user.id,\n    azure.tenant_id,\n    Esql.time_window\n\n| where Esql.distinct_endpoints >= 5\n\n| keep\n    user.id,\n    azure.tenant_id,\n    Esql.*"
      }
    ]
  },
  "automation": {
    "safe": [
      "Add recommendation as Sentinel incident comment.",
      "Run enrichment queries.",
      "Create ServiceNow SecOps task."
    ],
    "approval_required": [
      "Contain or disable the affected host/account.",
      "Any change to production configuration."
    ]
  },
  "escalation_criteria": [
    "Cloud Service Discovery activity observed on a privileged account or critical system.",
    "Activity follows or precedes other suspicious behaviour in the same investigation.",
    "Automated triage cannot confidently rule out malicious intent."
  ],
  "false_positive_considerations": [
    "Legitimate administrative or maintenance activity matching this pattern.",
    "Approved security testing or red team exercise.",
    "Known benign software producing similar telemetry."
  ],
  "confluence": {
    "title": "T1526 - Cloud Service Discovery Response Guidance",
    "labels": [
      "mitre",
      "attack",
      "d3fend",
      "secops",
      "basyrix"
    ],
    "sections": [
      "summary",
      "d3fend_mappings",
      "investigation_steps",
      "response_actions",
      "queries",
      "automation",
      "escalation_criteria",
      "false_positive_considerations"
    ]
  }
}