Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Potential Build Process Compromise

Back
Id5ef06767-b37c-4818-b035-47de950d0046
RulenamePotential Build Process Compromise
DescriptionThe query looks for source code files being modified immediately after a build process is started. The purpose of this is to look for malicious code injection during the build process.

More details: https://techcommunity.microsoft.com/t5/azure-sentinel/monitoring-the-software-supply-chain-with-azure-sentinel/ba-p/2176463
SeverityMedium
TacticsPersistence
TechniquesT1554
Required data connectorsSecurityEvents
WindowsForwardedEvents
WindowsSecurityEvents
KindScheduled
Query frequency1d
Query period1d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/PotentialBuildProcessCompromise.yaml
Version1.1.4
Arm template5ef06767-b37c-4818-b035-47de950d0046.json
Deploy To Azure
// How far back to look for events from
let timeframe = 1d;
// How close together build events and file modifications should occur to alert (make this smaller to reduce FPs)
let time_window = 5m;
// Edit this to include build processes used
let build_processes = dynamic(["MSBuild.exe", "dotnet.exe", "VBCSCompiler.exe"]);
// Include any processes that you want to allow to edit files during/around the build process
let allow_list = dynamic([""]);
(union isfuzzy=true
(SecurityEvent
| where TimeGenerated > ago(timeframe)
// Look for build process starts
| where EventID == 4688
| where Process has_any (build_processes)
| summarize by BuildParentProcess=ParentProcessName, BuildProcess=Process, BuildAccount = Account, Computer, BuildCommand=CommandLine, timekey= bin(TimeGenerated, time_window), BuildProcessTime=TimeGenerated
| join kind=inner(
SecurityEvent
| where TimeGenerated > ago(timeframe)
// Look for file modifications to code file
| where EventID == 4663
| where Process !in (allow_list)
// Look for code files, edit this to include file extensions used in build.
| where ObjectName endswith ".cs" or ObjectName endswith ".cpp"
// 0x6 and 0x4 for file append, 0x100 for file replacements
| where AccessMask == "0x6"  or AccessMask == "0x4" or AccessMask == "0X100"
| summarize by FileEditParentProcess=ParentProcessName, FileEditAccount = Account, Computer, FileEdited=ObjectName, FileEditProcess=ProcessName, timekey= bin(TimeGenerated, time_window), FileEditTime=TimeGenerated)
// join where build processes and file modifications seen at same time on same host
on timekey, Computer
// Limit to only where the file edit happens after the build process starts
| where BuildProcessTime <= FileEditTime
| summarize make_set(FileEdited), make_set(FileEditProcess), make_set(FileEditAccount) by timekey, Computer, BuildParentProcess, BuildProcess
),
(WindowsEvent
| where TimeGenerated > ago(timeframe)
// Look for build process starts
| where EventID == 4688 and EventData has_any (build_processes)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend Process=tostring(split(NewProcessName, '\\')[-1])
| where Process has_any (build_processes)
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| extend Account =  strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
| extend CommandLine = tostring(EventData.CommandLine) 
| summarize by BuildParentProcess=ParentProcessName, BuildProcess=Process, BuildAccount = Account, Computer, BuildCommand=CommandLine, timekey= bin(TimeGenerated, time_window), BuildProcessTime=TimeGenerated
| join kind=inner(
WindowsEvent
| where TimeGenerated > ago(timeframe)
// Look for file modifications to code file
| where EventID == 4663 and EventData has_any ("0x6", "0x4", "0X100") and EventData has_any (".cs", ".cpp")
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend Process=tostring(split(NewProcessName, '\\')[-1])
| where Process !in (allow_list)
// Look for code files, edit this to include file extensions used in build.
| extend ObjectName = tostring(EventData.ObjectName)
| where ObjectName endswith ".cs" or ObjectName endswith ".cpp"
// 0x6 and 0x4 for file append, 0x100 for file replacements
| extend AccessMask = tostring(EventData.AccessMask)  
| where AccessMask == "0x6"  or AccessMask == "0x4" or AccessMask == "0X100"
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| extend Account =  strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
| extend ProcessName = tostring(EventData.ProcessName)
| summarize by FileEditParentProcess=ParentProcessName, FileEditAccount = Account, Computer, FileEdited=ObjectName, FileEditProcess=ProcessName, timekey= bin(TimeGenerated, time_window), FileEditTime=TimeGenerated)
// join where build processes and file modifications seen at same time on same host
on timekey, Computer
// Limit to only where the file edit happens after the build process starts
| where BuildProcessTime <= FileEditTime
| summarize make_set(FileEdited), make_set(FileEditProcess), make_set(FileEditAccount) by timekey, Computer, BuildParentProcess, BuildProcess
))
| extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
| project-away DomainIndex
relevantTechniques:
- T1554
name: Potential Build Process Compromise
requiredDataConnectors:
- dataTypes:
  - SecurityEvent
  connectorId: SecurityEvents
