PowerShell Port Scanner

#######################################################################################
# Language     :  PowerShell 4.0
# Filename     :  PortScan.ps1 
# Autor        :  Brandon Lanczak
# Description  :  Asynchronus IPv4 Port Scanner
#######################################################################################

<#
    .SYNOPSIS
    Asynchronus IPv4 Port Scanner

    .DESCRIPTION
    This asynchronus IPv4 Port Scanner allows you to scan every Port-Range you want (500 to 2600 would work). Only TCP-Ports are scanned. 

    The result will contain the Port number, Protocol, Service name, Description and the Status.
    
    .EXAMPLE
    .\IPv4PortScan.ps1 -ComputerName rubbish.com -EndPort 500

    Port Protocol ServiceName  ServiceDescription               Status
    ---- -------- -----------  ------------------               ------
      53 tcp      domain       Domain Name Server               open
      80 tcp      http         World Wide Web HTTP              open
    
#>

[CmdletBinding()]
param(
    [Parameter(
        Position=0,
        Mandatory=$true,
        HelpMessage='ComputerName or IPv4-Address of the device which you want to scan')]
    [String]$ComputerName,

    [Parameter(
        Position=1,
        HelpMessage='First port which should be scanned (Default=1)')]
    [ValidateRange(1,65535)]
    [Int32]$StartPort=1,

    [Parameter(
        Position=2,
        HelpMessage='Last port which should be scanned (Default=65535)')]
    [ValidateRange(1,65535)]
    [ValidateScript({
        if($_ -lt $StartPort)
        {
            throw "Invalid Port-Range!"
        }
        else 
        {
            return $true
        }
    })]
    [Int32]$EndPort=65535,

    [Parameter(
        Position=3,
        HelpMessage='Maximum number of threads at the same time (Default=500)')]
    [Int32]$Threads=10,

    [Parameter(
        Position=4,
        HelpMessage='Execute script without user interaction')]
    [switch]$Force
)

Begin{
    Write-Verbose -Message "Script started at $(Get-Date)"

    $PortList_Path = "$PSScriptRoot\Resources\ports.txt"
}

