Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

RDP Nesting

Back
Id69a45b05-71f5-45ca-8944-2e038747fb39
RulenameRDP Nesting
DescriptionQuery detects potential lateral movement within a network by identifying when an RDP connection (EventID 4624, LogonType 10) is made to an initial system, followed by a subsequent RDP connection from that system to another, using the same account within a 60-minute window.

To reduce false positives, it excludes scenarios where the same account has made 5 or more connections to the same set of computers in the previous 7 days. This approach focuses on highlighting unusual RDP behaviour that suggests lateral movement, which is often associated with attacker tactics during a network breach.
SeverityMedium
TacticsLateralMovement
TechniquesT1021
Required data connectorsSecurityEvents
WindowsForwardedEvents
WindowsSecurityEvents
KindScheduled
Query frequency1d
Query period8d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/RDP_Nesting.yaml
Version1.2.7
Arm template69a45b05-71f5-45ca-8944-2e038747fb39.json
Deploy To Azure
let endtime = 1d;
let starttime = 8d;
// The threshold below excludes matching on RDP connection computer counts of 5 or more by a given account and IP in a given day. Change the threshold as needed.
let threshold = 5;
// Function to resolve hostname to IP address using DNS logs or a lookup table (example syntax)
let rdpConnections = 
    (union isfuzzy=true
        (
        SecurityEvent
        | where TimeGenerated >= ago(endtime)
        | where EventID == 4624 and LogonType == 10
        // Labeling the first RDP connection time, computer and ip
        | extend
            FirstHop = bin(TimeGenerated, 1m),
            FirstComputer = toupper(Computer),
            FirstIPAddress = IpAddress,
            Account = tolower(Account)
        ),
        (
        WindowsEvent
        | where TimeGenerated >= ago(endtime)
        | where EventID == 4624 and EventData has ("10")
        | extend LogonType = tostring(EventData.LogonType)
        | where LogonType == 10 // Labeling the first RDP connection time, computer and ip
        | extend Account = strcat(tostring(EventData.TargetDomainName), "\\", tostring(EventData.TargetUserName))
        | extend IpAddress = tostring(EventData.IpAddress)
        | extend
            FirstHop = bin(TimeGenerated, 1m),
            FirstComputer = toupper(Computer),
            FirstIPAddress = IpAddress,
            Account = tolower(Account)
        ))
    | join kind=inner (
        (union isfuzzy=true
            (
            SecurityEvent
            | where TimeGenerated >= ago(endtime)
            | where EventID == 4624 and LogonType == 10
            // Labeling the second RDP connection time, computer and ip
            | extend
                SecondHop = bin(TimeGenerated, 1m),
                SecondComputer = toupper(Computer),
                SecondIPAddress = IpAddress,
                Account = tolower(Account)
            ),
            (
            WindowsEvent
            | where TimeGenerated >= ago(endtime)
            | where EventID == 4624 and EventData has ("10")
            | extend LogonType = toint(EventData.LogonType)
            | where LogonType == 10 // Labeling the second RDP connection time, computer and ip
            | extend Account = strcat(tostring(EventData.TargetDomainName), "\\", tostring(EventData.TargetUserName))
            | extend IpAddress = tostring(EventData.IpAddress)
            | extend
                SecondHop = bin(TimeGenerated, 1m),
                SecondComputer = toupper(Computer),
                SecondIPAddress = IpAddress,
                Account = tolower(Account)
            ))
        )
        on Account
    // Ensure the first connection is before the second connection
    // Identify only RDP to another computer from within the first RDP connection by only choosing matches where the Computer names do not match
    // Ensure the IPAddresses do not match by excluding connections from the same computers with first hop RDP connections to multiple computers
    | where FirstComputer != SecondComputer
        and FirstIPAddress != SecondIPAddress
        and SecondHop > FirstHop
    // Ensure the second hop occurs within 30 minutes of the first hop
    | where SecondHop <= FirstHop + 30m
    | distinct
        Account,
        FirstHop,
        FirstComputer,
        FirstIPAddress,
        SecondHop,
        SecondComputer,
        SecondIPAddress,
        AccountType,
        Activity,
        LogonTypeName,
        ProcessName;        
