Detection rules › Kusto

SAP ETD - Login from unexpected network

This is a third-party alert feed, not a detection over modeled telemetry. The vendor product raised the finding; this rule forwards it into the SIEM. It is searchable for reference but is excluded from the detection-rule browse and the ATT&CK coverage matrix.

Status
available
Severity
medium
Time window
30m
Source
github.com/Azure/Azure-Sentinel

Identifies logons from an unexpected network. Source Action: Logon to the backend system from an IP address which is not assigned to one of the networks. networks can be maintained in the "SAP - Networks" watchlist of the Microsoft Sentinel Solution for SAP package. Data Sources: SAP Enterprise Thread Detection Solution - Alerts

MITRE ATT&CK coverage

TacticTechniques
DiscoveryNo specific technique

Rule body

id: 5dd72ebe-03ac-43ac-851b-68cfe5106e4f
kind: Scheduled
name: SAP ETD - Login from unexpected network
description: |
  Identifies logons from an unexpected network.
  Source Action: Logon to the backend system from an IP address which is not assigned to one of the networks.
  networks can be maintained in the "SAP - Networks" watchlist of the Microsoft Sentinel Solution for SAP package.
  
  *Data Sources: SAP Enterprise Thread Detection Solution -  Alerts*
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SAPETDAlerts
    dataTypes:
      - SAPETDAlerts_CL
queryFrequency: 5m
queryPeriod: 30m
triggerOperator: gt
triggerThreshold: 0
tactics: 
  - Discovery
relevantTechniques: []
query: |
  let AuditTimeAgo = 60m;
  let minThreshold= 1;
  let minScore= 50;
  let regex_sid = @"^([A-Z0-9]{3})/"; 
  let regex_client = @"/(\d{3})$";
  let SAPNetworks = _GetWatchlist('SAP - Networks');
  SAPETDAlerts_CL
  | where TimeGenerated > ago(AuditTimeAgo)
  | where Threshold >= minThreshold and Score >= minScore
  | where PatternName in ("Logon from external with SAP standard users","Access via unallowed IP Address")
  | mv-expand NormalizedTriggeringEvents
  | extend sapOriginalEvent = tostring(NormalizedTriggeringEvents)
  | extend Id_ = NormalizedTriggeringEvents.Id
  | extend extracted_user_ip = tostring(NormalizedTriggeringEvents.NetworkIPAddressInitiator)
  | where isnotempty(extracted_user_ip)
  | extend extracted_sap_user = NormalizedTriggeringEvents.UserAccountTargeted
  | extend extracted_sid = extract(regex_sid, 1, tostring(NormalizedTriggeringEvents.SystemIdActor))
  | extend extracted_client = extract(regex_client, 1, tostring(NormalizedTriggeringEvents.SystemIdActor))
  | extend extracted_instance_name = NormalizedTriggeringEvents.NetworkHostnameActor
  | extend extracted_instance_host = NormalizedTriggeringEvents.NetworkHostnameInitiator
  | evaluate ipv4_lookup(SAPNetworks, extracted_user_ip, Network, return_unmatched = true)
  | where isempty(Network)
  | project TimeGenerated, extracted_user_ip, extracted_sap_user, extracted_sid, extracted_client, extracted_instance_name, extracted_instance_host, AlertId, PatternName, PatternDescription, Status, NormalizedTriggeringEvents, Users
  | extend GeoLocation= iff(ipv4_is_private(extracted_user_ip), dynamic({"IsPrivate": true}), geo_info_from_ip_address(extracted_user_ip))
  | mv-expand Users
  | extend
    UserAccountName = tostring(Users.UserAccountName),
    UserEmail = tostring(Users.EmailAddresses[0])
eventGroupingSettings:
  aggregationKind: AlertPerResult
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: UserAccountName
  - entityType: Mailbox
    fieldMappings:
      - identifier: MailboxPrimaryAddress
        columnName: UserEmail
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: extracted_sid
      - identifier: InstanceName
        columnName: extracted_instance_name
  - entityType: Host
    fieldMappings:
      - identifier: FullName
        columnName: extracted_instance_host
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: extracted_user_ip
alertDetailsOverride:
  alertDisplayNameFormat: 'SAP ETD - {{PatternName}} '
  alertDescriptionFormat: |
    {{PatternDescription}}
