Detection rules › Kusto

GitLab - Abnormal number of repositories deleted

Status
available
Severity
medium
Time window
1h
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
ImpactT1485 Data Destruction

Rule body kusto

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;

The stages below define let GitLabRepositoryDestroyEvents (the rule's main pipeline source).

Stage 1: source

GitLabAudit

Stage 2: where

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

The stages below run on GitLabRepositoryDestroyEvents (the outer pipeline).

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

Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.

FieldKindValues
CountInRunTimegt
  • LearningThreshold transforms: cased
RemoveActioneq
  • project transforms: cased
  • repository transforms: cased

Output fields

Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.

FieldSource
AvgInLearningsummarize
StdInLearningsummarize
LearningThresholdextend