// Resolve hostnames to IP addresses device network Ip's
let listOfFirstComputer = rdpConnections | distinct FirstComputer;
let listOfSecondComputer = rdpConnections | distinct SecondComputer;
let resolvedIPs =  
    DeviceNetworkInfo
    | where TimeGenerated >= ago(endtime)
    | where isnotempty(ConnectedNetworks) and NetworkAdapterStatus == "Up"
    | extend ClientIP = tostring(parse_json(IPAddresses[0]).IPAddress)
    | where isnotempty(ClientIP)
    | where DeviceName in~  (listOfFirstComputer) or DeviceName in~ (listOfSecondComputer)
    | summarize arg_max(TimeGenerated, ClientIP) by Computer= DeviceName
    | project Computer=toupper(Computer), ResolvedIP = ClientIP;
// Join resolved IPs with the RDP connections
rdpConnections
| join kind=inner (resolvedIPs) on $left.FirstComputer == $right.Computer
| join kind=inner (resolvedIPs) on $left.SecondComputer == $right.Computer
| where ResolvedIP != ResolvedIP1
| distinct
    Account,
    FirstHop,
    FirstComputer,
    FirstIPAddress,
    SecondHop,
    SecondComputer,
    SecondIPAddress,
    AccountType,
    Activity,
    LogonTypeName,
    ProcessName
// Use left anti to exclude anything from the previous 7 days where the Account and IP has connected to 5 or more computers.
| join kind=leftanti (
    (union isfuzzy=true
        (
        SecurityEvent
        | where TimeGenerated >= ago(starttime) and TimeGenerated < ago(endtime)
        | where EventID == 4624 and LogonType == 10
        | summarize make_set(Computer), ComputerCount = dcount(Computer) by bin(TimeGenerated, 1d), Account = tolower(Account), IpAddress
        // Connection count to computer by same account and IP to exclude counts of 5 or more on a given day
        | where ComputerCount >= threshold
        | mvexpand set_Computer
        | extend Computer = toupper(set_Computer)
        ),
        (
        WindowsEvent
        | where TimeGenerated >= ago(starttime) and TimeGenerated < ago(endtime)
        | where EventID == 4624 and EventData has ("10")
        | extend LogonType = tostring(EventData.LogonType)
        | where LogonType == 10
        | extend Account = strcat(tostring(EventData.TargetDomainName), "\\", tostring(EventData.TargetUserName))
        | extend IpAddress = tostring(EventData.IpAddress)
        | summarize make_set(Computer), ComputerCount = dcount(Computer) by bin(TimeGenerated, 1d), Account = tolower(Account), IpAddress
        // Connection count to computer by same account and IP to exclude counts of 5 or more on a given day
        | where ComputerCount >= threshold
        | mvexpand set_Computer
        | extend Computer = toupper(set_Computer)
        ))
    )
    on
    Account,
    $left.SecondComputer == $right.Computer,
    $left.SecondIPAddress == $right.IpAddress
| summarize FirstHopFirstSeen = min(FirstHop), FirstHopLastSeen = max(FirstHop)
    by
    Account,
    FirstComputer,
    FirstIPAddress,
    SecondHop,
    SecondComputer,
    SecondIPAddress,
    AccountType,
    Activity,
    LogonTypeName,
    ProcessName
