Detection rules › Kusto
Spearphishing Attachment: ISO Images (Microsoft Sentinel)
ISO images are often meant to be used offline and they are often used by IT Admins and/or used on Servers.
Installation from an iso file don't require network connection most of the time. Activities deviating from these situations can be considered as highly suspicious. Below queries detects opening a mounted image, process creation under a mounted image, and network connection from a process created under a mounted image.
All detections can be used seperately or combined together to generate a higher fidelity alert. WARNING: Check your Sysmon parsing functions and verify you have the logs. Using "Rendered Description" field for parsing causes parsing issues for registry events. Detect opening of a mounted image:
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | No specific technique |
Telemetry coverage
Rule body
// Author: Cyb3rMonk(https://twitter.com/Cyb3rMonk, https://mergene.medium.com)
// Link to original post:
// Part-1: https://mergene.medium.com/detecting-initial-access-html-smuggling-and-iso-images-part-1-c4f953edd13f?source=friends_link&sk=e685d7d44928edd142972a4041463f10
// Part-2: https://mergene.medium.com/detecting-initial-access-html-smuggling-and-iso-images-part-2-f8dd600430e2?source=friends_link&sk=38b7cd310a4929c25d3eefc545683d5f
//
//
let parse_sysmon_events = (T:(TimeGenerated:datetime,EventID:int, Source:string, EventData:string))
{
T
| where Source == "Microsoft-Windows-Sysmon"
| extend EventData = parse_xml(EventData).DataItem.EventData.Data
| mv-expand bagexpansion=array EventData
| evaluate bag_unpack(EventData)
| extend Key=tostring(['@Name']), Value=['#text']
| evaluate pivot(Key, any(Value), TimeGenerated, Source, EventLog, Computer, EventLevel, EventLevelName, EventID, UserName, RenderedDescription, MG, ManagementGroupName, Type, _ResourceId)
};
// Query Parameters
let lookback = 2h;
Event
| where TimeGenerated > ago(lookback)
| where EventID == 11
| invoke parse_sysmon_events()
| where tostring(TargetFilename) endswith ".iso.lnk" or tostring(TargetFilename) endswith ".img.lnk"
// Exclude servers and workstation used by IT admins if needed.
// Author: Cyb3rMonk(https://twitter.com/Cyb3rMonk, https://mergene.medium.com)
// Link to original post:
// Part-1: https://mergene.medium.com/detecting-initial-access-html-smuggling-and-iso-images-part-1-c4f953edd13f?source=friends_link&sk=e685d7d44928edd142972a4041463f10
// Part-2: https://mergene.medium.com/detecting-initial-access-html-smuggling-and-iso-images-part-2-f8dd600430e2?source=friends_link&sk=38b7cd310a4929c25d3eefc545683d5f
//
//
let parse_sysmon_events = (T:(TimeGenerated:datetime,EventID:int, Source:string, EventData:string))
{
T
| where Source == "Microsoft-Windows-Sysmon"
| extend EventData = parse_xml(EventData).DataItem.EventData.Data
| mv-expand bagexpansion=array EventData
| evaluate bag_unpack(EventData)
| extend Key=tostring(['@Name']), Value=['#text']
| evaluate pivot(Key, any(Value), TimeGenerated, Source, EventLog, Computer, EventLevel, EventLevelName, EventID, UserName, RenderedDescription, MG, ManagementGroupName, Type, _ResourceId)
};
// Query Parameters
let lookback = 2h;
Event
| where TimeGenerated > ago(lookback)
| where EventID == 13
| invoke parse_sysmon_events()
| where TargetObject startswith @"HKLM\SYSTEM\MountedDevices\\DosDevices"
| extend Folder = replace(@'.*\\([A-Z]:)',@'\1\\',tostring(TargetObject))
| join kind = inner
(
Event
| where TimeGenerated > ago(lookback)
| where EventID == 1
| invoke parse_sysmon_events()
| extend Folder = tostring(CurrentDirectory)
) on Computer, Folder
// If needed, exclude servers from the results.
// Author: Cyb3rMonk(https://twitter.com/Cyb3rMonk, https://mergene.medium.com)
// Link to original post:
// Part-1: https://mergene.medium.com/detecting-initial-access-html-smuggling-and-iso-images-part-1-c4f953edd13f?source=friends_link&sk=e685d7d44928edd142972a4041463f10
// Part-2: https://mergene.medium.com/detecting-initial-access-html-smuggling-and-iso-images-part-2-f8dd600430e2?source=friends_link&sk=38b7cd310a4929c25d3eefc545683d5f
//
//
let parse_sysmon_events = (T:(TimeGenerated:datetime,EventID:int, Source:string, EventData:string))
{
T
| where Source == "Microsoft-Windows-Sysmon"
| extend EventData = parse_xml(EventData).DataItem.EventData.Data
| mv-expand bagexpansion=array EventData
| evaluate bag_unpack(EventData)
| extend Key=tostring(['@Name']), Value=['#text']
| evaluate pivot(Key, any(Value), TimeGenerated, Source, EventLog, Computer, EventLevel, EventLevelName, EventID, UserName, RenderedDescription, MG, ManagementGroupName, Type, _ResourceId)
};
// Query Parameters
let lookback = 30h;
Event
| where TimeGenerated > ago(lookback)
| where EventID == 13
| invoke parse_sysmon_events()
| where TargetObject startswith @"HKLM\SYSTEM\MountedDevices\\DosDevices"
| extend Folder = replace(@'.*\\([A-Z]:)',@'\1\\',tostring(TargetObject))
| join kind = inner
(
Event
| where TimeGenerated > ago(lookback)
| where EventID == 3
| invoke parse_sysmon_events()
| extend Folder = replace(@'^([A-Z]:\\).*',@'\1',tostring(Image))
) on Computer, Folder
// If needed, exclude the legitimate activity and servers
Stages and Predicates
Parameters
let lookback = 2h;
Let binding: parse_sysmon_events
let parse_sysmon_events = (T:(TimeGenerated:datetime,EventID:int, Source:string, EventData:string))
{
T
| where Source == "Microsoft-Windows-Sysmon"
| extend EventData = parse_xml(EventData).DataItem.EventData.Data
| mv-expand bagexpansion=array EventData
| evaluate bag_unpack(EventData)
| extend Key=tostring(['@Name']), Value=['#text']
| evaluate pivot(Key, any(Value), TimeGenerated, Source, EventLog, Computer, EventLevel, EventLevelName, EventID, UserName, RenderedDescription, MG, ManagementGroupName, Type, _ResourceId)
};
Stage 1: source
Event
Stage 2: where
where TimeGenerated > ago(7200s)
Stage 3: where
where EventID == 11
Stage 4: where
where Source =~ "Microsoft-Windows-Sysmon"
Stage 5: extend
extend EventData
Stage 6: mv-expand
mv-expand EventData
Stage 7: evaluate
evaluate
Stage 8: extend
extend Key, Value
Stage 9: evaluate
evaluate
Stage 10: where
where (TargetFilename endswith ".img.lnk" or TargetFilename endswith ".iso.lnk")
Stage 11: source
Event
Stage 12: where
where TimeGenerated > ago(7200s)
Stage 13: where
where EventID == 13
Stage 14: where
where Source =~ "Microsoft-Windows-Sysmon"
Stage 15: extend
extend EventData
Stage 16: mv-expand
mv-expand EventData
Stage 17: evaluate
evaluate
Stage 18: extend
extend Key, Value
Stage 19: evaluate
evaluate
Stage 20: where
where TargetObject startswith @"HKLM\SYSTEM\MountedDevices\\DosDevices"
Stage 21: extend
extend Folder
Stage 22: join
join kind=inner (Event) on Computer, Folder
Stage 23: source
Event
Stage 24: where
where TimeGenerated > ago(108000s)
Stage 25: where
where EventID == 13
Stage 26: where
where Source =~ "Microsoft-Windows-Sysmon"
Stage 27: extend
extend EventData
Stage 28: mv-expand
mv-expand EventData
Stage 29: evaluate
evaluate
Stage 30: extend
extend Key, Value
Stage 31: evaluate
evaluate
Stage 32: where
where TargetObject startswith @"HKLM\SYSTEM\MountedDevices\\DosDevices"
Stage 33: extend
extend Folder
Stage 34: join
join kind=inner (Event) on Computer, Folder
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
EventID | eq |
| field:"EventID" kind:eq |
TargetFilename | ends_with |
| field:"TargetFilename" kind:ends_with |
TargetObject | starts_with |
| field:"TargetObject" kind:starts_with value:"HKLM\SYSTEM\MountedDevices\\DosDevices" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
EventData | extend |
Key | extend |
Value | extend |