Skip to content

Commit

Permalink
Merge pull request #11345 from Azure/GSA-Private-Preview-Fixes
Browse files Browse the repository at this point in the history
Fix queries
  • Loading branch information
v-atulyadav authored Oct 30, 2024
2 parents df9648e + 4ae9274 commit bc85dd6
Show file tree
Hide file tree
Showing 13 changed files with 526 additions and 450 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
id: 4c9f0a9e-44d7-4c9b-b7f0-f6a6e0d8f8fa
name: Detect Connections Outside Operational Hours
name: GSA - Detect Connections Outside Operational Hours
description: This query identifies connections that occur outside of the defined operational hours. It helps in monitoring and flagging any unusual activity that may occur during non-business hours, indicating potential security concerns or policy violations.
severity: High
status: Available
Expand Down Expand Up @@ -36,5 +36,5 @@ entityMappings:
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
version: 1.0.1
kind: Scheduled
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
id: 57abf863-1c1e-46c6-85b2-35370b712c1e
name: Detect IP Address Changes and Overlapping Sessions
name: GSA - Detect IP Address Changes and Overlapping Sessions
description: |
This query identifies network sessions based on DeviceId and UserPrincipalName, then checks for changed IP addresses and overlapping session times.
severity: High
Expand All @@ -18,22 +18,37 @@ relevantTechniques:
- T1078
- T1133
query: |
// Identify sessions
let sessions =
NetworkAccessTraffic
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), SourceIps = make_set(SourceIp) by DeviceId, UserPrincipalName, SessionId
| sort by StartTime asc;
// Check for changed IP addresses and overlapping session times
sessions
| extend PreviousSourceIps = prev(SourceIps, 1)
| extend PreviousEndTime = prev(EndTime, 1)
| extend PreviousDeviceId = prev(DeviceId, 1)
| extend PreviousUserPrincipalName = prev(UserPrincipalName, 1)
| where DeviceId == PreviousDeviceId and UserPrincipalName == PreviousUserPrincipalName
| where set_difference(SourceIps, PreviousSourceIps) != dynamic([]) // Check if the current and previous IP sets differ
| where PreviousEndTime > StartTime // Check for overlapping session times
| project DeviceId, UserPrincipalName, SourceIps, PreviousSourceIps, StartTime, EndTime, PreviousEndTime
| extend IPCustomEntity = tostring(array_slice(SourceIps, 0, 1)[0]), PreviousIPCustomEntity = tostring(array_slice(PreviousSourceIps, 0, 1)[0]), AccountCustomEntity = UserPrincipalName
// Identify sessions
let sessions =
NetworkAccessTraffic
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
SourceIps = make_set(SourceIp)
by DeviceId, UserPrincipalName, SessionId
| sort by StartTime asc;
// Check for changed IP addresses and overlapping session times
sessions
| extend PreviousSourceIps = prev(SourceIps, 1)
| extend PreviousEndTime = prev(EndTime, 1)
| extend PreviousDeviceId = prev(DeviceId, 1)
| extend PreviousUserPrincipalName = prev(UserPrincipalName, 1)
| where DeviceId == PreviousDeviceId
and UserPrincipalName == PreviousUserPrincipalName
| where array_length(set_difference(SourceIps, PreviousSourceIps)) > 0 // Check if the current and previous IP sets differ
| where PreviousEndTime > StartTime // Check for overlapping session times
| project
DeviceId,
UserPrincipalName,
SourceIps,
PreviousSourceIps,
StartTime,
EndTime,
PreviousEndTime
| extend
IPCustomEntity = tostring(array_slice(SourceIps, 0, 1)[0]),
PreviousIPCustomEntity = tostring(array_slice(PreviousSourceIps, 0, 1)[0]),
AccountCustomEntity = UserPrincipalName
entityMappings:
- entityType: Account
fieldMappings:
Expand All @@ -43,5 +58,5 @@ entityMappings:
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
version: 1.0.2
kind: Scheduled
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,5 @@ entityMappings:
columnName: UserWhoDeletedAccountName
- identifier: UPNSuffix
columnName: UserWhoDeletedAccountUPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIP
version: 2.1.3
version: 2.1.4
kind: Scheduled
Original file line number Diff line number Diff line change
Expand Up @@ -23,53 +23,85 @@ relevantTechniques:
- T1114
- T1020
query: |
// OfficeActivity Query
let officeActivityQuery = OfficeActivity
| where OfficeWorkload == "Exchange"
| where Operation in~ ("New-TransportRule", "Set-TransportRule")
| mv-apply DynamicParameters = todynamic(Parameters) on (
summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))
)
| extend RuleName = case(
Operation =~ "Set-TransportRule", OfficeObjectId,
Operation =~ "New-TransportRule", ParsedParameters.Name,
"Unknown"
)
| mv-expand ExpandedParameters = todynamic(Parameters)
| where ExpandedParameters.Name in~ ("BlindCopyTo", "RedirectMessageTo") and isnotempty(ExpandedParameters.Value)
| extend RedirectTo = ExpandedParameters.Value
| extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIP)[0]
| extend From = ParsedParameters.From
| project TimeGenerated, RedirectTo, IPAddress = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1]), UserId, From, Operation, RuleName, Parameters
| extend AccountName = tostring(split(UserId, "@")[0]),
AccountUPNSuffix = tostring(split(UserId, "@")[1]);
// EnrichedMicrosoft365AuditLogs Query
let enrichedLogsQuery = EnrichedMicrosoft365AuditLogs
| where Workload == "Exchange"
| where Operation in~ ("New-TransportRule", "Set-TransportRule")
| extend AdditionalProps = parse_json(AdditionalProperties)
| mv-apply DynamicParameters = todynamic(AdditionalProps.Parameters) on (
summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))
)
| extend RuleName = case(
Operation =~ "Set-TransportRule", ObjectId,
Operation =~ "New-TransportRule", ParsedParameters.Name,
"Unknown"
)
| mv-expand ExpandedParameters = todynamic(AdditionalProps.Parameters)
| where ExpandedParameters.Name in~ ("BlindCopyTo", "RedirectMessageTo") and isnotempty(ExpandedParameters.Value)
| extend RedirectTo = ExpandedParameters.Value
| extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIp)[0]
| extend From = ParsedParameters.From
| extend UserAgent = tostring(AdditionalProps.UserAgent)
| project TimeGenerated, RedirectTo, IPAddress = tostring(ClientIPValues[0]), Port = tostring(ClientIPValues[1]), UserId, From, Operation, RuleName, Parameters = tostring(AdditionalProps.Parameters), UserAgent
| extend AccountName = tostring(split(UserId, "@")[0]),
AccountUPNSuffix = tostring(split(UserId, "@")[1]);
// Combine both queries
union isfuzzy=true officeActivityQuery, enrichedLogsQuery
| summarize arg_min(TimeGenerated, *) by RuleName, RedirectTo
| project TimeGenerated, RedirectTo, IPAddress, Port, UserId, From, Operation, RuleName, Parameters, AccountName, AccountUPNSuffix
| order by TimeGenerated desc;
// OfficeActivity Query
let officeActivityQuery = OfficeActivity
| where OfficeWorkload == "Exchange"
| where Operation in~ ("New-TransportRule", "Set-TransportRule")
| mv-apply DynamicParameters = todynamic(Parameters) on (
summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))
)
| extend RuleName = case(
Operation =~ "Set-TransportRule", OfficeObjectId,
Operation =~ "New-TransportRule", ParsedParameters.Name,
"Unknown"
)
| mv-expand ExpandedParameters = todynamic(Parameters)
| where ExpandedParameters.Name in~ ("BlindCopyTo", "RedirectMessageTo") and isnotempty(ExpandedParameters.Value)
| extend RedirectTo = tostring(ExpandedParameters.Value) // Cast to string
| extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIP)[0]
| extend From = ParsedParameters.From
| project
TimeGenerated,
RedirectTo,
IPAddress = tostring(ClientIPValues[0]),
Port = tostring(ClientIPValues[1]),
UserId,
From,
Operation,
RuleName,
Parameters
| extend
AccountName = tostring(split(UserId, "@")[0]),
AccountUPNSuffix = tostring(split(UserId, "@")[1]);
// EnrichedMicrosoft365AuditLogs Query
let enrichedLogsQuery = EnrichedMicrosoft365AuditLogs
| where Workload == "Exchange"
| where Operation in~ ("New-TransportRule", "Set-TransportRule")
| extend AdditionalProps = parse_json(AdditionalProperties)
| mv-apply DynamicParameters = todynamic(AdditionalProps.Parameters) on (
summarize ParsedParameters = make_bag(pack(tostring(DynamicParameters.Name), DynamicParameters.Value))
)
| extend RuleName = case(
Operation =~ "Set-TransportRule", ObjectId,
Operation =~ "New-TransportRule", ParsedParameters.Name,
"Unknown"
)
| mv-expand ExpandedParameters = todynamic(AdditionalProps.Parameters)
| where ExpandedParameters.Name in~ ("BlindCopyTo", "RedirectMessageTo") and isnotempty(ExpandedParameters.Value)
| extend RedirectTo = tostring(ExpandedParameters.Value) // Cast to string
| extend ClientIPValues = extract_all(@'\[?(::ffff:)?(?P<IPAddress>(\d+\.\d+\.\d+\.\d+)|[^\]]+)\]?([-:](?P<Port>\d+))?', dynamic(["IPAddress", "Port"]), ClientIp)[0]
| extend From = ParsedParameters.From
| extend UserAgent = tostring(AdditionalProps.UserAgent)
| project
TimeGenerated,
RedirectTo,
IPAddress = tostring(ClientIPValues[0]),
Port = tostring(ClientIPValues[1]),
UserId,
From,
Operation,
RuleName,
Parameters = tostring(AdditionalProps.Parameters),
UserAgent
| extend
AccountName = tostring(split(UserId, "@")[0]),
AccountUPNSuffix = tostring(split(UserId, "@")[1]);
// Combine both queries
union isfuzzy=true officeActivityQuery, enrichedLogsQuery
| summarize arg_min(TimeGenerated, *) by RuleName, RedirectTo
| project
TimeGenerated,
RedirectTo,
IPAddress,
Port,
UserId,
From,
Operation,
RuleName,
Parameters,
AccountName,
AccountUPNSuffix
| order by TimeGenerated desc
entityMappings:
- entityType: Account
fieldMappings:
Expand All @@ -83,5 +115,5 @@ entityMappings:
fieldMappings:
- identifier: Address
columnName: IPAddress
version: 2.1.4
version: 2.1.5
kind: Scheduled
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
id: e3b6a9e7-4c3a-45e6-8baf-1d3bfa8e0c2b
name: Detect Abnormal Deny Rate for Source to Destination IP
name: GSA - Detect Abnormal Deny Rate for Source to Destination IP
description: |
Identifies abnormal deny rate for specific source IP to destination IP based on the normal average and standard deviation learned during a configured period. This can indicate potential exfiltration, initial access, or C2, where an attacker tries to exploit the same vulnerability on machines in the organization but is being blocked by firewall rules.
configurableParameters:
Expand Down Expand Up @@ -54,5 +54,5 @@ entityMappings:
fieldMappings:
- identifier: Url
columnName: DestinationIp
version: 1.0.0
version: 1.0.1
kind: Scheduled
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
id: f6a8d6a5-3e9f-47c8-a8d5-1b2b9d3b7d6a
name: Detect Protocol Changes for Destination Ports
name: GSA - Detect Protocol Changes for Destination Ports
description: |
Identifies changes in the protocol used for specific destination ports, comparing the current runtime with a learned baseline. This can indicate potential protocol misuse or configuration changes.
Expand Down Expand Up @@ -50,5 +50,5 @@ entityMappings:
fieldMappings:
- identifier: Url
columnName: FqdnCustomEntity
version: 1.0.0
version: 1.0.1
kind: Scheduled
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
id: 82cfa6b9-5f7e-4b8b-8b2f-a63f21b7a7d1
name: Detect Source IP Scanning Multiple Open Ports
name: GSA - Detect Source IP Scanning Multiple Open Ports
description: |
Identifies a source IP scanning multiple open ports on Global Secure Access Firewall. This can indicate malicious scanning of ports by an attacker, trying to reveal open ports in the organization that can be compromised for initial access.
Configurable Parameters:
Expand All @@ -25,9 +25,9 @@ query: |
NetworkAccessTraffic
| where TimeGenerated > ago(1d)
| where Action == 'Allowed'
| summarize PortsScanned = dcount(DestinationPort) by SourceIp, bin(TimeGenerated, port_scan_time)
| summarize PortsScanned = dcount(DestinationPort) by SourceIp, DestinationFqdn, bin(TimeGenerated, port_scan_time)
| where PortsScanned > min_ports_threshold
| project SourceIp, PortsScanned, TimeGenerated
| project SourceIp, PortsScanned, TimeGenerated,DestinationFqdn
entityMappings:
- entityType: IP
fieldMappings:
Expand All @@ -36,6 +36,6 @@ entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Fqdn
version: 1.0.0
columnName: DestinationFqdn
version: 1.0.1
kind: Scheduled
Loading

0 comments on commit bc85dd6

Please sign in to comment.