Public/Invoke-OnyphePivot.ps1
|
Function Invoke-OnyphePivot { <# .SYNOPSIS main function/cmdlet - pivot from a prior Onyphe result set to a follow-up search, per distinct value of a field .DESCRIPTION main function/cmdlet - takes a prior Onyphe result set (e.g. from Search-OnypheInfo/Get-OnypheInfo), extracts every distinct value of -PivotField from it, and fires one follow-up Search-OnypheInfo call per distinct value using -PivotFilter as the target OQL filter name. Captures the "auto-pivot" idea from the official onyphe/cli's opp companion tool (Andlookup/Orlookup/Pivots/Whois processors, which issue new Onyphe API calls off a field pulled from a prior result set) without porting the rest of that tool's 24 purely client-side formatting/filtering processors - those are already covered natively and idiomatically by PowerShell's own object pipeline (Where-Object/Group-Object/ Sort-Object -Unique/etc.). One API call is made per distinct pivot value (not a single OR-combined query) - this mirrors the manual multi-step technique this project's own OSINT sessions have already used by hand (e.g. Results/CertAndDNS/sovcloud_domains_analysis.md's organization-to-domain and subnet-to-domain enumeration passes), and keeps clear provenance (which pivot value produced which hits) at the cost of one API call/credit per distinct value. Each returned result object is tagged with two extra NoteProperty fields, 'cli-pivot_source_field' and 'cli-pivot_source_value', recording which -PivotField value produced it. Only the first page of results is retrieved per pivot value (no -Page support) - use -Size to widen that first page if needed. .PARAMETER InputObject -InputObject PSOnyphe object[] a prior Onyphe result set (e.g. piped in from Search-OnypheInfo/Get-OnypheInfo) to pivot from .PARAMETER PivotField -PivotField string dotted property path to read off each -InputObject item (e.g. "subject.organization", "geolocus.organization") - the source of the distinct values to pivot on. If the resolved property value is itself an array (e.g. a SAN list), every element is treated as its own distinct value. .PARAMETER PivotCategory -PivotCategory string {Get-OnypheSearchCategories} Search Type or Category to use for the follow-up query (same meaning as Search-OnypheInfo's -Category) .PARAMETER PivotFilter -PivotFilter string {Get-OnypheSearchFilters} OQL filter name to use in the follow-up query (same meaning as Search-OnypheInfo's -SearchFilter) - deliberately a separate parameter from -PivotField, since the property name on the source object and the OQL filter name on the target category can differ (e.g. reading "organization" off a resolver result but pivoting into ctl using its "subject.organization" filter) .PARAMETER APIKey -APIKey string{APIKEY} set your APIKEY to be able to use Onyphe API. .PARAMETER Size -Size int{1 to 10000} number of results per page (server default is 100 when omitted), applied to every follow-up query .PARAMETER TrackQuery -TrackQuery switch ask Onyphe to return, for each result, which OQL filter matched it (applied to every follow-up query) .PARAMETER Calculated -Calculated switch ask Onyphe to enrich results with computed fields (applied to every follow-up query) .PARAMETER UseBetaFeatures -UseBetaFeatures switch use test.onyphe.io to use new beat features of Onyphe .PARAMETER Post -Post switch send each follow-up query's OQL as a POST request body instead of a GET query-string parameter (see Search-OnypheInfo's -Post) .PARAMETER Wait -Wait int{second} wait for x second before sending each follow-up query, to manage rate limiting across the (potentially many) API calls this cmdlet can fire - one per distinct pivot value .OUTPUTS TypeName: PSOnyphe .EXAMPLE pivot from a ctl result set to every other certificate sharing the same subject.organization value(s), using -PivotField and -PivotFilter as the same field name here (both live on the ctl category) C:\PS> Search-OnypheInfo -AdvancedSearch @("domain:example.com") -Category ctl | Invoke-OnyphePivot -PivotField "subject.organization" -PivotCategory ctl -PivotFilter "subject.organization" .EXAMPLE pivot from a resolver result set to every other hostname hosted on the same organization, waiting 2s between each follow-up call C:\PS> Search-OnypheInfo -AdvancedSearch @("domain:example.com") -Category resolver | Invoke-OnyphePivot -PivotField organization -PivotCategory resolver -PivotFilter organization -Wait 2 #> [cmdletbinding()] Param ( [parameter(ValueFromPipeline=$true,Mandatory=$true,Position=1)] [ValidateNotNullOrEmpty()] [array]$InputObject, [parameter(Mandatory=$true,Position=2)] [ValidateNotNullOrEmpty()] [string]$PivotField, [parameter(Mandatory=$false)] [ValidateLength(40,40)] [string]$APIKey, [parameter(Mandatory=$false)] [ValidateRange(1,10000)] [int]$Size, [parameter(Mandatory=$false)] [switch]$TrackQuery, [parameter(Mandatory=$false)] [switch]$Calculated, [parameter(Mandatory=$false)] [switch]$UseBetaFeatures, [parameter(Mandatory=$false)] [switch]$Post, [parameter(Mandatory=$false)] [int]$Wait ) DynamicParam { $ParameterNameCategory = 'PivotCategory' $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute $ParameterAttribute.ValueFromPipeline = $false $ParameterAttribute.ValueFromPipelineByPropertyName = $false $ParameterAttribute.Mandatory = $true $AttributeCollection.Add($ParameterAttribute) $arrSet = Get-OnypheSearchCategories if ($arrSet) { $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet) $AttributeCollection.Add($ValidateSetAttribute) } $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterNameCategory, [string], $AttributeCollection) $RuntimeParameterDictionary.Add($ParameterNameCategory, $RuntimeParameter) $ParameterNameFilter = 'PivotFilter' $AttributeCollection2 = New-Object System.Collections.ObjectModel.Collection[System.Attribute] $ParameterAttribute2 = New-Object System.Management.Automation.ParameterAttribute $ParameterAttribute2.ValueFromPipeline = $false $ParameterAttribute2.ValueFromPipelineByPropertyName = $false $ParameterAttribute2.Mandatory = $true $AttributeCollection2.Add($ParameterAttribute2) $arrSet2 = Get-OnypheSearchFilters if ($arrSet2) { $ValidateSetAttribute2 = New-Object System.Management.Automation.ValidateSetAttribute($arrSet2) $AttributeCollection2.Add($ValidateSetAttribute2) } $RuntimeParameter2 = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterNameFilter, [string], $AttributeCollection2) $RuntimeParameterDictionary.Add($ParameterNameFilter, $RuntimeParameter2) return $RuntimeParameterDictionary } Begin { $Config = Read-OnypheConfigFile Write-OnypheLog -Config $Config -Level Debug -CmdletName $MyInvocation.MyCommand.Name -Message 'Cmdlet invoked' -BoundParameters $PSBoundParameters $script:PivotCollectedObjects = @() } Process { $script:PivotCollectedObjects += $InputObject } End { $PivotCategory = $PsBoundParameters[$ParameterNameCategory] $PivotFilter = $PsBoundParameters[$ParameterNameFilter] if ($APIKey) {Set-OnypheAPIKey -APIKey $APIKey | out-null} $PathSegments = $PivotField -split '\.' $PivotValues = foreach ($obj in $script:PivotCollectedObjects) { $current = $obj foreach ($segment in $PathSegments) { if ($null -eq $current) { break } $current = $current.$segment } if ($null -ne $current) { $current } } $DistinctValues = $PivotValues | Where-Object { $_ } | Select-Object -Unique if (-not $DistinctValues) { Write-Warning -Message "No non-empty value found for -PivotField '$($PivotField)' across the supplied -InputObject set - nothing to pivot on" return } foreach ($Value in $DistinctValues) { if ($Wait) { Start-Sleep -s $Wait } $SearchParams = @{ SearchValue = $Value SearchFilter = $PivotFilter Category = $PivotCategory } if ($Size) { $SearchParams.Size = $Size } if ($TrackQuery) { $SearchParams.TrackQuery = $true } if ($Calculated) { $SearchParams.Calculated = $true } if ($UseBetaFeatures) { $SearchParams.UseBetaFeatures = $true } if ($Post) { $SearchParams.Post = $true } Write-OnypheLog -Config $Config -Level Information -CmdletName $MyInvocation.MyCommand.Name -Message "Pivoting on $($PivotField)=$($Value)" $Results = Search-OnypheInfo @SearchParams foreach ($Result in $Results) { if ($Result) { $Result | Add-Member -MemberType NoteProperty -Name 'cli-pivot_source_field' -Value $PivotField -Force $Result | Add-Member -MemberType NoteProperty -Name 'cli-pivot_source_value' -Value $Value -Force $Result } } } } } |