Functions/Get-RSCThreatHunts.ps1
################################################ # Function - Get-RSCThreatHunts - Getting all RSC Threat hunts ################################################ Function Get-RSCThreatHunts { <# .SYNOPSIS A Rubrik Security Cloud (RSC) Reporting Module Function that returns a list of all threat hunts created within the time frame specified, searches back 30 days unless configured otherwise .DESCRIPTION Makes the required GraphQL API calls to RSC via Invoke-RestMethod to get the data as described, then creates a usable array of the returned information, removing the need for the PowerShell user to understand GraphQL in order to interact with RSC. .LINK GraphQL schema reference: https://rubrikinc.github.io/rubrik-api-documentation/schema/reference .PARAMETER HoursToCapture The number of hours back to search for threat hunts, use one or the other. .PARAMETER DaysToCapture The number of days back to search for threat hunts, use one or the other. .OUTPUTS Returns an array of all the available information on the GraphQL endpoint in a uniform and usable format. .EXAMPLE Get-RSCThreatHunts .NOTES Author: Joshua Stenhouse Date: 05/11/2023 #> ################################################ # Paramater Config ################################################ Param ( $HoursToCapture,$DaysToCapture ) ################################################ # Importing Module & Running Required Functions ################################################ # Importing the module is it needs other modules Import-Module RSCReporting # Checking connectivity, exiting function with error if not connected Test-RSCConnection ################################################ # Getting times required ################################################ $MachineDateTime = Get-Date $UTCDateTime = [System.DateTime]::UtcNow # Nulling any set as 0, to avoid mistakes from user presuming they have to enter 0 IF($DaysToCapture -eq 0){$DaysToCapture = $Null} IF($HoursToCapture -eq 0){$HoursToCapture = $Null} # If null, setting to 30 days IF(($DaysToCapture -eq $null) -and ($HoursToCapture -eq $null)) { $DaysToCapture = 30 } # Calculating time range if hours specified IF($HoursToCapture -ne $null) { $TimeRangeUTC = $UTCDateTime.AddHours(-$HoursToCapture) $TimeRange = $MachineDateTime.AddHours(-$HoursToCapture) } # Calculating time range if days specified IF($DaysToCapture -ne $null) { $TimeRangeUTC = $UTCDateTime.AddDays(-$DaysToCapture) $TimeRange = $MachineDateTime.AddDays(-$DaysToCapture) } # Converting to UNIX time format $TimeRangeUNIX = $TimeRangeUTC.ToString("yyyy-MM-ddTHH:mm:ss.000Z") # Logging # Write-Host "CollectingEventsFrom: $TimeRange" ################################################ # Getting RSC Threat Hunts ################################################ # Creating array for events $RSCThreatHuntsList = @() # Building GraphQL query $RSCGraphQL = @{"operationName" = "ListThreatHuntsQuery"; "variables" = @{ "first" = 100 "beginTime" = "$TimeRangeUNIX" }; "query" = "query ListThreatHuntsQuery(`$beginTime: DateTime, `$first: Int) { threatHunts(beginTime: `$beginTime, first: `$first) { edges { node { huntId status stats { totalUniqueMatchedPaths totalAffectedSnapshots totalAffectedObjects totalSnapshotsScanned totalSucceededScans totalUniqueQuarantinedPaths } huntDetails { startTime endTime snapshots { objectId snapshotIds snapshotTimestamps } cdmId config { fileScanCriteria { fileSizeLimits { maximumSizeInBytes minimumSizeInBytes } fileTimeLimits { earliestCreationTime earliestModificationTime latestCreationTime latestModificationTime } pathFilter { exceptions excludes includes } } indicatorsOfCompromise { iocKind iocValue } maxMatchesPerSnapshot name notes requestedMatchDetails { requestedHashTypes } shouldTrustFilesystemTimeInfo snapshotScanLimit { endTime maxSnapshotsPerObject snapshotsToScanPerObject { id snapshots } startTime } clusterUuid objects { id name cdmId cluster { id name } objectType } } cluster { id name } } } } pageInfo { startCursor endCursor hasPreviousPage hasNextPage } } }" } ################################################ # API Call To RSC GraphQL URI ################################################ # Querying API $RSCThreatHuntsResponse = Invoke-RestMethod -Method POST -Uri $RSCGraphqlURL -Body $($RSCGraphQL | ConvertTo-JSON -Depth 32) -Headers $RSCSessionHeader $RSCThreatHuntsList += $RSCThreatHuntsResponse.data.threathunts.edges.node # Getting all results from paginations While ($RSCThreatHuntsResponse.data.threathunts.pageInfo.hasNextPage) { # Getting next set $RSCGraphQL.variables.after = $RSCThreatHuntsResponse.data.threathunts.pageInfo.endCursor $RSCThreatHuntsResponse = Invoke-RestMethod -Method POST -Uri $RSCGraphqlURL -Body $($RSCGraphQL | ConvertTo-JSON -Depth 20) -Headers $RSCSessionHeader $RSCThreatHuntsList += $RSCThreatHuntsResponse.data.threathunts.edges.node } # Counting $RSCThreatHuntsCount = $RSCThreatHuntsList | Measure-Object | Select-Object -ExpandProperty Count ################################################ # Processing Threat Hunts ################################################ $RSCThreatHunts = [System.Collections.ArrayList]@() # For Each Getting info ForEach ($ThreatHunt in $RSCThreatHuntsList) { # Setting variables $HuntID = $ThreatHunt.huntId $HuntStatus = $ThreatHunt.Status $HuntStats = $ThreatHunt.stats $HuntDetails = $ThreatHunt.huntDetails # Hunt stats $HuntMatchedObjects = $HuntStats.totalAffectedObjects $HuntMatchedSnapshots = $HuntStats.totalAffectedSnapshots $HuntSnapshotsScanned = $HuntStats.totalSnapshotsScanned $HuntMatchedPaths = $HuntStats.totalUniqueMatchedPaths $HuntSnapshotsWithNoMatches = $HuntSnapshotsScanned - $HuntMatchedSnapshots # Hunt details $HuntStartUNIX = $HuntDetails.startTime $HuntEndUNIX = $HuntDetails.endTime $HuntSnapshots = $HuntDetails.snapshots $HuntConfig = $HuntDetails.config $HuntCluster = $HuntDetails.cluster # Cluster info $HuntClusterID = $HuntCluster.id $HuntClusterName = $HuntCluster.name # Converting times IF($HuntStartUNIX -ne $null){$HuntStartUTC = Convert-RSCUNIXTime $HuntStartUNIX}ELSE{$HuntStartUTC = $null} IF($HuntEndUNIX -ne $null){$HuntEndUTC = Convert-RSCUNIXTime $HuntEndUNIX}ELSE{$HuntEndUTC = $null} # Getting hunt file criteria $HuntFileScanCriteria = $HuntConfig.fileScanCriteria $HuntFileSizeLimits = $HuntFileScanCriteria.fileSizeLimits $HuntFileSizeMax = $HuntFileSizeLimits.maximumSizeInBytes $HuntFileSizeMin = $HuntFileSizeLimits.minimumSizeInBytes $HuntFilepathFilter = $HuntFileScanCriteria.pathFilter $HuntFileExceptions = $HuntFilepathFilter.exceptions $HuntFileExcludes = $HuntFilepathFilter.excludes $HuntFileIncludes = $HuntFilepathFilter.includes # Getting hunt IOC info $HuntIOCConfig = $HuntConfig.indicatorsOfCompromise $HuntIOCType = $HuntIOCConfig.iocKind $HuntIOCValue = $HuntIOCConfig.iocValue # Other detail $HuntMaxMatchesPerSnapshot = $HuntConfig.maxMatchesPerSnapshot $HuntName = $HuntConfig.name $HuntNotes = $HuntConfig.notes # Snapshot scan limits $HuntSnapshotScanLimit = $HuntConfig.snapshotScanLimit $HuntMaxSnapShotsPerObject = $HuntSnapshotScanLimit.maxSnapshotsPerObject # Scan objects $HuntObjects = $HuntConfig.objects $HuntObjectCount = $HuntObjects | Measure-Object | Select-Object -ExpandProperty Count $HuntObjectsWithNoMatches = $HuntObjectCount - $HuntMatchedObjects # Calculating timespans if not null IF (($HuntStartUTC -ne $null) -and ($HuntEndUTC -ne $null)) { $HuntRuntime = New-TimeSpan -Start $HuntStartUTC -End $HuntEndUTC $HuntMinutes = $HuntRuntime | Select-Object -ExpandProperty TotalMinutes $HuntSeconds = $HuntRuntime | Select-Object -ExpandProperty TotalSeconds $HuntDuration = "{0:g}" -f $HuntRuntime IF ($HuntDuration -match "."){$HuntDuration = $HuntDuration.split('.')[0]} # Calculating seconds per object and snapshot $HuntSecondsPerObject = $HuntSeconds / $HuntObjectCount $HuntSecondsPerSnapshot = $HuntSeconds / $HuntSnapshotsScanned # Rounding $HuntSecondsPerObject = [Math]::Round($HuntSecondsPerObject) $HuntSecondsPerSnapshot = [Math]::Round($HuntSecondsPerSnapshot) # Calculating minutes per object and snapshot $HuntMinutesPerObject = $HuntSecondsPerObject / 60 $HuntMinutesPerSnapshot = $HuntSecondsPerSnapshot / 60 # Rounding $HuntMinutesPerObject = [Math]::Round($HuntMinutesPerObject) $HuntMinutesPerSnapshot = [Math]::Round($HuntMinutesPerSnapshot) } ELSE { $HuntMinutes = $null $HuntSeconds = $null $HuntDuration = $null $HuntSecondsPerObject = $null $HuntSecondsPerSnapshot = $null $HuntMinutesPerObject = $null $HuntMinutesPerSnapshot = $null } # Creating URL to view/manage threat hunt # Example: https://rubrik-gaia.my.rubrik.com/radar/investigations/threat_hunts/a57dd4d3-2d1a-5fca-8dab-e0403235a8a6/details # Basic URL if not known: https://rubrik-gaia.my.rubrik.com/radar/investigations/threat_hunts # Creating URL $RSCThreatHuntURL = $RSCURL + "/radar/investigations/threat_hunts/" + $HuntID + "/details" ############################ # Adding To Array ############################ $Object = New-Object PSObject $Object | Add-Member -MemberType NoteProperty -Name "RSCInstance" -Value $RSCInstance $Object | Add-Member -MemberType NoteProperty -Name "ThreatHunt" -Value $HuntName $Object | Add-Member -MemberType NoteProperty -Name "ThreatHuntID" -Value $HuntID $Object | Add-Member -MemberType NoteProperty -Name "Status" -Value $HuntStatus $Object | Add-Member -MemberType NoteProperty -Name "Notes" -Value $HuntNotes $Object | Add-Member -MemberType NoteProperty -Name "Type" -Value $HuntIOCType $Object | Add-Member -MemberType NoteProperty -Name "RubrikCluster" -Value $HuntClusterName $Object | Add-Member -MemberType NoteProperty -Name "RubrikClusterID" -Value $HuntClusterID # Hunt results $Object | Add-Member -MemberType NoteProperty -Name "Objects" -Value $HuntObjectCount $Object | Add-Member -MemberType NoteProperty -Name "ObjectsWithMatches" -Value $HuntMatchedObjects $Object | Add-Member -MemberType NoteProperty -Name "ObjectsWithoutMatches" -Value $HuntObjectsWithNoMatches $Object | Add-Member -MemberType NoteProperty -Name "SnapshotsScanned" -Value $HuntSnapshotsScanned $Object | Add-Member -MemberType NoteProperty -Name "SnapshotsWithMatches" -Value $HuntMatchedSnapshots $Object | Add-Member -MemberType NoteProperty -Name "SnapshotsWithoutMatches" -Value $HuntSnapshotsWithNoMatches $Object | Add-Member -MemberType NoteProperty -Name "MatchedFiles" -Value $HuntMatchedPaths # Timing $Object | Add-Member -MemberType NoteProperty -Name "StartUTC" -Value $HuntStartUTC $Object | Add-Member -MemberType NoteProperty -Name "EndUTC" -Value $HuntEndUTC $Object | Add-Member -MemberType NoteProperty -Name "Duration" -Value $HuntDuration $Object | Add-Member -MemberType NoteProperty -Name "DurationSeconds" -Value $HuntSeconds # $Object | Add-Member -MemberType NoteProperty -Name "SecondsPerObject" -Value $HuntSecondsPerObject $Object | Add-Member -MemberType NoteProperty -Name "SecondsPerSnapshot" -Value $HuntSecondsPerSnapshot # $Object | Add-Member -MemberType NoteProperty -Name "MinutesPerObject" -Value $HuntMinutesPerObject $Object | Add-Member -MemberType NoteProperty -Name "MinutesPerSnapshot" -Value $HuntMinutesPerSnapshot # Hunt config detail $Object | Add-Member -MemberType NoteProperty -Name "MaxMatchesPerSnapshot" -Value $HuntMaxMatchesPerSnapshot $Object | Add-Member -MemberType NoteProperty -Name "MaxSnapShotsPerObject" -Value $HuntMaxSnapShotsPerObject $Object | Add-Member -MemberType NoteProperty -Name "FileSizeMax" -Value $HuntFileSizeMax $Object | Add-Member -MemberType NoteProperty -Name "FileSizeMin" -Value $HuntFileSizeMin $Object | Add-Member -MemberType NoteProperty -Name "FileExceptions" -Value $HuntFileExceptions $Object | Add-Member -MemberType NoteProperty -Name "FileExcludes" -Value $HuntFileExcludes $Object | Add-Member -MemberType NoteProperty -Name "FileIncludes" -Value $HuntFileIncludes # IOC definition $Object | Add-Member -MemberType NoteProperty -Name "IOCConfig" -Value $HuntIOCValue # URL $Object | Add-Member -MemberType NoteProperty -Name "URL" -Value $RSCThreatHuntURL # Adding to array $RSCThreatHunts.Add($Object) | Out-Null # # End of for each hunt below } # End of for each hunt above # Returning array Return $RSCThreatHunts # End of function } |