Process{
    if(Test-Path -Path $PortList_Path -PathType Leaf)
    {        
        $PortsHashTable = @{ }

        Write-Verbose -Message "Read ports.txt and fill hash table..."

        foreach($Line in Get-Content -Path $PortList_Path)
        {
            if(-not([String]::IsNullOrEmpty($Line)))
            {
                try{
                    $HashTableData = $Line.Split('|')
                    
                    if($HashTableData[1] -eq "tcp")
                    {
                        $PortsHashTable.Add([int]$HashTableData[0], [String]::Format("{0}|{1}",$HashTableData[2],$HashTableData[3]))
                    }
                }
                catch [System.ArgumentException] { } # Catch if port is already added to hash table
            }
        }

        $AssignServiceWithPort = $true
    }
    else 
    {
        $AssignServiceWithPort = $false    

        Write-Warning -Message "No port-file to assign service with port found! Execute the script ""Create-PortListFromWeb.ps1"" to download the latest version.. This warning doesn`t affect the scanning procedure."
    }

    # Check if host is reachable
    Write-Verbose -Message "Test if host is reachable..."
    if(-not(Test-Connection -ComputerName $ComputerName -Count 2 -Quiet))
    {
        Write-Warning -Message "$ComputerName is not reachable!"

        if($Force -eq $false)
        {
            $Title = "Continue"
            $Info = "Would you like to continue? (perhaps only ICMP is blocked)"
            
            $Options = [System.Management.Automation.Host.ChoiceDescription[]] @("&Yes", "&No")
            [int]$DefaultChoice = 0
            $Opt =  $host.UI.PromptForChoice($Title , $Info, $Options, $DefaultChoice)

            switch($Opt)
            {                    
                1 { 
                    return
                }
            }
        }
    }

    $PortsToScan = ($EndPort - $StartPort)

    Write-Verbose -Message "Scanning range from $StartPort to $EndPort ($PortsToScan Ports)"
    Write-Verbose -Message "Running with max $Threads threads"

    # Check if ComputerName is already an IPv4-Address, if not... try to resolve it
    $IPv4Address = [String]::Empty
	
	if([bool]($ComputerName -as [IPAddress]))
	{
		$IPv4Address = $ComputerName
	}
	else
	{
		# Get IP from Hostname (IPv4 only)
		try{
			$AddressList = @(([System.Net.Dns]::GetHostEntry($ComputerName)).AddressList)
			
			foreach($Address in $AddressList)
			{
				if($Address.AddressFamily -eq "InterNetwork") 
				{					
					$IPv4Address = $Address.IPAddressToString 
					break					
				}
			}					
		}
		catch{ }	# Can't get IPAddressList 					

       	if([String]::IsNullOrEmpty($IPv4Address))
		{
			throw "Could not get IPv4-Address for $ComputerName. (Try to enter an IPv4-Address instead of the Hostname)"
		}		
	}

    # Scriptblock --> will run in runspaces (threads)...
    [System.Management.Automation.ScriptBlock]$ScriptBlock = {
        Param(
			$IPv4Address,
			$Port
        )

        try{                      
            $Socket = New-Object System.Net.Sockets.TcpClient($IPv4Address,$Port)
            
            if($Socket.Connected)
            {
                $Status = "Open"             
                $Socket.Close()
            }
            else 
            {
                $Status = "Closed"    
            }
        }
        catch{
            $Status = "Closed"
        }   

        if($Status -eq "Open")
        {
            [pscustomobject] @{
                Port = $Port
                Protocol = "tcp"
                Status = $Status
            }
        }
    }

    Write-Verbose -Message "Setting up RunspacePool..."

    # Create RunspacePool and Jobs
    $RunspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(1, $Threads, $Host)
    $RunspacePool.Open()
    [System.Collections.ArrayList]$Jobs = @()

    Write-Verbose -Message "Setting up Jobs..."
    
    #Set up job for each port...
    foreach($Port in $StartPort..$EndPort)
    {
        $ScriptParams =@{
			IPv4Address = $IPv4Address
			Port = $Port
		}

        # Catch when trying to divide through zero
        try {
			$Progress_Percent = (($Port - $StartPort) / $PortsToScan) * 100 
		} 
		catch { 
			$Progress_Percent = 100 
		}

        Write-Progress -Activity "Setting up jobs..." -Id 1 -Status "Current Port: $Port" -PercentComplete ($Progress_Percent)
        
        # Create mew job
        $Job = [System.Management.Automation.PowerShell]::Create().AddScript($ScriptBlock).AddParameters($ScriptParams)
        $Job.RunspacePool = $RunspacePool
        
        $JobObj = [pscustomobject] @{
            RunNum = $Port - $StartPort
            Pipe = $Job
            Result = $Job.BeginInvoke()
        }

        # Add job to collection
        [void]$Jobs.Add($JobObj)
    }

    Write-Verbose -Message "Waiting for jobs to complete & starting to process results..."

    # Total jobs to calculate percent complete, because jobs are removed after they are processed
    $Jobs_Total = $Jobs.Count

     # Process results, while waiting for other jobs
    Do {
        # Get all jobs, which are completed
        $Jobs_ToProcess = $Jobs | Where-Object -FilterScript {$_.Result.IsCompleted}
  
        # If no jobs finished yet, wait 500 ms and try again
        if($null -eq $Jobs_ToProcess)
        {
            Write-Verbose -Message "No jobs completed, wait 500ms..."

            Start-Sleep -Milliseconds 500
            continue
        }
        
        # Get jobs, which are not complete yet
        $Jobs_Remaining = ($Jobs | Where-Object -FilterScript {$_.Result.IsCompleted -eq $false}).Count

        # Catch when trying to divide through zero
        try {            
            $Progress_Percent = 100 - (($Jobs_Remaining / $Jobs_Total) * 100) 
        }
        catch {
            $Progress_Percent = 100
        }

        Write-Progress -Activity "Waiting for jobs to complete... ($($Threads - $($RunspacePool.GetAvailableRunspaces())) of $Threads threads running)" -Id 1 -PercentComplete $Progress_Percent -Status "$Jobs_Remaining remaining..."
      
        Write-Verbose -Message "Processing $(if($null -eq $Jobs_ToProcess.Count){"1"}else{$Jobs_ToProcess.Count}) job(s)..."

        # Processing completed jobs
        foreach($Job in $Jobs_ToProcess)
        {       
            # Get the result...     
            $Job_Result = $Job.Pipe.EndInvoke($Job.Result)
            $Job.Pipe.Dispose()

            # Remove job from collection
            $Jobs.Remove($Job)
           
            # Check if result is null --> if not, return it
            if($Job_Result.Status)
            {        
                if($AssignServiceWithPort)
                {
                    $Service = [String]::Empty

                    $Service = $PortsHashTable.Get_Item($Job_Result.Port).Split('|')
                
                    [pscustomobject] @{
                        Port = $Job_Result.Port
                        Protocol = $Job_Result.Protocol
                        ServiceName = $Service[0]
                        ServiceDescription = $Service[1]
                        Status = $Job_Result.Status
                    }
                }   
                else 
                {
                    $Job_Result    
                }             
            }
        } 

    } While ($Jobs.Count -gt 0)
    
    Write-Verbose -Message "Closing RunspacePool and free resources..."

    # Close the RunspacePool and free resources
    $RunspacePool.Close()
    $RunspacePool.Dispose()

    Write-Verbose -Message "Script finished at $(Get-Date)"
}

