In today's digital landscape, quickly understanding what's happening behind the scenes on an individual machine can be crucial. Introducing ConnArtist, a streamlined, intuitive PowerShell script designed specifically for monitoring network connections on a single endpoint—ideal for security checks or diagnostic tasks without needing heavyweight tools like Wireshark.

What is ConnArtist?

ConnArtist is a simple yet powerful PowerShell-based network monitoring tool built specifically for single endpoints. Perfect for quick checks by security analysts, sysadmins, or tech support, ConnArtist helps you swiftly identify unexpected or problematic network activity.

With ConnArtist, you can:

How Does ConnArtist Work?

Here's how it operates:

Initialization

The script prompts two straightforward questions:

Based on your responses, ConnArtist adjusts its monitoring appropriately.

Active Monitoring

ConnArtist continuously:

Clear Logging

Data captured is neatly formatted and stored locally:

Implementing ConnArtist

Step 1: Preparation

Step 2: Execution

Step 3: Continuous Monitoring

Example Use Cases

Customizing ConnArtist

Adjust log locations or intervals as needed:

$tcpLogFile = "C:\Users\Public\tcp_log.txt"
$dnsLogFile = "C:\Users\Public\dns_log.txt"

# Modify monitoring interval as desired
Start-Sleep -Seconds 5

Adjusting Local Network Filtering

For accurate filtering, you might need to customize your local network's private IP ranges. Modify the following function to match your network's octets:

function IsPrivateIP($ipAddress) {
    try {
        $ip = [System.Net.IPAddress]::Parse($ipAddress)
        if ($ip.AddressFamily -eq 'InterNetwork') { # IPv4
            return ($ipAddress -like '10.*' -or
                    $ipAddress -like '172.16.*' -or
                    $ipAddress -like '192.168.*') # Modify these octets to your local network range
        } elseif ($ip.AddressFamily -eq 'InterNetworkV6') { # IPv6
            return ($ip.IsIPv6LinkLocal -or $ip.IsIPv6SiteLocal)
        } else {
            return $false
        }
    } catch {
        return $false
    }
}

Why Use ConnArtist?


ConnArtist simplifies endpoint network visibility, allowing quick identification of potential security concerns or troubleshooting network issues swiftly and effectively.

Happy Monitoring!

#Another        /\_[]_/\
#    fine      |] _||_ [|
#       ___     \/ || \/
#      /___\       ||
#     (|0 0|)      ||
#   __/{\U/}\_ ___/vvv
#  / \  {~}   / _|_P|
#  | /\  ~   /_/   []
#  |_| (____)        
#  \_]/______\  Barberion  
#     _\_||_/_     Production      
#    (_,_||_,_)
#
# Define log file paths for TCP and DNS logs
$tcpLogFile = "C:\Users\Public\tcp_log.txt"
$dnsLogFile = "C:\Users\Public\dns_log.txt"

# Function to check if an IP address is private
function IsPrivateIP($ipAddress) {
    try {
        $ip = [System.Net.IPAddress]::Parse($ipAddress)
        if ($ip.AddressFamily -eq 'InterNetwork') { # IPv4
            return ($ipAddress -like '10.*' -or
                    $ipAddress -like '172.16.*' -or
                    $ipAddress -like '192.168.*')
        } elseif ($ip.AddressFamily -eq 'InterNetworkV6') { # IPv6
            return ($ip.IsIPv6LinkLocal -or $ip.IsIPv6SiteLocal)
        } else {
            return $false
        }
    } catch {
        return $false
    }
}

# Ask the user if they want to filter out local traffic
$filterPrivateIPs = (Read-Host "Do you want to filter out local traffic? (yes/no)").Trim().ToLower()
$filterPrivate = $filterPrivateIPs -eq 'yes' -or $filterPrivateIPs -eq 'y'

# Ask the user if they want to capture DNS requests
$dnsCaptureInput = (Read-Host "Do you want to capture DNS requests? (yes/no)").Trim().ToLower()
$captureDNS = $dnsCaptureInput -eq 'yes' -or $dnsCaptureInput -eq 'y'

# Output the monitoring message at the top
Write-Host "Monitoring network connections. Press Ctrl+C to stop."
Add-Content -Path $tcpLogFile -Value "Monitoring TCP connections. Press Ctrl+C to stop."
Add-Content -Path $dnsLogFile -Value "Monitoring DNS connections. Press Ctrl+C to stop."

