How to tail netstat and pass results to IP intelligence function

Quick question… let’s say I want to plug this script into a constant running netstat to scan IPs targeting a specific port. Anyone know how to basically tail netstat and constantly be passing new IP connections to a particular script?

#Just replace all of "GET-YOUR-OWN" with relevant API keys
###This is for running jobs in-process [[[START11 ; look for END11]]]
$code = @'
using System;
using System.Collections.Generic;
using System.Text;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
namespace InProcess
{
    public class InMemoryJob : System.Management.Automation.Job
    {
        public InMemoryJob(PowerShell PowerShell, string name)
        {
            _PowerShell = PowerShell;
            SetUpStreams(name);
        }
        private void SetUpStreams(string name)
        {
            _PowerShell.Streams.Verbose = this.Verbose;
            _PowerShell.Streams.Error = this.Error;
            _PowerShell.Streams.Debug = this.Debug;
            _PowerShell.Streams.Warning = this.Warning;
            _PowerShell.Streams.Information = this.Information;
            _PowerShell.Runspace.AvailabilityChanged += new EventHandler<RunspaceAvailabilityEventArgs>(Runspace_AvailabilityChanged);
            int id = System.Threading.Interlocked.Add(ref InMemoryJobNumber, 1);
            if (!string.IsNullOrEmpty(name))
            {
                this.Name = name;
            }
            else
            {
                this.Name = "InProcessJob" + id;
            }
        }
        void Runspace_AvailabilityChanged(object sender, RunspaceAvailabilityEventArgs e)
        {
            if (e.RunspaceAvailability == RunspaceAvailability.Available)
            {
                this.SetJobState(JobState.Completed);
            }
        }
        PowerShell _PowerShell;
        static int InMemoryJobNumber = 0;
        public override bool HasMoreData
        {
            get {
                return (Output.Count > 0);
            }
        }
        public override string Location
        {
            get { return "In Process"; }
        }
        public override string StatusMessage
        {
            get { return "A new status message"; }
        }
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (!isDisposed)
                {
                    isDisposed = true;
                    try
                    {
                        if (!IsFinishedState(JobStateInfo.State))
                        {
                            StopJob();
                        }
                        foreach (Job job in ChildJobs)
                        {
                            job.Dispose();
                        }
                    }
                    finally
                    {
                        base.Dispose(disposing);
                    }
                }
            }
        }
        private bool isDisposed = false;
        internal bool IsFinishedState(JobState state)
        {
            return (state == JobState.Completed || state == JobState.Failed || state == JobState.Stopped);
        }
        public override void StopJob()
        {
            _PowerShell.Stop();
            _PowerShell.EndInvoke(_asyncResult);
            SetJobState(JobState.Stopped);
        }
        public void Start()
        {
            _asyncResult = _PowerShell.BeginInvoke<PSObject, PSObject>(null, Output);
            SetJobState(JobState.Running);
        }
        IAsyncResult _asyncResult;
        public void WaitJob()
        {
            _asyncResult.AsyncWaitHandle.WaitOne();
        }
        public void WaitJob(TimeSpan timeout)
        {
            _asyncResult.AsyncWaitHandle.WaitOne(timeout);
        }
    }
}
'@

Add-Type -TypeDefinition $code

function Start-JobInProcess
{
    [CmdletBinding()]
    param
    (
        [scriptblock] $ScriptBlock,
        $ArgumentList,
        [string] $Name
    )
    function Get-JobRepository
    {
        [cmdletbinding()]
        param()
        $pscmdlet.JobRepository
    }
    function Add-Job
    {
        [cmdletbinding()]
        param
        (
            $job
        )
        $pscmdlet.JobRepository.Add($job)
    }

    $PowerShell = [PowerShell]::Create().AddScript($ScriptBlock)

    if ($ArgumentList)
    {
        $ArgumentList | ForEach-Object {
            $PowerShell.AddArgument($_)
        }
    }

    $MemoryJob = New-Object InProcess.InMemoryJob $PowerShell, $Name

    $MemoryJob.Start()
    Add-Job $MemoryJob
    $MemoryJob
}
###This is for running jobs in-process [[[END11 ; look for START11]]]


#This is going to remove any completed jobs just because
Remove-Job -State Completed | Out-Null

#Was checking jobs for debugging
#Get-Job

#Clean console
Clear-Host

#Ask for IPv4 address e.g. 8.8.8.8
Write-Host -NoNewline "IPv4 address: "

#Read input, IP address
$global:remoteIp = Read-Host

#Take the progress-bar out of invoked webreqs
$ProgressPreference = 'SilentlyContinue'
Start-JobInProcess -Name ($global:remoteIp + "ipinfo") -ArgumentList $global:remoteIp {(Invoke-WebRequest -Uri ("https://ipinfo.io/" + $args[0] + "?token=GET-YOUR-OWN")).content}

#This goes in -Body
#$json = @"{"alert_policy":[{"enabled":"true"}]"@

#############################
#############################
#AbuseIPDB as a source below
#############################
#############################
$ProgressPreference = 'SilentlyContinue'
Start-JobInProcess -Name ($global:remoteIp + "abuse") -ArgumentList $global:remoteIp {(Invoke-RestMethod -Uri "https://api.abuseipdb.com/api/v2/check" -Body @{"ipAddress" = $args[0]} -ContentType "application/json" -Headers @{"Key" = "GET-YOUR-OWN"; "Accept" = "application/json"} -Method Get -UseBasicParsing)}

#############################
#############################
#Auth0 as a source below
#############################
#############################