End{

}
# Create Ports hash file
#[xml]$LatestPorts = Get-Content -Path "$PSScriptRoot\Service Name and Transport Protocol Port Number Registry.xml"
[xml]$LatestPorts = (Invoke-WebRequest -Uri "https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml").Content

$Output = ""

foreach($record in $LatestPorts.ChildNodes.record)
{
    if([string]::IsNullOrEmpty($record.number) -or ([string]::IsNullOrEmpty($record.protocol)))
    {        
        continue   
    }
    
    $Description = ($record.description -replace '`n','') -replace '\s+',' '

    $Number = $record.number

    if($Number -like "*-*")
    {
        $NumberArr = $Number.Split('-')

        foreach($Number1 in $NumberArr[0]..$NumberArr[1])
        {
            $Output += "$Number1|$($record.protocol)|$($record.name)|$Description`n"   
        }
    }
    else 
    {
        $Output += "$Number|$($record.protocol)|$($record.name)|$Description`n"   
    }    
}

Out-File -InputObject $Output -FilePath "$PSScriptRoot\Resources\ports.txt"

PowerShell Constant Attention

# Time for loop to run
param($minutes = 920)

# Loop
$myshell = New-Object -com "Wscript.Shell"

for ($i = 0; $i -lt $minutes; $i++) {
    $myshell.sendkeys("{SCROLLLOCK}")
    Get-Date -Format "dddd MM/dd/yyyy HH:mm K" | Write-Host
    Start-Sleep -Milliseconds 100
    $myshell.sendkeys("{SCROLLLOCK}")
    Echo $i
    Start-Sleep -Seconds 60
}

DC GPO for the PCD to source NTP

hat should start with creating a WMI filter that will be used to ensure only the PDCe is allowed to sync from an external time source

1.   Right-click the WMI Filters folder and right-click New

2.  Give the new filter a meaningful name, for example PDCe DC Role Filte

3. Give the new filter a meaningful description such as “This filter will search for a computer with the PDCe FSMO Role [DomainRole 5]”

4. Create a new query by clicking Add

5. Leave the Namespace field at the default value of root\CIMv2

6. Enter the following text for the Query value: Select * from Win32_ComputerSystem where DomainRole = 5

Useful Microsoft Time Configuration References

