Detection rules › Kusto
GitLab - User Impersonation
'This queries GitLab Audit Logs for user impersonation. A malicious operator or a compromised admin account could leverage the impersonation feature of GitLab to change code or repository settings bypassing usual processes. This hunting queries allows you to track the audit actions done under impersonation.'
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence |
Rule body
id: 0f4f16a2-b464-4c10-9a42-993da3e15a40
name: GitLab - User Impersonation
description: |
'This queries GitLab Audit Logs for user impersonation. A malicious operator or a compromised admin account could leverage the impersonation feature of GitLab to change code or repository settings bypassing usual processes. This hunting queries allows you to track the audit actions done under impersonation.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SyslogAma
dataTypes:
- Syslog
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
relevantTechniques:
- T1078
query: |
let impersonationStart = (GitLabAudit
| where CustomMessage == 'Started Impersonation');
let impersonationStop = (GitLabAudit
| where CustomMessage == 'Stopped Impersonation');
impersonationStart
| join kind=inner impersonationStop on $left.TargetID == $right.TargetID and $left.AuthorID == $right.AuthorID
| where EventTime1 > EventTime
| extend TimeGenerated, AuthorID, AuthorName, TargetID, TargetDetails = TargetDetails, IPStart = IPAddress, IPStop = IPAddress1, ImpStartTime = EventTime, ImpStopTime = EventTime1, EntityName
| join kind=inner (GitLabAudit | extend ActionTime = EventTime, AuthorName) on $left.TargetDetails == $right.AuthorName
| where ImpStartTime < ActionTime and ActionTime > ImpStopTime
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPStart
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPStop
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: EntityName
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetDetails
version: 1.0.2
kind: Scheduled
Stages and Predicates
let impersonationStart is inlined into the numbered stages below.
Let binding: impersonationStop
let impersonationStop = (GitLabAudit
| where CustomMessage == 'Stopped Impersonation');
Stages 1 to 2 define let impersonationStart (the rule's main pipeline source); stages 3 to 7 run on it.
Stage 1: source
GitLabAudit
Stage 2: where
| where CustomMessage == 'Started Impersonation'
Stage 3: join
impersonationStart
| join kind=inner impersonationStop on $left.TargetID == $right.TargetID and $left.AuthorID == $right.AuthorID
Stage 4: where
| where EventTime1 > EventTime
Stage 5: extend
| extend TimeGenerated, AuthorID, AuthorName, TargetID, TargetDetails = TargetDetails, IPStart = IPAddress, IPStop = IPAddress1, ImpStartTime = EventTime, ImpStopTime = EventTime1, EntityName
Stage 6: join
| join kind=inner (GitLabAudit | extend ActionTime = EventTime, AuthorName) on $left.TargetDetails == $right.AuthorName
Stage 7: where
| where ImpStartTime < ActionTime and ActionTime > ImpStopTime
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
ActionTime | cross_field_compare |
| field:"ActionTime" kind:cross_field_compare value:"ImpStopTime" |
CustomMessage | eq |
| field:"CustomMessage" kind:eq |
EventTime1 | cross_field_compare |
| field:"EventTime1" kind:cross_field_compare value:"EventTime" |
ImpStartTime | cross_field_compare |
| field:"ImpStartTime" kind:cross_field_compare value:"ActionTime" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
AuthorID | extend |
AuthorName | extend |
EntityName | extend |
IPStart | extend |
IPStop | extend |
ImpStartTime | extend |
ImpStopTime | extend |
TargetDetails | extend |
TargetID | extend |
TimeGenerated | extend |