{
  "technique_id": "T1595",
  "name": "Active Scanning",
  "priority": "medium",
  "status": "complete",
  "version": "0.1.0",
  "last_reviewed": "2026-07-23",
  "generated_by": "SOC Response Atlas by Basyrix",
  "tactics": [
    "Reconnaissance"
  ],
  "platforms": [
    "PRE"
  ],
  "summary": "Adversaries may execute active reconnaissance scans to gather information that can be used during targeting. Active scans are those where the adversary probes victim infrastructure via network traffic, as opposed to other forms of reconnaissance that do not involve direct interaction. Adversaries may perform different forms of active scanning depending on what information they seek to gather...",
  "soc_recommendation": "Investigate Active Scanning activity in the context of Reconnaissance: confirm scope, affected host/identity, and whether it matches expected administrative behaviour before deciding this is benign.",
  "d3fend_mappings": [
    {
      "id": "D3-NTA",
      "name": "Network Traffic Analysis",
      "relationship": "detect",
      "practical_action": "Monitor for Network Traffic Analysis indicators relevant to this technique.",
      "tooling": [
        "Sentinel",
        "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 active scanning 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: \"Active Scanning of External Infrastructure\" -- Flags systematic scanning of external-facing infrastructure - vulnerability scanner fingerprints at the WAF, or port-scan signatures at the firewall/IPS. Sustained scanning targeting a specific org often precedes exploitation within hours or days."
    ]
  },
  "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": "Block persistent, high-volume scanning IPs at WAF",
      "category": "Response",
      "risk": "Medium",
      "automation_safe": false,
      "approval_required": true,
      "tool": "See investigation guide"
    },
    {
      "name": "If targeted CVE scanning: escalate patching of that vulnerab",
      "category": "Response",
      "risk": "Low",
      "automation_safe": false,
      "approval_required": true,
      "tool": "See investigation guide",
      "notes": "If targeted CVE scanning: escalate patching of that vulnerability immediately"
    },
    {
      "name": "Enable WAF in Prevention mode if in Detection mode",
      "category": "Response",
      "risk": "Low",
      "automation_safe": false,
      "approval_required": true,
      "tool": "See investigation guide"
    },
    {
      "name": "Add scanning IP to Sentinel TI watchlist for correlation",
      "category": "Response",
      "risk": "Low",
      "automation_safe": false,
      "approval_required": true,
      "tool": "Sentinel"
    }
  ],
  "queries": {
    "kql": [
      {
        "name": "GEN-RC-001 — Active Scanning of External Infrastructure",
        "description": "Flags systematic scanning of external-facing infrastructure - vulnerability scanner fingerprints at the WAF, or port-scan signatures at the firewall/IPS. Sustained scanning targeting a specific org often precedes exploitation within hours or days. (Source: Bell Integration baseline detection library.)",
        "query": "let WafScans = (\n    AzureDiagnostics\n    | where TimeGenerated >= ago(1h)\n    | where Category in (\"ApplicationGatewayFirewallLog\", \"ApplicationGatewayAccessLog\", \"FrontdoorWebApplicationFirewallLog\")\n    | where action_s in (\"Blocked\", \"Detected\")\n    | where details_message_s has_any (\n        \"nmap\", \"masscan\", \"shodan\", \"nuclei\", \"nikto\", \"sqlmap\", \"dirb\", \"gobuster\",\n        \"zgrab\", \"zmap\", \"metasploit\", \"hydra\", \"burp suite\", \"nessus\", \"openvas\",\n        \"scanner\", \"vulnerability scanner\", \"wpscan\"\n    )\n        or ruleSetType_s has_any (\"Microsoft_BotManagerRuleSet\", \"Microsoft_DefaultRuleSet\")\n    | summarize ScanCount = count(), TargetPaths = make_set(requestUri_s, 10)\n        by SourceIP = clientIp_s, Hostname = hostname_s\n    | where ScanCount > 20\n    | extend DetectionType = \"WAF_Scanner_Fingerprint\"\n);\nlet FirewallScans = (\n    CommonSecurityLog\n    | where TimeGenerated >= ago(1h)\n    | where Activity has_any (\"portscan\", \"port scan\", \"Port Scan\", \"TCP SYN\", \"SYN Flood\", \"Nmap\", \"Scanner\")\n        or DeviceEventClassID has_any (\"portscan\", \"probe\")\n    | summarize ScanCount = count(), UniqueDestPorts = dcount(DestinationPort)\n        by SourceIP, Hostname = DestinationIP\n    | where ScanCount > 50 or UniqueDestPorts > 20\n    | extend DetectionType = \"Firewall_Port_Scan\"\n);\nWafScans\n| union FirewallScans\n| extend timestamp = now(), IPCustomEntity = SourceIP\n| order by ScanCount desc"
      },
      {
        "name": "9b8dd8fd-f192-42eb-84f6-541920400a7a — App Gateway WAF - Scanner Detection",
        "description": "Identifies a match for a Scanner detection user agent based attack in the App Gateway WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements. References: https://owasp.org/www-community/Vulnerability_Scanning_Tools' (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed).)",
        "query": "let Threshold = 3;\nAGWFirewallLogs\n| where Action == \"Matched\"\n| where Message contains \"Found User-Agent associated with security scanner\"\n| project TransactionId, Hostname, RequestUri, TimeGenerated, ClientIp, Message, DetailedMessage, DetailedData\n| join kind = inner(\nAGWFirewallLogs\n| where Action == \"Blocked\"\n) on TransactionId\n| extend Uri = strcat(Hostname,RequestUri)\n| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), TransactionID = make_set(TransactionId), Message = make_set(Message), Detail_Message = make_set(DetailedMessage), Detail_Data = make_set(DetailedData), Total_TransactionId = dcount(TransactionId) by ClientIp, Uri, Action\n| where Total_TransactionId >= Threshold"
      },
      {
        "name": "e261b70a-3005-4a1b-a7a2-2c8147fafed7 — blacklens Insights",
        "description": "Creates incidents from blacklens.io Attack Surface Management alerts ingested into the blacklens_CL table. Alert severity is mapped dynamically from the source data.' (Source: Microsoft's official Azure-Sentinel Detections (MIT licensed).)",
        "query": "blacklens_CL\n| summarize arg_max(TimeGenerated, *) by id\n| extend AlertSeverity = case(\n    tolower(severity) == \"critical\", \"High\",\n    tolower(severity) == \"high\", \"High\",\n    tolower(severity) == \"medium\", \"Medium\",\n    tolower(severity) == \"low\", \"Low\",\n    \"Informational\"\n)"
      }
    ],
    "spl": [],
    "esql": [
      {
        "name": "bd7345e5-c822-41de-a393-7573fec07ef4 — GKE Anonymous Endpoint Permission Enumeration",
        "description": "(ESQL) Detects bursts of GKE API requests from an anonymous identity that probe many distinct actions and resources with mostly failed outcomes. This pattern is consistent with unauthenticated permission enumeration against an exposed API server... (Source: Elastic's official detection-rules repository (Elastic License v2).)",
        "query": "from logs-gcp.audit-* metadata _id, _index, _version\n| where data_stream.dataset == \"gcp.audit\"\n    and service.name == \"k8s.io\"\n    and (\n      client.user.email in (\"system:anonymous\", \"system:unauthenticated\")\n      or client.user.email is null\n    )\n    and not gcp.audit.resource_name in (\"readyz\", \"livez\", \"healthz\", \"version\")\n| stats\n    Esql.document_count = count(),\n    Esql.failure_count = sum(case(event.outcome == \"failure\", 1, 0)),\n    Esql.event_action_count_distinct = count_distinct(event.action),\n    Esql.resource_name_count_distinct = count_distinct(gcp.audit.resource_name),\n    Esql.event_action_values = values(event.action),\n    Esql.resource_name_values = values(gcp.audit.resource_name),\n    Esql.event_outcome_values = values(event.outcome),\n    Esql.client_user_email_values = values(client.user.email),\n    Esql.timestamp = VALUES(@timestamp),\n    Esql.data_stream_namespace = VALUES(data_stream.namespace),\n    Esql.user_agent_original_values = VALUES(user_agent.original)\n  by source.ip\n| where Esql.event_action_count_distinct > 5\n    and Esql.resource_name_count_distinct > 3\n    and Esql.document_count < 50\n    and Esql.failure_count >= 1\n| keep Esql.*, source.ip"
      },
      {
        "name": "2dd0d4fd-0cc9-4d18-8b46-1a507e28bbc0 — Kubernetes Potential Endpoint Permission Enumeration Attempt by Anonymous User Detected",
        "description": "(ESQL) This rule detects potential endpoint enumeration attempts by an anonymous user. An anonymous user is a user that is not authenticated or authorized to access the Kubernetes API server. By looking for a series of failed API requests, on multiple endpoints, and a limited number of documents, this rule can detect automated permission enumeration attempts... (Source: Elastic's official detection-rules repository (Elastic License v2).)",
        "query": "from logs-kubernetes.audit_logs-* metadata _id, _index, _version\n| where (\n    kubernetes.audit.user.username in (\"system:anonymous\", \"system:unauthenticated\") or\n    kubernetes.audit.user.username is null or\n    kubernetes.audit.user.username == \"\"\n  ) and\n  kubernetes.audit.level in (\"RequestResponse\", \"ResponseComplete\", \"Request\")\n\n| eval Esql.decision = `kubernetes.audit.annotations.authorization_k8s_io/decision`\n| eval Esql.code = kubernetes.audit.responseStatus.code\n\n| eval Esql.outcome = case(\n    Esql.decision == \"allow\", \"authz_allow\",\n    Esql.decision == \"forbid\", \"authz_forbid\",\n\n    // fallback: infer from status when decision is missing\n    Esql.code in (401, 403), \"authn_authz_failed\",\n    (Esql.code >= 200 and Esql.code < 300), \"success\",\n    Esql.code == 404, \"not_found\",\n    Esql.code is null, \"unknown\",\n    true, \"other_error\"\n  )\n\n| stats\n    Esql.document_count = count(),\n\n    Esql.authz_allow_count = sum(case(Esql.outcome == \"authz_allow\", 1, 0)),\n    Esql.authz_forbid_count = sum(case(Esql.outcome == \"authz_forbid\", 1, 0)),\n\n    Esql.status_fail_count = sum(case(Esql.outcome == \"authn_authz_failed\", 1, 0)),\n    Esql.success_count = sum(case(Esql.outcome == \"success\", 1, 0)),\n    Esql.not_found_count = sum(case(Esql.outcome == \"not_found\", 1, 0)),\n    Esql.other_error_count = sum(case(Esql.outcome == \"other_error\", 1, 0)),\n    Esql.unknown_count = sum(case(Esql.outcome == \"unknown\", 1, 0)),\n\n    Esql.kubernetes_audit_verb_count_distinct = count_distinct(kubernetes.audit.verb),\n    Esql.kubernetes_audit_requestURI_count_distinct = count_distinct(kubernetes.audit.requestURI),\n    Esql.kubernetes_audit_objectRef_resource_count_distinct = count_distinct(kubernetes.audit.objectRef.resource),\n\n    Esql.kubernetes_audit_outcome_values = values(Esql.outcome),\n    Esql.kubernetes_audit_decision_values = values(Esql.decision),\n    Esql.kubernetes_audit_responseStatus_code_values = values(Esql.code),\n    Esql.kubernetes_audit_responseStatus_message_values = values(kubernetes.audit.responseStatus.message),\n\n    Esql.kubernetes_audit_verb_values = values(kubernetes.audit.verb),\n    Esql.kubernetes_audit_objectRef_resource_values = values(kubernetes.audit.objectRef.resource),\n    Esql.kubernetes_audit_objectRef_namespace_values = values(kubernetes.audit.objectRef.namespace),\n    Esql.kubernetes_audit_user_username_values = values(kubernetes.audit.user.username),\n    Esql.kubernetes_audit_user_groups_values = values(kubernetes.audit.user.groups),\n    Esql.kubernetes_audit_requestURI_values = values(kubernetes.audit.requestURI),\n    Esql.data_stream_namespace_values = values(data_stream.namespace)\n\n  BY kubernetes.audit.sourceIPs, user_agent.original\n\n| where\n    Esql.kubernetes_audit_requestURI_count_distinct > 5 and\n    Esql.kubernetes_audit_objectRef_resource_count_distinct > 3 and\n    Esql.document_count < 50 and\n    (Esql.authz_forbid_count >= 1 or Esql.status_fail_count >= 1 or Esql.not_found_count >= 3)\n\n| keep Esql.*, kubernetes.audit.sourceIPs, user_agent.original"
      }
    ]
  },
  "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": [
    "Active Scanning 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": "T1595 - Active Scanning Response Guidance",
    "labels": [
      "mitre",
      "attack",
      "d3fend",
      "secops",
      "basyrix"
    ],
    "sections": [
      "summary",
      "d3fend_mappings",
      "investigation_steps",
      "response_actions",
      "queries",
      "automation",
      "escalation_criteria",
      "false_positive_considerations"
    ]
  }
}