• '0x01' = 'SpecialInterval'
• '0x02' = 'UseAsFallbackOnly'
• '0x03' = 'SpecialInterval+UseAsFallbackOnly'
• '0x04' = 'SymmetricActive'
• '0x05' = 'SpecialInterval+SymmetricActive'
• '0x06' = 'UseAsFallbackOnly+SymmetricActive'
• '0x07' = 'SpecialInterval+UseAsFallbackOnly+SymmetricActive'
• '0x08' = 'Client'
• '0x09' = 'SpecialInterval+Client'
• '0x0A' = 'UseAsFallbackOnly+Client'
• '0x0B' = 'SpecialInterval+UseAsFallbackOnly+Client'
• '0x0C' = 'SymmetricActive+Client'
• '0x0D' = 'SpecialInterval+SymmetricActive+Client'
• '0x0E' = 'UseAsFallbackOnly+SymmetricActive+Client'
• '0x0F' = 'SpecialInterval+UseAsFallbackOnly+SymmetricActive+Client'

The most useful:

• '0x1' = 'SpecialInterval'
Indicates to sync time with external server in SpecialPollInterval configured in “SpecialPollInterval” registry value.

• '0x2' = 'UseAsFallbackOnly'
Use this time source only as a fallback. If all time sources that are NOT fallbacks have failed, then the system selects one fallback time source at random and uses it. If primary is not available then sync to this server.

• '0x8' = 'Client'
Set the local computer to operate in client mode in the association with this source.
Use client mode association while sync time to external time source.

• '0x9' = 'SpecialInterval+Client'

PowerShell Query CISA Known Exploited Vulnerabilities

Function Get-CISAVulnerabilitiesReport {
<#
    .SYNOPSIS
        Get known exploited vulnerabilities

    .DESCRIPTION
        Get the known exploited vulnerabilities catalog from CISA

    .PARAMETER StartDate
        Datetime object used to filter the catalog

    .PARAMETER Last
        Last number of entries in the catalog sorted by published date

    .EXAMPLE
        Get-CISAVulnerabilitiesReport

        Get all the known exploited vulnerabilities from the catalog published by CISA

    .EXAMPLE
        Get-CISAVulnerabilitiesReport | Measure-Object

        Get the count of all the known exploited vulnerabilities published in the catalog by CISA

    .EXAMPLE
        Get-CISAVulnerabilitiesReport -Last 3

        Get the 3 most recent known exploited vulnerabilities from the catalog published by CISA
    .EXAMPLE
        Get-CISAVulnerabilitiesReport -StartDate (Get-Date).AddDays(-15)

        Get the known exploited vulnerabilities from the catalog published by CISA over the last 15 days
#>
[CmdletBinding(DefaultParameterSetName='__AllParameterSets')]
Param(
    [Parameter(ParameterSetName = 'ByDate')]
    [datetime]$StartDate,

    [Parameter(ParameterSetName = 'ByLast')]
    [int32]$Last
)
Begin {}
Process {
    $HT = @{
        URI = 'https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json'
        ErrorAction = 'Stop'
        UseBasicParsing = [switch]::Present
    }
    try {
        $vuln = (Invoke-RestMethod @HT).vulnerabilities |
        ForEach-Object -Process {
            [PSCustomObject]@{
                CVEId = $_.cveID
                Vendor = $_.vendorProject
                ProductName = $_.product
                Name = $_.vulnerabilityName
                StartDate =  ([datetime]$_.dateAdded)
                Description = $_.shortDescription
                ActionRequired = $_.requiredAction
                DueDate = ([datetime]$_.dueDate)
            }
        }
    } catch {
        Write-Warning -Message "Failed to get data from CISA because $($_.Exception.Message)"
    }
    if ($vuln) {
        Switch ($PSCmdlet.ParameterSetName) {
            'ByDate' {
                $vuln | Where-Object { $_.StartDate -gt $StartDate }
                break
            }
            'ByLast' {
                $vuln | Sort-Object -Property StartDate -Descending | Select-Object -First $Last
                break
            }
            default {
                $vuln
            }
        }
    }
}
End {}
}

SQL Backup Database

Backup-SqlDatabase -ServerInstance SQL-01 -Database Whatever -BackupFile "C:\WhereEver\Backup\Test.bak" -Verbose

Find Source of AD Account Lockout

