core/tasks/Invoke-MonkeyScanner.ps1
# Monkey365 - the PowerShell Cloud Security Tool for Azure and Microsoft 365 (copyright 2022) by Juan Garrido # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. Function Invoke-MonkeyScanner{ <# .SYNOPSIS Sets up a new scan with a custom configuration, variables and a Runspace .DESCRIPTION .INPUTS .OUTPUTS .EXAMPLE .NOTES Author : Juan Garrido Twitter : @tr1ana File Name : Invoke-MonkeyScanner Version : 1.0 .LINK https://github.com/silverhack/monkey365 #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("InjectionRisk.Create", "", Scope="Function")] Param ( [parameter(Mandatory=$false, HelpMessage="Provider")] [ValidateSet("Azure","EntraID","Microsoft365")] [String]$Provider = "Azure", [Parameter(Mandatory=$false, HelpMessage="Out data")] [Object]$ReturnData, [Parameter(Mandatory=$false, HelpMessage="Change the threads settings. Default is 2")] [int32]$Throttle = 2, [Parameter(Mandatory=$false, HelpMessage="ApartmentState of the thread")] [ValidateSet("STA","MTA")] [String]$ApartmentState = "STA", [Parameter(HelpMessage="Timeout before a thread stops trying to gather the information")] [ValidateRange(1,65535)] [int32]$Timeout = 30, [Parameter(HelpMessage="Increase Sleep Timer in seconds between child objects")] [ValidateRange(1,65535)] [int32]$SleepTimer = 5, [Parameter(HelpMessage="Pause between batchs in milliseconds")] [int32]$BatchSleep = 0, [Parameter(HelpMessage="BatchSize")] [int32]$BatchSize = 100, [Parameter(HelpMessage="Increase Sleep Timer in seconds between child objects")] [ValidateRange(1,65535)] [int32]$MaxQueue = 1 ) Begin{ $rdata = $null #set MaxQueue if( -not $PSBoundParameters.ContainsKey('MaxQueue') ) { $MaxQueue = $Throttle * 3 } else { $MaxQueue = $MaxQueue } #Set runspaces array $all_scans = [System.Collections.Generic.List[System.Collections.Hashtable]]::new(); } Process{ #Get Initialize-MonkeyRuleset params $newPsboundParams = [ordered]@{} $MetaData = New-Object -TypeName "System.Management.Automation.CommandMetaData" (Get-Command -Name "Initialize-MonkeyScan") if($null -ne $MetaData){ $param = $MetaData.Parameters.Keys foreach($p in $param.GetEnumerator()){ if($PSBoundParameters.ContainsKey($p)){ $newPsboundParams.Add($p,$PSBoundParameters.Item($p)) } } } $all_scans = Initialize-MonkeyScan @newPsboundParams #Get ReturnData object if($PSBoundParameters.ContainsKey('ReturnData') -and $PSBoundParameters['ReturnData']){ $rdata = $PSBoundParameters['ReturnData']; } elseif($null -ne (Get-Variable -Name returnData -ErrorAction Ignore)){ $rdata = (Get-Variable -Name returnData -ErrorAction Ignore).Value; } else{ Set-Variable returnData -Value ([hashtable]::Synchronized(@{})) -Scope Script -Force $rdata = (Get-Variable -Name returnData -ErrorAction Ignore).Value; } #Populate vars foreach($scan in $all_scans){ #Set synchronized hashtable $scan.vars.returnData = $rdata; } #Populate runspace vars $rndScan = $all_scans.Where({$null -ne $_.vars}) | Select-Object -First 1 if($null -ne $rndScan){ $O365Object.runspace_vars = $rndScan.vars; } } End{ If($null -ne $all_scans -and @($all_scans).Count -gt 0){ #Set runspaces array $all_runspaces = [System.Collections.Generic.List[System.Management.Automation.Runspaces.RunspacePool]]::new(); #Launch scans try{ #Set nested runspace $myscan = $all_scans | Select-Object -First 1 #Get all libs $libs = $all_scans.libCommands | Select-Object -Unique if($null -ne $myscan){ $nestedParam = @{ ImportVariables = $myscan.vars; ImportModules = $myscan.modules; ImportCommands = $libs; ApartmentState = $myscan.apartmentState; Throttle = $myscan.threads; StartUpScripts = $myscan.startUpScripts; ThrowOnRunspaceOpenError = $true; Verbose = $O365Object.verbose; Debug = $O365Object.debug; InformationAction = $O365Object.InformationAction; } #Set a second runspace for nested executions $nestedRunspace = New-RunspacePool @nestedParam if($null -ne $nestedRunspace -and $nestedRunspace -is [System.Management.Automation.Runspaces.RunspacePool]){ #Add to array [void]$all_runspaces.Add($nestedRunspace) #Add to object $O365Object.monkey_runspacePool = $nestedRunspace; } } foreach($scan in $all_scans.GetEnumerator()){ $msg = @{ MessageData = ("Starting new scan for {0}" -f $scan.scanName); callStack = (Get-PSCallStack | Select-Object -First 1); logLevel = 'info'; InformationAction = $O365Object.InformationAction; Tags = @('Monkey365Scanner'); } Write-Information @msg #Set Generic list $commands = [System.Collections.Generic.List[System.Object]]::new(); #Add collectors to libcommands foreach($collector in @($scan.collectors)){ [void]$commands.Add($collector.File); } #Add commands foreach($command in @($scan.libCommands)){ [void]$commands.Add($command); } $rsParam = @{ ImportVariables = $scan.vars; ImportModules = $scan.modules; ImportCommands = $commands; ApartmentState = $scan.apartmentState; Throttle = $scan.threads; StartUpScripts = $scan.startUpScripts; ThrowOnRunspaceOpenError = $true; Verbose = $O365Object.verbose; Debug = $O365Object.debug; InformationAction = $O365Object.InformationAction; } #Get runspace pool $runspacepool = New-RunspacePool @rsParam if($null -ne $runspacepool -and $runspacepool -is [System.Management.Automation.Runspaces.RunspacePool]){ $runspacepool.Open() #Add to array [void]$all_runspaces.Add($runspacepool) } if($null -ne $runspacepool -and $runspacepool.RunspacePoolStateInfo.State -eq [System.Management.Automation.Runspaces.RunspaceState]::Opened){ foreach($collector in $scan.collectors){ $p = @{ Runspacepool = $runspacepool; ReuseRunspacePool = $true; MaxQueue = $MaxQueue; Debug = $O365Object.Debug; Verbose = $O365Object.Verbose; BatchSleep = $O365Object.BatchSleep; BatchSize = $O365Object.BatchSize; InformationAction = $O365Object.InformationAction; } $Id = ([System.Guid]::NewGuid()).ToString() $argument = @{CollectorId = $Id} #$sb = [ScriptBlock]::Create('{0}' -f $collector.File.FullName) Invoke-MonkeyJob -Command $collector.collectorName -Arguments $argument @p } } else{ if($null -ne $runspacepool -and $runspacepool.RunspacePoolStateInfo.State -ne [System.Management.Automation.Runspaces.RunspaceState]::Opened){ Write-Error ($message.RunspaceError) return } else{ Write-Error ($message.UnknownError) return } } } } Catch{ Write-Error $_ } Finally{ #Dispose all runspacepool foreach($rs in $all_runspaces){ $rs.Dispose() } #collect garbage #[gc]::Collect() [System.GC]::GetTotalMemory($true) | out-null } } Else{ $msg = @{ MessageData = ("Unable to initialize the Monkey365 scan for {0}. Collectors for {1} are empty" -f $Provider, $Provider); callStack = (Get-PSCallStack | Select-Object -First 1); logLevel = 'warning'; InformationAction = $O365Object.InformationAction; Tags = @('Monkey365Scanner'); } Write-Warning @msg } } } |