- dataTypes:
  - SecurityEvent
  connectorId: WindowsSecurityEvents
- dataTypes:
  - SecurityEvents
  connectorId: WindowsSecurityEvents
- dataTypes:
  - WindowsEvent
  connectorId: WindowsForwardedEvents
entityMappings:
- fieldMappings:
  - identifier: FullName
    columnName: Computer
  - identifier: HostName
    columnName: HostName
  - identifier: DnsDomain
    columnName: HostNameDomain
  entityType: Host
triggerThreshold: 0
id: 5ef06767-b37c-4818-b035-47de950d0046
tactics:
- Persistence
version: 1.1.4
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/PotentialBuildProcessCompromise.yaml
queryPeriod: 1d
kind: Scheduled
tags:
- Solorigate
- NOBELIUM
metadata:
  categories:
    domains:
    - Security - Others
  author:
    name: Microsoft Security Research
  support:
    tier: Community
  source:
    kind: Community
queryFrequency: 1d
severity: Medium
description: |
  'The query looks for source code files being modified immediately after a build process is started. The purpose of this is to look for malicious code injection during the build process.
  More details: https://techcommunity.microsoft.com/t5/azure-sentinel/monitoring-the-software-supply-chain-with-azure-sentinel/ba-p/2176463'  
query: |
  // How far back to look for events from
  let timeframe = 1d;
  // How close together build events and file modifications should occur to alert (make this smaller to reduce FPs)
  let time_window = 5m;
  // Edit this to include build processes used
  let build_processes = dynamic(["MSBuild.exe", "dotnet.exe", "VBCSCompiler.exe"]);
  // Include any processes that you want to allow to edit files during/around the build process
  let allow_list = dynamic([""]);
  (union isfuzzy=true
  (SecurityEvent
  | where TimeGenerated > ago(timeframe)
  // Look for build process starts
  | where EventID == 4688
  | where Process has_any (build_processes)
  | summarize by BuildParentProcess=ParentProcessName, BuildProcess=Process, BuildAccount = Account, Computer, BuildCommand=CommandLine, timekey= bin(TimeGenerated, time_window), BuildProcessTime=TimeGenerated
  | join kind=inner(
  SecurityEvent
  | where TimeGenerated > ago(timeframe)
  // Look for file modifications to code file
  | where EventID == 4663
  | where Process !in (allow_list)
  // Look for code files, edit this to include file extensions used in build.
  | where ObjectName endswith ".cs" or ObjectName endswith ".cpp"
  // 0x6 and 0x4 for file append, 0x100 for file replacements
  | where AccessMask == "0x6"  or AccessMask == "0x4" or AccessMask == "0X100"
  | summarize by FileEditParentProcess=ParentProcessName, FileEditAccount = Account, Computer, FileEdited=ObjectName, FileEditProcess=ProcessName, timekey= bin(TimeGenerated, time_window), FileEditTime=TimeGenerated)
  // join where build processes and file modifications seen at same time on same host
  on timekey, Computer
  // Limit to only where the file edit happens after the build process starts
  | where BuildProcessTime <= FileEditTime
  | summarize make_set(FileEdited), make_set(FileEditProcess), make_set(FileEditAccount) by timekey, Computer, BuildParentProcess, BuildProcess
  ),
  (WindowsEvent
  | where TimeGenerated > ago(timeframe)
  // Look for build process starts
  | where EventID == 4688 and EventData has_any (build_processes)
  | extend NewProcessName = tostring(EventData.NewProcessName)
  | extend Process=tostring(split(NewProcessName, '\\')[-1])
  | where Process has_any (build_processes)
  | extend ParentProcessName = tostring(EventData.ParentProcessName)
  | extend Account =  strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
  | extend CommandLine = tostring(EventData.CommandLine) 
  | summarize by BuildParentProcess=ParentProcessName, BuildProcess=Process, BuildAccount = Account, Computer, BuildCommand=CommandLine, timekey= bin(TimeGenerated, time_window), BuildProcessTime=TimeGenerated
  | join kind=inner(
  WindowsEvent
  | where TimeGenerated > ago(timeframe)
  // Look for file modifications to code file
  | where EventID == 4663 and EventData has_any ("0x6", "0x4", "0X100") and EventData has_any (".cs", ".cpp")
  | extend NewProcessName = tostring(EventData.NewProcessName)
  | extend Process=tostring(split(NewProcessName, '\\')[-1])
  | where Process !in (allow_list)
  // Look for code files, edit this to include file extensions used in build.
  | extend ObjectName = tostring(EventData.ObjectName)
  | where ObjectName endswith ".cs" or ObjectName endswith ".cpp"
  // 0x6 and 0x4 for file append, 0x100 for file replacements
  | extend AccessMask = tostring(EventData.AccessMask)  
  | where AccessMask == "0x6"  or AccessMask == "0x4" or AccessMask == "0X100"
  | extend ParentProcessName = tostring(EventData.ParentProcessName)
  | extend Account =  strcat(tostring(EventData.SubjectDomainName),"\\", tostring(EventData.SubjectUserName))
  | extend ProcessName = tostring(EventData.ProcessName)
  | summarize by FileEditParentProcess=ParentProcessName, FileEditAccount = Account, Computer, FileEdited=ObjectName, FileEditProcess=ProcessName, timekey= bin(TimeGenerated, time_window), FileEditTime=TimeGenerated)
  // join where build processes and file modifications seen at same time on same host
  on timekey, Computer
  // Limit to only where the file edit happens after the build process starts
  | where BuildProcessTime <= FileEditTime
  | summarize make_set(FileEdited), make_set(FileEditProcess), make_set(FileEditAccount) by timekey, Computer, BuildParentProcess, BuildProcess
  ))
  | extend HostName = tostring(split(Computer, ".")[0]), DomainIndex = toint(indexof(Computer, '.'))
  | extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)
  | project-away DomainIndex  