<#
.SYNOPSIS
    This function locates the computer that processed a failed user logon attempt which caused a user account to become locked out.

.DESCRIPTION
    This function will locate the computer that processed a failed user logon attempt which caused the user account to become locked out.
    The locked out location is found by querying the PDC Emulator for locked out events (4740).
    The function will display the BadPasswordTime attribute on all of the domain controllers to add in further troubleshooting.

.EXAMPLE
    PS C:\>Get-LockedOutLocation -Identity Jimmy.John


    This example will find the locked out location for Jimmy John.
.NOTE
-This function is only compatible with an environment where the domain controller with the PDC Emulator role is running Windows Server 2008 SP2 and higher.
-The script is dependent on the ActiveDirectory PowerShell module, which requires the AD Web services to be running on at least one domain controller.
    
Author: Brandon Lanczak
    
Last Modified: 10-08-2022 @ 21:00 CST
#>

Function Get-LockedOutLocation {

    [CmdletBinding()]

    Param(
      [Parameter(Mandatory=$True)]
      [String]$Identity
    )

    Begin {

        $DCCounter = 0
        $LockedOutStats = @()

        Try { Import-Module ActiveDirectory -ErrorAction Stop }

        Catch {

           Write-Warning $_
           Break
        }
    }

    Process {

        #Get all domain controllers in domain
        $DomainControllers = Get-ADDomainController -Filter *
        $PDCEmulator = $DomainControllers | Where-Object { $_.OperationMasterRoles -contains "PDCEmulator" }

        Write-Verbose "Finding the domain controllers in the domain"

        ForEach($DC in $DomainControllers) {

            $DCCounter++
            Write-Progress -Activity "Contacting DCs for lockout info" -Status "Querying $($DC.Hostname)" -PercentComplete (($DCCounter/$DomainControllers.Count) * 100)
            Try { $UserInfo = Get-ADUser -Identity $Identity  -Server $DC.Hostname -Properties AccountLockoutTime,LastBadPasswordAttempt,BadPwdCount,LockedOut -ErrorAction Stop }

            Catch {

                Write-Warning $_
                Continue
            }

            if ($UserInfo.LastBadPasswordAttempt) {

                $LockedOutStats += New-Object -TypeName PSObject -Property @{
                        Name                   = $UserInfo.SamAccountName
                        SID                    = $UserInfo.SID.Value
                        LockedOut              = $UserInfo.LockedOut
                        BadPwdCount            = $UserInfo.BadPwdCount
                        BadPasswordTime        = $UserInfo.BadPasswordTime
                        DomainController       = $DC.Hostname
                        AccountLockoutTime     = $UserInfo.AccountLockoutTime
                        LastBadPasswordAttempt = ($UserInfo.LastBadPasswordAttempt).ToLocalTime()
                }
            }
        }

        $LockedOutStats | Format-Table -Property Name,LockedOut,DomainController,BadPwdCount,AccountLockoutTime,LastBadPasswordAttempt -AutoSize

        #Get User Info
        Try {

           Write-Verbose "Querying event log on $($PDCEmulator.HostName)"
           $LockedOutEvents = Get-WinEvent -ComputerName $PDCEmulator.HostName -FilterHashtable @{LogName='Security';Id=4740} -ErrorAction Stop | Sort-Object -Property TimeCreated -Descending
        }

        Catch {

           Write-Warning $_
           Continue
        }

        ForEach ($Event in $LockedOutEvents) {

           if ($Event | Where {$_.Properties[2].value -match $UserInfo.SID.Value}) {

              $Event | Select-Object -Property @(
                @{Label = 'User';               Expression = {$_.Properties[0].Value}}
                @{Label = 'DomainController';   Expression = {$_.MachineName}}
                @{Label = 'EventId';            Expression = {$_.Id}}
                @{Label = 'LockedOutTimeStamp'; Expression = {$_.TimeCreated}}
                @{Label = 'Message';            Expression = {$_.Message -split "`r" | Select -First 1}}
                @{Label = 'LockedOutLocation';  Expression = {$_.Properties[1].Value}}
              )
          }
       }
    }
}