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.
This commit is contained in:
18
TODO.md
18
TODO.md
@@ -10,6 +10,9 @@
|
|||||||
- [x] Network Connection History
|
- [x] Network Connection History
|
||||||
- [x] Hotspot Connections
|
- [x] Hotspot Connections
|
||||||
- [x] Recent Documents (RecentDocs)
|
- [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
|
### User Activity Artifacts
|
||||||
- [ ] UserAssist - Programs run by user through Windows Explorer
|
- [ ] UserAssist - Programs run by user through Windows Explorer
|
||||||
@@ -48,6 +51,21 @@
|
|||||||
- [ ] VPN connections
|
- [ ] VPN connections
|
||||||
- [ ] Remote Desktop 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
|
## Other Operating Systems
|
||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|||||||
216
windows/Get-SSHArtifacts.ps1
Normal file
216
windows/Get-SSHArtifacts.ps1
Normal file
@@ -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
|
||||||
Reference in New Issue
Block a user