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
metadata:
  categories:
    domains:
    - Security - Threat Protection
  source:
    kind: Community
  support:
    tier: Community
  author:
    name: Shain
version: 1.2.3
name: RDP Nesting
severity: Medium
queryFrequency: 1d
kind: Scheduled
queryPeriod: 8d
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'  
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  
tactics:
- LateralMovement
triggerOperator: gt
entityMappings:
- entityType: Account
  fieldMappings:
  - columnName: AccountCustomEntity
    identifier: FullName
- entityType: Host
  fieldMappings:
  - columnName: HostCustomEntity
    identifier: FullName
- entityType: IP
  fieldMappings:
  - columnName: IPCustomEntity
    identifier: Address
triggerThreshold: 0
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/RDP_Nesting.yaml
requiredDataConnectors:
- connectorId: SecurityEvents
  dataTypes:
  - SecurityEvent
- connectorId: WindowsSecurityEvents
  dataTypes:
  - SecurityEvent
- connectorId: WindowsForwardedEvents
  dataTypes:
  - WindowsEvent
relevantTechniques:
- T1021
id: 69a45b05-71f5-45ca-8944-2e038747fb39
{
  "$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-preview",
      "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"
      }
    }
  ]
}