Files
artif/windows/Get-SSHArtifacts.ps1
mnerv dcebc0f4fa 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.
2026-02-03 21:48:51 +01:00

217 lines
9.3 KiB
PowerShell

# 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