From e2130d5cbd58342e95fe3899e34c8420931b4030 Mon Sep 17 00:00:00 2001 From: Jt3kt Date: Thu, 15 Jul 2021 09:10:00 -0600 Subject: [PATCH 1/5] Update Get-LrtAzSecurityAlerts.ps1 Correct pagination behavior to prevent data return error based on endpoint restriction on number of returned results. Corrected behavior of -top when applied without any other filter criteria. --- .../Security/Get-LrtAzSecurityAlerts.ps1 | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/src/Public/Azure/Graph/Security/Get-LrtAzSecurityAlerts.ps1 b/src/Public/Azure/Graph/Security/Get-LrtAzSecurityAlerts.ps1 index 1ebca04..5459778 100644 --- a/src/Public/Azure/Graph/Security/Get-LrtAzSecurityAlerts.ps1 +++ b/src/Public/Azure/Graph/Security/Get-LrtAzSecurityAlerts.ps1 @@ -219,7 +219,12 @@ Function Get-LrtAzSecurityAlerts { } if ($Top) { - $QueryString = $QueryString + "&`$top=$Top" + if ($QueryString) { + $QueryString = $QueryString + "&`$top=$Top" + } else { + $QueryString = "?`$top=$Top" + } + Write-Verbose "[$Me]: QueryString is [$QueryString]" } $RequestUrl = $RequestUri + $QueryString @@ -236,7 +241,7 @@ Function Get-LrtAzSecurityAlerts { throw [Exception] "[$Me] [$($Err.error.code)]: $($Err.error.message)`n" } - + $ResultsCount = $($Response.value.count) # Cast result to List Write-Verbose "Results: $($Response.value.count)" [List[Object]] $ResultSet = $Response.value @@ -251,7 +256,7 @@ Function Get-LrtAzSecurityAlerts { $PageCount = 0 # Begin paging until no more pages. while ($Paging) { - Write-Verbose ">>>> More Results, calling next page <<<<" + # Iterate the page count $PageCount += 1 # Make the next request, using the nextLink property. @@ -264,25 +269,32 @@ Function Get-LrtAzSecurityAlerts { $Err = Get-RestErrorMessage $_ throw [Exception] "[$Me] [$($Err.error.code)]: $($Err.error.message)`n" } + + $ResultsCount += $($Response.value.count) + Write-Verbose "Current Page: $PageCount Request Results: $($Response.value.count) Total Results: $ResultsCount" # Cast result to List and append to ResultSet - Write-Verbose "Results: $($Response.value.count)" [List[Object]] $PageSet = $Response.value $ResultSet.AddRange($PageSet) - - # Check if done - if ($Response.'@odata.nextLink') { - $NextPage = $Response.'@odata.nextLink' - } elseif ($PageCount -ge 20) { - $Paging = $false + # Prevent from requesting over 5,000 results, GraphAPI pagination halt point. + # The check is if the current count + a return sample result of the last result is greater than 5,000, do not request the next page. + if (($ResultsCount + $($Response.value.count) -lt 5000)) { + if ($Response.'@odata.nextLink') { + $NextPage = $Response.'@odata.nextLink' + } elseif ($PageCount -ge 20) { + Write-Verbose "Stopping pagination. Reason: Iterated over 20 pages. Currently limited to only 20 pages, apply -Top to retrieve more results per request." + $Paging = $false + } else { + Write-Verbose "Stopping pagination. Reason: All results returned." + $Paging = $false + } } else { + Write-Verbose "Stopping pagination. Reason: Result greater than 5,000 values. GraphAPI limited to 5,000 values or less." $Paging = $false } } - } - #> - + } #endregion return $ResultSet From 6ba423b69700e82ed839f9ed746683b3cb2b39c9 Mon Sep 17 00:00:00 2001 From: Jt3kt Date: Thu, 19 Aug 2021 14:32:12 -0600 Subject: [PATCH 2/5] Update Invoke-AzureSecEventSync.ps1 Remove use of FileBeat, shifted integration to leverage Webhook Beat with SDP. --- .../Invoke-AzureSecEventSync.ps1 | 522 +++++++++++------- 1 file changed, 334 insertions(+), 188 deletions(-) diff --git a/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 b/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 index dfe5d22..2d03be4 100644 --- a/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 +++ b/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 @@ -13,6 +13,15 @@ $CleanupDate = (Get-Date).AddDays(-90).Date # OpenCollector Webhook Endpoint $OCEndpoint = 'http://172.17.5.20:8080/webhook' +# Enable Azure Alert Providers +$AZAlertProviders = [List[string]]::new() +$AZAlertProviders.add('AzureATP') +$AZAlertProviders.add('AzureSecurityCenter') +$AZAlertProviders.add('MCAS') +$AZAlertProviders.add('AzureADIdentityProtection') +$AZAlertProviders.add('DefenderATP') +$AZAlertProviders.add('AzureSentinel') + ##### # Log Path @@ -33,215 +42,352 @@ if (!(Test-Path $SecEventSyncLogPath -PathType Leaf)) { # Load in Security Events Log $SecEventLogs = Import-Csv -Path $SecEventSyncLogPath -# Begin Section - AzureATP -$AzureATP_SecEvents = Get-LrtAzSecurityAlerts -AzureATP -Status 'newAlert' -$AzureATP_LoggedEvents = $SecEventLogs | Where-Object -Property "type" -like "AzureATP" - - -# Loop through results and proceed to process identified new events -ForEach ($AZAtpSecurityEvent in $AzureATP_SecEvents) { - if ($AzureATP_LoggedEvents.Id -notcontains $AZAtpSecurityEvent.Id) { - # New Event - # Establish Log Entry - $SecEvent = [PSCustomObject]@{ - log_timestamp = (get-date -Format yyyy-MM-ddTHH:mm:ss:ffffffK) - event_timestamp = $AZAtpSecurityEvent.createdDateTime - type = "AzureATP" - id = $AZAtpSecurityEvent.id - azureTenantId = $AZAtpSecurityEvent.azureTenantId - severity = $AZAtpSecurityEvent.severity - } - - # Write out JSON event for FileBeat - Try { - Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($AZAtpSecurityEvent | ConvertTo-Json -Depth 50 -Compress) - - # Record Log Entry - Try { - $SecEvent | Export-Csv -Path $SecEventSyncLogPath -NoTypeInformation -Append - } Catch { - Write-Host $_ - } - } Catch { - Write-Host $_ - } +ForEach ($AZAlertProvider in $AZAlertProviders) { + write-host "Processing: $AZAlertProvider" + switch ($AZAlertProvider) { + 'AzureATP' { $SecEvents = Get-LrtAzSecurityAlerts -AzureATP -Status 'newAlert' } + 'AzureSecurityCenter' { $SecEvents = Get-LrtAzSecurityAlerts -AzureSecurityCenter -Status 'newAlert' } + 'MCAS' { $SecEvents = Get-LrtAzSecurityAlerts -MCAS -Status 'newAlert' } + 'AzureADIdentityProtection' { $SecEvents = Get-LrtAzSecurityAlerts -AzureADIdentityProtection -Status 'newAlert' } + 'DefenderATP' { $SecEvents = Get-LrtAzSecurityAlerts -DefenderATP -Status 'newAlert' } + 'AzureSentinel' { $SecEvents = Get-LrtAzSecurityAlerts -AzureSentinel -Status 'newAlert' } } -} -# End Section - AzureATP - -# Begin Section - AzureSecurityCenter -$AzureSecurityCenter_SecEvents = Get-LrtAzSecurityAlerts -AzureSecurityCenter -Status 'newAlert' -$AzureSecurityCenter_LoggedEvents = $SecEventLogs | Where-Object -Property "type" -like "AzureSecurityCenter" - -# Loop through results and proceed to process identified new events -ForEach ($AzSecCenSecurityEvent in $AzureSecurityCenter_SecEvents) { - if ($AzureSecurityCenter_LoggedEvents.Id -notcontains $AzSecCenSecurityEvent.Id) { - # New Event - # Establish Log Entry - $SecEvent = [PSCustomObject]@{ - log_timestamp = (get-date -Format yyyy-MM-ddTHH:mm:ss:ffffffK) - event_timestamp = $AzSecCenSecurityEvent.createdDateTime - type = "AzureSecurityCenter" - id = $AzSecCenSecurityEvent.id - azureTenantId = $AzSecCenSecurityEvent.azureTenantId - severity = $AzSecCenSecurityEvent.severity - } + $LoggedEvents = $SecEventLogs | Where-Object -Property "type" -like $AZAlertProvider - # Write out JSON event for FileBeat - Try { - Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($AzSecCenSecurityEvent | ConvertTo-Json -Depth 50 -Compress) + ForEach ($SecEvent in $SecEvents) { + if ($LoggedEvents.Id -notcontains $SecEvent.Id) { + # Establish log entry. + $LoggedEvent = [PSCustomObject]@{ + log_timestamp = (get-date -Format yyyy-MM-ddTHH:mm:ss:ffffffK) + event_timestamp = $SecEvent.createdDateTime + type = $AZAlertProvider + id = $SecEvent.id + azureTenantId = $SecEvent.azureTenantId + severity = $SecEvent.severity + } - # Record Log Entry - Try { - $SecEvent | Export-Csv -Path $SecEventSyncLogPath -NoTypeInformation -Append - } Catch { - Write-Host $_ + # User States + if ($null -ne $SecEvent.userStates) { + $UserStates = [list[object]]::new() + ForEach ($UserState in $SecEvent.userStates) { + if ($UserStates -notcontains $UserState) { + $UserStates.add($UserState) + + } + } + $UserStateCount = $UserStates.count + } + + # Host States + if ($null -ne $SecEvent.hostStates) { + $HostStates = [list[object]]::new() + ForEach ($HostState in $SecEvent.hostStates) { + if ($HostStates -notcontains $HostState) { + + $HostStates.add($HostState) + } + } + $HostStateCount = $HostStates.count } - } Catch { - Write-Host $_ - } - } -} -# End Section - AzureSecurityCenter - - -# Begin Section - MCAS -$MCAS_SecEvents = Get-LrtAzSecurityAlerts -MCAS -Status 'newAlert' -$MCAS_LoggedEvents = $SecEventLogs | Where-Object -Property "type" -like "MCAS" - -# Loop through results and proceed to process identified new events -ForEach ($MCASSecurityEvent in $MCAS_SecEvents) { - if ($MCAS_LoggedEvents.Id -notcontains $MCASSecurityEvent.Id) { - # New Event - # Establish Log Entry - $SecEvent = [PSCustomObject]@{ - log_timestamp = (get-date -Format yyyy-MM-ddTHH:mm:ss:ffffffK) - event_timestamp = $MCASSecurityEvent.createdDateTime - type = "MCAS" - id = $MCASSecurityEvent.id - azureTenantId = $MCASSecurityEvent.azureTenantId - severity = $MCASSecurityEvent.severity - } - $MCASSecurityEvent | Add-Member -MemberType NoteProperty -Name "tag" -Value "AZURE_MCAS" -Force + # VulnerabilityStates + if ($null -ne $SecEvent.vulnerabilityStates) { + $VulnStates = [list[object]]::new() + ForEach ($VulnState in $SecEvent.vulnerabilityStates) { + if ($VulnStates -notcontains $VulnState) { + $VulnStates.add($VulnState) + } + } + $VulnStateCount = $VulnStates.count + } - # Write out JSON event for FileBeat - Try { - Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($MCASSecurityEvent | ConvertTo-Json -Depth 50 -Compress) - Try { - $SecEvent | Export-Csv -Path $SecEventSyncLogPath -NoTypeInformation -Append - } Catch { - Write-Host $_ + # UserClickSecurityStates + + # CloudAppStates + if ($null -ne $SecEvent.cloudAppStates) { + $AppStates = [list[object]]::new() + ForEach ($AppState in $SecEvent.cloudAppStates) { + if ($AppStates -notcontains $AppState) { + $AppStates.add($AppState) + } + } + $AppStateCount = $AppStates.count } - } Catch { - Write-Host $_ - } - } -} -# End Section - MCAS - -# Begin Section - AzureADIdentityProtection -$AzureADIdentityProtection_SecEvents = Get-LrtAzSecurityAlerts -AzureADIdentityProtection -Status 'newAlert' -$AzureADIdentityProtection_LoggedEvents = $SecEventLogs | Where-Object -Property "type" -like "AzureADIdentityProtection" - -# Loop through results and proceed to process identified new events -ForEach ($AZADIdProtSecurityEvent in $AzureADIdentityProtection_SecEvents) { - if ($AzureADIdentityProtection_LoggedEvents.Id -notcontains $AZADIdProtSecurityEvent.Id) { - # New Event - # Establish Log Entry - $SecEvent = [PSCustomObject]@{ - log_timestamp = (get-date -Format yyyy-MM-ddTHH:mm:ss:ffffffK) - event_timestamp = $AZADIdProtSecurityEvent.createdDateTime - type = "AzureADIdentityProtection" - id = $AZADIdProtSecurityEvent.id - azureTenantId = $AZADIdProtSecurityEvent.azureTenantId - severity = $AZADIdProtSecurityEvent.severity - } - # Write out JSON event for FileBeat - Try { - Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($AZADIdProtSecurityEvent | ConvertTo-Json -Depth 50 -Compress) + # FileStates + if ($null -ne $SecEvent.fileStates) { + $FileStates = [list[object]]::new() + ForEach ($FileState in $SecEvent.fileStates) { + if ($FileStates -notcontains $FileState) { + $FileStates.add($FileState) + } + } + $FileStateCount = $FileStates.count + } - # Record Log Entry - Try { - $SecEvent | Export-Csv -Path $SecEventSyncLogPath -NoTypeInformation -Append - } Catch { - Write-Host "Unable to append to file: $SecEventSyncLogPath" + # InvestigationSecurityStates + + # MalwareStates + if ($null -ne $SecEvent.malwareStates) { + $MalwareStates = [list[object]]::new() + ForEach ($MalwareState in $SecEvent.malwareStates) { + if ($MalwareStates -notcontains $MalwareState) { + $MalwareStates.add($MalwareState) + } + } + $MalwareStateCount = $MalwareStates.count } - } Catch { - Write-Host $_ - } - } -} -# End Section - AzureADIdentityProtection - -# Begin Section - AzureSentinel -$AzureSentinel_SecEvents = Get-LrtAzSecurityAlerts -AzureSentinel -Status 'newAlert' -$AzureSentinel_LoggedEvents = $SecEventLogs | Where-Object -Property "type" -like "AzureSentinel" - -# Loop through results and proceed to process identified new events -ForEach ($AZSentSecurityEvent in $AzureSentinel_SecEvents) { - if ($AzureSentinel_LoggedEvents.Id -notcontains $AZSentSecurityEvent.Id) { - # New Event - # Establish Log Entry - $SecEvent = [PSCustomObject]@{ - log_timestamp = (get-date -Format yyyy-MM-ddTHH:mm:ss:ffffffK) - event_timestamp = $AZSentSecurityEvent.createdDateTime - type = "AzureSentinel" - id = $AZSentSecurityEvent.id - azureTenantId = $AZSentSecurityEvent.azureTenantId - severity = $AZSentSecurityEvent.severity - } - # Write out JSON event for FileBeat - Try { - Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($AZSentSecurityEvent | ConvertTo-Json -Depth 50 -Compress) + # MessageSecurityStates + if ($null -ne $SecEvent.messageSecurityStates) { + $MsgSecStates = [list[object]]::new() + ForEach ($MsgSecState in $SecEvent.messageSecurityStates) { + if ($MsgSecStates -notcontains $MsgSecState) { + $MsgSecStates.add($MsgSecState) + } + } + $MsgSecStateCount = $MsgSecStates.count + } + + # NetworkConnections + if ($null -ne $SecEvent.networkConnections) { + $NetConStates = [list[object]]::new() + ForEach ($NetConState in $SecEvent.networkConnections) { + if ($NetConStates -notcontains $NetConState) { + $NetConStates.add($NetConState) + } + } + $NetConStateCount = $NetConStates.count + } + + # Processes + if ($null -ne $SecEvent.processes) { + $ProcessesStates = [list[object]]::new() + ForEach ($ProcessesState in $SecEvent.processes) { + if ($ProcessesStates -notcontains $ProcessesState) { + $ProcessesStates.add($ProcessesState) + } + } + $ProcessesStateCount = $ProcessesStates.count + } + + # RegistryKeyStates + + # Security Resources + + # Triggers + # + # Global alert parsing + $OCLog = [PSCustomObject]@{ + tag1 = $SecEvent.vendorInformation.provider + tag2 = $SecEvent.category + object = $SecEvent.vendorInformation.provider + objecttype = $null + severity = $SecEvent.severity + vmid = $SecEvent.azureSubscriptionId + policy = $SecEvent.category + serialnumber = $SecEvent.azureTenantId + session = $SecEvent.id + reason = $SecEvent.description + status = $SecEvent.status + quantity = $null + amount = $null + threatname = $SecEvent.title + threatid = $null + dname = $null + sname = $null + hash = $null + account = $null + login = $null + sip = $null + dip = $null + snatip = $null + dnatip = $null + process = $null + useragent = $null + size = $null + domainorigin = $null + domainimpacted = $null + action = $null + vendorinfo = $null + "timestamp.iso8601" = $('{0:yyyy-MM-ddTHH:mm:ssZ}' -f $($([DateTime]$SecEvent.createdDateTime).ToUniversalTime())) + original_message = $SecEvent + whsdp = $true + fullyqualifiedbeatname = "webhookbeat_AzureGraph-$($SecEvent.vendorInformation.provider.replace(' ',''))2" + } + + # Recommended Actions - Action + if ($SecEvent.recommendedActions) { + if ($SecEvent.recommendedActions.count -gt 1) { + $SecEvent.recommendedActions = $([string[]]$SecEVent.recommendedActions -join ", ") + } + $OCLog | Add-Member -MemberType NoteProperty -Name 'action' -Value $SecEvent.recommendedActions -Force + } + + # Source Materials - VendorInfo + if ($SecEvent.sourceMaterials) { + if ($SecEvent.sourceMaterials.count -gt 1) { + $SecEvent.sourceMaterials = $([string[]]$SecEVent.sourceMaterials -join ", ") + } + $OCLog | Add-Member -MemberType NoteProperty -Name 'vendorinfo' -Value $SecEvent.sourceMaterials -Force + } + + # Seen in MCAS + if ($SecEvent.cloudAppStates.destinationServiceIp) { + $OCLog | Add-Member -MemberType NoteProperty -Name 'dip' -Value $SecEVent.cloudAppStates.destinationServiceIp -Force + } + + if ($FileStates) { + $EventCurrent = 0 + $OCLog.quantity = $FileStateCount + $OCLog.objecttype = 'FileState' + ForEach ($FileState in $FileStates) { + $EventCurrent++ + $OCLog.amount = $EventCurrent + + # Capture File Names + if ($FileState.name) { + $OCLog.process = $FileState.name + } + + # Capture File Hashes + if ($FileState.fileHash) { + $OCLog.hash = $FileState.fileHash + } + + Try { + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) + } Catch { + Write-Host $_ + } + } + } + + + if ($MalwareStates) { + $EventCurrent = 0 + $OCLog.quantity = $MalwareStateCount + $OCLog.objecttype = 'MalwareState' + ForEach ($MalwareState in $MalwareStates) { + $EventCurrent++ + $OCLog.amount = $EventCurrent + + # Capture Malware Name as ThreatID + if ($MalwareState.name) { + $OCLog.threatid = $MalwareState.name + } + + + Try { + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) + } Catch { + Write-Host $_ + } + } + } + + + if ($HostStates) { + $EventCurrent = 0 + $OCLog.quantity = $HostStateCount + ForEach ($HostState in $HostStates) { + $EventCurrent++ + $OCLog.amount = $EventCurrent + $OCLog.objecttype = 'HostState' + if ($HostState.fqdn) { + $OCLog.sname = $HostState.fqdn + } elseif ($HostState.netBiosName) { + $OCLog.sname = $HostState.fqdn + } + + if ($HostState.isAzureAdJoined) { + + } + + if ($HostState.isAzureAdRegistered) { + + } + + if ($HostState.isHybridAzureDomainJoined) { + + } + + if ($HostState.os) { + $OCLog.useragent = $HostState.os + } + + if ($HostState.privateIpAddress) { + $OCLog.sip = $HostState.privateIpAddress + } + + if ($HostState.publicIpAddress) { + $OCLog.snatip = $HostState.publicIpAddress + } + + if ($HostState.riskScore) { + $OCLog.size = $HostState.riskScore + } + Try { + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) + } Catch { + Write-Host $_ + } + } + } + # Submit one log for each UserState entry + if ($UserStates) { + $EventCurrent = 0 + $OCLog.quantity = $UserStateCount + $OCLog.objecttype = 'UserStates' + ForEach ($UserState in $UserStates) { + $EventCurrent++ + $OCLog.amount = $EventCurrent + if ($UserState.userPrincipalName) { + $OCLog.login = $UserState.userPrincipalName + } + + if ($UserState.logonIp) { + $OCLog.sip = $UserState.logonIp + } + + if ($UserState.domainName) { + $OCLog.domainorigin = $UserState.domainName + } + Try { + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) + } Catch { + Write-Host $_ + } + } + } + # Record Log Entry Try { - $SecEvent | Export-Csv -Path $SecEventSyncLogPath -NoTypeInformation -Append + $LoggedEvent | Export-Csv -Path $SecEventSyncLogPath -NoTypeInformation -Append } Catch { Write-Host $_ } - } Catch { - Write-Host $_ } } + } -# End Section - AzureSentinel - -# Begin Section - DefenderATP -$DefenderATP_SecEvents = Get-LrtAzSecurityAlerts -DefenderATP -Status 'newAlert' -$DefenderATP_LoggedEvents = $SecEventLogs | Where-Object -Property "type" -like "DefenderATP" - -# Loop through results and proceed to process identified new events -ForEach ($DefSecurityEvent in $DefenderATP_SecEvents) { - if ($DefenderATP_LoggedEvents.Id -notcontains $DefSecurityEvent.Id) { - # New Event - # Establish Log Entry - $SecEvent = [PSCustomObject]@{ - log_timestamp = (get-date -Format yyyy-MM-ddTHH:mm:ss:ffffffK) - event_timestamp = $DefSecurityEvent.createdDateTime - type = "DefenderATP" - id = $DefSecurityEvent.id - azureTenantId = $DefSecurityEvent.azureTenantId - severity = $DefSecurityEvent.severity - } - # Write out JSON event for FileBeat - Try { - Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($DefSecurityEvent | ConvertTo-Json -Depth 50 -Compress) - - # Record Log Entry - Try { - $SecEvent | Export-Csv -Path $SecEventSyncLogPath -NoTypeInformation -Append - } Catch { - Write-Host $_ - } - } Catch { - Write-Host $_ - } + +# Refresh the $State variable from the appended CSV content. +$State = Import-Csv -Path $SecEventSyncLogPath +$StateCleanup = [list[object]]::new() +ForEach ($StateEntry in $State) { + Try { + $AlertDate = [DateTime]$StateEntry.event_timestamp + } Catch { + continue + } + if ($AlertDate -ge $CleanupDate) { + $StateCleanup.add($StateEntry) } } -# End Section - DefenderATP + +# Overwrite the CSV State File representing only the entries that are valid within the script's configured $eventLookBack time period. +$StateCleanup | Export-Csv -Path $SecEventSyncLogPath -NoTypeInformation -Force \ No newline at end of file From 3e79a2c682fa638110453c0fb8db19e0ea4eb673 Mon Sep 17 00:00:00 2001 From: Jt3kt Date: Tue, 24 Aug 2021 11:14:26 -0600 Subject: [PATCH 3/5] Update Invoke-AzureSecEventSync.ps1 Mature parsing content. --- .../Invoke-AzureSecEventSync.ps1 | 82 ++++++++++++++----- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 b/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 index 2d03be4..eaa55ac 100644 --- a/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 +++ b/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 @@ -11,7 +11,7 @@ $CleanupDate = (Get-Date).AddDays(-90).Date # OpenCollector Webhook Endpoint -$OCEndpoint = 'http://172.17.5.20:8080/webhook' +$OCEndpoint = 'http://172.17.5.20:8085/webhook' # Enable Azure Alert Providers $AZAlertProviders = [List[string]]::new() @@ -84,7 +84,6 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $HostStates = [list[object]]::new() ForEach ($HostState in $SecEvent.hostStates) { if ($HostStates -notcontains $HostState) { - $HostStates.add($HostState) } } @@ -177,13 +176,25 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { # Security Resources # Triggers + + # Processes + if ($null -ne $SecEvent.recommendedActions) { + $RecommendedActions = [list[object]]::new() + ForEach ($RecommendedAction in $SecEvent.recommendedActions) { + if ($RecommendedActions -notcontains $RecommendedAction) { + $RecommendedActions.add($RecommendedAction) + } + } + $RecommendedActionCount = $RecommendedActions.count + } + # # Global alert parsing $OCLog = [PSCustomObject]@{ tag1 = $SecEvent.vendorInformation.provider tag2 = $SecEvent.category object = $SecEvent.vendorInformation.provider - objecttype = $null + objecttype = 'BaseAlert' severity = $SecEvent.severity vmid = $SecEvent.azureSubscriptionId policy = $SecEvent.category @@ -211,33 +222,48 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { domainimpacted = $null action = $null vendorinfo = $null + url = $null "timestamp.iso8601" = $('{0:yyyy-MM-ddTHH:mm:ssZ}' -f $($([DateTime]$SecEvent.createdDateTime).ToUniversalTime())) original_message = $SecEvent whsdp = $true - fullyqualifiedbeatname = "webhookbeat_AzureGraph-$($SecEvent.vendorInformation.provider.replace(' ',''))2" + fullyqualifiedbeatname = "webhookbeat_AzureGraph-$($SecEvent.vendorInformation.provider.replace(' ',''))" + } + # (get-date -Format yyyy-MM-ddTHH:mm:ssZ) + + Try { + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null + } Catch { + Write-Host $_ } - # Recommended Actions - Action - if ($SecEvent.recommendedActions) { - if ($SecEvent.recommendedActions.count -gt 1) { - $SecEvent.recommendedActions = $([string[]]$SecEVent.recommendedActions -join ", ") - } - $OCLog | Add-Member -MemberType NoteProperty -Name 'action' -Value $SecEvent.recommendedActions -Force + # Seen in MCAS + if ($SecEvent.cloudAppStates.destinationServiceIp) { + $OCLog | Add-Member -MemberType NoteProperty -Name 'dip' -Value $SecEVent.cloudAppStates.destinationServiceIp -Force } - # Source Materials - VendorInfo if ($SecEvent.sourceMaterials) { - if ($SecEvent.sourceMaterials.count -gt 1) { - $SecEvent.sourceMaterials = $([string[]]$SecEVent.sourceMaterials -join ", ") + $EventCurrent = 0 + $OCLog.quantity = $($SecEvent.sourceMaterials | Sort-Object -Unique).count + $OCLog.objecttype = 'SourceMaterials' + ForEach ($SourceMaterial in $($SecEvent.sourceMaterials | Sort-Object -Unique)) { + $EventCurrent++ + $OCLog.amount = $EventCurrent + + $OCLog.vendorinfo = $SourceMaterial + $OCLog.url = $SourceMaterial + + Try { + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null + } Catch { + Write-Host $_ + } + $OCLog.vendorinfo = $null + $OCLog.url = $null } - $OCLog | Add-Member -MemberType NoteProperty -Name 'vendorinfo' -Value $SecEvent.sourceMaterials -Force - } - # Seen in MCAS - if ($SecEvent.cloudAppStates.destinationServiceIp) { - $OCLog | Add-Member -MemberType NoteProperty -Name 'dip' -Value $SecEVent.cloudAppStates.destinationServiceIp -Force } + # File States if ($FileStates) { $EventCurrent = 0 $OCLog.quantity = $FileStateCount @@ -257,10 +283,12 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { } Try { - Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null } Catch { Write-Host $_ } + $OCLog.process = $null + $OCLog.hash = $null } } @@ -280,11 +308,13 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { Try { - Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null } Catch { Write-Host $_ } + $OCLog.threatid = $null } + } @@ -329,10 +359,15 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $OCLog.size = $HostState.riskScore } Try { - Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null } Catch { Write-Host $_ } + $OCLog.size = $null + $OCLog.snatip = $null + $OCLog.sip = $null + $OCLog.useragent = $null + $OCLog.sname = $null } } @@ -356,10 +391,13 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $OCLog.domainorigin = $UserState.domainName } Try { - Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null } Catch { Write-Host $_ } + $OCLog.login = $null + $OCLog.sip = $null + $OCLog.domainorigin = $null } } From 40d243b489e50c1cf9e2279aeee34bc52e8160d4 Mon Sep 17 00:00:00 2001 From: Jt3kt Date: Thu, 26 Aug 2021 15:00:28 -0600 Subject: [PATCH 4/5] Update Invoke-AzureSecEventSync.ps1 Mature collection and incorporate additional metadata mapping. --- .../Invoke-AzureSecEventSync.ps1 | 322 ++++++++++++++++-- 1 file changed, 287 insertions(+), 35 deletions(-) diff --git a/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 b/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 index eaa55ac..a239995 100644 --- a/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 +++ b/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 @@ -105,13 +105,13 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { # CloudAppStates if ($null -ne $SecEvent.cloudAppStates) { - $AppStates = [list[object]]::new() + $CloudAppStates = [list[object]]::new() ForEach ($AppState in $SecEvent.cloudAppStates) { - if ($AppStates -notcontains $AppState) { - $AppStates.add($AppState) + if ($CloudAppStates -notcontains $AppState) { + $CloudAppStates.add($AppState) } } - $AppStateCount = $AppStates.count + $CloudAppStateCount = $AppStates.count } # FileStates @@ -174,6 +174,15 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { # RegistryKeyStates # Security Resources + if ($null -ne $SecEvent.securityResources) { + $SecResources = [list[object]]::new() + ForEach ($SecResource in $SecEvent.processes) { + if ($SecResources -notcontains $SecResource) { + $SecResources.add($SecResource) + } + } + $SecResourceCount = $SecResources.count + } # Triggers @@ -187,14 +196,12 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { } $RecommendedActionCount = $RecommendedActions.count } - # # Global alert parsing $OCLog = [PSCustomObject]@{ tag1 = $SecEvent.vendorInformation.provider tag2 = $SecEvent.category object = $SecEvent.vendorInformation.provider - objecttype = 'BaseAlert' severity = $SecEvent.severity vmid = $SecEvent.azureSubscriptionId policy = $SecEvent.category @@ -202,43 +209,88 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { session = $SecEvent.id reason = $SecEvent.description status = $SecEvent.status - quantity = $null - amount = $null threatname = $SecEvent.title - threatid = $null + # These properties change as the objecttype shifts from BaseAlert to the other alert elements + # The updates to these metadata values align to the same usecase and purpose of the values set on the BaseAlert. + objecttype = 'BaseAlert' + size = $SecEvent.riskScore + vendorinfo = 'https://docs.microsoft.com/en-us/graph/api/resources/alert?view=graph-rest-1.0' + dname = $null - sname = $null - hash = $null + domainorigin = $null + domainimpacted = $null account = $null - login = $null - sip = $null + action = $null + amount = $null dip = $null - snatip = $null dnatip = $null + dport = $null + hash = $null + login = $null + objectname = $null process = $null - useragent = $null - size = $null - domainorigin = $null - domainimpacted = $null - action = $null - vendorinfo = $null + processid = $null + parentprocessname = $null + parentprocesspath = $null + quantity = $null + sessiontype = $null + sip = $null + snatip = $null + sname = $null + subject = $null + sport = $null + threatid = $null url = $null + useragent = $null "timestamp.iso8601" = $('{0:yyyy-MM-ddTHH:mm:ssZ}' -f $($([DateTime]$SecEvent.createdDateTime).ToUniversalTime())) original_message = $SecEvent whsdp = $true fullyqualifiedbeatname = "webhookbeat_AzureGraph-$($SecEvent.vendorInformation.provider.replace(' ',''))" } # (get-date -Format yyyy-MM-ddTHH:mm:ssZ) - Try { Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null } Catch { Write-Host $_ } - # Seen in MCAS - if ($SecEvent.cloudAppStates.destinationServiceIp) { - $OCLog | Add-Member -MemberType NoteProperty -Name 'dip' -Value $SecEVent.cloudAppStates.destinationServiceIp -Force + if ($CloudAppStates) { + $EventCurrent = 0 + $OCLog.quantity = $CloudAppStateCount + $OCLog.objecttype = 'CloudAppState' + ForEach ($CloudAppState in $CloudAppStates) { + $EventCurrent++ + $OCLog.amount = $EventCurrent + + # Capture Destination Service IP + if ($CloudAppState.destinationServiceIp) { + $OCLog.dip = $CloudAppState.destinationServiceIp + } + + # Capture DestinationServiceName + if ($CloudAppState.destinationServiceName) { + $OCLog.process = $CloudAppState.destinationServiceName + } + + # Capture CloudAppState Risk Score + if ($CloudAppState.riskScore) { + $OCLog.size = $CloudAppState.riskScore + } + + $OCLog.vendorinfo = 'https://docs.microsoft.com/en-us/graph/api/resources/cloudappsecuritystate?view=graph-rest-1.0' + + Try { + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null + } Catch { + Write-Host $_ + } + $OCLog.dip = $null + $OCLog.process = $null + $OCLog.size = $null + $OCLog.vendorinfo = $null + } + $OCLog.quantity = $null + $OCLog.objecttype = $null } if ($SecEvent.sourceMaterials) { @@ -249,9 +301,10 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $EventCurrent++ $OCLog.amount = $EventCurrent - $OCLog.vendorinfo = $SourceMaterial $OCLog.url = $SourceMaterial + $OCLog.vendorinfo = 'https://docs.microsoft.com/en-us/graph/api/resources/alert?view=graph-rest-1.0' + Try { Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null } Catch { @@ -260,7 +313,8 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $OCLog.vendorinfo = $null $OCLog.url = $null } - + $OCLog.quantity = $null + $OCLog.objecttype = $null } # File States @@ -273,15 +327,25 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $OCLog.amount = $EventCurrent # Capture File Names + if ($FileState.fileHash.hashValue) { + $OCLog.hash = $FileState.fileHash.hashValue + } + + # Capture File Hashes + if ($FileState.fileHash.hashType) { + $OCLog.objectname = $FileState.fileHash.hashType + } + if ($FileState.name) { $OCLog.process = $FileState.name } - # Capture File Hashes - if ($FileState.fileHash) { - $OCLog.hash = $FileState.fileHash + if ($FileState.riskScore) { + $OCLog.size = $FileState.riskScore } + $OCLog.vendorinfo = 'https://docs.microsoft.com/en-us/graph/api/resources/filesecuritystate?view=graph-rest-1.0' + Try { Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null } Catch { @@ -289,7 +353,118 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { } $OCLog.process = $null $OCLog.hash = $null + $OCLog.size = $null + $OCLog.objectname = $null + $OCLog.vendorinfo = $null + } + $OCLog.quantity = $null + $OCLog.objecttype = $null + } + + # Security Resources + if ($SecResources) { + $EventCurrent = 0 + $OCLog.quantity = $SecResourceCount + $OCLog.objecttype = 'SecurityResource' + ForEach ($SecResource in $SecResources) { + $EventCurrent++ + $OCLog.amount = $EventCurrent + + if ($SecResource.resource) { + $OCLog.objectname = $SecResource.resource + } + + if ($SecResource.resourceType) { + switch ($SecResource.resourceType) { + 1 {$OCLog.subject = "The resource was attacked in the alert."} + 2 {$OCLog.subject = "The resource is related to the alert, though not directly attacked."} + 'attacked' {$OCLog.subject = "The resource was attacked in the alert."} + 'related' {$OCLog.subject = "The resource is related to the alert, though not directly attacked."} + default { + $OCLog.subject = "The resource has not been defined as being attacked or related to the attack." + } + } + + } + + $OCLog.vendorinfo = 'https://docs.microsoft.com/en-us/graph/api/resources/securityresource?view=graph-rest-1.0' + + Try { + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null + } Catch { + Write-Host $_ + } + + $OCLog.objectname = $null + $OCLog.subject = $null + $OCLog.vendorinfo = $null + } + $OCLog.quantity = $null + $OCLog.objecttype = $null + } + + + # File States + if ($NetConStates) { + $EventCurrent = 0 + $OCLog.quantity = $NetConStateCount + $OCLog.objecttype = 'NetworkConnection' + ForEach ($NetConn in $NetConStates) { + $EventCurrent++ + $OCLog.amount = $EventCurrent + + if ($NetConn.sourceAddress) { + $OCLog.sip = $NetConn.sourceAddress + } + + if ($NetConn.destinationAddress) { + $OCLog.dip = $NetConn.destinationAddress + } + + if ($NetConn.sourcePort) { + $OCLog.sport = $NetConn.sourceAddress + } + + if ($NetConn.destinationPort) { + $OCLog.dport = $NetConn.destinationPort + } + + if ($NetConn.natSourceAddress) { + $OCLog.snatip = $NetConn.natSourceAddress + } + + if ($NetConn.natDestinationAddress) { + $OCLog.dnatip = $NetConn.natDestinationAddress + } + + if ($NetConn.destinationUrl) { + $OCLog.url = $NetCon.destinationUrl + } + + if ($NetConn.riskScore) { + $OCLog.size = $NetConn.riskScore + } + + $OCLog.vendorinfo = 'https://docs.microsoft.com/en-us/graph/api/resources/networkconnection?view=graph-rest-1.0' + + Try { + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null + } Catch { + Write-Host $_ + } + $OCLog.sip = $null + $OCLog.dip = $null + $OCLog.hash = $null + $OCLog.url = $null + $OCLog.dnatip = $null + $OCLog.snatip = $null + $OCLog.dport = $null + $OCLog.sport = $null + $OCLog.size = $null + $OCLog.vendorinfo = $null } + $OCLog.quantity = $null + $OCLog.objecttype = $null } @@ -306,6 +481,25 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $OCLog.threatid = $MalwareState.name } + if ($MalwareState.category) { + $OCLog.objectname = $MalwareState.category + } + + if ($MalwareStates.family) { + $OCLog.process = $MalwareState.family + } + + if ($MalwareStates.severity) { + $OCLog.subject = $MalwareStates.severity + } + + if ($MalwareStates.wasRunning) { + $OCLog.sessiontype = "Malware reported as running at the time of detection." + } else { + $OCLog.sessiontype = "Malware reported as not running at the time of detection." + } + + $OCLog.vendorinfo = 'https://docs.microsoft.com/en-us/graph/api/resources/malwarestate?view=graph-rest-1.0' Try { Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null @@ -313,8 +507,14 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { Write-Host $_ } $OCLog.threatid = $null + $OCLog.process = $null + $OCLog.subject = $null + $OCLog.objectname = $null + $OCLog.sessiontype = $null + $OCLog.vendorinfo = $null } - + $OCLog.quantity = $null + $OCLog.objecttype = $null } @@ -325,22 +525,31 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $EventCurrent++ $OCLog.amount = $EventCurrent $OCLog.objecttype = 'HostState' + if ($HostState.fqdn) { $OCLog.sname = $HostState.fqdn } elseif ($HostState.netBiosName) { - $OCLog.sname = $HostState.fqdn + Try { + $netBNtoIP = [IPAddress] $HostState.netBiosName + if ($null -eq $HostState.privateIpAddress) { + $OCLog.sip = $netBNtoIP.IPAddressToString + } + } Catch { + $OCLog.sname = $HostState.netBiosName + } + } if ($HostState.isAzureAdJoined) { - + $OCLog.sessiontype = "AzureAdJoined" } if ($HostState.isAzureAdRegistered) { - + $OCLog.objectname = "AzureAdRegistered" } - + if ($HostState.isHybridAzureDomainJoined) { - + $OCLog.subject = "HybridAzureDomainJoined" } if ($HostState.os) { @@ -358,6 +567,9 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { if ($HostState.riskScore) { $OCLog.size = $HostState.riskScore } + + $OCLog.vendorinfo = 'https://docs.microsoft.com/en-us/graph/api/resources/hostsecuritystate?view=graph-rest-1.0' + Try { Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null } Catch { @@ -368,7 +580,13 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $OCLog.sip = $null $OCLog.useragent = $null $OCLog.sname = $null + $OCLog.subject = $null + $OCLog.objectname = $null + $OCLog.sessiontype = $null + $OCLog.vendorinfo = $null } + $OCLog.quantity = $null + $OCLog.objecttype = $null } # Submit one log for each UserState entry @@ -379,26 +597,60 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { ForEach ($UserState in $UserStates) { $EventCurrent++ $OCLog.amount = $EventCurrent + + # Preference to userPrincipalName as this will be username+domainName if ($UserState.userPrincipalName) { $OCLog.login = $UserState.userPrincipalName + } elseif ($UserState.accountName) { + $OCLog.login = $UserState.accountName + } + + if ($UserState.aadUserId) { + $OCLog.subject = $UserState.aadUserId } if ($UserState.logonIp) { $OCLog.sip = $UserState.logonIp } + + if ($UserState.isVpn) { + $OCLog.sessiontype = "VPN" + } elseif ($UserState.logonType) { + $OCLog.sessiontype = $UserState.logonType + } + + if ($UserState.userAccountType) { + $OCLog.objectname = $UserState.userAccountType + } if ($UserState.domainName) { $OCLog.domainorigin = $UserState.domainName } + + if ($UserState.riskScore) { + $OCLog.size = $UserState.riskScore + } + + $OCLog.vendorinfo = 'https://docs.microsoft.com/en-us/graph/api/resources/usersecuritystate?view=graph-rest-1.0' + Try { Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null } Catch { Write-Host $_ } + # Reset variables before proceeding to next state + $OCLog.amount = $null $OCLog.login = $null $OCLog.sip = $null $OCLog.domainorigin = $null + $OCLog.size = $null + $OCLog.objectname = $null + $OCLog.sessiontype = $null + $OCLog.subject = $null + $OCLog.vendorinfo = $null } + $OCLog.quantity = $null + $OCLog.objecttype = $null } # Record Log Entry From 9a2ed879c063538980511174283690d0fad8c00b Mon Sep 17 00:00:00 2001 From: Jt3kt Date: Tue, 7 Sep 2021 11:07:21 -0600 Subject: [PATCH 5/5] Update Invoke-AzureSecEventSync.ps1 Code cleanup, reduce complexity. --- .../Invoke-AzureSecEventSync.ps1 | 242 +++++++----------- 1 file changed, 89 insertions(+), 153 deletions(-) diff --git a/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 b/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 index a239995..84e0d0b 100644 --- a/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 +++ b/examples/Azure/GraphAPI/SecurityEvents_to_OC/Invoke-AzureSecEventSync.ps1 @@ -66,145 +66,15 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { azureTenantId = $SecEvent.azureTenantId severity = $SecEvent.severity } - - # User States - if ($null -ne $SecEvent.userStates) { - $UserStates = [list[object]]::new() - ForEach ($UserState in $SecEvent.userStates) { - if ($UserStates -notcontains $UserState) { - $UserStates.add($UserState) - - } - } - $UserStateCount = $UserStates.count - } - - # Host States - if ($null -ne $SecEvent.hostStates) { - $HostStates = [list[object]]::new() - ForEach ($HostState in $SecEvent.hostStates) { - if ($HostStates -notcontains $HostState) { - $HostStates.add($HostState) - } - } - $HostStateCount = $HostStates.count - } - # VulnerabilityStates - if ($null -ne $SecEvent.vulnerabilityStates) { - $VulnStates = [list[object]]::new() - ForEach ($VulnState in $SecEvent.vulnerabilityStates) { - if ($VulnStates -notcontains $VulnState) { - $VulnStates.add($VulnState) - } - } - $VulnStateCount = $VulnStates.count - } - - # UserClickSecurityStates - - # CloudAppStates - if ($null -ne $SecEvent.cloudAppStates) { - $CloudAppStates = [list[object]]::new() - ForEach ($AppState in $SecEvent.cloudAppStates) { - if ($CloudAppStates -notcontains $AppState) { - $CloudAppStates.add($AppState) - } - } - $CloudAppStateCount = $AppStates.count - } - - # FileStates - if ($null -ne $SecEvent.fileStates) { - $FileStates = [list[object]]::new() - ForEach ($FileState in $SecEvent.fileStates) { - if ($FileStates -notcontains $FileState) { - $FileStates.add($FileState) - } - } - $FileStateCount = $FileStates.count - } - - # InvestigationSecurityStates - - # MalwareStates - if ($null -ne $SecEvent.malwareStates) { - $MalwareStates = [list[object]]::new() - ForEach ($MalwareState in $SecEvent.malwareStates) { - if ($MalwareStates -notcontains $MalwareState) { - $MalwareStates.add($MalwareState) - } - } - $MalwareStateCount = $MalwareStates.count - } - - # MessageSecurityStates - if ($null -ne $SecEvent.messageSecurityStates) { - $MsgSecStates = [list[object]]::new() - ForEach ($MsgSecState in $SecEvent.messageSecurityStates) { - if ($MsgSecStates -notcontains $MsgSecState) { - $MsgSecStates.add($MsgSecState) - } - } - $MsgSecStateCount = $MsgSecStates.count - } - - # NetworkConnections - if ($null -ne $SecEvent.networkConnections) { - $NetConStates = [list[object]]::new() - ForEach ($NetConState in $SecEvent.networkConnections) { - if ($NetConStates -notcontains $NetConState) { - $NetConStates.add($NetConState) - } - } - $NetConStateCount = $NetConStates.count - } - - # Processes - if ($null -ne $SecEvent.processes) { - $ProcessesStates = [list[object]]::new() - ForEach ($ProcessesState in $SecEvent.processes) { - if ($ProcessesStates -notcontains $ProcessesState) { - $ProcessesStates.add($ProcessesState) - } - } - $ProcessesStateCount = $ProcessesStates.count - } - - # RegistryKeyStates - - # Security Resources - if ($null -ne $SecEvent.securityResources) { - $SecResources = [list[object]]::new() - ForEach ($SecResource in $SecEvent.processes) { - if ($SecResources -notcontains $SecResource) { - $SecResources.add($SecResource) - } - } - $SecResourceCount = $SecResources.count - } - - # Triggers - - # Processes - if ($null -ne $SecEvent.recommendedActions) { - $RecommendedActions = [list[object]]::new() - ForEach ($RecommendedAction in $SecEvent.recommendedActions) { - if ($RecommendedActions -notcontains $RecommendedAction) { - $RecommendedActions.add($RecommendedAction) - } - } - $RecommendedActionCount = $RecommendedActions.count - } - # # Global alert parsing $OCLog = [PSCustomObject]@{ tag1 = $SecEvent.vendorInformation.provider tag2 = $SecEvent.category object = $SecEvent.vendorInformation.provider severity = $SecEvent.severity - vmid = $SecEvent.azureSubscriptionId policy = $SecEvent.category + vmid = $SecEvent.azureSubscriptionId serialnumber = $SecEvent.azureTenantId session = $SecEvent.id reason = $SecEvent.description @@ -232,6 +102,7 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { processid = $null parentprocessname = $null parentprocesspath = $null + parentprocessid = $null quantity = $null sessiontype = $null sip = $null @@ -254,11 +125,11 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { Write-Host $_ } - if ($CloudAppStates) { + if ($null -ne $SecEvent.cloudAppStates) { $EventCurrent = 0 - $OCLog.quantity = $CloudAppStateCount + $OCLog.quantity = $SecEvent.cloudAppStates.count $OCLog.objecttype = 'CloudAppState' - ForEach ($CloudAppState in $CloudAppStates) { + ForEach ($CloudAppState in $SecEvent.cloudAppStates) { $EventCurrent++ $OCLog.amount = $EventCurrent @@ -318,11 +189,11 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { } # File States - if ($FileStates) { + if ($null -ne $SecEvent.fileStates) { $EventCurrent = 0 - $OCLog.quantity = $FileStateCount + $OCLog.quantity = $SecEvent.fileStates.count $OCLog.objecttype = 'FileState' - ForEach ($FileState in $FileStates) { + ForEach ($FileState in $SecEvent.fileStates) { $EventCurrent++ $OCLog.amount = $EventCurrent @@ -361,12 +232,74 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $OCLog.objecttype = $null } + # Process States + if ($null -ne $SecEvent.processes) { + $EventCurrent = 0 + $OCLog.quantity = $SecEvent.processes.count + $OCLog.objecttype = 'Process' + ForEach ($Process in $SecEvent.processes) { + $EventCurrent++ + $OCLog.amount = $EventCurrent + + # Capture File Names + if ($Process.accountName) { + $OCLog.login = $Process.accountName + } + + # Capture File Hashes + if ($Process.name) { + $OCLog.process = $Process.name + } + + if ($Process.processId) { + $OCLog.processid = $Process.processId + } + + if ($Process.commandLine) { + $OCLog.process = $Process.commandLine + } + + if ($Process.parentProcessName) { + $OCLog.parentprocessname = $Process.parentProcessName + } + + if ($Process.parentProcessId) { + $OCLog.parentprocessid = $Process.parentProcessId + } + + if ($Process.fileHash.hashValue) { + $OCLog.hash = $Process.fileHash.hashValue + } + + if ($Process.fileHash.hashType) { + $OCLog.subject = $Process.fileHash.hashType + } + + + $OCLog.vendorinfo = 'https://docs.microsoft.com/en-us/graph/api/resources/process?view=graph-rest-1.0' + + Try { + Invoke-RestMethod -Method 'post' -uri $OCendpoint -Headers @{'Content-Type' = 'application/json; charset=utf-8'} -Body $($OCLog | ConvertTo-Json -Depth 10 -Compress) | Out-Null + } Catch { + Write-Host $_ + } + $OCLog.process = $null + $OCLog.hash = $null + $OCLog.size = $null + $OCLog.objectname = $null + $OCLog.vendorinfo = $null + } + $OCLog.quantity = $null + $OCLog.objecttype = $null + } + + # Security Resources - if ($SecResources) { + if ($null -ne $SecEvent.securityResources) { $EventCurrent = 0 - $OCLog.quantity = $SecResourceCount + $OCLog.quantity = $SecEvent.securityResources.count $OCLog.objecttype = 'SecurityResource' - ForEach ($SecResource in $SecResources) { + ForEach ($SecResource in $SecEvent.securityResources) { $EventCurrent++ $OCLog.amount = $EventCurrent @@ -404,12 +337,13 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { } - # File States - if ($NetConStates) { + + # NetworkConnections + if ($null -ne $SecEvent.networkConnections) { $EventCurrent = 0 - $OCLog.quantity = $NetConStateCount + $OCLog.quantity = $SecEvent.networkConnections.count $OCLog.objecttype = 'NetworkConnection' - ForEach ($NetConn in $NetConStates) { + ForEach ($NetConn in $SecEvent.networkConnections) { $EventCurrent++ $OCLog.amount = $EventCurrent @@ -468,11 +402,12 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { } - if ($MalwareStates) { + + if ($null -ne $SecEvent.malwareStates) { $EventCurrent = 0 - $OCLog.quantity = $MalwareStateCount + $OCLog.quantity = $SecEvent.malwareStates.count $OCLog.objecttype = 'MalwareState' - ForEach ($MalwareState in $MalwareStates) { + ForEach ($MalwareState in $SecEvent.malwareStates) { $EventCurrent++ $OCLog.amount = $EventCurrent @@ -518,10 +453,10 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { } - if ($HostStates) { + if ($null -ne $SecEvent.hostStates) { $EventCurrent = 0 - $OCLog.quantity = $HostStateCount - ForEach ($HostState in $HostStates) { + $OCLog.quantity = $SecEvent.hostStates.count + ForEach ($HostState in $SecEvent.hostStates) { $EventCurrent++ $OCLog.amount = $EventCurrent $OCLog.objecttype = 'HostState' @@ -588,13 +523,14 @@ ForEach ($AZAlertProvider in $AZAlertProviders) { $OCLog.quantity = $null $OCLog.objecttype = $null } + # Submit one log for each UserState entry - if ($UserStates) { + if ($null -ne $SecEvent.userStates) { $EventCurrent = 0 - $OCLog.quantity = $UserStateCount + $OCLog.quantity = $SecEvent.userStates.count $OCLog.objecttype = 'UserStates' - ForEach ($UserState in $UserStates) { + ForEach ($UserState in $SecEvent.userStates) { $EventCurrent++ $OCLog.amount = $EventCurrent