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

SSH - Potential Brute Force

Back
Ide1ce0eab-10d1-4aae-863f-9a383345ba88
RulenameSSH - Potential Brute Force
DescriptionIdentifies an IP address that had 15 failed attempts to sign in via SSH in a 4 hour block during a 24 hour time period.

Please note that entity mapping for arrays is not supported, so when there is a single value in an array, we will pull that

value from the array as a single string to populate the entity to support entity mapping features within Sentinel. Additionally,

if the array is multivalued, we will input a string to indicate this with a unique hash so that matching will not occur.

As an example - ComputerList is an array that we check for a single value and write that into the HostName field for use in

the entity mapping within Sentinel.
SeverityLow
TacticsCredentialAccess
TechniquesT1110
Required data connectorsSyslog
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Syslog/Analytic Rules/ssh_potentialBruteForce.yaml
Version1.1.2
Arm templatee1ce0eab-10d1-4aae-863f-9a383345ba88.json
Deploy To Azure

let threshold = 15;
Syslog
| where ProcessName =~ "sshd" 
| where SyslogMessage contains "Failed password for invalid user"
| parse kind=relaxed SyslogMessage with * "invalid user " user " from " ip " port" port " ssh2" *
// using distinct below as it has been seen that Syslog can duplicate entries depending on implementation
| distinct TimeGenerated, Computer, user, ip, port, SyslogMessage, _ResourceId
| summarize EventTimes = make_list(TimeGenerated), PerHourCount = count() by bin(TimeGenerated,4h), ip, Computer, user, _ResourceId
| where PerHourCount > threshold
| mvexpand EventTimes
| extend EventTimes = tostring(EventTimes) 
| summarize StartTime = min(EventTimes), EndTime = max(EventTimes), UserList = make_set(user), ComputerList = make_set(Computer), ResourceIdList = make_set(_ResourceId), sum(PerHourCount) by IPAddress = ip
// bringing through single computer and user if array only has 1, otherwise, referencing the column and hashing the ComputerList or UserList so we don't get accidental entity matches when reviewing alerts
| extend HostName = iff(array_length(ComputerList) == 1, tostring(ComputerList[0]), strcat("SeeComputerListField","_", tostring(hash(tostring(ComputerList)))))
| extend Account = iff(array_length(ComputerList) == 1, tostring(UserList[0]), strcat("SeeUserListField","_", tostring(hash(tostring(UserList)))))
| extend ResourceId = iff(array_length(ResourceIdList) == 1, tostring(ResourceIdList[0]), strcat("SeeResourceIdListField","_", tostring(hash(tostring(ResourceIdList)))))
severity: Low
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Syslog/Analytic Rules/ssh_potentialBruteForce.yaml
description: |
  'Identifies an IP address that had 15 failed attempts to sign in via SSH in a 4 hour block during a 24 hour time period.
   Please note that entity mapping for arrays is not supported, so when there is a single value in an array, we will pull that
   value from the array as a single string to populate the entity to support entity mapping features within Sentinel. Additionally,
   if the array is multivalued, we will input a string to indicate this with a unique hash so that matching will not occur.
   As an example - ComputerList is an array that we check for a single value and write that into the HostName field for use in 
   the entity mapping within Sentinel.'  
triggerOperator: gt
queryPeriod: 1d
requiredDataConnectors:
- dataTypes:
  - Syslog
  connectorId: Syslog
queryFrequency: 1d
triggerThreshold: 0
tactics:
- CredentialAccess
query: |2

  let threshold = 15;
  Syslog
  | where ProcessName =~ "sshd" 
  | where SyslogMessage contains "Failed password for invalid user"
  | parse kind=relaxed SyslogMessage with * "invalid user " user " from " ip " port" port " ssh2" *
  // using distinct below as it has been seen that Syslog can duplicate entries depending on implementation
  | distinct TimeGenerated, Computer, user, ip, port, SyslogMessage, _ResourceId
  | summarize EventTimes = make_list(TimeGenerated), PerHourCount = count() by bin(TimeGenerated,4h), ip, Computer, user, _ResourceId
  | where PerHourCount > threshold
  | mvexpand EventTimes
  | extend EventTimes = tostring(EventTimes) 
  | summarize StartTime = min(EventTimes), EndTime = max(EventTimes), UserList = make_set(user), ComputerList = make_set(Computer), ResourceIdList = make_set(_ResourceId), sum(PerHourCount) by IPAddress = ip
  // bringing through single computer and user if array only has 1, otherwise, referencing the column and hashing the ComputerList or UserList so we don't get accidental entity matches when reviewing alerts
  | extend HostName = iff(array_length(ComputerList) == 1, tostring(ComputerList[0]), strcat("SeeComputerListField","_", tostring(hash(tostring(ComputerList)))))
  | extend Account = iff(array_length(ComputerList) == 1, tostring(UserList[0]), strcat("SeeUserListField","_", tostring(hash(tostring(UserList)))))
  | extend ResourceId = iff(array_length(ResourceIdList) == 1, tostring(ResourceIdList[0]), strcat("SeeResourceIdListField","_", tostring(hash(tostring(ResourceIdList)))))
