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
DescriptionIdentifies when an RDP connection is made to a first system and then an RDP connection is made from the first system

to another system with the same account within the 60 minutes. Additionally, if historically daily

RDP connections are indicated by the logged EventID 4624 with LogonType = 10
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.3
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;
(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 = TimeGenerated, 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 = TimeGenerated, 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 = TimeGenerated, 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 = TimeGenerated, SecondComputer = toupper(Computer), SecondIPAddress = IpAddress, Account = tolower(Account)
))
) on Account
// Make sure that the first connection is after the second connection --> SecondHop > FirstHop
// Then identify only RDP to another computer from within the first RDP connection by only choosing matches where the Computer names do not match --> FirstComputer != SecondComputer
// Then make sure the IPAddresses do not match by excluding connections from the same computers with first hop RDP connections to multiple computers --> FirstIPAddress != SecondIPAddress
| where FirstComputer != SecondComputer and FirstIPAddress != SecondIPAddress and SecondHop > FirstHop
// where 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
// use left anti to exclude anything from the previous 7 days where the Account and IP has connected 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 makeset(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 makeset(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 timestamp = FirstHopFirstSeen, AccountCustomEntity = Account, HostCustomEntity = FirstComputer, IPCustomEntity = FirstIPAddress
severity: Medium
triggerThreshold: 0
metadata:
  source:
    kind: Community
  support:
    tier: Community
  categories:
    domains:
    - Security - Threat Protection
  author:
    name: Shain
queryFrequency: 1d
requiredDataConnectors:
- connectorId: SecurityEvents
  dataTypes:
  - SecurityEvent
- connectorId: WindowsSecurityEvents
  dataTypes:
  - SecurityEvent
- connectorId: WindowsForwardedEvents
  dataTypes:
  - WindowsEvent
id: 69a45b05-71f5-45ca-8944-2e038747fb39
version: 1.2.3
name: RDP Nesting
kind: Scheduled
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/RDP_Nesting.yaml
queryPeriod: 8d
relevantTechniques:
- T1021
triggerOperator: gt
tactics:
- LateralMovement
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;
  (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 = TimeGenerated, 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 = TimeGenerated, 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 = TimeGenerated, 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 = TimeGenerated, SecondComputer = toupper(Computer), SecondIPAddress = IpAddress, Account = tolower(Account)
  ))
  ) on Account
  // Make sure that the first connection is after the second connection --> SecondHop > FirstHop
  // Then identify only RDP to another computer from within the first RDP connection by only choosing matches where the Computer names do not match --> FirstComputer != SecondComputer
  // Then make sure the IPAddresses do not match by excluding connections from the same computers with first hop RDP connections to multiple computers --> FirstIPAddress != SecondIPAddress
  | where FirstComputer != SecondComputer and FirstIPAddress != SecondIPAddress and SecondHop > FirstHop
  // where 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
  // use left anti to exclude anything from the previous 7 days where the Account and IP has connected 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 makeset(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 makeset(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 timestamp = FirstHopFirstSeen, AccountCustomEntity = Account, HostCustomEntity = FirstComputer, IPCustomEntity = FirstIPAddress  
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: AccountCustomEntity
- entityType: Host
  fieldMappings:
  - identifier: FullName
    columnName: HostCustomEntity
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: IPCustomEntity
description: |
  'Identifies when an RDP connection is made to a first system and then an RDP connection is made from the first system
  to another system with the same account within the 60 minutes. Additionally, if historically daily
  RDP connections are indicated by the logged EventID 4624 with LogonType = 10'  
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/69a45b05-71f5-45ca-8944-2e038747fb39')]",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/69a45b05-71f5-45ca-8944-2e038747fb39')]",
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
      "kind": "Scheduled",
      "apiVersion": "2022-11-01",
      "properties": {
        "displayName": "RDP Nesting",
        "description": "'Identifies when an RDP connection is made to a first system and then an RDP connection is made from the first system\nto another system with the same account within the 60 minutes. Additionally, if historically daily\nRDP connections are indicated by the logged EventID 4624 with LogonType = 10'\n",
        "severity": "Medium",
        "enabled": true,
        "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(union isfuzzy=true\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 FirstHop = TimeGenerated, FirstComputer = toupper(Computer), FirstIPAddress = IpAddress, Account = tolower(Account)\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 FirstHop = TimeGenerated, FirstComputer = toupper(Computer), FirstIPAddress = IpAddress, Account = tolower(Account)\n))\n| join kind=inner (\n(union isfuzzy=true\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 SecondHop = TimeGenerated, SecondComputer = toupper(Computer), SecondIPAddress = IpAddress, Account = tolower(Account)\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 SecondHop = TimeGenerated, SecondComputer = toupper(Computer), SecondIPAddress = IpAddress, Account = tolower(Account)\n))\n) on Account\n// Make sure that the first connection is after the second connection --> SecondHop > FirstHop\n// Then identify only RDP to another computer from within the first RDP connection by only choosing matches where the Computer names do not match --> FirstComputer != SecondComputer\n// Then make sure the IPAddresses do not match by excluding connections from the same computers with first hop RDP connections to multiple computers --> FirstIPAddress != SecondIPAddress\n| where FirstComputer != SecondComputer and FirstIPAddress != SecondIPAddress and SecondHop > FirstHop\n// where the second hop occurs within 30 minutes of the first hop\n| where SecondHop <= FirstHop+30m\n| distinct Account, FirstHop, FirstComputer, FirstIPAddress, SecondHop, SecondComputer, SecondIPAddress, AccountType, Activity, LogonTypeName, ProcessName\n// use left anti to exclude anything from the previous 7 days where the Account and IP has connected 5 or more computers.\n| join kind=leftanti (\n(union isfuzzy=true\n(SecurityEvent\n| where TimeGenerated >= ago(starttime) and TimeGenerated < ago(endtime)\n| where EventID == 4624 and LogonType == 10\n| summarize makeset(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(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 makeset(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) on Account, $left.SecondComputer == $right.Computer, $left.SecondIPAddress == $right.IpAddress\n| summarize FirstHopFirstSeen = min(FirstHop), FirstHopLastSeen = max(FirstHop) by Account, FirstComputer, FirstIPAddress, SecondHop, SecondComputer,\nSecondIPAddress, AccountType, Activity, LogonTypeName, ProcessName\n| extend timestamp = FirstHopFirstSeen, AccountCustomEntity = Account, HostCustomEntity = FirstComputer, IPCustomEntity = FirstIPAddress\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P8D",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0,
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "LateralMovement"
        ],
        "techniques": [
          "T1021"
        ],
        "alertRuleTemplateName": "69a45b05-71f5-45ca-8944-2e038747fb39",
        "customDetails": null,
        "entityMappings": [
          {
            "fieldMappings": [
              {
                "columnName": "AccountCustomEntity",
                "identifier": "FullName"
              }
            ],
            "entityType": "Account"
          },
          {
            "fieldMappings": [
              {
                "columnName": "HostCustomEntity",
                "identifier": "FullName"
              }
            ],
            "entityType": "Host"
          },
          {
            "fieldMappings": [
              {
                "columnName": "IPCustomEntity",
                "identifier": "Address"
              }
            ],
            "entityType": "IP"
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/RDP_Nesting.yaml",
        "templateVersion": "1.2.3"
      }
    }
  ]
}