triggerOperator: gt
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/5ef06767-b37c-4818-b035-47de950d0046')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/5ef06767-b37c-4818-b035-47de950d0046')]",
      "properties": {
        "alertRuleTemplateName": "5ef06767-b37c-4818-b035-47de950d0046",
        "customDetails": null,
        "description": "'The query looks for source code files being modified immediately after a build process is started. The purpose of this is to look for malicious code injection during the build process.\nMore details: https://techcommunity.microsoft.com/t5/azure-sentinel/monitoring-the-software-supply-chain-with-azure-sentinel/ba-p/2176463'\n",
        "displayName": "Potential Build Process Compromise",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Host",
            "fieldMappings": [
              {
                "columnName": "Computer",
                "identifier": "FullName"
              },
              {
                "columnName": "HostName",
                "identifier": "HostName"
              },
              {
                "columnName": "HostNameDomain",
                "identifier": "DnsDomain"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Detections/SecurityEvent/PotentialBuildProcessCompromise.yaml",
        "query": "// How far back to look for events from\nlet timeframe = 1d;\n// How close together build events and file modifications should occur to alert (make this smaller to reduce FPs)\nlet time_window = 5m;\n// Edit this to include build processes used\nlet build_processes = dynamic([\"MSBuild.exe\", \"dotnet.exe\", \"VBCSCompiler.exe\"]);\n// Include any processes that you want to allow to edit files during/around the build process\nlet allow_list = dynamic([\"\"]);\n(union isfuzzy=true\n(SecurityEvent\n| where TimeGenerated > ago(timeframe)\n// Look for build process starts\n| where EventID == 4688\n| where Process has_any (build_processes)\n| summarize by BuildParentProcess=ParentProcessName, BuildProcess=Process, BuildAccount = Account, Computer, BuildCommand=CommandLine, timekey= bin(TimeGenerated, time_window), BuildProcessTime=TimeGenerated\n| join kind=inner(\nSecurityEvent\n| where TimeGenerated > ago(timeframe)\n// Look for file modifications to code file\n| where EventID == 4663\n| where Process !in (allow_list)\n// Look for code files, edit this to include file extensions used in build.\n| where ObjectName endswith \".cs\" or ObjectName endswith \".cpp\"\n// 0x6 and 0x4 for file append, 0x100 for file replacements\n| where AccessMask == \"0x6\"  or AccessMask == \"0x4\" or AccessMask == \"0X100\"\n| summarize by FileEditParentProcess=ParentProcessName, FileEditAccount = Account, Computer, FileEdited=ObjectName, FileEditProcess=ProcessName, timekey= bin(TimeGenerated, time_window), FileEditTime=TimeGenerated)\n// join where build processes and file modifications seen at same time on same host\non timekey, Computer\n// Limit to only where the file edit happens after the build process starts\n| where BuildProcessTime <= FileEditTime\n| summarize make_set(FileEdited), make_set(FileEditProcess), make_set(FileEditAccount) by timekey, Computer, BuildParentProcess, BuildProcess\n),\n(WindowsEvent\n| where TimeGenerated > ago(timeframe)\n// Look for build process starts\n| where EventID == 4688 and EventData has_any (build_processes)\n| extend NewProcessName = tostring(EventData.NewProcessName)\n| extend Process=tostring(split(NewProcessName, '\\\\')[-1])\n| where Process has_any (build_processes)\n| extend ParentProcessName = tostring(EventData.ParentProcessName)\n| extend Account =  strcat(tostring(EventData.SubjectDomainName),\"\\\\\", tostring(EventData.SubjectUserName))\n| extend CommandLine = tostring(EventData.CommandLine) \n| summarize by BuildParentProcess=ParentProcessName, BuildProcess=Process, BuildAccount = Account, Computer, BuildCommand=CommandLine, timekey= bin(TimeGenerated, time_window), BuildProcessTime=TimeGenerated\n| join kind=inner(\nWindowsEvent\n| where TimeGenerated > ago(timeframe)\n// Look for file modifications to code file\n| where EventID == 4663 and EventData has_any (\"0x6\", \"0x4\", \"0X100\") and EventData has_any (\".cs\", \".cpp\")\n| extend NewProcessName = tostring(EventData.NewProcessName)\n| extend Process=tostring(split(NewProcessName, '\\\\')[-1])\n| where Process !in (allow_list)\n// Look for code files, edit this to include file extensions used in build.\n| extend ObjectName = tostring(EventData.ObjectName)\n| where ObjectName endswith \".cs\" or ObjectName endswith \".cpp\"\n// 0x6 and 0x4 for file append, 0x100 for file replacements\n| extend AccessMask = tostring(EventData.AccessMask)  \n| where AccessMask == \"0x6\"  or AccessMask == \"0x4\" or AccessMask == \"0X100\"\n| extend ParentProcessName = tostring(EventData.ParentProcessName)\n| extend Account =  strcat(tostring(EventData.SubjectDomainName),\"\\\\\", tostring(EventData.SubjectUserName))\n| extend ProcessName = tostring(EventData.ProcessName)\n| summarize by FileEditParentProcess=ParentProcessName, FileEditAccount = Account, Computer, FileEdited=ObjectName, FileEditProcess=ProcessName, timekey= bin(TimeGenerated, time_window), FileEditTime=TimeGenerated)\n// join where build processes and file modifications seen at same time on same host\non timekey, Computer\n// Limit to only where the file edit happens after the build process starts\n| where BuildProcessTime <= FileEditTime\n| summarize make_set(FileEdited), make_set(FileEditProcess), make_set(FileEditAccount) by timekey, Computer, BuildParentProcess, BuildProcess\n))\n| extend HostName = tostring(split(Computer, \".\")[0]), DomainIndex = toint(indexof(Computer, '.'))\n| extend HostNameDomain = iff(DomainIndex != -1, substring(Computer, DomainIndex + 1), Computer)\n| project-away DomainIndex\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P1D",
        "severity": "Medium",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Persistence"
        ],
        "tags": [
          "Solorigate",
          "NOBELIUM"
        ],
        "techniques": [
          "T1554"
        ],
        "templateVersion": "1.1.4",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}