public/Test-TNAccessibility.ps1
function Test-TNAccessibility { <# .SYNOPSIS Tests the Credentialing and Accessibility of ACAS. .DESCRIPTION Tests the Credentialing and Accessibility of ACAS. This command automates the checklist found at: https://docs.tenable.com/nessus/Content/EnableWindowsLoginsForLocalAndRemoteAudits.htm This includes: *1) The Windows Management Instrumentation (WMI) service must be enabled on the target. (https://technet.microsoft.com/en-us/library/cc180684.aspx) *2) The Remote Registry service must be enabled on the target. *3) File & Printer Sharing must be enabled in the target's network configuration. 4) An SMB account must be used that has local administrator rights on the target. (You can use a domain account, but that account must be a local administrator on the devices being scanned.) *5) Ports 139 (TCP) and 445 (TCP) must be open between the Nessus scanner and the target. 6) Ensure that no Windows security policies are in place that block access to these services. See below for more information. 7) The default administrative shares (i.e. IPC$, ADMIN$, C$) must be enabled (AutoShareServer = 1). These are enabled by default and can cause other issues if disabled (http://support.microsoft.com/kb/842715/en-us). RemoteRegistry .PARAMETER ComputerName The network name or names of the target computers. Using an IP address will be slower and potentially less accurate. .PARAMETER ServiceAccount The ACAS Service account. .PARAMETER Credential Optional parameter to run the script checker as an alternative user. Useful if running as the ACAS service account is desired .EXAMPLE PS> Test-TNAccessibility -ComputerName WS101 Remotely connects to computer WS101 and performs tests. Assumes svc.acas.* is the service account .EXAMPLE PS> Test-TNAccessibility -ComputerName WS101 -Credential ad\acaschecker -ServiceAccount acas2 Remotely connects to computer WS101 as ad\acaschecker and performs tests. Checks to see if an account matching acas2 is a local admin. #> [CmdletBinding()] param ( [Parameter(Mandatory)] [String[]]$ComputerName, [String]$ServiceAccount = $env:USERNAME, [PSCredential]$Credential, [switch]$EnableException ) begin { $scriptblock = { function Get-ErrorMessage { [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.Management.Automation.ErrorRecord]$Record ) process { $innermessage = $Record.Exception.InnerException.InnerException.InnerException.InnerException.InnerException.Message if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.InnerException.InnerException.InnerException.Message } if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.InnerException.InnerException.Message } if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.InnerException.Message } if (-not $innermessage) { $innermessage = $Record.Exception.InnerException.Message } if (-not $innermessage) { $innermessage = $Record.Exception.Message } return $innermessage } } if ($PSBoundParameters.Credential) { $executedasuser = $Credential.UserName } else { $executedasuser = "$env:USERDOMAIN\$env:USERNAME" } $ServiceAccount = $args if ((Invoke-Command -ScriptBlock { net.exe localgroup administrators } | Out-String | Where-Object { $psitem -match "$ServiceAccount" })) { $isadmin = $true } else { $isadmin = $false } [PSCustomObject]@{ ComputerName = $env:COMPUTERNAME.ToUpper() ExecutedAsUser = $executedasuser Name = "$ServiceAccount is local administrator" Value = $isadmin Errors = "None" Compliant = $isadmin } try { $WMIService = Get-Service -Name Winmgmt -ErrorAction Stop $stoperror = "None" } catch { $stoperror = Get-ErrorMessage -Record $_ } [PSCustomObject]@{ ComputerName = $env:COMPUTERNAME.ToUpper() ExecutedAsUser = $executedasuser Name = 'WMI Service Startup' Value = $WMIService.StartType Errors = $stoperror Compliant = $WMIService.StartType -eq 'Automatic' } [PSCustomObject]@{ ComputerName = $env:COMPUTERNAME.ToUpper() ExecutedAsUser = $executedasuser Name = 'WMI Service Status' Value = $WMIService.Status Errors = $stoperror Compliant = $WMIService.Status -eq 'Running' } try { $RemoteRegService = Get-Service -Name RemoteRegistry -ErrorAction Stop $stoperror = "None" } catch { $stoperror = Get-ErrorMessage -Record $_ } [PSCustomObject]@{ ComputerName = $env:COMPUTERNAME.ToUpper() ExecutedAsUser = $executedasuser Name = 'Remote Registry Startup' Value = $RemoteRegService.StartType Errors = $stoperror Compliant = $RemoteRegService.StartType -eq 'Automatic' } [PSCustomObject]@{ ComputerName = $env:COMPUTERNAME.ToUpper() ExecutedAsUser = $executedasuser Name = 'Remote Registry Status' Value = $RemoteRegService.Status Errors = $stoperror Compliant = $RemoteRegService.Status -eq 'Running' } try { # shhh, we decided on WmiObject over CimInstance because it is more reliable $share = Get-WmiObject -Class Win32_Share -ErrorAction Stop $stoperror = "None" } catch { $stoperror = Get-ErrorMessage -Record $_ } $RemoteAdmin = $share | Where-Object Description -eq "Remote Admin" $RemoteIPC = $share | Where-Object Description -eq "Remote IPC" $DefaultShareC = $share | Where-Object { $_.Description -eq "Default Share" -and $_.Name -like "C*" } [PSCustomObject]@{ ComputerName = $env:COMPUTERNAME.ToUpper() ExecutedAsUser = $executedasuser Name = 'Remote Admin Share Exists' Value = (-not (-not $RemoteAdmin)) # LOL Errors = $stoperror Compliant = (-not (-not $RemoteAdmin)) -eq $true } [PSCustomObject]@{ ComputerName = $env:COMPUTERNAME.ToUpper() ExecutedAsUser = $executedasuser Name = 'Remote IPC Exists' Value = (-not (-not $RemoteIPC)) Errors = $stoperror Compliant = (-not (-not $RemoteIPC)) -eq $true } [PSCustomObject]@{ ComputerName = $env:COMPUTERNAME.ToUpper() ExecutedAsUser = $executedasuser Name = 'Default Share C$ Exists' Value = (-not (-not $DefaultShareC)) Errors = $stoperror Compliant = (-not (-not $DefaultShareC)) -eq $true } } if ($PSBoundParameters.Credential) { $executedasuser = $Credential.UserName } else { $executedasuser = "$env:USERDOMAIN\$env:USERNAME" } } process { foreach ($Computer in $ComputerName) { $stepCounter = 0 if ($Computer -as [ipaddress]) { Write-Message -Level Warning -Message "An IP address was specified instead of a hostname. Attempting to resolve hostname, this may be less accurate" $resolved = (Resolve-NetworkName -Computer $Computer -ErrorAction Ignore).ComputerName if ($resolved -as [ipaddress]) { Stop-PSFFunction -EnableException:$EnableException -Message "Unable to resolve $computer to a hostname. Please use the network name instead. You can potentially find the network name by using ping -a $computer" -Continue } else { $Computer = $resolved } } $splat = @{ ComputerName = $Computer Credential = $Credential } Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Connecting to $Computer and performing tests" Invoke-Command2 @splat -ScriptBlock $scriptblock -ArgumentList $ServiceAccount | Select-Object -Property ComputerName, ExecutedAsUser, Name, Value, Errors, Compliant try { $Port139 = Test-NetConnection -ComputerName $computer -Port 139 -ErrorAction Stop $stoperror = "None" } catch { $stoperror = Get-ErrorMessage -Record $_ } Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Testing port 139 on $Computer" [PSCustomObject]@{ ComputerName = $Computer.ToUpper() ExecutedAsUser = "$env:USERDOMAIN\$env:USERNAME" Name = 'Port 139 Accessible' Value = $Port139.TcpTestSucceeded Errors = $stoperror Compliant = $Port139.TcpTestSucceeded -eq $true } try { $Port445 = Test-NetConnection -ComputerName $computer -Port 445 -ErrorAction Stop $stoperror = "None" } catch { $stoperror = Get-ErrorMessage -Record $_ } Write-ProgressHelper -StepNumber ($stepCounter++) -Message "Testing port 445 on $Computer" [PSCustomObject]@{ ComputerName = $Computer.ToUpper() ExecutedAsUser = "$env:USERDOMAIN\$env:USERNAME" Name = 'Port 445 Accessible' Value = $Port445.TcpTestSucceeded Errors = $stoperror Compliant = $Port445.TcpTestSucceeded -eq $true } } } } |