SMBWindows Admin Shares
Id | 9da25366-2c77-41a5-a159-0da5e2f5fb90 |
Rulename | SMB/Windows Admin Shares |
Description | This query is based on detecting incoming RPC/TCP on the SCM, followed by the start of a child process of services.exe. Remotely interacting with the SCM triggers the RPC/TCP traffic on services.exe, and the creation of the child processes is a result of starting the service. The query might look intimidating given its size. That’s why we’ve commented the query per logic block to walk you through the details. |
Severity | Medium |
Tactics | LateralMovement |
Techniques | T1021.002 |
Required data connectors | MicrosoftThreatProtection |
Kind | Scheduled |
Query frequency | 1h |
Query period | 1h |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/FalconFriday/Analytic Rules/SMBWindowsAdminShares.yaml |
Version | 1.0.0 |
Arm template | 9da25366-2c77-41a5-a159-0da5e2f5fb90.json |
// This rule is fairly complex, hence this documentation.
// The rule tries to detect network activity from services.exe followed by the start of a new childvprocess of services.exe (i.e. a service start).
// The rule tries to filter false positives as much as possible.
// Since the FileProfile info is currently super-unreliable (unsure why), we have to work around it.
//
// The following list of LOLBINs is used to include all results which have a high reputation, but are LOLBINs.
let lolbins = dynamic(["At.exe", "Atbroker.exe", "Bash.exe", "Bitsadmin.exe", "CertReq.exe", "Certutil.exe", "Cmd.exe", "Cmdkey.exe", "Cmstp.exe", "Control.exe", "Csc.exe", "Cscript.exe", "Desktopimgdownldr.exe", "Dfsvc.exe", "Diantz.exe", "Diskshadow.exe", "Dnscmd.exe", "Esentutl.exe", "Eventvwr.exe", "Expand.exe", "Extexport.exe", "Extrac32.exe", "Findstr.exe", "Forfiles.exe", "Ftp.exe", "GfxDownloadWrapper.exe", "Gpscript.exe", "Hh.exe", "Ie4uinit.exe", "Ieexec.exe", "Ilasm.exe", "Infdefaultinstall.exe", "Installutil.exe", "Jsc.exe", "Makecab.exe", "Mavinject.exe", "Microsoft.Workflow.Compiler.exe", "Mmc.exe", "MpCmdRun.exe", "Msbuild.exe", "Msconfig.exe", "Msdt.exe", "Mshta.exe", "Msiexec.exe", "Netsh.exe", "Odbcconf.exe", "Pcalua.exe", "Pcwrun.exe", "Pktmon.exe", "Presentationhost.exe", "Print.exe", "Psr.exe", "Rasautou.exe", "Reg.exe", "Regasm.exe", "Regedit.exe", "Regini.exe", "Register-cimprovider.exe", "Regsvcs.exe", "Regsvr32.exe", "Replace.exe", "Rpcping.exe", "Rundll32.exe", "Runonce.exe", "Runscripthelper.exe", "Sc.exe", "Schtasks.exe", "Scriptrunner.exe", "SyncAppvPublishingServer.exe", "Ttdinject.exe", "Tttracer.exe", "vbc.exe", "Verclsid.exe", "Wab.exe", "Wmic.exe", "Wscript.exe", "Wsreset.exe", "Xwizard.exe", "AgentExecutor.exe", "Appvlp.exe", "Bginfo.exe", "Cdb.exe", "csi.exe", "Devtoolslauncher.exe", "dnx.exe", "Dotnet.exe", "Dxcap.exe", "Excel.exe", "Mftrace.exe", "Msdeploy.exe", "msxsl.exe", "ntdsutil.exe", "Powerpnt.exe", "rcsi.exe", "Sqldumper.exe", "Sqlps.exe", "SQLToolsPS.exe", "Squirrel.exe", "te.exe", "Tracker.exe", "Update.exe", "vsjitdebugger.exe", "Winword.exe", "Wsl.exe"]);
// First, we want to get all the network events triggered by services.exe.
let networkEvents = materialize(DeviceNetworkEvents
| where InitiatingProcessFileName in~ ("services.exe")
| where ActionType == "InboundConnectionAccepted"
| project-rename TimestampNetworkAct=Timestamp);
// Next, we want to get the list of child processes created by services.exe.
let allServices = materialize ((
DeviceProcessEvents
// This is for optimization purposes, as filtering is way faster than joins.
| where DeviceId in ((networkEvents | project DeviceId))
//svchost and sppsvc are created very often as child processes of services.exe
| where InitiatingProcessFileName =~ "services.exe"
| project TimestampChild=Timestamp, DeviceId, DeviceName, FileName, ProcessCommandLine, SHA1,
InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessSHA1, InitiatingProcessId, TimestampServicesExe=InitiatingProcessCreationTime));
// Now we are going to join the process creations and network events
// and filter out all the tables _after_ the join where the child process
// of svchost is not created shortly after the network activity (between 0 and 10 seconds).
let serviceNetworkEvents = materialize(
(networkEvents
| join kind=inner hint.strategy=shuffle allServices on DeviceId, InitiatingProcessId, InitiatingProcessFileName
)
| where datetime_diff("Second", TimestampChild, TimestampNetworkAct) between (0 .. 10)
// Only get the results where the network activity occured more than 1 minute after services.exe has started (i.e. system boot).
| where datetime_diff("Second", TimestampNetworkAct, TimestampServicesExe) > 60);
// Next, we want to check the reputation of all procssess.
// Since FileProfile is not properly working, we use the built-in DeviceFileCertificateInfo for the AntiJoin.
// The goal is to create a list of SHA1 hashes of the spawned processess
// which have a low prevelance and are not in de DeviceFileCertificateInfo.
let serviceNetworkEventsWithSHA1 = materialize(serviceNetworkEvents
| summarize count() by SHA1
| join kind=leftanti hint.strategy=broadcast DeviceFileCertificateInfo on SHA1
| where count_ < 100);
// Finally, we need to bring everything together.
// We take our subset of the child processess created by services.exe (serviceNetworkEvents).
// Everything which is on our deny-list of SHA1 processess OR are LOLBINs, are filtered out.
// Additionally, we filter out msiexec, since that appears to come very often.
serviceNetworkEvents
| where SHA1 in ((serviceNetworkEventsWithSHA1 | project SHA1)) or FileName in~ (lolbins)
| where ProcessCommandLine !~ "msiexec.exe /V"
// This summarize is optional if you want to group similar stuff.
//| summarize ActionType=make_set(ActionType), RemoteIPs=make_set(strcat(RemoteIP, ":", RemotePort, " (", RemoteUrl, ")")), LocalPort=make_set(LocalPort) by bin(TimestampNetworkAct, 1m), DeviceId, DeviceName, LocalIP, Protocol, AdditionalFields, bin(TimestampChild, 1m), FileName, ProcessCommandLine, SHA1, InitiatingProcessFileName, InitiatingProcessCommandLine1, bin(TimestampServicesExe, 1m)
relevantTechniques:
- T1021.002
name: SMB/Windows Admin Shares
requiredDataConnectors:
- dataTypes:
- DeviceProcessEvents
- DeviceNetworkEvents
- DeviceFileCertificateInfo
connectorId: MicrosoftThreatProtection
entityMappings:
- fieldMappings:
- identifier: FullName
columnName: DeviceName
entityType: Host
- fieldMappings:
- identifier: Sid
columnName: InitiatingProcessAccountSid
- identifier: Name
columnName: InitiatingProcessAccountName
- identifier: NTDomain
columnName: InitiatingProcessAccountDomain
entityType: Account
- fieldMappings:
- identifier: CommandLine
columnName: InitiatingProcessCommandLine
entityType: Process
- fieldMappings:
- identifier: Address
columnName: RemoteIP
entityType: IP
triggerThreshold: 0
id: 9da25366-2c77-41a5-a159-0da5e2f5fb90
tactics:
- LateralMovement
version: 1.0.0
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/FalconFriday/Analytic Rules/SMBWindowsAdminShares.yaml
queryPeriod: 1h
kind: Scheduled
queryFrequency: 1h
severity: Medium
status: Available
description: |
This query is based on detecting incoming RPC/TCP on the SCM, followed by the start of a child process of services.exe. Remotely interacting with the SCM triggers the RPC/TCP traffic on services.exe, and the creation of the child processes is a result of starting the service.
The query might look intimidating given its size. That's why we've commented the query per logic block to walk you through the details.
query: |
// This rule is fairly complex, hence this documentation.
// The rule tries to detect network activity from services.exe followed by the start of a new childvprocess of services.exe (i.e. a service start).
// The rule tries to filter false positives as much as possible.
// Since the FileProfile info is currently super-unreliable (unsure why), we have to work around it.
//
// The following list of LOLBINs is used to include all results which have a high reputation, but are LOLBINs.
let lolbins = dynamic(["At.exe", "Atbroker.exe", "Bash.exe", "Bitsadmin.exe", "CertReq.exe", "Certutil.exe", "Cmd.exe", "Cmdkey.exe", "Cmstp.exe", "Control.exe", "Csc.exe", "Cscript.exe", "Desktopimgdownldr.exe", "Dfsvc.exe", "Diantz.exe", "Diskshadow.exe", "Dnscmd.exe", "Esentutl.exe", "Eventvwr.exe", "Expand.exe", "Extexport.exe", "Extrac32.exe", "Findstr.exe", "Forfiles.exe", "Ftp.exe", "GfxDownloadWrapper.exe", "Gpscript.exe", "Hh.exe", "Ie4uinit.exe", "Ieexec.exe", "Ilasm.exe", "Infdefaultinstall.exe", "Installutil.exe", "Jsc.exe", "Makecab.exe", "Mavinject.exe", "Microsoft.Workflow.Compiler.exe", "Mmc.exe", "MpCmdRun.exe", "Msbuild.exe", "Msconfig.exe", "Msdt.exe", "Mshta.exe", "Msiexec.exe", "Netsh.exe", "Odbcconf.exe", "Pcalua.exe", "Pcwrun.exe", "Pktmon.exe", "Presentationhost.exe", "Print.exe", "Psr.exe", "Rasautou.exe", "Reg.exe", "Regasm.exe", "Regedit.exe", "Regini.exe", "Register-cimprovider.exe", "Regsvcs.exe", "Regsvr32.exe", "Replace.exe", "Rpcping.exe", "Rundll32.exe", "Runonce.exe", "Runscripthelper.exe", "Sc.exe", "Schtasks.exe", "Scriptrunner.exe", "SyncAppvPublishingServer.exe", "Ttdinject.exe", "Tttracer.exe", "vbc.exe", "Verclsid.exe", "Wab.exe", "Wmic.exe", "Wscript.exe", "Wsreset.exe", "Xwizard.exe", "AgentExecutor.exe", "Appvlp.exe", "Bginfo.exe", "Cdb.exe", "csi.exe", "Devtoolslauncher.exe", "dnx.exe", "Dotnet.exe", "Dxcap.exe", "Excel.exe", "Mftrace.exe", "Msdeploy.exe", "msxsl.exe", "ntdsutil.exe", "Powerpnt.exe", "rcsi.exe", "Sqldumper.exe", "Sqlps.exe", "SQLToolsPS.exe", "Squirrel.exe", "te.exe", "Tracker.exe", "Update.exe", "vsjitdebugger.exe", "Winword.exe", "Wsl.exe"]);
// First, we want to get all the network events triggered by services.exe.
let networkEvents = materialize(DeviceNetworkEvents
| where InitiatingProcessFileName in~ ("services.exe")
| where ActionType == "InboundConnectionAccepted"
| project-rename TimestampNetworkAct=Timestamp);
// Next, we want to get the list of child processes created by services.exe.
let allServices = materialize ((
DeviceProcessEvents
// This is for optimization purposes, as filtering is way faster than joins.
| where DeviceId in ((networkEvents | project DeviceId))
//svchost and sppsvc are created very often as child processes of services.exe
| where InitiatingProcessFileName =~ "services.exe"
| project TimestampChild=Timestamp, DeviceId, DeviceName, FileName, ProcessCommandLine, SHA1,
InitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessSHA1, InitiatingProcessId, TimestampServicesExe=InitiatingProcessCreationTime));
// Now we are going to join the process creations and network events
// and filter out all the tables _after_ the join where the child process
// of svchost is not created shortly after the network activity (between 0 and 10 seconds).
let serviceNetworkEvents = materialize(
(networkEvents
| join kind=inner hint.strategy=shuffle allServices on DeviceId, InitiatingProcessId, InitiatingProcessFileName
)
| where datetime_diff("Second", TimestampChild, TimestampNetworkAct) between (0 .. 10)
// Only get the results where the network activity occured more than 1 minute after services.exe has started (i.e. system boot).
| where datetime_diff("Second", TimestampNetworkAct, TimestampServicesExe) > 60);
// Next, we want to check the reputation of all procssess.
// Since FileProfile is not properly working, we use the built-in DeviceFileCertificateInfo for the AntiJoin.
// The goal is to create a list of SHA1 hashes of the spawned processess
// which have a low prevelance and are not in de DeviceFileCertificateInfo.
let serviceNetworkEventsWithSHA1 = materialize(serviceNetworkEvents
| summarize count() by SHA1
| join kind=leftanti hint.strategy=broadcast DeviceFileCertificateInfo on SHA1
| where count_ < 100);
// Finally, we need to bring everything together.
// We take our subset of the child processess created by services.exe (serviceNetworkEvents).
// Everything which is on our deny-list of SHA1 processess OR are LOLBINs, are filtered out.
// Additionally, we filter out msiexec, since that appears to come very often.
serviceNetworkEvents
| where SHA1 in ((serviceNetworkEventsWithSHA1 | project SHA1)) or FileName in~ (lolbins)
| where ProcessCommandLine !~ "msiexec.exe /V"
// This summarize is optional if you want to group similar stuff.
//| summarize ActionType=make_set(ActionType), RemoteIPs=make_set(strcat(RemoteIP, ":", RemotePort, " (", RemoteUrl, ")")), LocalPort=make_set(LocalPort) by bin(TimestampNetworkAct, 1m), DeviceId, DeviceName, LocalIP, Protocol, AdditionalFields, bin(TimestampChild, 1m), FileName, ProcessCommandLine, SHA1, InitiatingProcessFileName, InitiatingProcessCommandLine1, bin(TimestampServicesExe, 1m)
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/9da25366-2c77-41a5-a159-0da5e2f5fb90')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/9da25366-2c77-41a5-a159-0da5e2f5fb90')]",
"properties": {
"alertRuleTemplateName": "9da25366-2c77-41a5-a159-0da5e2f5fb90",
"customDetails": null,
"description": "This query is based on detecting incoming RPC/TCP on the SCM, followed by the start of a child process of services.exe. Remotely interacting with the SCM triggers the RPC/TCP traffic on services.exe, and the creation of the child processes is a result of starting the service. \nThe query might look intimidating given its size. That's why we've commented the query per logic block to walk you through the details.\n",
"displayName": "SMB/Windows Admin Shares",
"enabled": true,
"entityMappings": [
{
"entityType": "Host",
"fieldMappings": [
{
"columnName": "DeviceName",
"identifier": "FullName"
}
]
},
{
"entityType": "Account",
"fieldMappings": [
{
"columnName": "InitiatingProcessAccountSid",
"identifier": "Sid"
},
{
"columnName": "InitiatingProcessAccountName",
"identifier": "Name"
},
{
"columnName": "InitiatingProcessAccountDomain",
"identifier": "NTDomain"
}
]
},
{
"entityType": "Process",
"fieldMappings": [
{
"columnName": "InitiatingProcessCommandLine",
"identifier": "CommandLine"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "RemoteIP",
"identifier": "Address"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/FalconFriday/Analytic Rules/SMBWindowsAdminShares.yaml",
"query": "// This rule is fairly complex, hence this documentation.\n// The rule tries to detect network activity from services.exe followed by the start of a new childvprocess of services.exe (i.e. a service start).\n// The rule tries to filter false positives as much as possible. \n// Since the FileProfile info is currently super-unreliable (unsure why), we have to work around it.\n// \n// The following list of LOLBINs is used to include all results which have a high reputation, but are LOLBINs.\nlet lolbins = dynamic([\"At.exe\", \"Atbroker.exe\", \"Bash.exe\", \"Bitsadmin.exe\", \"CertReq.exe\", \"Certutil.exe\", \"Cmd.exe\", \"Cmdkey.exe\", \"Cmstp.exe\", \"Control.exe\", \"Csc.exe\", \"Cscript.exe\", \"Desktopimgdownldr.exe\", \"Dfsvc.exe\", \"Diantz.exe\", \"Diskshadow.exe\", \"Dnscmd.exe\", \"Esentutl.exe\", \"Eventvwr.exe\", \"Expand.exe\", \"Extexport.exe\", \"Extrac32.exe\", \"Findstr.exe\", \"Forfiles.exe\", \"Ftp.exe\", \"GfxDownloadWrapper.exe\", \"Gpscript.exe\", \"Hh.exe\", \"Ie4uinit.exe\", \"Ieexec.exe\", \"Ilasm.exe\", \"Infdefaultinstall.exe\", \"Installutil.exe\", \"Jsc.exe\", \"Makecab.exe\", \"Mavinject.exe\", \"Microsoft.Workflow.Compiler.exe\", \"Mmc.exe\", \"MpCmdRun.exe\", \"Msbuild.exe\", \"Msconfig.exe\", \"Msdt.exe\", \"Mshta.exe\", \"Msiexec.exe\", \"Netsh.exe\", \"Odbcconf.exe\", \"Pcalua.exe\", \"Pcwrun.exe\", \"Pktmon.exe\", \"Presentationhost.exe\", \"Print.exe\", \"Psr.exe\", \"Rasautou.exe\", \"Reg.exe\", \"Regasm.exe\", \"Regedit.exe\", \"Regini.exe\", \"Register-cimprovider.exe\", \"Regsvcs.exe\", \"Regsvr32.exe\", \"Replace.exe\", \"Rpcping.exe\", \"Rundll32.exe\", \"Runonce.exe\", \"Runscripthelper.exe\", \"Sc.exe\", \"Schtasks.exe\", \"Scriptrunner.exe\", \"SyncAppvPublishingServer.exe\", \"Ttdinject.exe\", \"Tttracer.exe\", \"vbc.exe\", \"Verclsid.exe\", \"Wab.exe\", \"Wmic.exe\", \"Wscript.exe\", \"Wsreset.exe\", \"Xwizard.exe\", \"AgentExecutor.exe\", \"Appvlp.exe\", \"Bginfo.exe\", \"Cdb.exe\", \"csi.exe\", \"Devtoolslauncher.exe\", \"dnx.exe\", \"Dotnet.exe\", \"Dxcap.exe\", \"Excel.exe\", \"Mftrace.exe\", \"Msdeploy.exe\", \"msxsl.exe\", \"ntdsutil.exe\", \"Powerpnt.exe\", \"rcsi.exe\", \"Sqldumper.exe\", \"Sqlps.exe\", \"SQLToolsPS.exe\", \"Squirrel.exe\", \"te.exe\", \"Tracker.exe\", \"Update.exe\", \"vsjitdebugger.exe\", \"Winword.exe\", \"Wsl.exe\"]);\n// First, we want to get all the network events triggered by services.exe.\nlet networkEvents = materialize(DeviceNetworkEvents\n| where InitiatingProcessFileName in~ (\"services.exe\")\n| where ActionType == \"InboundConnectionAccepted\"\n| project-rename TimestampNetworkAct=Timestamp);\n// Next, we want to get the list of child processes created by services.exe. \nlet allServices = materialize ((\nDeviceProcessEvents \n// This is for optimization purposes, as filtering is way faster than joins.\n| where DeviceId in ((networkEvents | project DeviceId))\n//svchost and sppsvc are created very often as child processes of services.exe\n| where InitiatingProcessFileName =~ \"services.exe\" \n| project TimestampChild=Timestamp, DeviceId, DeviceName, FileName, ProcessCommandLine, SHA1, \nInitiatingProcessFileName, InitiatingProcessCommandLine, InitiatingProcessSHA1, InitiatingProcessId, TimestampServicesExe=InitiatingProcessCreationTime));\n// Now we are going to join the process creations and network events \n// and filter out all the tables _after_ the join where the child process \n// of svchost is not created shortly after the network activity (between 0 and 10 seconds). \nlet serviceNetworkEvents = materialize(\n(networkEvents \n | join kind=inner hint.strategy=shuffle allServices on DeviceId, InitiatingProcessId, InitiatingProcessFileName\n) \n| where datetime_diff(\"Second\", TimestampChild, TimestampNetworkAct) between (0 .. 10)\n// Only get the results where the network activity occured more than 1 minute after services.exe has started (i.e. system boot).\n| where datetime_diff(\"Second\", TimestampNetworkAct, TimestampServicesExe) > 60);\n// Next, we want to check the reputation of all procssess.\n// Since FileProfile is not properly working, we use the built-in DeviceFileCertificateInfo for the AntiJoin.\n// The goal is to create a list of SHA1 hashes of the spawned processess \n// which have a low prevelance and are not in de DeviceFileCertificateInfo.\nlet serviceNetworkEventsWithSHA1 = materialize(serviceNetworkEvents\n| summarize count() by SHA1\n| join kind=leftanti hint.strategy=broadcast DeviceFileCertificateInfo on SHA1\n| where count_ < 100);\n// Finally, we need to bring everything together.\n// We take our subset of the child processess created by services.exe (serviceNetworkEvents).\n// Everything which is on our deny-list of SHA1 processess OR are LOLBINs, are filtered out.\n// Additionally, we filter out msiexec, since that appears to come very often.\nserviceNetworkEvents\n| where SHA1 in ((serviceNetworkEventsWithSHA1 | project SHA1)) or FileName in~ (lolbins) \n| where ProcessCommandLine !~ \"msiexec.exe /V\" \n// This summarize is optional if you want to group similar stuff.\n//| summarize ActionType=make_set(ActionType), RemoteIPs=make_set(strcat(RemoteIP, \":\", RemotePort, \" (\", RemoteUrl, \")\")), LocalPort=make_set(LocalPort) by bin(TimestampNetworkAct, 1m), DeviceId, DeviceName, LocalIP, Protocol, AdditionalFields, bin(TimestampChild, 1m), FileName, ProcessCommandLine, SHA1, InitiatingProcessFileName, InitiatingProcessCommandLine1, bin(TimestampServicesExe, 1m) \n",
"queryFrequency": "PT1H",
"queryPeriod": "PT1H",
"severity": "Medium",
"status": "Available",
"subTechniques": [
"T1021.002"
],
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"LateralMovement"
],
"techniques": [
"T1021"
],
"templateVersion": "1.0.0",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}