Detection rules › Kusto

Suspicious office child process created

Group by
DeviceFileEvents_FileName
Author
FalconForce
Source
github.com/FalconForceTeam/FalconFriday

This query obtains a list of downloaded Office documents (doc, xls, etc.) by looking at files written by commonly used web browsers. It then searches for invocations of an Office program by double-clicking on these files. If these processes spawn an uncommon child process this is reported as suspicious.

Known false positives

  • Some Office plugins might cause false positives if they change the Office program behavior.

MITRE ATT&CK coverage

TacticTechniques
Execution

Telemetry coverage

Rule body

let timeframe = 2*1d;
let browsers = dynamic(["iexplore.exe", "chrome.exe", "firefox.exe", "msedge.exe", "msedgewebview2.exe"]);
let ext = dynamic([".docm", ".xlsm", ".xls", ".doc", ".pptm", ".ppt"]);
let officeApps = dynamic(["winword.exe", "excel.exe", "powerpnt.exe"]);
let whitelist = dynamic(["MSOSYNC.exe", "splwow64.exe", "csc.exe", "outlook.exe", "AcroRd32.exe", "Acrobat.exe", "explorer.exe", "DW20.exe",
"Microsoft.Mashup.Container.Loader.exe", "Microsoft.Mashup.Container.NetFX40.exe", "WerFault.exe", "CLVIEW.exe", "wermgr.exe"]);
let whitelistedDomains = dynamic([""]);
let binPeriodForSearch = 1h;
let timeDiffFileCreateNetworkEvent = 15; // In seconds. Don't make this more than 15s because of comparison later on.
// List all filecreate events where the filename has a known Office extension which can contain macros.
let fileDownloads = materialize(
  DeviceFileEvents
  | where ingestion_time() >= ago(timeframe)
  // We need to have FileCreated and FileRenamed here because some browsers first download the file under a different name and rename it when it's done.
  // For example, the Chrome .crdownload files are all renamed to the intended name after the download has finished.
  | where ActionType in~ ("FileCreated", "FileRenamed") and InitiatingProcessFileName in~ (browsers) and FileName has_any (ext)
  // We need to do this to limit the search of deviceNetworkEvents. Otherwise, the dataset becomes too big to join in MDE.
  | extend period=bin(Timestamp, binPeriodForSearch)
  // Optimizations to keep MDE happy. Otherwise the database gets too big.
  | project DeviceId, InitiatingProcessFileName, InitiatingProcessId, period, FileName, Timestamp
  // The renames are meant to avoid confusions as there will be a lot of FileNames from different tables.
  | project-rename DeviceFileEvents_InitiatingProcessFileName = InitiatingProcessFileName,
                   DeviceFileEvents_FileName = FileName,
                   DeviceFileEvents_Timestamp = Timestamp
);
// Now we need to find the network event that triggered the the filewrite.
// This is an approximation based on timestamp, deviceid, pid and process name.
let downloadSource = materialize(
  DeviceNetworkEvents
  | where ingestion_time() >= ago(timeframe)
  | where DeviceId in~ ((fileDownloads | project DeviceId)) and RemotePort in (80, 443)
  | extend period=bin(Timestamp, binPeriodForSearch)
  // Exclude allow-listed domains. Here you want to allow-list your internal Sharepoint environment.
  // This can be useful if you want to use this rule for external attacks.
  // There is a trade-off, as an internal attacker might abuse your Sharepoint for malware.
  | where parse_url(RemoteUrl).Host !in~ (whitelistedDomains)
  | lookup kind=inner fileDownloads on DeviceId, InitiatingProcessId, $left.InitiatingProcessFileName == $right.DeviceFileEvents_InitiatingProcessFileName, period
  | extend TimeDiff = datetime_diff('second', Timestamp, DeviceFileEvents_Timestamp)
  // The filecreate and network event should happen within max 15 second of each other.
  | where  -timeDiffFileCreateNetworkEvent < TimeDiff and TimeDiff < timeDiffFileCreateNetworkEvent
  // We're now only interested in the unique filenames of the downloads and the location they're possibly downloaded from.
  | summarize possibelURLs=make_set(RemoteUrl) by DeviceFileEvents_FileName
);
// Final step in tying everything together.
// Find Office applications that create a child process which is in our previously generated list.
// We don't filter on devicename because we want to see all instances of this file being run.
DeviceProcessEvents
| where ingestion_time() >= ago(timeframe)
| where ActionType =~ "ProcessCreated"
| where InitiatingProcessFileName in~ (officeApps) and FileName !in~ (officeApps) and FileName !in~ (whitelist) and FileName !in~ (browsers)
| project-rename DeviceProcessEvents_InitiatingProcessCommandLine = InitiatingProcessCommandLine
| where DeviceProcessEvents_InitiatingProcessCommandLine has_any (( downloadSource | project DeviceFileEvents_FileName))
| where not(FolderPath matches regex @"C:\\Program Files \(x86\)\\TechSmith\\Camtasia Studio \d+\\TscHelp\.exe")
| where not(ProcessCommandLine matches regex @"^""?rundll32""? C:\\WINDOWS\\system32\\spool\\DRIVERS\\((x64)|(x86))\\\d\\hpmsn\d+.((dll)|(DLL)),")
// Verclsid cannot be filtered as it's a LOLBIN which allows creating arbitrary COM objects.
// We can only filter the execution of very specific COM objects.
// Embedded Outlook item in an Office file.
//00020D0B-0000-0000-C000-000000000046 == Outlook 97-2003 Object. 00000112-0000-0000-C000-000000000046 == IOLEInterface 0x5 == CLSCTX_INPROC_SERVER	| CLSCTX_LOCAL_SERVER
| where not(ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{00020D0B-0000-0000-C000-000000000046\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")
// Embedded Adobe document in Office file.
| where not(ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{B801CA65-A1FC-11D0-85AD-444553540000\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")
// Embedded XML file in Office file.
| where not(ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{48123BC4-99D9-11D1-A6B3-00C04FD91555\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")
// Bitmap image.
| where not(ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{D3E34B21-9D75-101A-8C3D-00AA001A1652\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")
// ZIP folder.
| where not(ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{E88DCCE0-B7B3-11D1-A9F0-00AA0060FA31\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")
// HTML document.
| where not(ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{25336920-03F9-11CF-8FD0-00AA00686F13\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")
// FileProfile is case-sensitive and works on lower-case hashes.
| extend SHA1=tolower(SHA1)
| invoke FileProfile(SHA1, 1000)
| where not(FolderPath startswith @"C:\Windows\System32\spool\drivers\" and IsCertificateValid and IsRootSignerMicrosoft) // All drivers signed by Microsoft are trusted.
// Begin environment-specific filter.
// End environment-specific filter.

Stages and Predicates

Parameters

let timeframe = 2*1d;
let browsers = dynamic(["iexplore.exe", "chrome.exe", "firefox.exe", "msedge.exe", "msedgewebview2.exe"]);
let ext = dynamic([".docm", ".xlsm", ".xls", ".doc", ".pptm", ".ppt"]);
let officeApps = dynamic(["winword.exe", "excel.exe", "powerpnt.exe"]);
let whitelistedDomains = dynamic([""]);
let binPeriodForSearch = 1h;
let timeDiffFileCreateNetworkEvent = 15;

Let binding: whitelist

let whitelist = dynamic(["MSOSYNC.exe", "splwow64.exe", "csc.exe", "outlook.exe", "AcroRd32.exe", "Acrobat.exe", "explorer.exe", "DW20.exe",
"Microsoft.Mashup.Container.Loader.exe", "Microsoft.Mashup.Container.NetFX40.exe", "WerFault.exe", "CLVIEW.exe", "wermgr.exe"]);

Let binding: fileDownloads used in Stage 1

let fileDownloads = materialize(
  DeviceFileEvents
  | where ingestion_time() >= ago(timeframe)
  | where ActionType in~ ("FileCreated", "FileRenamed") and InitiatingProcessFileName in~ (browsers) and FileName has_any (ext)
  | extend period=bin(Timestamp, binPeriodForSearch)
  | project DeviceId, InitiatingProcessFileName, InitiatingProcessId, period, FileName, Timestamp
  | project-rename DeviceFileEvents_InitiatingProcessFileName = InitiatingProcessFileName,
                   DeviceFileEvents_FileName = FileName,
                   DeviceFileEvents_Timestamp = Timestamp
);

Let binding: downloadSource used in Stage 2

let downloadSource = materialize(
  DeviceNetworkEvents
  | where ingestion_time() >= ago(timeframe)
  | where DeviceId in~ ((fileDownloads | project DeviceId)) and RemotePort in (80, 443)
  | extend period=bin(Timestamp, binPeriodForSearch)
  | where parse_url(RemoteUrl).Host !in~ (whitelistedDomains)
  | lookup kind=inner fileDownloads on DeviceId, InitiatingProcessId, $left.InitiatingProcessFileName == $right.DeviceFileEvents_InitiatingProcessFileName, period
  | extend TimeDiff = datetime_diff('second', Timestamp, DeviceFileEvents_Timestamp)
  | where  -timeDiffFileCreateNetworkEvent < TimeDiff and TimeDiff < timeDiffFileCreateNetworkEvent
  | summarize possibelURLs=make_set(RemoteUrl) by DeviceFileEvents_FileName
);

Stage 1: source

let fileDownloads

Stage 2: source

let downloadSource

Stage 3: source

DeviceProcessEvents

Stage 4: where

where ...

Stage 5: where

where ActionType =~ "ProcessCreated"

Stage 6: where

where not (FileName in~ ("AcroRd32.exe", "Acrobat.exe", "CLVIEW.exe", "DW20.exe", "MSOSYNC.exe", "Microsoft.Mashup.Container.Loader.exe", "Microsoft.Mashup.Container.NetFX40.exe", "WerFault.exe", "csc.exe", "explorer.exe", "outlook.exe", "splwow64.exe", "wermgr.exe")) and not (FileName in~ ("chrome.exe", "firefox.exe", "iexplore.exe", "msedge.exe", "msedgewebview2.exe")) and not (FileName in~ ("excel.exe", "powerpnt.exe", "winword.exe")) and InitiatingProcessFileName in~ ("excel.exe", "powerpnt.exe", "winword.exe")

Stage 7: project-rename

project-rename

Stage 8: where

where /* macro: (DeviceProcessEvents_InitiatingProcessCommandLine has_any <NestedQuery>) */

Stage 9: where

where not (FolderPath matches regex @"C:\\Program Files \(x86\)\\TechSmith\\Camtasia Studio \d+\\TscHelp\.exe")

Stage 10: where

where not (ProcessCommandLine matches regex @"^""?rundll32""? C:\\WINDOWS\\system32\\spool\\DRIVERS\\((x64)|(x86))\\\d\\hpmsn\d+.((dll)|(DLL)),")

Stage 11: where

where not (ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{00020D0B-0000-0000-C000-000000000046\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")

Stage 12: where

where not (ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{B801CA65-A1FC-11D0-85AD-444553540000\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")

Stage 13: where

where not (ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{48123BC4-99D9-11D1-A6B3-00C04FD91555\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")

Stage 14: where

where not (ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{D3E34B21-9D75-101A-8C3D-00AA001A1652\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")

Stage 15: where

where not (ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{E88DCCE0-B7B3-11D1-A9F0-00AA0060FA31\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")

Stage 16: where

where not (ProcessCommandLine matches regex @"^""?verclsid\.exe""? \/S \/C \{25336920-03F9-11CF-8FD0-00AA00686F13\} \/I \{00000112-0000-0000-C000-000000000046\} \/X 0x5$")

Stage 17: extend

extend SHA1

Stage 18: invoke

invoke

Stage 19: where

where not (FolderPath startswith @"C:\Windows\System32\spool\drivers\")

Exclusions

The rule actively suppresses these predicates.

FieldKindExcluded valuesSearch
FileNameinAcroRd32.exe, Acrobat.exe, CLVIEW.exe, DW20.exe, MSOSYNC.exe, Microsoft.Mashup.Container.Loader.exe, Microsoft.Mashup.Container.NetFX40.exe, WerFault.exe, csc.exe, explorer.exe, outlook.exe, splwow64.exe, wermgr.exeexcludes:FileName
FileNameinchrome.exe, firefox.exe, iexplore.exe, msedge.exe, msedgewebview2.exeexcludes:FileName
FileNameinexcel.exe, powerpnt.exe, winword.exeexcludes:FileName field:"FileName" value:"excel.exe" field:"FileName" value:"powerpnt.exe" field:"FileName" value:"winword.exe"
FolderPathregex_matchC:\Program Files (x86)\TechSmith\Camtasia Studio \d+\TscHelp.exeexcludes:FolderPath field:"FolderPath" value:"C:\Program Files (x86)\TechSmith\Camtasia Studio \d+\TscHelp.exe"
ProcessCommandLineregex_match^"?rundll32"? C:\WINDOWS\system32\spool\DRIVERS\((x64)|(x86))\\d\hpmsn\d+.((dll)|(DLL)),excludes:ProcessCommandLine
ProcessCommandLineregex_match^"?verclsid.exe"? \/S \/C {00020D0B-0000-0000-C000-000000000046} \/I {00000112-0000-0000-C000-000000000046} \/X 0x5$excludes:ProcessCommandLine
ProcessCommandLineregex_match^"?verclsid.exe"? \/S \/C {B801CA65-A1FC-11D0-85AD-444553540000} \/I {00000112-0000-0000-C000-000000000046} \/X 0x5$excludes:ProcessCommandLine
ProcessCommandLineregex_match^"?verclsid.exe"? \/S \/C {48123BC4-99D9-11D1-A6B3-00C04FD91555} \/I {00000112-0000-0000-C000-000000000046} \/X 0x5$excludes:ProcessCommandLine
ProcessCommandLineregex_match^"?verclsid.exe"? \/S \/C {D3E34B21-9D75-101A-8C3D-00AA001A1652} \/I {00000112-0000-0000-C000-000000000046} \/X 0x5$excludes:ProcessCommandLine
ProcessCommandLineregex_match^"?verclsid.exe"? \/S \/C {E88DCCE0-B7B3-11D1-A9F0-00AA0060FA31} \/I {00000112-0000-0000-C000-000000000046} \/X 0x5$excludes:ProcessCommandLine
ProcessCommandLineregex_match^"?verclsid.exe"? \/S \/C {25336920-03F9-11CF-8FD0-00AA00686F13} \/I {00000112-0000-0000-C000-000000000046} \/X 0x5$excludes:ProcessCommandLine
FolderPathstarts_withC:\Windows\System32\spool\drivers\excludes:FolderPath field:"FolderPath" value:"C:\Windows\System32\spool\drivers\"

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
DeviceProcessEvents_InitiatingProcessCommandLineproject-rename
SHA1extend