Detection rules › Kusto

GitLab - Abnormal number of repositories deleted

Status
available
Severity
medium
Time window
1h
Group by
Dummy
Source
github.com/Azure/Azure-Sentinel

'This hunting queries identify an unusual increase of repo deletion activities adversaries may want to disrupt availability or compromise integrity by deleting business data.'

MITRE ATT&CK coverage

TacticTechniques
Impact

Rule body

id: 3efd09bd-a582-4410-b7ec-5ff21cfad7bd
name: GitLab - Abnormal number of repositories deleted
description: |
  'This hunting queries identify an unusual increase of repo deletion activities adversaries may want to disrupt availability or compromise integrity by deleting business data.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SyslogAma
    dataTypes: 
      - Syslog
queryFrequency: 1h
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1485
query: |
  let LearningPeriod = 7d;
  let BinTime = 1h;
  let RunTime = 1h;
  let StartTime = 1h;
  let NumberOfStds = 3;
  let MinThreshold = 10.0;
  let EndRunTime = StartTime - RunTime;
  let EndLearningTime = StartTime + LearningPeriod;
  let GitLabRepositoryDestroyEvents = (GitLabAudit
  | where RemoveAction == "project" or RemoveAction == "repository");
  GitLabRepositoryDestroyEvents
  | where TimeGenerated between (ago(EndLearningTime) .. ago(StartTime))
  | summarize count() by bin(TimeGenerated, BinTime)
  | summarize AvgInLearning = avg(count_), StdInLearning = stdev(count_)
  | extend LearningThreshold = max_of(AvgInLearning + StdInLearning * NumberOfStds, MinThreshold)
  | extend Dummy = 1
  | join kind=innerunique (GitLabRepositoryDestroyEvents
  | where TimeGenerated between (ago(StartTime) .. ago(EndRunTime))
  | summarize CountInRunTime = count() by bin(TimeGenerated, BinTime)
  | extend Dummy = 1) on Dummy
  | project-away Dummy
  | where CountInRunTime > LearningThreshold
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPAddress
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AuthorName
version: 1.0.1
kind: Scheduled

Stages and Predicates

Parameters

let LearningPeriod = 7d;
let BinTime = 1h;
let RunTime = 1h;
let StartTime = 1h;
let NumberOfStds = 3;
let MinThreshold = 10.0;
let EndRunTime = StartTime - RunTime;
let EndLearningTime = StartTime + LearningPeriod;

let GitLabRepositoryDestroyEvents is inlined into the numbered stages below.

Stages 1 to 2 define let GitLabRepositoryDestroyEvents (the rule's main pipeline source); stages 3 to 10 run on it.

Stage 1: source

GitLabAudit

Stage 2: where

| where RemoveAction == "project" or RemoveAction == "repository"

Stage 3: where

GitLabRepositoryDestroyEvents
| where TimeGenerated between (ago(EndLearningTime) .. ago(StartTime))

Stage 4: summarize

| summarize count() by bin(TimeGenerated, BinTime)

Stage 5: summarize

| summarize AvgInLearning = avg(count_), StdInLearning = stdev(count_)

Stage 6: extend

| extend LearningThreshold = max_of(AvgInLearning + StdInLearning * NumberOfStds, MinThreshold)

Stage 7: extend

| extend Dummy = 1

Stage 8: join

| join kind=innerunique (GitLabRepositoryDestroyEvents
| where TimeGenerated between (ago(StartTime) .. ago(EndRunTime))
| summarize CountInRunTime = count() by bin(TimeGenerated, BinTime)
| extend Dummy = 1) on Dummy

Stage 9: project-away

| project-away Dummy

Stage 10: where

| where CountInRunTime > LearningThreshold

Indicators

These rows show field, operator, and value matches.

FieldKindValuesSearch
CountInRunTimecross_field_compare
  • LearningThreshold transforms: op:gt
field:"CountInRunTime" kind:cross_field_compare value:"LearningThreshold"
RemoveActioneq
  • project
  • repository
field:"RemoveAction" kind:eq

Output fields

These fields are emitted when the rule matches.

FieldSource
AvgInLearningsummarize
StdInLearningsummarize
LearningThresholdextend