PowerShell: Check RPC Services on Remote Windows Machines

In order for RPC to work at the remote server (SERVER9000), these checks must pass:

1. The display name "Remote Procedure Call (RPC)" must be running. This also has the service name of "RpcSs" and its path to execute is "C:\Windows\system32\svchost.exe -k rpcss". Its dependencies are DCOM and RPC Endpoint Mapper.
2. Inbound port TCP-135 must be allowed (in Windows firewall, endpoint firewall, and network firewalls)
3. Outbound random ports ranging from 1022-5000 (Windows 2003) and 49152-65535 (Windows 2008 and newer) must also be permitted
4. Inbound port TCP-445 for SMB (RPC dependency) must be open

Here are some tools to test:
1. Systernals:
portqry -n SERVER9000 -e 135
portqry -n SERVER9000 -e 445

2. PowerShell v3 or higher
Test-NetConnection -ComputerName $server -port 135 #This only tests 1 item

3. PowerShell older versions
# Note: The proper function is to issue Test-Connection or Test-NetConnection commands against the target server, retrieve the target's responded dynamic ports, re-test those ports, etc. The Test-Path command is a short cut to validate whether SMB is functional and the RPC's dependency (the admin$ share) is present.
$servers="SERVER9000"
$servers | foreach {
$testRPC=Test-Path "\\$_\admin$"
"$_` RPC Accessible: $testRPC"
}

4. Telnet. Must manually test dynamic ports.
telnet SERVER9000:135


Peripheral Information:

Administrators can configure RPC to use a narrow list of dynamic port range with these commands:
netsh int ipv4 set dynamicport tcp start=10000 num=10200
netsh int ipv4 set dynamicport udp start=10000 num=10200
netsh int ipv6 set dynamicport tcp start=10000 num=10200
netsh int ipv6 set dynamicport udp start=10000 num=10200
or
reg add HKLM\SOFTWARE\Microsoft\Rpc\Internet /v Ports /t REG_MULTI_SZ /f /d 10000-10200
reg add HKLM\SOFTWARE\Microsoft\Rpc\Internet /v PortsInternetAvailable /t REG_SZ /f /d Y
reg add HKLM\SOFTWARE\Microsoft\Rpc\Internet /v UseInternetPorts /t REG_SZ /f /d Y
<# Test-RPC-Service-Ports.ps1
This PowerShell script will test the connectivity of inbound default port 135 to the Destination server as well as the outbound dynamic ports being used by that specific server for RPC communication.
There are 3 functions and 1 workflow nested inside the 3rd function
1. checkInternetAccess: checks if proxy is configured; if so, ensure that the current PS session uses the correct proxy server. Afterward, try to test connectivity to download.microsoft.com.
2. importPortQry: if PortQry.exe doesn't exist in the system, install it.
3. testRPC (with testPort workflow sub-function nested inside)
#>

function checkInternetAccess{
# Change this value to reflect your proxy node
$proxy="http://proxy:80";

# Check Proxy and configure when necessary
$proxyEnabled=(Get-ItemProperty -Path "Registry::HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings").ProxyEnable
if ($proxyEnabled){
#"Proxy Enabled: $proxyEnabled"
$PSDefaultParameterValues = @{"*:Proxy"="$proxy";}
#"Setting proxy toward: $proxy"
}
$externalNetConnection=Test-NetConnection -Computername download.microsoft.com -Port 443 -InformationLevel Quiet
if ($externalNetConnection){return $true;}
else {return $false;}
}

