From dcebc0f4fa8c39802956bb63f4833b4d6175b390 Mon Sep 17 00:00:00 2001 From: mnerv <24420859+mnerv@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:48:51 +0100 Subject: [PATCH] Add SSH artifacts collection (Get-SSHArtifacts.ps1) Collect SSH forensic data: known hosts with counts, SSH config, keys, authorized_keys, PuTTY sessions, and server logs. Update TODO.md with completed scripts. --- TODO.md | 18 +++ windows/Get-SSHArtifacts.ps1 | 216 +++++++++++++++++++++++++++++++++++ 2 files changed, 234 insertions(+) create mode 100644 windows/Get-SSHArtifacts.ps1 diff --git a/TODO.md b/TODO.md index 877855a..7d6de12 100644 --- a/TODO.md +++ b/TODO.md @@ -10,6 +10,9 @@ - [x] Network Connection History - [x] Hotspot Connections - [x] Recent Documents (RecentDocs) +- [x] System Information (Get-Info.ps1) +- [x] User Accounts (Get-Users.ps1) +- [x] SSH sessions and known hosts ### User Activity Artifacts - [ ] UserAssist - Programs run by user through Windows Explorer @@ -48,6 +51,21 @@ - [ ] VPN connections - [ ] Remote Desktop connections +### Developer & Security Artifacts +- [x] SSH sessions and known hosts +- [ ] Git repositories and commit history +- [ ] WSL (Windows Subsystem for Linux) artifacts +- [ ] PowerShell history (ConsoleHost_history.txt) +- [ ] Terminal/Command Prompt history +- [ ] Docker containers and images +- [ ] Virtual machines (VirtualBox, VMware, Hyper-V) +- [ ] IDE recent projects (VS Code, Visual Studio, JetBrains) +- [ ] Package manager caches (npm, pip, cargo, nuget) +- [ ] Environment variables and PATH modifications +- [ ] Installed development tools and SDKs +- [ ] Code signing certificates +- [ ] API keys and tokens in config files + ## Other Operating Systems ### Linux diff --git a/windows/Get-SSHArtifacts.ps1 b/windows/Get-SSHArtifacts.ps1 new file mode 100644 index 0000000..93664ce --- /dev/null +++ b/windows/Get-SSHArtifacts.ps1 @@ -0,0 +1,216 @@ +# Get-SSHArtifacts.ps1 +# Collects SSH session artifacts, known hosts, and configuration + +param( + [switch]$ShowKeys +) + +Write-Host "=== SSH Artifacts ===" -ForegroundColor Cyan + +# Check common SSH directory locations +$sshPaths = @( + "$env:USERPROFILE\.ssh", + "$env:PROGRAMDATA\ssh" +) + +$foundArtifacts = $false + +foreach ($sshPath in $sshPaths) { + if (Test-Path $sshPath) { + $foundArtifacts = $true + + Write-Host "`n--- SSH Directory: $sshPath ---" -ForegroundColor Yellow + + # Known hosts + $knownHostsPath = Join-Path $sshPath "known_hosts" + if (Test-Path $knownHostsPath) { + Write-Host "`nKnown Hosts:" -ForegroundColor Green + try { + $knownHosts = Get-Content $knownHostsPath -ErrorAction SilentlyContinue + $hostCounts = @{} + + foreach ($line in $knownHosts) { + if ($line -and -not $line.StartsWith("#")) { + # Extract hostname/IP (first field before space or comma) + $parts = $line -split '\s+', 2 + if ($parts[0]) { + # Handle hashed hosts and comma-separated hosts + $hostname = $parts[0] -split ',' | Select-Object -First 1 + if ($hostCounts.ContainsKey($hostname)) { + $hostCounts[$hostname]++ + } else { + $hostCounts[$hostname] = 1 + } + } + } + } + + if ($hostCounts.Count -gt 0) { + $count = 0 + foreach ($hostname in ($hostCounts.Keys | Sort-Object)) { + $count++ + $appearances = $hostCounts[$hostname] + $plural = if ($appearances -eq 1) { "entry" } else { "entries" } + Write-Host " $count. $hostname ($appearances $plural)" -ForegroundColor Cyan + } + } else { + Write-Host " No hosts found" -ForegroundColor Gray + } + } catch { + Write-Host " Error reading known_hosts: $_" -ForegroundColor Red + } + } + + # SSH Config + $configPath = Join-Path $sshPath "config" + if (Test-Path $configPath) { + Write-Host "`nSSH Config:" -ForegroundColor Green + try { + $config = Get-Content $configPath -ErrorAction SilentlyContinue + $currentHost = $null + + foreach ($line in $config) { + $trimmed = $line.Trim() + + if ($trimmed -match '^Host\s+(.+)') { + $currentHost = $matches[1] + Write-Host " Host: $currentHost" -ForegroundColor Yellow + } elseif ($currentHost -and $trimmed -match '^HostName\s+(.+)') { + Write-Host " Hostname: $($matches[1])" -ForegroundColor Cyan + } elseif ($currentHost -and $trimmed -match '^User\s+(.+)') { + Write-Host " User: $($matches[1])" -ForegroundColor Cyan + } elseif ($currentHost -and $trimmed -match '^Port\s+(.+)') { + Write-Host " Port: $($matches[1])" -ForegroundColor Cyan + } elseif ($currentHost -and $trimmed -match '^IdentityFile\s+(.+)') { + Write-Host " IdentityFile: $($matches[1])" -ForegroundColor Cyan + } + } + } catch { + Write-Host " Error reading config: $_" -ForegroundColor Red + } + } + + # SSH Keys + Write-Host "`nSSH Keys:" -ForegroundColor Green + $keyFiles = Get-ChildItem -Path $sshPath -Filter "id_*" -ErrorAction SilentlyContinue + + if ($keyFiles) { + foreach ($keyFile in $keyFiles) { + $isPrivate = $keyFile.Name -notmatch '\.pub$' + $keyType = if ($isPrivate) { "Private" } else { "Public" } + + Write-Host " [$keyType] $($keyFile.Name)" -ForegroundColor $(if ($isPrivate) { "Red" } else { "Green" }) + Write-Host " Path: $($keyFile.FullName)" -ForegroundColor Gray + Write-Host " Modified: $($keyFile.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss'))" -ForegroundColor Gray + + # Show public key content if requested + if ($ShowKeys -and -not $isPrivate) { + try { + $content = Get-Content $keyFile.FullName -ErrorAction SilentlyContinue + if ($content) { + Write-Host " Content: $content" -ForegroundColor Cyan + } + } catch { + Write-Host " Error reading key: $_" -ForegroundColor Red + } + } + } + } else { + Write-Host " No SSH keys found" -ForegroundColor Gray + } + + # Authorized keys + $authorizedKeysPath = Join-Path $sshPath "authorized_keys" + if (Test-Path $authorizedKeysPath) { + Write-Host "`nAuthorized Keys:" -ForegroundColor Green + try { + $authKeys = Get-Content $authorizedKeysPath -ErrorAction SilentlyContinue + $count = 0 + foreach ($line in $authKeys) { + if ($line -and -not $line.StartsWith("#")) { + $count++ + # Extract key type and comment + if ($line -match '^(ssh-\S+)\s+\S+\s*(.*)$') { + $keyType = $matches[1] + $comment = $matches[2] + Write-Host " $count. $keyType $(if ($comment) { "($comment)" })" -ForegroundColor Cyan + } + } + } + if ($count -eq 0) { + Write-Host " No authorized keys found" -ForegroundColor Gray + } + } catch { + Write-Host " Error reading authorized_keys: $_" -ForegroundColor Red + } + } + + # List all other files in .ssh directory + Write-Host "`nOther Files:" -ForegroundColor Green + $otherFiles = Get-ChildItem -Path $sshPath -File -ErrorAction SilentlyContinue | + Where-Object { $_.Name -notin @('known_hosts', 'config', 'authorized_keys') -and $_.Name -notmatch '^id_' } + + if ($otherFiles) { + foreach ($file in $otherFiles) { + Write-Host " $($file.Name) - Modified: $($file.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss'))" -ForegroundColor Cyan + } + } else { + Write-Host " No other files" -ForegroundColor Gray + } + } +} + +# Check for PuTTY sessions (Windows alternative to OpenSSH) +Write-Host "`n--- PuTTY Sessions ---" -ForegroundColor Yellow +try { + $puttyPath = "HKCU:\Software\SimonTatham\PuTTY\Sessions" + + if (Test-Path $puttyPath) { + $sessions = Get-ChildItem -Path $puttyPath -ErrorAction SilentlyContinue + + if ($sessions) { + foreach ($session in $sessions) { + $sessionName = [System.Web.HttpUtility]::UrlDecode($session.PSChildName) + $props = Get-ItemProperty -Path $session.PSPath -ErrorAction SilentlyContinue + + Write-Host "`nSession: $sessionName" -ForegroundColor Green + if ($props.HostName) { Write-Host " Hostname: $($props.HostName)" -ForegroundColor Cyan } + if ($props.UserName) { Write-Host " Username: $($props.UserName)" -ForegroundColor Cyan } + if ($props.PortNumber) { Write-Host " Port: $($props.PortNumber)" -ForegroundColor Cyan } + if ($props.Protocol) { Write-Host " Protocol: $($props.Protocol)" -ForegroundColor Cyan } + } + } else { + Write-Host "No PuTTY sessions found" -ForegroundColor Gray + } + } else { + Write-Host "PuTTY not installed or no sessions configured" -ForegroundColor Gray + } +} catch { + Write-Host "Error reading PuTTY registry: $_" -ForegroundColor Red +} + +# Check for Windows OpenSSH Server logs +Write-Host "`n--- OpenSSH Server Logs ---" -ForegroundColor Yellow +$sshServerLog = "$env:ProgramData\ssh\logs\sshd.log" +if (Test-Path $sshServerLog) { + Write-Host "Server log found: $sshServerLog" -ForegroundColor Green + try { + $recentLogs = Get-Content $sshServerLog -Tail 20 -ErrorAction SilentlyContinue + Write-Host "`nRecent log entries (last 20 lines):" -ForegroundColor Cyan + $recentLogs | ForEach-Object { + Write-Host " $_" -ForegroundColor Gray + } + } catch { + Write-Host "Error reading log: $_" -ForegroundColor Red + } +} else { + Write-Host "No OpenSSH server logs found" -ForegroundColor Gray +} + +if (-not $foundArtifacts) { + Write-Host "`nNo SSH artifacts found" -ForegroundColor Gray + Write-Host "SSH might not be configured on this system" -ForegroundColor Gray +} + +Write-Host "`nNote: Use -ShowKeys to display public key contents" -ForegroundColor Cyan +Write-Host "Private keys are never displayed for security reasons" -ForegroundColor Cyan