customDetails:
  SAP_User: extracted_sap_user
  SAP_UserEmail: UserEmail
  ETD_AlertNumber: AlertId
version: 1.0.4

Stages and Predicates

Parameters

let AuditTimeAgo = 60m;
let minThreshold = 1;
let minScore = 50;
let regex_sid = @"^([A-Z0-9]{3})/";
let regex_client = @"/(\d{3})$";
let SAPNetworks = _GetWatchlist('SAP - Networks');

Stage 1: source

SAPETDAlerts_CL

Stage 2: where

| where TimeGenerated > ago(AuditTimeAgo)

Stage 3: where

| where Threshold >= minThreshold and Score >= minScore

Stage 4: where

| where PatternName in ("Logon from external with SAP standard users","Access via unallowed IP Address")

Stage 5: mv-expand

| mv-expand NormalizedTriggeringEvents

Stage 6: extend (3 consecutive steps)

| extend sapOriginalEvent = tostring(NormalizedTriggeringEvents)
| extend Id_ = NormalizedTriggeringEvents.Id
| extend extracted_user_ip = tostring(NormalizedTriggeringEvents.NetworkIPAddressInitiator)

Stage 7: where

| where isnotempty(extracted_user_ip)

Stage 8: extend (5 consecutive steps)

| extend extracted_sap_user = NormalizedTriggeringEvents.UserAccountTargeted
| extend extracted_sid = extract(regex_sid, 1, tostring(NormalizedTriggeringEvents.SystemIdActor))
| extend extracted_client = extract(regex_client, 1, tostring(NormalizedTriggeringEvents.SystemIdActor))
| extend extracted_instance_name = NormalizedTriggeringEvents.NetworkHostnameActor
| extend extracted_instance_host = NormalizedTriggeringEvents.NetworkHostnameInitiator

Stage 9: evaluate

| evaluate ipv4_lookup(SAPNetworks, extracted_user_ip, Network, return_unmatched = true)

Stage 10: where

| where isempty(Network)

Stage 11: project

| project TimeGenerated, extracted_user_ip, extracted_sap_user, extracted_sid, extracted_client, extracted_instance_name, extracted_instance_host, AlertId, PatternName, PatternDescription, Status, NormalizedTriggeringEvents, Users

Stage 12: extend

| extend GeoLocation= iff(ipv4_is_private(extracted_user_ip), dynamic({"IsPrivate": true}), geo_info_from_ip_address(extracted_user_ip))
GeoLocation =
if(ipv4_is_in_range(extracted_user_ip, "10.0.0.0/8") or ipv4_is_in_range(extracted_user_ip, "172.16.0.0/12") or ipv4_is_in_range(extracted_user_ip, "192.168.0.0/16") or ipv4_is_in_range(extracted_user_ip, "169.254.0.0/16") or ipv4_is_in_range(extracted_user_ip, "127.0.0.0/8"))dynamic({<dynamic>, <dynamic>})
elsegeo_info_from_ip_address(extracted_user_ip)

Stage 13: mv-expand

| mv-expand Users

Stage 14: extend

| extend
  UserAccountName = tostring(Users.UserAccountName),
  UserEmail = tostring(Users.EmailAddresses[0])

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
Networkis_null
  • (no value, null check)
field:"Network" kind:is_null
PatternNamein
  • Access via unallowed IP Address
  • Logon from external with SAP standard users
field:"PatternName" kind:in
Scorege
  • 50
field:"Score" kind:ge value:"50"
Thresholdge
  • 1
field:"Threshold" kind:ge value:"1"
extracted_user_ipis_not_null
  • (no value, null check)
field:"extracted_user_ip" kind:is_not_null

Output fields

These fields are emitted when the rule matches.

FieldSource
AlertIdproject
NormalizedTriggeringEventsproject
PatternDescriptionproject
PatternNameproject
Statusproject
TimeGeneratedproject
Usersproject
extracted_clientproject
extracted_instance_hostproject
extracted_instance_nameproject
extracted_sap_userproject
extracted_sidproject
extracted_user_ipproject
GeoLocationextend
UserAccountNameextend
UserEmailextend