$ProgressPreference = 'SilentlyContinue'
Start-JobInProcess -Name ($global:remoteIp + "auth")  -ArgumentList $global:remoteIp {(Invoke-RestMethod -Uri ("https://signals.api.auth0.com/v2.0/ip/" + $args[0]) -ContentType "application/json" -Headers @{"x-auth-token" = "GET-YOUR-OWN"; "accept" = "application/json"} -Method Get -UseBasicParsing)}

Get-Job -Name ($global:remoteIp + "auth") | Wait-Job
Get-Job -Name ($global:remoteIp + "abuse") | Wait-Job
Get-Job -Name ($global:remoteIp + "ipinfo") | Wait-Job

$authData = Get-Job -Name ($global:remoteIp + "auth") | Select Id
$abuseData = Get-Job -Name ($global:remoteIp + "abuse") | Select Id
$ipData =  Get-Job -Name ($global:remoteIp + "ipinfo") | Select Id

$authParse = Receive-Job -Id $authData.Id
$abuseParse = Receive-Job -Id $abuseData.Id
$ipParse = Receive-Job -Id $ipData.Id

Write-Host "Auth0 score - low bad: "$authParse.fullip.score
Write-Host "AbuseIPDB score - high bad: "$abuseParse.data.abuseConfidenceScore

#Figure out willing risk IP, do what you want

Hmm, so with no reply yet I am going to stab in the dark at my own question:

netstat -a | grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}"

Could be written out to a file (in a loop), as the file modification is completed some form of SQLite database is checked against ref https://social.technet.microsoft.com/wiki/contents/articles/30562.powershell-accessing-sqlite-databases.aspx

While you are PowerShelling and adding the IPs to either a whitelist or blacklist in the SQLite - we are not going to waste API token calls.

Essentially this is a great case for SQLite in my opinion, to handle IP lists and then we could properly call Windows’ in-built firewall functionality to block the connections.

Hmm… neat, now where to buy a lotta friggin’ coffee… I don’t like the idea of all that I/O for writing the file though.

Surely someone here knows a better way to throw that grep IP memory list against the SQLite perhaps? Meh, idk… if anyone has ideas on better direction pls lmkkk < 3 ily

Okay so after brainstorming more:

  1. Call the netstat and grep out all the IPs
  2. For each item, check against SQLite whitelist and blacklist for the IPs
  3. Kick off all the scans in some sorta queue / jobs list
  4. Keep the netstat generation going and queueing up the scan list
  5. Firewall off any blacklist IPs

Hmm, seems legit - now to go from pseudocode to beardspeak

Why can’t you use Get-NetTcpConnection? It would return objects instead of text that has to be parsed.

$Baseline = Get-NetTCPConnection
While ($true) {
    $current = Get-NetTCPConnection
    #just checking new remote addresses but modify to look for whatever you want
    $deltas = $current | Where-Object { $_.RemoteAddress -notin $Baseline.RemoteAddress } 
    #call your script and pass the relevant arguments here if you have any deltas....

    Start-Sleep -Seconds 10 #or whatever cycle you want
} # while loop

 

 

That would work, assuming you also grab the UDP ports - not just TCP

If you need UDP as well, you’ll probably need to use netstat. I through together a quick PowerShell wrapper for netstat -ano. This should get you what you need. Just call Get-Netstat and it will return objects with the needed properties.

 

function fixaddr ($addr){
    $data = @{}
    if ($addr -match "\[.*") {
        $data.address = ($addr -split "]:")[0] -replace "\[",""
        $data.port    = ($addr -split "]:")[1]
    } #if ipv6
    else {
        $data.address = ($addr -split ":")[0]
        $data.port    = ($addr -split ":")[1]
    } #else ipv4
    [pscustomobject]$data
} #fixaddr

function Get-Netstat {
    $raw = (netstat -ano) | Select-Object -Skip 4
   
    foreach ($con in $raw) {
        $data = @{}
        $cols = -split $con
        $local = fixaddr -addr $cols[1]
        $remote = fixaddr -addr $cols[2]
        $data.localaddr = $local.address
        $data.localport = $local.port
        $data.remoteaddr = $remote.address
        $data.remoteport = $remote.port
        if ($cols[0] -eq "TCP") {
            $data.protocol = "TCP"
            $data.state    = $cols[3]
            $data.pid      = $cols[4]
        } #if TCP
        else {
            $data.protocol = "UDP"
            $data.state    = "STATELESS"
            $data.pid      = $cols[3]
        } # else UDP  
        [pscustomobject]$data  
    } #foreach    row
} #function get-netstat

Whoa, this is exactly perfectly awesome - you are a rockstar Mike :slight_smile:

Thank you for parsing out the info from netstat.

I am going to make use of this to feed the data into an SQLite database to get some neat statistics with bytes sent/received.

Malicious scores, along with being able to sort through most noisy IPs (data, time connected, ports, etc)

A lot of potential seems available with making use of simple in-built tools

Here’s Mike’s nice code just written a bit differently.

$data = switch -Regex (netstat -ano){
    '\s+(TCP)\s+(.+):(.+)\s+(.+):(.+)\s+(\w+)\s+(\d+)' {
        [PSCustomObject]@{
            Protocol   = $matches.1
            LocalAddr  = $matches.2 -replace '\[|\]'
            LocalPort  = $matches.3
            RemoteAddr = $matches.4 -replace '\[|\]'
            RemotePort = $matches.5
            State      = $matches.6
            PID        = $matches.7
        }
    }
    '\s+(UDP)\s+(.+):(.+)\s+(.+):(.+)\s+(\d+)' {
        [PSCustomObject]@{
            Protocol   = $matches.1
            LocalAddr  = $matches.2 -replace '\[|\]'
            LocalPort  = $matches.3
            RemoteAddr = $matches.4 -replace '\[|\]'
            RemotePort = $matches.5
            State      = 'STATELESS'
            PID        = $matches.6
        }
    }
}

Mike’s is much faster.