| extend
    AccountName = tostring(split(Account, @"\")[1]),
    AccountNTDomain = tostring(split(Account, @"\")[0])
| extend
    HostName1 = tostring(split(FirstComputer, ".")[0]),
    DomainIndex = toint(indexof(FirstComputer, '.'))
| extend HostNameDomain1 = iff(DomainIndex != -1, substring(FirstComputer, DomainIndex + 1), FirstComputer)
| extend
    HostName2 = tostring(split(SecondComputer, ".")[0]),
    DomainIndex = toint(indexof(SecondComputer, '.'))
| extend HostNameDomain2 = iff(DomainIndex != -1, substring(SecondComputer, DomainIndex + 1), SecondComputer)
| project-away DomainIndex
version: 1.2.7
severity: Medium
queryFrequency: 1d
metadata:
  categories:
    domains:
    - Security - Threat Protection
  author:
    name: Microsoft Security Research
  support:
    tier: Community
  source:
    kind: Community
triggerOperator: gt
relevantTechniques:
- T1021
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/RDP_Nesting.yaml
kind: Scheduled
triggerThreshold: 0
query: |
  let endtime = 1d;
  let starttime = 8d;
  // The threshold below excludes matching on RDP connection computer counts of 5 or more by a given account and IP in a given day. Change the threshold as needed.
  let threshold = 5;
  // Function to resolve hostname to IP address using DNS logs or a lookup table (example syntax)
  let rdpConnections = 
      (union isfuzzy=true
          (
          SecurityEvent
          | where TimeGenerated >= ago(endtime)
          | where EventID == 4624 and LogonType == 10
          // Labeling the first RDP connection time, computer and ip
          | extend
              FirstHop = bin(TimeGenerated, 1m),
              FirstComputer = toupper(Computer),
              FirstIPAddress = IpAddress,
              Account = tolower(Account)
          ),
          (
          WindowsEvent
          | where TimeGenerated >= ago(endtime)
          | where EventID == 4624 and EventData has ("10")
          | extend LogonType = tostring(EventData.LogonType)
          | where LogonType == 10 // Labeling the first RDP connection time, computer and ip
          | extend Account = strcat(tostring(EventData.TargetDomainName), "\\", tostring(EventData.TargetUserName))
          | extend IpAddress = tostring(EventData.IpAddress)
          | extend
              FirstHop = bin(TimeGenerated, 1m),
              FirstComputer = toupper(Computer),
              FirstIPAddress = IpAddress,
              Account = tolower(Account)
          ))
      | join kind=inner (
          (union isfuzzy=true
              (
              SecurityEvent
              | where TimeGenerated >= ago(endtime)
              | where EventID == 4624 and LogonType == 10
              // Labeling the second RDP connection time, computer and ip
              | extend
                  SecondHop = bin(TimeGenerated, 1m),
                  SecondComputer = toupper(Computer),
                  SecondIPAddress = IpAddress,
                  Account = tolower(Account)
              ),
              (
              WindowsEvent
              | where TimeGenerated >= ago(endtime)
              | where EventID == 4624 and EventData has ("10")
              | extend LogonType = toint(EventData.LogonType)
              | where LogonType == 10 // Labeling the second RDP connection time, computer and ip
              | extend Account = strcat(tostring(EventData.TargetDomainName), "\\", tostring(EventData.TargetUserName))
              | extend IpAddress = tostring(EventData.IpAddress)
              | extend
                  SecondHop = bin(TimeGenerated, 1m),
                  SecondComputer = toupper(Computer),
                  SecondIPAddress = IpAddress,
                  Account = tolower(Account)
              ))
          )
          on Account
      // Ensure the first connection is before the second connection
      // Identify only RDP to another computer from within the first RDP connection by only choosing matches where the Computer names do not match
      // Ensure the IPAddresses do not match by excluding connections from the same computers with first hop RDP connections to multiple computers
      | where FirstComputer != SecondComputer
          and FirstIPAddress != SecondIPAddress
          and SecondHop > FirstHop
      // Ensure the second hop occurs within 30 minutes of the first hop
      | where SecondHop <= FirstHop + 30m
      | distinct
          Account,
          FirstHop,
          FirstComputer,
          FirstIPAddress,
          SecondHop,
          SecondComputer,
          SecondIPAddress,
          AccountType,
          Activity,
          LogonTypeName,
          ProcessName;        
  // Resolve hostnames to IP addresses device network Ip's
  let listOfFirstComputer = rdpConnections | distinct FirstComputer;
  let listOfSecondComputer = rdpConnections | distinct SecondComputer;
  let resolvedIPs =  
      DeviceNetworkInfo
      | where TimeGenerated >= ago(endtime)
      | where isnotempty(ConnectedNetworks) and NetworkAdapterStatus == "Up"
      | extend ClientIP = tostring(parse_json(IPAddresses[0]).IPAddress)
      | where isnotempty(ClientIP)
      | where DeviceName in~  (listOfFirstComputer) or DeviceName in~ (listOfSecondComputer)
      | summarize arg_max(TimeGenerated, ClientIP) by Computer= DeviceName
      | project Computer=toupper(Computer), ResolvedIP = ClientIP;
  // Join resolved IPs with the RDP connections
  rdpConnections
  | join kind=inner (resolvedIPs) on $left.FirstComputer == $right.Computer
  | join kind=inner (resolvedIPs) on $left.SecondComputer == $right.Computer
  | where ResolvedIP != ResolvedIP1
  | distinct
      Account,
      FirstHop,
      FirstComputer,
      FirstIPAddress,
      SecondHop,
      SecondComputer,
      SecondIPAddress,
      AccountType,
      Activity,
      LogonTypeName,
      ProcessName
  // Use left anti to exclude anything from the previous 7 days where the Account and IP has connected to 5 or more computers.
  | join kind=leftanti (
      (union isfuzzy=true
          (
          SecurityEvent
          | where TimeGenerated >= ago(starttime) and TimeGenerated < ago(endtime)
          | where EventID == 4624 and LogonType == 10
          | summarize make_set(Computer), ComputerCount = dcount(Computer) by bin(TimeGenerated, 1d), Account = tolower(Account), IpAddress
          // Connection count to computer by same account and IP to exclude counts of 5 or more on a given day
          | where ComputerCount >= threshold
          | mvexpand set_Computer
          | extend Computer = toupper(set_Computer)
          ),
          (
          WindowsEvent
          | where TimeGenerated >= ago(starttime) and TimeGenerated < ago(endtime)
          | where EventID == 4624 and EventData has ("10")
          | extend LogonType = tostring(EventData.LogonType)
          | where LogonType == 10
          | extend Account = strcat(tostring(EventData.TargetDomainName), "\\", tostring(EventData.TargetUserName))
          | extend IpAddress = tostring(EventData.IpAddress)
          | summarize make_set(Computer), ComputerCount = dcount(Computer) by bin(TimeGenerated, 1d), Account = tolower(Account), IpAddress
          // Connection count to computer by same account and IP to exclude counts of 5 or more on a given day
          | where ComputerCount >= threshold
          | mvexpand set_Computer
          | extend Computer = toupper(set_Computer)
          ))
      )
      on
      Account,
      $left.SecondComputer == $right.Computer,
      $left.SecondIPAddress == $right.IpAddress
  | summarize FirstHopFirstSeen = min(FirstHop), FirstHopLastSeen = max(FirstHop)
      by
      Account,
      FirstComputer,
      FirstIPAddress,
      SecondHop,
      SecondComputer,
      SecondIPAddress,
      AccountType,
      Activity,
      LogonTypeName,
      ProcessName
  | extend
      AccountName = tostring(split(Account, @"\")[1]),
      AccountNTDomain = tostring(split(Account, @"\")[0])
  | extend
      HostName1 = tostring(split(FirstComputer, ".")[0]),
      DomainIndex = toint(indexof(FirstComputer, '.'))
  | extend HostNameDomain1 = iff(DomainIndex != -1, substring(FirstComputer, DomainIndex + 1), FirstComputer)
  | extend
      HostName2 = tostring(split(SecondComputer, ".")[0]),
      DomainIndex = toint(indexof(SecondComputer, '.'))
  | extend HostNameDomain2 = iff(DomainIndex != -1, substring(SecondComputer, DomainIndex + 1), SecondComputer)
  | project-away DomainIndex  
entityMappings:
- fieldMappings:
  - columnName: Account
    identifier: FullName
  - columnName: AccountName
    identifier: Name
  - columnName: AccountNTDomain
    identifier: NTDomain
  entityType: Account
- fieldMappings:
  - columnName: FirstComputer
    identifier: FullName
  - columnName: HostName1
    identifier: HostName
  - columnName: HostNameDomain1
    identifier: NTDomain
  entityType: Host
- fieldMappings:
  - columnName: SecondComputer
    identifier: FullName
  - columnName: HostName2
    identifier: HostName
  - columnName: HostNameDomain2
    identifier: NTDomain
  entityType: Host
- fieldMappings:
  - columnName: FirstIPAddress
    identifier: Address
  entityType: IP
name: RDP Nesting
queryPeriod: 8d
description: |
  'Query detects potential lateral movement within a network by identifying when an RDP connection (EventID 4624, LogonType 10) is made to an initial system, followed by a subsequent RDP connection from that system to another, using the same account within a 60-minute window.
   To reduce false positives, it excludes scenarios where the same account has made 5 or more connections to the same set of computers in the previous 7 days. This approach focuses on highlighting unusual RDP behaviour that suggests lateral movement, which is often associated with attacker tactics during a network breach.'  
requiredDataConnectors:
- dataTypes:
  - SecurityEvent
  connectorId: SecurityEvents
- dataTypes:
  - SecurityEvent
  connectorId: WindowsSecurityEvents
- dataTypes:
  - WindowsEvent
  connectorId: WindowsForwardedEvents
id: 69a45b05-71f5-45ca-8944-2e038747fb39
tactics:
- LateralMovement
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/69a45b05-71f5-45ca-8944-2e038747fb39')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/69a45b05-71f5-45ca-8944-2e038747fb39')]",
      "properties": {
        "alertRuleTemplateName": "69a45b05-71f5-45ca-8944-2e038747fb39",
        "customDetails": null,
        "description": "'Query detects potential lateral movement within a network by identifying when an RDP connection (EventID 4624, LogonType 10) is made to an initial system, followed by a subsequent RDP connection from that system to another, using the same account within a 60-minute window.\n To reduce false positives, it excludes scenarios where the same account has made 5 or more connections to the same set of computers in the previous 7 days. This approach focuses on highlighting unusual RDP behaviour that suggests lateral movement, which is often associated with attacker tactics during a network breach.'\n",
        "displayName": "RDP Nesting",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "Account",
                "identifier": "FullName"
              },
              {
                "columnName": "AccountName",
                "identifier": "Name"
              },
              {
                "columnName": "AccountNTDomain",
                "identifier": "NTDomain"
              }
            ]
          },
          {
            "entityType": "Host",
            "fieldMappings": [
              {
                "columnName": "FirstComputer",
                "identifier": "FullName"
              },
              {
                "columnName": "HostName1",
                "identifier": "HostName"
              },
              {
                "columnName": "HostNameDomain1",
                "identifier": "NTDomain"
              }
            ]
          },
          {
            "entityType": "Host",
            "fieldMappings": [
              {
                "columnName": "SecondComputer",
                "identifier": "FullName"
              },
              {
                "columnName": "HostName2",
                "identifier": "HostName"
              },
              {
                "columnName": "HostNameDomain2",
                "identifier": "NTDomain"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "FirstIPAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/RDP_Nesting.yaml",
        "query": "let endtime = 1d;\nlet starttime = 8d;\n// The threshold below excludes matching on RDP connection computer counts of 5 or more by a given account and IP in a given day. Change the threshold as needed.\nlet threshold = 5;\n// Function to resolve hostname to IP address using DNS logs or a lookup table (example syntax)\nlet rdpConnections = \n    (union isfuzzy=true\n        (\n        SecurityEvent\n        | where TimeGenerated >= ago(endtime)\n        | where EventID == 4624 and LogonType == 10\n        // Labeling the first RDP connection time, computer and ip\n        | extend\n            FirstHop = bin(TimeGenerated, 1m),\n            FirstComputer = toupper(Computer),\n            FirstIPAddress = IpAddress,\n            Account = tolower(Account)\n        ),\n        (\n        WindowsEvent\n        | where TimeGenerated >= ago(endtime)\n        | where EventID == 4624 and EventData has (\"10\")\n        | extend LogonType = tostring(EventData.LogonType)\n        | where LogonType == 10 // Labeling the first RDP connection time, computer and ip\n        | extend Account = strcat(tostring(EventData.TargetDomainName), \"\\\\\", tostring(EventData.TargetUserName))\n        | extend IpAddress = tostring(EventData.IpAddress)\n        | extend\n            FirstHop = bin(TimeGenerated, 1m),\n            FirstComputer = toupper(Computer),\n            FirstIPAddress = IpAddress,\n            Account = tolower(Account)\n        ))\n    | join kind=inner (\n        (union isfuzzy=true\n            (\n            SecurityEvent\n            | where TimeGenerated >= ago(endtime)\n            | where EventID == 4624 and LogonType == 10\n            // Labeling the second RDP connection time, computer and ip\n            | extend\n                SecondHop = bin(TimeGenerated, 1m),\n                SecondComputer = toupper(Computer),\n                SecondIPAddress = IpAddress,\n                Account = tolower(Account)\n            ),\n            (\n            WindowsEvent\n            | where TimeGenerated >= ago(endtime)\n            | where EventID == 4624 and EventData has (\"10\")\n            | extend LogonType = toint(EventData.LogonType)\n            | where LogonType == 10 // Labeling the second RDP connection time, computer and ip\n            | extend Account = strcat(tostring(EventData.TargetDomainName), \"\\\\\", tostring(EventData.TargetUserName))\n            | extend IpAddress = tostring(EventData.IpAddress)\n            | extend\n                SecondHop = bin(TimeGenerated, 1m),\n                SecondComputer = toupper(Computer),\n                SecondIPAddress = IpAddress,\n                Account = tolower(Account)\n            ))\n        )\n        on Account\n    // Ensure the first connection is before the second connection\n    // Identify only RDP to another computer from within the first RDP connection by only choosing matches where the Computer names do not match\n    // Ensure the IPAddresses do not match by excluding connections from the same computers with first hop RDP connections to multiple computers\n    | where FirstComputer != SecondComputer\n        and FirstIPAddress != SecondIPAddress\n        and SecondHop > FirstHop\n    // Ensure the second hop occurs within 30 minutes of the first hop\n    | where SecondHop <= FirstHop + 30m\n    | distinct\n        Account,\n        FirstHop,\n        FirstComputer,\n        FirstIPAddress,\n        SecondHop,\n        SecondComputer,\n        SecondIPAddress,\n        AccountType,\n        Activity,\n        LogonTypeName,\n        ProcessName;        \n// Resolve hostnames to IP addresses device network Ip's\nlet listOfFirstComputer = rdpConnections | distinct FirstComputer;\nlet listOfSecondComputer = rdpConnections | distinct SecondComputer;\nlet resolvedIPs =  \n    DeviceNetworkInfo\n    | where TimeGenerated >= ago(endtime)\n    | where isnotempty(ConnectedNetworks) and NetworkAdapterStatus == \"Up\"\n    | extend ClientIP = tostring(parse_json(IPAddresses[0]).IPAddress)\n    | where isnotempty(ClientIP)\n    | where DeviceName in~  (listOfFirstComputer) or DeviceName in~ (listOfSecondComputer)\n    | summarize arg_max(TimeGenerated, ClientIP) by Computer= DeviceName\n    | project Computer=toupper(Computer), ResolvedIP = ClientIP;\n// Join resolved IPs with the RDP connections\nrdpConnections\n| join kind=inner (resolvedIPs) on $left.FirstComputer == $right.Computer\n| join kind=inner (resolvedIPs) on $left.SecondComputer == $right.Computer\n| where ResolvedIP != ResolvedIP1\n| distinct\n    Account,\n    FirstHop,\n    FirstComputer,\n    FirstIPAddress,\n    SecondHop,\n    SecondComputer,\n    SecondIPAddress,\n    AccountType,\n    Activity,\n    LogonTypeName,\n    ProcessName\n// Use left anti to exclude anything from the previous 7 days where the Account and IP has connected to 5 or more computers.\n| join kind=leftanti (\n    (union isfuzzy=true\n        (\n        SecurityEvent\n        | where TimeGenerated >= ago(starttime) and TimeGenerated < ago(endtime)\n        | where EventID == 4624 and LogonType == 10\n        | summarize make_set(Computer), ComputerCount = dcount(Computer) by bin(TimeGenerated, 1d), Account = tolower(Account), IpAddress\n        // Connection count to computer by same account and IP to exclude counts of 5 or more on a given day\n        | where ComputerCount >= threshold\n        | mvexpand set_Computer\n        | extend Computer = toupper(set_Computer)\n        ),\n        (\n        WindowsEvent\n        | where TimeGenerated >= ago(starttime) and TimeGenerated < ago(endtime)\n        | where EventID == 4624 and EventData has (\"10\")\n        | extend LogonType = tostring(EventData.LogonType)\n        | where LogonType == 10\n        | extend Account = strcat(tostring(EventData.TargetDomainName), \"\\\\\", tostring(EventData.TargetUserName))\n        | extend IpAddress = tostring(EventData.IpAddress)\n        | summarize make_set(Computer), ComputerCount = dcount(Computer) by bin(TimeGenerated, 1d), Account = tolower(Account), IpAddress\n        // Connection count to computer by same account and IP to exclude counts of 5 or more on a given day\n        | where ComputerCount >= threshold\n        | mvexpand set_Computer\n        | extend Computer = toupper(set_Computer)\n        ))\n    )\n    on\n    Account,\n    $left.SecondComputer == $right.Computer,\n    $left.SecondIPAddress == $right.IpAddress\n| summarize FirstHopFirstSeen = min(FirstHop), FirstHopLastSeen = max(FirstHop)\n    by\n    Account,\n    FirstComputer,\n    FirstIPAddress,\n    SecondHop,\n    SecondComputer,\n    SecondIPAddress,\n    AccountType,\n    Activity,\n    LogonTypeName,\n    ProcessName\n| extend\n    AccountName = tostring(split(Account, @\"\\\")[1]),\n    AccountNTDomain = tostring(split(Account, @\"\\\")[0])\n| extend\n    HostName1 = tostring(split(FirstComputer, \".\")[0]),\n    DomainIndex = toint(indexof(FirstComputer, '.'))\n| extend HostNameDomain1 = iff(DomainIndex != -1, substring(FirstComputer, DomainIndex + 1), FirstComputer)\n| extend\n    HostName2 = tostring(split(SecondComputer, \".\")[0]),\n    DomainIndex = toint(indexof(SecondComputer, '.'))\n| extend HostNameDomain2 = iff(DomainIndex != -1, substring(SecondComputer, DomainIndex + 1), SecondComputer)\n| project-away DomainIndex\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P8D",
        "severity": "Medium",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "LateralMovement"
        ],
        "techniques": [
          "T1021"
        ],
        "templateVersion": "1.2.7",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}