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
eventGroupingSettings:
aggregationKind: SingleAlert
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: HostIP
- entityType: Account
fieldMappings:
- identifier: Name
columnName: PureLogin
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: PureArrayName
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Pure Storage/Analytic Rules/PureFailedLogin.yaml
suppressionEnabled: false
incidentConfiguration:
createIncident: true
groupingConfiguration:
lookbackDuration: PT5H
reopenClosedIncident: false
groupByCustomDetails: []
enabled: false
groupByAlertDetails: []
groupByEntities: []
matchingMethod: AllEntities
relevantTechniques:
- T1212
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
version: 1.0.0
severity: High
kind: NRT
id: ed32b115-5001-43a7-a2bb-f53026db4d97
description: Detect failed login attacks and delete user
suppressionDuration: 5h
name: Pure Failed Login
alertDetailsOverride:
alertDynamicProperties: []
tactics:
- CredentialAccess