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. ConnArtist 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: Log active TCP connections along with the responsible processes. Optionally capture DNS queries made by the endpoint. Filter out local (private) IP traffic for concise logs. Continuously monitor and prevent redundant logging through smart duplicate detection. Log active TCP connections along with the responsible processes. Optionally capture DNS queries made by the endpoint. Filter out local (private) IP traffic for concise logs. Continuously monitor and prevent redundant logging through smart duplicate detection. How Does ConnArtist Work? Here's how it operates: Initialization The script prompts two straightforward questions: Whether to filter local (private IP) traffic. Whether to capture DNS queries. Whether to filter local (private IP) traffic. Whether to capture DNS queries. Based on your responses, ConnArtist adjusts its monitoring appropriately. Active Monitoring ConnArtist continuously: Logs active TCP connections, including detailed metadata (time, process, remote address, remote host). Optionally logs DNS queries, capturing query names and timestamps. Avoids duplicate entries by maintaining internal tracking. Logs active TCP connections, including detailed metadata (time, process, remote address, remote host). Optionally logs DNS queries, capturing query names and timestamps. Avoids duplicate entries by maintaining internal tracking. Clear Logging Data captured is neatly formatted and stored locally: TCP connections logged to tcp_log.txt. DNS queries stored in dns_log.txt. TCP connections logged to tcp_log.txt. tcp_log.txt DNS queries stored in dns_log.txt. dns_log.txt Implementing ConnArtist Step 1: Preparation Save the provided PowerShell script as ConnArtist.ps1. Ensure you have permissions to write logs (C:\Users\Public\). Save the provided PowerShell script as ConnArtist.ps1. ConnArtist.ps1 Ensure you have permissions to write logs (C:\Users\Public\). C:\Users\Public\ Step 2: Execution Run PowerShell as Administrator. Execute the script: powershell.exe -ExecutionPolicy Bypass -File "C:\Path\To\ConnArtist.ps1" Answer prompts regarding local traffic filtering and DNS capture based on your use-case. Run PowerShell as Administrator. Run PowerShell as Administrator. Execute the script: powershell.exe -ExecutionPolicy Bypass -File "C:\Path\To\ConnArtist.ps1" Execute the script: powershell.exe -ExecutionPolicy Bypass -File "C:\Path\To\ConnArtist.ps1" powershell.exe -ExecutionPolicy Bypass -File "C:\Path\To\ConnArtist.ps1" Answer prompts regarding local traffic filtering and DNS capture based on your use-case. Answer prompts regarding local traffic filtering and DNS capture based on your use-case. Step 3: Continuous Monitoring ConnArtist monitors the network every 5 seconds, providing real-time insights. Press Ctrl+C to stop monitoring at any time. ConnArtist monitors the network every 5 seconds, providing real-time insights. Press Ctrl+C to stop monitoring at any time. Ctrl+C Example Use Cases Security Checks: Quickly identify unexpected external connections that could indicate malware or unauthorized activity. Diagnostic Tasks: Troubleshoot connectivity issues by pinpointing problematic connections or DNS resolutions. Security Checks: Quickly identify unexpected external connections that could indicate malware or unauthorized activity. Security Checks: Diagnostic Tasks: Troubleshoot connectivity issues by pinpointing problematic connections or DNS resolutions. Diagnostic Tasks: 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 $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 } } 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? Lightweight: No installations or complex setups required. Clear Insights: Straightforward, easily readable logs. Focused: Designed for pinpoint checks on single endpoints. Real-time Monitoring: Quick detection of unusual activities or connection issues. Lightweight: No installations or complex setups required. Lightweight: Clear Insights: Straightforward, easily readable logs. Clear Insights: Focused: Designed for pinpoint checks on single endpoints. Focused: Real-time Monitoring: Quick detection of unusual activities or connection issues. Real-time Monitoring: 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 } #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 }