# Define the headers
$tcpHeader = "{0,-20} {1,-20} {2,-25} {3}" -f "Date/Time", "Process", "Remote Address", "Remote Host"
$dnsHeader = "{0,-20} {1,-30}" -f "Date/Time", "DNS Query"

Write-Host $tcpHeader
Write-Host ("-" * 90)
Add-Content -Path $tcpLogFile -Value $tcpHeader
Add-Content -Path $tcpLogFile -Value ("-" * 90)

if ($captureDNS) {
    $dnsLogName = "Microsoft-Windows-DNS-Client/Operational"
    if (-not (Get-WinEvent -ListLog $dnsLogName).IsEnabled) {
        Write-Host "Enabling DNS client operational log..."
        try {
            wevtutil sl $dnsLogName /e:true
        } catch {
            Write-Host "Failed to enable DNS client operational log. You may need to run PowerShell as Administrator."
            $captureDNS = $false
        }
    }
    if ($captureDNS) {
        # Initialize the last DNS check time
        $lastDNSCheckTime = Get-Date
        # Output the DNS header
        Write-Host $dnsHeader
        Write-Host ("-" * 50)
        Add-Content -Path $dnsLogFile -Value $dnsHeader
        Add-Content -Path $dnsLogFile -Value ("-" * 50)
    }
}

# To prevent duplication, maintain hashsets of logged connections and DNS queries
$loggedConnections = @{}
$loggedDNSQueries = @{}

while ($true) {
    # Get current network connections
    $currentConnections = Get-NetTCPConnection -State Established

    if ($filterPrivate) {
        $currentConnections = $currentConnections | Where-Object {
            $_.RemoteAddress -ne '127.0.0.1' -and
            $_.RemoteAddress -ne '::1'
        }
    }

    foreach ($conn in $currentConnections) {
        $connectionKey = "$($conn.OwningProcess)|$($conn.RemoteAddress):$($conn.RemotePort)"
        if (-not $loggedConnections.ContainsKey($connectionKey)) {
            if (-not $filterPrivate -or (-not (IsPrivateIP $conn.RemoteAddress))) {
                $dateTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
                $process = Get-Process -Id $conn.OwningProcess -ErrorAction SilentlyContinue
                $processName = if ($process) { $process.ProcessName } else { 'N/A' }
                $remoteHost = $conn.RemoteAddress
                try {
                    $dnsEntry = [System.Net.Dns]::GetHostEntry($conn.RemoteAddress)
                    $remoteHost = $dnsEntry.HostName
                } catch {
                    # Could not resolve host
                }
                $remoteAddressPort = "$($conn.RemoteAddress):$($conn.RemotePort)"
                $logEntry = "TCP {0,-20} {1,-20} {2,-25} {3}" -f $dateTime, $processName, $remoteAddressPort, $remoteHost
                Add-Content -Path $tcpLogFile -Value $logEntry
                Write-Host $logEntry
                # Add the connection to the loggedConnections hashset to prevent future duplicates
                $loggedConnections[$connectionKey] = $true
            }
        }
    }

    if ($captureDNS) {
        try {
            # Get new DNS query events
            $dnsEvents = Get-WinEvent -FilterHashtable @{
                LogName = 'Microsoft-Windows-DNS-Client/Operational';
                Id = 3008;
                StartTime = $lastDNSCheckTime
            } -ErrorAction SilentlyContinue
            
            if ($dnsEvents) {
                foreach ($event in $dnsEvents) {
                    $dateTime = $event.TimeCreated.ToString('yyyy-MM-dd HH:mm:ss')
                    $queryName = $event.Properties[0].Value
                    if (-not $loggedDNSQueries.ContainsKey($queryName)) {
                        $dnsEntry = "DNS {0,-20} {1,-30}" -f $dateTime, $queryName
                        Add-Content -Path $dnsLogFile -Value $dnsEntry
                        Write-Host $dnsEntry
                        # Add the DNS query to the loggedDNSQueries hashset to prevent future duplicates
                        $loggedDNSQueries[$queryName] = $true
                    }
                }
                # Update the last DNS check time
                $lastDNSCheckTime = Get-Date
            }
        } catch {
            # Suppress any errors related to DNS event fetching
        }
    }

    Start-Sleep -Seconds 5
}