function importPortQry{
# This function is currently not good enough because I have not yet figured out how to extract PortQryV2.exe with pure command-line, no GUI, and no confirmation

# Change these values to reflect your desired downloads
[string]$fileSource = "https://download.microsoft.com/download/0/d/9/0d9d81cf-4ef2-4aa5-8cea-95a935ee09c9/PortQryV2.exe";
$fileName="PortQry.exe"
[string]$saveAs = "C:\Temp\$fileName";
$destination="C:\WINDOWS\System32\SysInternals\";

$fileExists=Test-Path "$destination$fileName" -PathType Leaf
if (!($fileExists)){
if (!(checkInternetAccess)){
"No Internet access detected as required to download PortQry.exe. Program will now terminate.";
"Please manually copy that file into the 'C:\Windows\System32' location and re-run this program.";
exit;
}

# Check for the BitsTransfer module and import when necessary
if(!(Get-Module BitsTransfer)){
"Importing BitsTransfer Module...";
Import-Module BitsTransfer;
sleep 10;
}

$download=Start-BitsTransfer -Source $fileSource -Destination $saveAs -Asynchronous;

While( ($download.JobState.ToString() -eq 'Transferring') -or ($download.JobState.ToString() -eq 'Connecting') )
{
$percent = [int](($download.BytesTransferred*100) / $download.BytesTotal)
Write-Progress -Activity "Downloading..." -CurrentOperation "$percent`% complete"
}
Complete-BitsTransfer -BitsJob $download

# Run the Install
C:\Temp\PortQry.exe /Q /T:C:\Temp

# Secured systems will not allow scripted extraction of files. Thus this manual workaround is used.
"Please click on the 'Unzip' button with this default location C:\PortQryV2`nPlease perform this extraction or this process will repeats until those files exist. Ctrl C to stop the loop."
Pause;

}
else{"portqry.exe already exists at $saveAs.";}

$fileExtracted="C:\PortQryV2\PortQry.exe"
if ($fileExtracted){
cp C:\PortQryV2\PortQry.exe C:\WINDOWS\System32\SysInternals;
}
else {
# recurse function
"$fileExtracted is not found. Function will now repeat the download and extraction process to ensure existence of the extracted file."
importPortQry;
}
return $fileExists
}

function testRPC{
Param([string]$server)

# Default RPC port
$rpcPort = "135"

# using a workflow to test each ports in the array
workflow testPort {
# Casting parameters is better than passing arguments as that allows for switches that can be arranged in any order
param ([string[]]$RPCServer,[string]$initPort,[array]$arrRPCPorts)
$source = (hostname).toUpper()
$destination=$RPCServer.toUpper()
"`This server '$source' is able to reach the destination server '$destination' on the primary port of $initPort.`n"
"Testing RPC Dynamic Ports on $RPCServer`:`n----------------------------------------------------"

# Test all ports using multi-threaded sessions to save time
foreach -parallel ($RPCPort in $arrRPCPorts){
#Ah, the magic of inline-scripting
$testResult = InlineScript {Test-NetConnection -ComputerName $Using:RPCServer -port $Using:RPCPort -InformationLevel Quiet;}
If ($testResult){
Write-Output "$RPCPort`: reachable"
}
Else{
Write-Output "$RPCPort`: unreachable"
}
}#End foreach
} #End testPort

# Check for existence of PortQry.exe
$portQryExists=Get-Command "PortQry.exe" -ErrorAction SilentlyContinue
while (!($portQryExists)){
importPortQry;
sleep 1;
$portQryExists #refreshes the Get-Command query
}

"PortQry function is avalailable in this system. Program now proceeds using that method...`n";
$strInvokeCommand = "PortQry.exe -e $rpcPort -n $server";
$arrQuryResult = Invoke-Expression $strInvokeCommand;

$arrPorts = @(); #Initiates and clears the collection of ports array
ForEach ($strResult in $arrQuryResult)
{
If ($strResult.Contains("ip_tcp"))
{
$arrSplt = $strResult.Split("[");
$strPort = $arrSplt[1];
$strPort = $strPort.Replace("]","");
$arrPorts += $strPort;
}
}

# Remove duplicate port records from within the array
$arrPorts = $arrPorts | select -uniq

# Pass the ports array into the workflow as a parameter
testPort -RPCServer $server -initPort $rpcPort -arrRPCPorts $arrPorts
}

$remoteServer = Read-Host -Prompt 'Input the remote server name to test'
testRPC $remoteServer
Output...

PS C:\> C:\Users\kimconnect\Desktop\check-rpc.ps1
PortQry function is avalailable in this system. Program may proceed...

Testing RPC Dynamic Ports on SERVER01.KIMCONNECT.COM:
----------------------------------------------------
5722: reachable
49159: reachable
49234: reachable
49155: reachable
49242: reachable
49240: reachable
49153: reachable
49154: reachable
49152: reachable

Leave a Reply

Your email address will not be published. Required fields are marked *