Detection rules › Kusto
Process executed from binary hidden in Base64 encoded file
Encoding malicious software is a technique used to obfuscate files from detection. The first CommandLine component is looking for Python decoding base64. The second CommandLine component is looking for Bash/sh command line base64 decoding. The third one is looking for Ruby decoding base64.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Execution | T1059 Command and Scripting Interpreter |
| Stealth | T1027 Obfuscated Files or Information, T1140 Deobfuscate/Decode Files or Information |
Event coverage
| Provider | Event | Title |
|---|---|---|
| Security-Auditing | Event ID 4688 | A new process has been created. |
Rule body kusto
id: d6190dde-8fd2-456a-ac5b-0a32400b0464
name: Process executed from binary hidden in Base64 encoded file
description: |
'Encoding malicious software is a technique used to obfuscate files from detection.
The first CommandLine component is looking for Python decoding base64.
The second CommandLine component is looking for Bash/sh command line base64 decoding.
The third one is looking for Ruby decoding base64.'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: SecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvent
- connectorId: WindowsSecurityEvents
dataTypes:
- SecurityEvents
- connectorId: WindowsForwardedEvents
dataTypes:
- WindowsEvent
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Execution
- DefenseEvasion
relevantTechniques:
- T1059
- T1027
- T1140
query: |
let ProcessCreationEvents=(union isfuzzy=true
(SecurityEvent
| where EventID==4688
| where isnotempty(CommandLine)
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, FileName = Process, CommandLine, ParentProcessName
),
(WindowsEvent
| where EventID==4688
| where EventData has_any (".decode('base64')", "base64 --decode", ".decode64(" )
| extend CommandLine = tostring(EventData.CommandLine)
| where isnotempty(CommandLine)
| extend SubjectUserName = tostring(EventData.SubjectUserName)
| extend SubjectDomainName = tostring(EventData.SubjectDomainName)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend FileName=tostring(split(NewProcessName, '\\')[-1])
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, CommandLine, ParentProcessName
));
ProcessCreationEvents
| where CommandLine contains ".decode('base64')"
or CommandLine contains "base64 --decode"
or CommandLine contains ".decode64("
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), CountToday = count() by Computer, Account, AccountDomain, FileName, CommandLine, ParentProcessName
| extend HostName = iif(Computer has '.',substring(Computer,0,indexof(Computer,'.')),Computer) , DnsDomain = iif(Computer has '.',substring(Computer,indexof(Computer,'.')+1),'')
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Account
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: Computer
- identifier: HostName
columnName: HostName
- identifier: DnsDomain
columnName: DnsDomain
version: 1.1.3
kind: Scheduled
Stages and Predicates
Stage 0: let
let ProcessCreationEvents = (union <inlined as stages below>;
union isfuzzy=true (2 sources)
Each leg below queries one source; the rule matches if any leg does. Sources: SecurityEvent, WindowsEvent
Leg 1: SecurityEvent
SecurityEvent
| where EventID==4688
| where isnotempty(CommandLine)
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, FileName = Process, CommandLine, ParentProcessName
Leg 2: WindowsEvent
WindowsEvent
| where EventID==4688
| where EventData has_any (".decode('base64')", "base64 --decode", ".decode64(" )
| extend CommandLine = tostring(EventData.CommandLine)
| where isnotempty(CommandLine)
| extend SubjectUserName = tostring(EventData.SubjectUserName)
| extend SubjectDomainName = tostring(EventData.SubjectDomainName)
| extend NewProcessName = tostring(EventData.NewProcessName)
| extend FileName=tostring(split(NewProcessName, '\\')[-1])
| extend ParentProcessName = tostring(EventData.ParentProcessName)
| project TimeGenerated, Computer, Account = SubjectUserName, AccountDomain = SubjectDomainName, CommandLine, ParentProcessName
Applied to the combined result
| where CommandLine contains ".decode('base64')"
or CommandLine contains "base64 --decode"
or CommandLine contains ".decode64(" | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), CountToday = count() by Computer, Account, AccountDomain, FileName, CommandLine, ParentProcessName | extend HostName = iif(Computer has '.',substring(Computer,0,indexof(Computer,'.')),Computer) , DnsDomain = iif(Computer has '.',substring(Computer,indexof(Computer,'.')+1),'')
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.
| Field | Kind | Values |
|---|---|---|
CommandLine | contains |
|
CommandLine | is_not_null | |
EventData | match |
|
EventID | eq |
|
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.
| Field | Source |
|---|---|
Account | summarize |
AccountDomain | summarize |
CommandLine | summarize |
Computer | summarize |
CountToday | summarize |
EndTimeUtc | summarize |
FileName | summarize |
ParentProcessName | summarize |
StartTimeUtc | summarize |
DnsDomain | extend |
HostName | extend |