From Promiscuous to Port Scanning with Powershell

powershell1

It’s been a while since my last post regarding Powershell which showed how to scan hosts for network interfaces in promiscuous mode. This time around, we’ll scan for some well known ports in our Active Directory to see who has a local IIS or SQL Express running on their machine. I know what you’re thinking. A port scanner? I already use trusty old NMAP or Superscan. This is not about port scanning, its about Powershell. In the last post, we used the MSNdis_CurrentPackFilter class of WMI to find any network cards in promiscuous mode, using Active Directory computer objects as our targets. Once again, I keep the AD query of computer objects as a way to source target hosts, but the scripts can easily be modified to take in a hosts file or an IP range. For making connections, let’s look into the System.Net.Sockets class.

When I started looking into a method I can use to establish a connection to a given port in order to check if it was open, I went with a Connect method using the System.Net.Sockets.Socket class. This isn’t what I ended up using in the finished script, but I want to mention this class, because it can be used to send data to a connected socket, or to receive data on a listening socket (there is a listen method as well). Perhaps a more detailed post will materialize on those items, but I haven’t thought of a reason to use them yet. Maybe we can convert Gaffie’s Python code that crashes Windows 7 into a Powershell script one day.

Why didn’t I end up using the socket.connect method? The timeout was too long and I lost patience fiddling with the ConnectAsync method. If interested, here is the code for the socket.connect:

$computer = “test”
$ipport = [int]80
$comp = [Net.Dns]::GetHostEntry($computer)
foreach ($ip in $comp.AddressList) {
   $ep = New-Object System.Net.IPEndPoint($ip, $ipport)
   $socket = New-Object System.Net.Sockets.Socket([System.Net.Sockets.AddressFamily]::InterNetwork,
                                                  [System.Net.Sockets.SocketType]::Stream,
                                                  [System.Net.Sockets.ProtocolType]::Tcp)
   $optlevel = [System.Net.Sockets.SocketOptionLevel]::"Socket"
   $optname = [System.Net.Sockets.SocketOptionName]::"SendTimeout"
   $timeout = [Int]100
   $socket.SetSocketOption($optlevel,$optname,$timeout)
   $socket.Connect($ep)
   $socket.Close()

I’ve left out the AD code and the extra code that’s in the port scan script to show just the use of socket.connect. The workstation in the script is “test” and we’re trying to connect to port 80. If the host is using a firewall that will actively refuse the connection, we get a quick response which would be fine for a port scanner; but, if the port is simply not listening and there is no firewall to actively refuse the connection, there is a pretty significant delay before getting the connection error. This delay is not suitable for port scanning.

Instead of System.Net.Sockets.Socket, we will use the System.Net.Sockets.TcpClient class with the BeginConnect Method in conjunction with a timeout (if it doesn’t complete in a given time, we assume the connection is not available). I’ve seen examples of this used on poshcode.org for testing a connection to port 135 prior to making WMI calls or other RPC calls.

$HostEntry = [Net.Dns]::GetHostEntry($CompName)
foreach ($ip in $HostEntry.AddressList) {
   Write-Host "Checking: $CompName on $ip"
   foreach ($tcpport in $PortList) {
      $TCPclient = new-Object system.Net.Sockets.TcpClient
      $Connection = $TCPclient.BeginConnect($ip,$tcpport,$null,$null)
      $TimeOut = $Connection.AsyncWaitHandle.WaitOne(3000,$false)  ## 3 second timeout can be modified
    if(!$TimeOut)   {
       $TCPclient.Close()
       Write-Host "     OK: Port $tcpport is closed."
       }
    else {
       try {
          $TCPclient.EndConnect($Connection) | out-Null
          $TCPclient.Close()
          ## Next line outputs that the port is closed. I prefer to see output 
          ## processing; comment for outputting only open ports.
          Write-Host "     " -nonewline
          Write-Host "Host: $CompName has port $tcpport open!" -foregroundcolor red -backgroundcolor yellow
          } 
       catch {
          ## Machine actively refused the connection. The port is not open but $TimeOut was still true
          ## Uncomment next line to output the error for this.
          ## write-host $_
         write-host "     OK: Port $tcpport is closed."
       }

You can see by the code snippet above that we are using Sockets.TcpClient rather than Sockets.Socket, and the method used is BeginConnect rather than Connect as in the previous example. If the connection is not available in 3 seconds, the timeout is expired and the script reports the port is closed. In some cases, Windows firewall will refuse the connection while the timeout is not expired. I found in these cases, when attempting to close the connection, an error reported that the connection was refused and was never connected to begin with. This is where the TRY / CATCH comes in, as we check that we can cleanly close the open connection, which will then report that the port is open.

You can find the full script here, and while by no means is this to replace your standard port scanner, it can provide a quick way to scan your AD hosts for open ports that you specify or get you digging further into Sockets.Socket which can lead to many places.

Note: the script requires a parameter which is comma separated list of ports.

Example: ad-portscan.ps1 23,80,443,1433


Related Posts:




Filed Under: Security

Tags: , , , , , , ,

Leave a Reply




If you want a picture to show with your comment, go get a Gravatar.