RareDNSLookupWithDataTransfer
Description
'This query helps identify rare DNS connections and resulting data transfer to/from the associated domain. It can help identify unexpected large data transfers to or from internal systems which may indicate data exfil or malicious tool download.'
Query · kql
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let lookback = totimespan((endtime-starttime)*7);
let lookupThreshold = 21;
let lookbackint = 7;
let binvalue = 1;
let bintime = make_timespan(binvalue,0);
let avgCalc = starttime/1h;
// Identify all domain lookups before start time and after lookback time
let DomainLookups = DnsEvents
| where TimeGenerated between(ago(lookback)..starttime)
| where SubType == "LookupQuery"
| where isnotempty(IPAddresses)
| extend Domain = iff(countof(Name,'.') >= 2, strcat(split(Name,'.')[-2], '.',split(Name,'.')[-1]), Name)
| summarize DomainCount = count() by Domain
| project Domain, DailyAvgLookupCountOverLookback = DomainCount/lookbackint;
// Common lookups should not include items that occurred more rarely over the lookback period.
let CommonLookups = DomainLookups
| where DailyAvgLookupCountOverLookback > lookupThreshold;
// Get current lookups to compare against the lookback period
let TodayLookups = DnsEvents
| where TimeGenerated between(starttime..endtime)
| where SubType == "LookupQuery"
| where isnotempty(IPAddresses)
| extend Domain = iff(countof(Name,'.') >= 2, strcat(split(Name,'.')[-2], '.',split(Name,'.')[-1]), Name)
| summarize LookupStartTime = min(TimeGenerated), LookupEndTime = max(TimeGenerated), LookupCountToday = count() by ClientIP, Domain, IPAddresses
| project LookupStartTime, LookupEndTime, ClientIP, LookupCountToday, Domain, IPAddresses;
// Remove Common Lookups from lookback period from Todays lookups
let UncommonLookupsToday = TodayLookups
| join kind=leftanti (
CommonLookups
)
on Domain;
// Join back the Daily Average Lookup Count to add context to rarity over lookback period
let RareLookups = UncommonLookupsToday | join kind= innerunique (
DomainLookups
) on Domain
| project LookupStartTime, LookupEndTime, ClientIP, Domain, IPAddresses, LookupCountToday, DailyAvgLookupCountOverLookback;
let DNSIPBreakout = RareLookups
| extend DnsIPAddress = iff(IPAddresses has ",", split(IPAddresses, ","), todynamic(IPAddresses))
| mvexpand DnsIPAddress
| extend DnsIPAddress = tostring(DnsIPAddress)
| distinct LookupStartTime, LookupEndTime, ClientIP, Domain, DnsIPAddress, LookupCountToday, DailyAvgLookupCountOverLookback
| extend IPCustomEntity = DnsIPAddress
| where ipv4_is_private(DnsIPAddress) == false
;
let DataMovement = ( union isfuzzy=true
(CommonSecurityLog
| where TimeGenerated between(starttime..endtime)
| where DeviceVendor =="Palo Alto Networks" and Activity == "TRAFFIC"
| where ipv4_is_private(DestinationIP) == false
| project DataType = DeviceVendor, TimeGenerated, SourceIP , SourcePort , DestinationIP, DestinationPort, ReceivedBytes, SentBytes
| sort by SourceIP asc, SourcePort asc,TimeGenerated asc, DestinationIP asc, DestinationPort asc
| summarize sum(ReceivedBytes), sum(SentBytes), ConnectionCount = count() by DataType, SourceIP, SourcePort, DestinationIP, DestinationPort
| extend IPCustomEntity = DestinationIP
| sort by sum_SentBytes desc
),
(VMConnection
| where TimeGenerated between(starttime..endtime)
| where Direction =~ "Outbound"
| where ipv4_is_private(DestinationIp) == false
| project DataType = Type, TimeGenerated, SourceIP = SourceIp , DestinationIP = DestinationIp, DestinationPort, ReceivedBytes = BytesReceived, SentBytes = BytesSent
| summarize sum(ReceivedBytes), sum(SentBytes), ConnectionCount = count() by DataType, SourceIP, DestinationIP, DestinationPort
| sort by sum_SentBytes desc
| extend IPCustomEntity = DestinationIP
)
);
DNSIPBreakout | join kind = leftouter (
DataMovement
) on $left.DnsIPAddress == $right.DestinationIP and $left.ClientIP == $right.SourceIP
| project-away DnsIPAddress, ClientIP
// The below condition can be removed to see all DNS results.
// This is used here as the goal of the query is to connect rare DNS lookups to a data type that can show byte transfers to that given DestinationIP
| where isnotempty(DataType)
| extend timestamp = LookupStartTime, IPCustomEntity = DestinationIP