Syslog
| where SyslogMessage has "purity.alert" and SyslogMessage has "invalid username or password"
| extend Message = SyslogMessage
| extend ParsedLog = extract_all(@"((?P<process>.*?)\[(?P<processid>.*?)\][\s\S]*?Array name:\s*'(?P<arrayname>\S+)'[\s\S]*?Controller:\s*'?(?P<controller>[^']+)'?[\s\S]*Interface:\s*'(?P<interface>\S+)'.*?User:\s'(?P<login>.*?)'\sLocation: '(?P<location>[^']+)'\sSublocation: '(?P<sublocation>[^']+)\s*(?P<part2log>[\s\S]*))", dynamic(['process', 'processid', 'arrayname', 'controller', 'interface','login', 'location', 'sublocation', 'part2log']), Message)
| mv-expand ParsedLog
| extend ResidueLog = tostring(ParsedLog[8])
| extend Rlog = extract_all(@"[\s\S]*Action:\s'(?P<action>[^']+)'[\s\S]*Method:\s'(?P<method>[^']+)'[\s\S]*Result:\s(?P<result>[^']+)[\s\S]*Description:\s'(?P<description>[^']*)'", dynamic(['action', 'method', 'result', 'description']), ResidueLog)
| mv-expand Rlog
| extend PureLogType = ParsedLog[0], PureProcessID = ParsedLog[1], PureArrayName = ParsedLog[2], PureController = ParsedLog[3], PureInterface = ParsedLog[4], PureLogin = ParsedLog [5], PureLocation = ParsedLog [6], PureSublocation = ParsedLog [7], PureAction = Rlog [0], PureMethod = Rlog [1], PureResult = Rlog [2], PureDescription = Rlog [3]
| project-away ResidueLog, Rlog, ParsedLog
| summarize count() by tostring(PureLogin), tostring(PureArrayName), HostIP
| where count_ >= 10
suppressionEnabled: false
relevantTechniques:
- T1212
incidentConfiguration:
groupingConfiguration:
groupByCustomDetails: []
reopenClosedIncident: false
enabled: false
matchingMethod: AllEntities
groupByEntities: []
lookbackDuration: PT5H
groupByAlertDetails: []
createIncident: true
version: 1.0.0
suppressionDuration: 5h
description: Detect failed login attacks and delete user
query: |-
Syslog
| where SyslogMessage has "purity.alert" and SyslogMessage has "invalid username or password"
| extend Message = SyslogMessage
| extend ParsedLog = extract_all(@"((?P<process>.*?)\[(?P<processid>.*?)\][\s\S]*?Array name:\s*'(?P<arrayname>\S+)'[\s\S]*?Controller:\s*'?(?P<controller>[^']+)'?[\s\S]*Interface:\s*'(?P<interface>\S+)'.*?User:\s'(?P<login>.*?)'\sLocation: '(?P<location>[^']+)'\sSublocation: '(?P<sublocation>[^']+)\s*(?P<part2log>[\s\S]*))", dynamic(['process', 'processid', 'arrayname', 'controller', 'interface','login', 'location', 'sublocation', 'part2log']), Message)
| mv-expand ParsedLog
| extend ResidueLog = tostring(ParsedLog[8])
| extend Rlog = extract_all(@"[\s\S]*Action:\s'(?P<action>[^']+)'[\s\S]*Method:\s'(?P<method>[^']+)'[\s\S]*Result:\s(?P<result>[^']+)[\s\S]*Description:\s'(?P<description>[^']*)'", dynamic(['action', 'method', 'result', 'description']), ResidueLog)
| mv-expand Rlog
| extend PureLogType = ParsedLog[0], PureProcessID = ParsedLog[1], PureArrayName = ParsedLog[2], PureController = ParsedLog[3], PureInterface = ParsedLog[4], PureLogin = ParsedLog [5], PureLocation = ParsedLog [6], PureSublocation = ParsedLog [7], PureAction = Rlog [0], PureMethod = Rlog [1], PureResult = Rlog [2], PureDescription = Rlog [3]
| project-away ResidueLog, Rlog, ParsedLog
| summarize count() by tostring(PureLogin), tostring(PureArrayName), HostIP
| where count_ >= 10
severity: High
alertDetailsOverride:
alertDynamicProperties: []
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Pure Storage/Analytic Rules/PureFailedLogin.yaml
id: ed32b115-5001-43a7-a2bb-f53026db4d97
entityMappings:
- fieldMappings:
- columnName: HostIP
identifier: Address
entityType: IP
- fieldMappings:
- columnName: PureLogin
identifier: Name
entityType: Account
- fieldMappings:
- columnName: PureArrayName
identifier: HostName
entityType: Host
eventGroupingSettings:
aggregationKind: SingleAlert
name: Pure Failed Login
tactics:
- CredentialAccess
kind: NRT