PowerShell Forensics Script: Non-Privileged Evidence Capture

1. Project Overview
~This idea came from an incident I handled where a suspicious USB was found plugged into an endpoint we had physical access to. Forensics’ first move was to capture memory and disk images, but that takes time, and we needed to correlate what was happening now with our logs. To bypass LAPS and get actionable info fast, I researched what data would be useful to grab and which directories are accessible by default to non-privileged accounts. And so, this approach was born! Feel free to tweak it for your environment—it’ll need some tuning to fit your setup.~ This project involves creating a PowerShell script designed for digital forensics, capable of capturing critical system information without requiring administrative privileges. The script collects data such as command history, scheduled tasks, startup items, running processes, network connections, DNS queries, and recently modified files, saving them to a timestamped folder on the user's desktop for analysis by a Security Operations Center (SOC) team.- Objective: Develop a lightweight forensics tool for evidence collection in environments with restricted permissions.
- Requirements: Windows workstation, PowerShell enabled for standard user. (THIS WILL NOT WORK FOR EVERYONE'S ENVIRONMENT; IT IS MEANT AS A TEMPLATE FOR YOU TO WORK FROM)!!!!
- Hardware Used: Windows-based system (tested on Windows 10/11).
- Software Used: PowerShell 5.1 or later, Windows Command Prompt.
- Skills Learned: PowerShell scripting, forensic data collection, error handling, log generation.
2. Script Breakdown and Forensic Relevance
Security Note: Always review code before executing. Verify integrity with the SHA256 hash provided for the full script below.
2.1 Setup and Logging
Relevance: Creating a timestamped output directory ensures all collected evidence is organized and traceable to the time of capture, which is critical for maintaining the chain of custody in forensic investigations. Logging all actions provides an audit trail, documenting what was captured or any errors encountered, which is essential for validating the integrity of the collected data.
# Get the current user and their desktop path
$currentUser = $env:USERNAME
$desktopPath = [System.IO.Path]::Combine([System.Environment]::GetFolderPath('Desktop'), "SystemCapture_$(Get-Date -Format 'yyyyMMdd_HHmmss')")
# Create the output directory on the desktop
try {
New-Item -Path $desktopPath -ItemType Directory -Force -ErrorAction Stop | Out-Null
Write-Host "Created output directory: $desktopPath" -ForegroundColor Green
}
catch {
Write-Host "Error creating output directory: $_" -ForegroundColor Red
exit 1
}
# Function to log messages
function Write-Log {
param($Message)
$logMessage = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $Message"
Add-Content -Path "$desktopPath\CaptureLog.txt" -Value $logMessage
Write-Host $logMessage
}

2.2 PowerShell Command History
Relevance: The PowerShell command history can reveal commands executed by the user or an attacker, such as file manipulations, network operations, or malicious scripts. This is critical for reconstructing the timeline of an incident and identifying unauthorized activities.
# Capture PowerShell Command History
try {
$psHistoryPath = "$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt"
if (Test-Path $psHistoryPath) {
Copy-Item -Path $psHistoryPath -Destination "$desktopPath\PowerShell_History.txt" -ErrorAction Stop
Write-Log "Captured PowerShell command history"
} else {
Write-Log "PowerShell command history file not found"
}
}
catch {
Write-Log "Error capturing PowerShell command history: $_"
}
2.3 Scheduled Tasks
Relevance: Scheduled tasks can be used by attackers to execute malicious code at specific times or intervals, ensuring persistence. Capturing this data helps investigators identify unauthorized tasks that may indicate malware or backdoors.
# Capture Scheduled Tasks
try {
schtasks /query /fo LIST /v | Out-File "$desktopPath\Scheduled_Tasks.txt"
Write-Log "Captured scheduled tasks"
}
catch {
Write-Log "Error capturing scheduled tasks: $_"
}
2.4 Startup Items
Relevance: Startup items, including those in the startup folder and registry autorun keys, can reveal programs set to run automatically, which is a common persistence mechanism for malware. This data helps identify suspicious applications that may require further analysis.
# Capture Startup Items
try {
# Startup folder
$startupFolder = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup"
if (Test-Path $startupFolder) {
dir $startupFolder | Out-File "$desktopPath\Startup_Folder.txt"
} else {
Write-Log "Startup folder not found"
}
# Registry autorun entries
reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Run | Out-File "$desktopPath\Autorun_HKLM.txt"
reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run | Out-File "$desktopPath\Autorun_HKCU.txt"
Write-Log "Captured startup items"
}
catch {
Write-Log "Error capturing startup items: $_"
}
2.5 Running Processes
Relevance: A snapshot of running processes can uncover malicious or unrecognized applications, such as remote access tools or cryptominers. Details like process names, IDs, and file paths aid in correlating processes with potential threats.
# Capture Running Processes
try {
Get-Process | Select-Object Name, Id, Path, Company, Description | Export-Csv -Path "$desktopPath\Processes.csv" -NoTypeInformation
Write-Log "Captured running processes"
}
catch {
Write-Log "Error capturing processes: $_"
}
2.6 Network Connections
Relevance: Network connections reveal active communications, including connections to command-and-control servers or data exfiltration endpoints. Capturing this data with process IDs helps trace suspicious activity to specific applications.
# Capture Network Connections (netstat)
try {
netstat -ano | Out-File "$desktopPath\Netstat.txt"
Write-Log "Captured network connections"
}
catch {
Write-Log "Error capturing network connections: $_"
}
2.7 DNS Queries
Relevance: The DNS cache can reveal domains accessed by the system, which may include malicious or phishing sites. This data is crucial for identifying potential command-and-control communications or unauthorized web activity.
# Capture DNS Queries
try {
ipconfig /displaydns | Out-File "$desktopPath\DNS_Queries.txt"
Write-Log "Captured DNS queries"
}
catch {
Write-Log "Error capturing DNS queries: $_"
}
2.8 Recently Modified Files
Relevance: Recently modified files in key directories like Temp and AppData can indicate malicious activity, such as temporary files created by malware or attacker scripts. Sorting by modification time helps prioritize files for further investigation.
# Capture Recently Modified Files in Key Directories
try {
$dirsToCheck = @(
"$env:LOCALAPPDATA\Temp",
"$env:APPDATA"
)
foreach ($dir in $dirsToCheck) {
if (Test-Path $dir) {
$dirName = [System.IO.Path]::GetFileName($dir)
dir $dir -Recurse | Sort-Object LastWriteTime -Descending | Select-Object -First 50 | Out-File "$desktopPath\RecentFiles_$dirName.txt"
}
}
Write-Log "Captured recently modified files in key directories"
}
catch {
Write-Log "Error capturing recently modified files: $_"
}
3. Tips and Lessons Learned
Practical advice and insights gained from developing the script:
- Tip 1: Run the script in at user level by copy and pasting into shell not running as a .PS1 to avoid permission issues and execution policy, as it is designed for "non-privileged environments".
- Tip 2: Review the log file (
CaptureLog.txt
) to identify any errors during execution. - Tip 3: Zip the output folder before sending to the SOC team to preserve file integrity.
- Lesson Learned: Error handling is critical to ensure the script continues running even if certain data cannot be captured.
- Future Improvements: Add support for capturing browser history or clipboard contents to enhance evidence collection.
4. Conclusion
The PowerShell forensics script successfully captures critical system information without requiring administrative privileges, making it suitable for rapid evidence collection in restricted environments. The script enhances forensic investigations by providing structured, timestamped data for SOC analysis. Future enhancements may include additional data sources and automated analysis features.
5. Full Code
Security Note: Always review code before executing. Verify integrity with SHA256 Hash: 2CD211F1B3F88A49CC28FFB009955BFA4F80255A482986ABBA8922D46E4D27FD.
# Script to capture specific system information for forensic analysis
# Purpose: Collects evidence without requiring admin privileges for incident response
# Get the current user and their desktop path
# Retrieves the username of the logged-in user from the environment variable
$currentUser = $env:USERNAME
# Creates a timestamped folder name (e.g., SystemCapture_20250407_121530) on the desktop
$desktopPath = [System.IO.Path]::Combine([System.Environment]::GetFolderPath('Desktop'), "SystemCapture_$(Get-Date -Format 'yyyyMMdd_HHmmss')")
# Create the output directory on the desktop
# Uses try-catch to handle potential errors (e.g., permission issues)
try {
# Creates the timestamped directory, -Force overwrites if it exists, -ErrorAction Stop ensures errors are caught
New-Item -Path $desktopPath -ItemType Directory -Force -ErrorAction Stop | Out-Null
# Notifies user of successful directory creation in green text
Write-Host "Created output directory: $desktopPath" -ForegroundColor Green
}
catch {
# Displays error message in red if directory creation fails and exits script
Write-Host "Error creating output directory: $_" -ForegroundColor Red
exit 1
}
# Function to log messages
# Defines a reusable function to log actions to a file and console
function Write-Log {
# Accepts a message parameter to log
param($Message)
# Formats the log entry with a timestamp (e.g., 2025-04-07 12:15:30 - Action completed)
$logMessage = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $Message"
# Appends the log entry to CaptureLog.txt in the output directory
Add-Content -Path "$desktopPath\CaptureLog.txt" -Value $logMessage
# Displays the log message in the console for real-time feedback
Write-Host $logMessage
}
# Start logging
# Logs the start of the evidence collection process, including the current user
Write-Log "Starting system information capture for user: $currentUser"
# 1. Capture PowerShell Command History
# Attempts to collect the user's PowerShell command history for forensic analysis
try {
# Defines the path to the PowerShell history file (stored in PSReadLine)
$psHistoryPath = "$env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt"
# Checks if the history file exists
if (Test-Path $psHistoryPath) {
# Copies the history file to the output directory for preservation
Copy-Item -Path $psHistoryPath -Destination "$desktopPath\PowerShell_History.txt" -ErrorAction Stop
# Logs successful capture
Write-Log "Captured PowerShell command history"
} else {
# Logs if the history file is not found (e.g., PSReadLine disabled)
Write-Log "PowerShell command history file not found"
}
}
catch {
# Logs any errors encountered during history capture (e.g., access denied)
Write-Log "Error capturing PowerShell command history: $_"
}
# 2. Capture Scheduled Tasks
# Collects information about scheduled tasks, which could indicate malicious persistence
try {
# Runs schtasks with /query /fo LIST /v to get detailed task information and saves to a file
schtasks /query /fo LIST /v | Out-File "$desktopPath\Scheduled_Tasks.txt"
# Logs successful capture
Write-Log "Captured scheduled tasks"
}
catch {
# Logs any errors (e.g., command execution failure)
Write-Log "Error capturing scheduled tasks: $_"
}
# 3. Capture Startup Items
# Collects programs set to run at startup, a common malware persistence mechanism
try {
# Defines the path to the user's startup folder
$startupFolder = "$env:APPDATA\Microsoft\Windows\Start Menu\Programs\Startup"
# Checks if the startup folder exists
if (Test-Path $startupFolder) {
# Lists contents of the startup folder and saves to a file
dir $startupFolder | Out-File "$desktopPath\Startup_Folder.txt"
} else {
# Logs if the startup folder is not found
Write-Log "Startup folder not found"
}
# Queries the HKLM registry for system-wide autorun entries and saves to a file
reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Run | Out-File "$desktopPath\Autorun_HKLM.txt"
# Queries the HKCU registry for user-specific autorun entries and saves to a file
reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run | Out-File "$desktopPath\Autorun_HKCU.txt"
# Logs successful capture
Write-Log "Captured startup items"
}
catch {
# Logs any errors (e.g., registry access issues)
Write-Log "Error capturing startup items: $_"
}
# 4. Capture Running Processes
# Collects a list of running processes to identify potential malicious applications
try {
# Uses Get-Process to retrieve process details (Name, ID, Path, Company, Description)
# Exports to a CSV file for easy analysis, -NoTypeInformation removes extra metadata
Get-Process | Select-Object Name, Id, Path, Company, Description | Export-Csv -Path "$desktopPath\Processes.csv" -NoTypeInformation
# Logs successful capture
Write-Log "Captured running processes"
}
catch {
# Logs any errors (e.g., access restrictions)
Write-Log "Error capturing processes: $_"
}
# 5. Capture Network Connections (netstat)
# Collects active network connections to detect unauthorized communications
try {
# Runs netstat -ano to list connections with process IDs and saves to a file
netstat -ano | Out-File "$desktopPath\Netstat.txt"
# Logs successful capture
Write-Log "Captured network connections"
}
catch {
# Logs any errors (e.g., command execution failure)
Write-Log "Error capturing network connections: $_"
}
# 6. Capture DNS Queries
# Collects the DNS cache to identify accessed domains, which may include malicious ones
try {
# Runs ipconfig /displaydns to list cached DNS entries and saves to a file
ipconfig /displaydns | Out-File "$desktopPath\DNS_Queries.txt"
# Logs successful capture
Write-Log "Captured DNS queries"
}
catch {
# Logs any errors (e.g., command execution failure)
Write-Log "Error capturing DNS queries: $_"
}
# 7. Capture Recently Modified Files in Key Directories
# Collects recently modified files in Temp and AppData to detect suspicious activity
try {
# Defines directories to check (common locations for temporary or malicious files)
$dirsToCheck = @(
"$env:LOCALAPPDATA\Temp",
"$env:APPDATA"
)
# Iterates through each directory
foreach ($dir in $dirsToCheck) {
# Checks if the directory exists
if (Test-Path $dir) {
# Gets the directory name for the output file
$dirName = [System.IO.Path]::GetFileName($dir)
# Lists files recursively, sorts by LastWriteTime, takes the top 50, and saves to a file
dir $dir -Recurse | Sort-Object LastWriteTime -Descending | Select-Object -First 50 | Out-File "$desktopPath\RecentFiles_$dirName.txt"
}
}
# Logs successful capture
Write-Log "Captured recently modified files in key directories"
}
catch {
# Logs any errors (e.g., access issues or directory traversal errors)
Write-Log "Error capturing recently modified files: $_"
}
# Finalize
# Logs the completion of the evidence collection process
Write-Log "System information capture completed. All files saved to: $desktopPath"
# Instructs the user to send the output folder to the SOC team for analysis
Write-Host "Capture completed. Please send the folder $desktopPath to your local SOC team for analysis." -ForegroundColor Green