kind: Scheduled
relevantTechniques:
- T1110
version: 1.1.2
id: e1ce0eab-10d1-4aae-863f-9a383345ba88
entityMappings:
- fieldMappings:
  - columnName: Account
    identifier: Name
  entityType: Account
- fieldMappings:
  - columnName: IPAddress
    identifier: Address
  entityType: IP
- fieldMappings:
  - columnName: HostName
    identifier: HostName
  entityType: Host
- fieldMappings:
  - columnName: ResourceId
    identifier: ResourceId
  entityType: AzureResource
name: SSH - Potential Brute Force
{
  "$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/e1ce0eab-10d1-4aae-863f-9a383345ba88')]",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/e1ce0eab-10d1-4aae-863f-9a383345ba88')]",
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
      "kind": "Scheduled",
      "apiVersion": "2022-11-01-preview",
      "properties": {
        "displayName": "SSH - Potential Brute Force",
        "description": "'Identifies an IP address that had 15 failed attempts to sign in via SSH in a 4 hour block during a 24 hour time period.\n Please note that entity mapping for arrays is not supported, so when there is a single value in an array, we will pull that\n value from the array as a single string to populate the entity to support entity mapping features within Sentinel. Additionally,\n if the array is multivalued, we will input a string to indicate this with a unique hash so that matching will not occur.\n As an example - ComputerList is an array that we check for a single value and write that into the HostName field for use in \n the entity mapping within Sentinel.'\n",
        "severity": "Low",
        "enabled": true,
        "query": "\nlet threshold = 15;\nSyslog\n| where ProcessName =~ \"sshd\" \n| where SyslogMessage contains \"Failed password for invalid user\"\n| parse kind=relaxed SyslogMessage with * \"invalid user \" user \" from \" ip \" port\" port \" ssh2\" *\n// using distinct below as it has been seen that Syslog can duplicate entries depending on implementation\n| distinct TimeGenerated, Computer, user, ip, port, SyslogMessage, _ResourceId\n| summarize EventTimes = make_list(TimeGenerated), PerHourCount = count() by bin(TimeGenerated,4h), ip, Computer, user, _ResourceId\n| where PerHourCount > threshold\n| mvexpand EventTimes\n| extend EventTimes = tostring(EventTimes) \n| summarize StartTime = min(EventTimes), EndTime = max(EventTimes), UserList = make_set(user), ComputerList = make_set(Computer), ResourceIdList = make_set(_ResourceId), sum(PerHourCount) by IPAddress = ip\n// bringing through single computer and user if array only has 1, otherwise, referencing the column and hashing the ComputerList or UserList so we don't get accidental entity matches when reviewing alerts\n| extend HostName = iff(array_length(ComputerList) == 1, tostring(ComputerList[0]), strcat(\"SeeComputerListField\",\"_\", tostring(hash(tostring(ComputerList)))))\n| extend Account = iff(array_length(ComputerList) == 1, tostring(UserList[0]), strcat(\"SeeUserListField\",\"_\", tostring(hash(tostring(UserList)))))\n| extend ResourceId = iff(array_length(ResourceIdList) == 1, tostring(ResourceIdList[0]), strcat(\"SeeResourceIdListField\",\"_\", tostring(hash(tostring(ResourceIdList)))))\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P1D",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0,
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "CredentialAccess"
        ],
        "techniques": [
          "T1110"
        ],
        "alertRuleTemplateName": "e1ce0eab-10d1-4aae-863f-9a383345ba88",
        "customDetails": null,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "Account",
                "identifier": "Name"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "IPAddress",
                "identifier": "Address"
              }
            ]
          },
          {
            "entityType": "Host",
            "fieldMappings": [
              {
                "columnName": "HostName",
                "identifier": "HostName"
              }
            ]
          },
          {
            "entityType": "AzureResource",
            "fieldMappings": [
              {
                "columnName": "ResourceId",
                "identifier": "ResourceId"
              }
            ]
          }
        ],
        "templateVersion": "1.1.2",
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Syslog/Analytic Rules/ssh_potentialBruteForce.yaml"
      }
    }
  ]
}