Detection rules › Kusto

Hunt for public facing devices via public tag

Group by
DeviceName, InternetFacingLocalIp, InternetFacingLocalPort, InternetFacingPublicScannedIp, InternetFacingPublicScannedPort, InternetFacingReason, InternetFacingTransportProtocol
Author
Robbe Van den Daele
Source
github.com/HybridBrothers/Hunting-Queries-Detection-Rules

Find public facing devices via the public device tag in the DeviceInfo table. The internet facing reason is also included in this query.

MITRE ATT&CK coverage

TacticTechniques
Initial AccessT1190 Exploit Public-Facing Application

References

Rule body yaml

DeviceInfo
| where Timestamp > ago(7d)
| extend AdditionalFields = todynamic(AdditionalFields)
| where todatetime(AdditionalFields.InternetFacingLastSeen) > ago(7d)
| extend InternetFacingLastSeen = tostring(AdditionalFields.InternetFacingLastSeen)
    , InternetFacingReason = tostring(AdditionalFields.InternetFacingReason)
    , InternetFacingLocalIp = tostring(AdditionalFields.InternetFacingLocalIp)
    , InternetFacingPublicScannedIp = tostring(AdditionalFields.InternetFacingPublicScannedIp)
    , InternetFacingLocalPort = tostring(AdditionalFields.InternetFacingLocalPort)
    , InternetFacingPublicScannedPort = tostring(AdditionalFields.InternetFacingPublicScannedPort)
    , InternetFacingTransportProtocol = tostring(AdditionalFields.InternetFacingTransportProtocol)
| summarize arg_max(InternetFacingLastSeen, *) by DeviceName, InternetFacingLocalIp, InternetFacingLocalPort, InternetFacingPublicScannedIp, InternetFacingPublicScannedPort, InternetFacingTransportProtocol, InternetFacingReason
| project InternetFacingLastSeen, DeviceName, InternetFacingLocalIp, InternetFacingLocalPort, InternetFacingPublicScannedIp, InternetFacingPublicScannedPort, InternetFacingTransportProtocol, InternetFacingReason

Stages and Predicates

Stage 1: source

DeviceInfo

Stage 2: where

| where Timestamp > ago(7d)

Stage 3: extend

| extend AdditionalFields = todynamic(AdditionalFields)

Stage 4: where

| where todatetime(AdditionalFields.InternetFacingLastSeen) > ago(7d)

Stage 5: extend

| extend InternetFacingLastSeen = tostring(AdditionalFields.InternetFacingLastSeen)
    , InternetFacingReason = tostring(AdditionalFields.InternetFacingReason)
    , InternetFacingLocalIp = tostring(AdditionalFields.InternetFacingLocalIp)
    , InternetFacingPublicScannedIp = tostring(AdditionalFields.InternetFacingPublicScannedIp)
    , InternetFacingLocalPort = tostring(AdditionalFields.InternetFacingLocalPort)
    , InternetFacingPublicScannedPort = tostring(AdditionalFields.InternetFacingPublicScannedPort)
    , InternetFacingTransportProtocol = tostring(AdditionalFields.InternetFacingTransportProtocol)

Stage 6: summarize

| summarize arg_max(InternetFacingLastSeen, *) by DeviceName, InternetFacingLocalIp, InternetFacingLocalPort, InternetFacingPublicScannedIp, InternetFacingPublicScannedPort, InternetFacingTransportProtocol, InternetFacingReason

Stage 7: project

| project InternetFacingLastSeen, DeviceName, InternetFacingLocalIp, InternetFacingLocalPort, InternetFacingPublicScannedIp, InternetFacingPublicScannedPort, InternetFacingTransportProtocol, InternetFacingReason

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.

FieldSource
DeviceNameproject
InternetFacingLastSeenproject
InternetFacingLocalIpproject
InternetFacingLocalPortproject
InternetFacingPublicScannedIpproject
InternetFacingPublicScannedPortproject
InternetFacingReasonproject
InternetFacingTransportProtocolproject