Detection rules › Kusto

SlackAudit - Public link created for file which can contain sensitive information.

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

'Detects public links created for files that may contain sensitive data such as passwords, authentication tokens, secret keys, or private configuration files. Tune exclusions using the SlackAuditSensitiveFile_Allowlist_File and SlackAuditSensitiveFile_Allowlist_Account watchlists when known benign files or accounts generate expected public-link activity.'

MITRE ATT&CK coverage

Rule body kusto

id: 279316e8-8965-47d2-9788-b94dc352c853
name: SlackAudit - Public link created for file which can contain sensitive information.
description: |
  'Detects public links created for files that may contain sensitive data such as passwords, authentication tokens,
  secret keys, or private configuration files. Tune exclusions using the SlackAuditSensitiveFile_Allowlist_File and SlackAuditSensitiveFile_Allowlist_Account
  watchlists when known benign files or accounts generate expected public-link activity.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: SlackAuditAPI
    dataTypes:
      - SlackAudit_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
relevantTechniques:
  - T1048
  - T1567.002
query: |
  let AllowedFiles = toscalar(_GetWatchlist('SlackAuditSensitiveFile_Allowlist_File') | summarize make_set(tostring(SearchKey)));
  let AllowedUsers = toscalar(_GetWatchlist('SlackAuditSensitiveFile_Allowlist_Account') | summarize make_set(tostring(SearchKey)));
  SlackAudit
  | where Action =~ 'file_public_link_created'
  | extend FileNameLower = tolower(EntityFileName), UserLower = tolower(SrcUserName)
  | where EntityFileName in~ ('id_rsa')
      or FileNameLower has_any ('password', 'secret', 'token', 'credential', 'private key', 'api key')
      or FileNameLower has_any ('.ssh', '.npmrc', '.muttrc', '.gitconfig', '.netrc', 'package.json', 'Gemfile', 'bower.json', 'config.gypi', 'travis.yml', 'config.json')
  | where isempty(AllowedFiles) or EntityFileName !in~ (AllowedFiles)
  | where isempty(AllowedUsers) or UserLower !in~ (AllowedUsers)
  | extend AccountCustomEntity = SrcUserName
  | extend IPCustomEntity = SrcIpAddr
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: IPCustomEntity
alertDetailsOverride:
  alertDisplayNameFormat: Slack public link created for sensitive file {{EntityFileName}} by {{SrcUserName}}
  alertDescriptionFormat: Public link created for {{EntityFileName}} by {{SrcUserName}} from {{SrcIpAddr}}
customDetails:
  FileName: EntityFileName
  Action: Action
  SourceUser: SrcUserName
  SourceIP: SrcIpAddr
  ActorEntity: AccountCustomEntity
  IpEntity: IPCustomEntity
version: 1.0.1
kind: Scheduled

Stages and Predicates

Let binding: AllowedFiles

let AllowedFiles = toscalar(_GetWatchlist('SlackAuditSensitiveFile_Allowlist_File') | summarize make_set(tostring(SearchKey)));

Let binding: AllowedUsers

let AllowedUsers = toscalar(_GetWatchlist('SlackAuditSensitiveFile_Allowlist_Account') | summarize make_set(tostring(SearchKey)));

Stage 1: source

SlackAudit

Stage 2: where

| where Action =~ 'file_public_link_created'

Stage 3: extend

| extend FileNameLower = tolower(EntityFileName), UserLower = tolower(SrcUserName)

Stage 4: where

| where EntityFileName in~ ('id_rsa')
    or FileNameLower has_any ('password', 'secret', 'token', 'credential', 'private key', 'api key')
    or FileNameLower has_any ('.ssh', '.npmrc', '.muttrc', '.gitconfig', '.netrc', 'package.json', 'Gemfile', 'bower.json', 'config.gypi', 'travis.yml', 'config.json')

Stage 5: where

| where isempty(AllowedFiles) or EntityFileName !in~ (AllowedFiles)

References AllowedFiles (defined above).

Stage 6: where

| where isempty(AllowedUsers) or UserLower !in~ (AllowedUsers)

References AllowedUsers (defined above).

Stage 7: extend

| extend AccountCustomEntity = SrcUserName

Stage 8: extend

| extend IPCustomEntity = SrcIpAddr

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
Actioneq
  • file_public_link_created
AllowedFilesis_null
  • (no value, null check)
AllowedUsersis_null
  • (no value, null check)
EntityFileNamein
  • id_rsa
FileNameLowermatch
  • .gitconfig
  • .muttrc
  • .netrc
  • .npmrc
  • .ssh
  • Gemfile
  • api key
  • bower.json
  • config.gypi
  • config.json
  • credential
  • package.json
  • password
  • private key
  • secret
  • token
  • travis.yml

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
FileNameLowerextend
UserLowerextend
AccountCustomEntityextend
IPCustomEntityextend