# Author: Scott Sutherland, NetSPI (@_nullbind / nullbind) function Parse-UnattendFile { param ( [string]$filePath ) # Load the XML file [xml]$xmlContent = Get-Content -Path $filePath # Create an array to store the parsed credentials $credentials = @() # Define namespaces used in the XML file $namespace = @{ "unattend" = "urn:schemas-microsoft-com:unattend" "wcm" = "http://schemas.microsoft.com/WMIConfig/2002/State" } # Function to decode Base64 if password is encoded function Decode-PasswordIfNeeded { param ( [string]$passwordValue, [bool]$isPlainText ) if ($isPlainText -eq $false) { try { # Decode Base64 password $decodedPassword = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($passwordValue)) return $decodedPassword } catch { Write-Host "Error: Unable to decode Base64 string, returning original value." return $passwordValue } } else { return $passwordValue } } # Parse AutoLogon credentials $autoLogon = $xmlContent.unattend.settings.component | Where-Object { $_.name -eq "Microsoft-Windows-Shell-Setup" -and $_.AutoLogon -ne $null } if ($autoLogon) { $username = $autoLogon.AutoLogon.Username $password = $autoLogon.AutoLogon.Password.Value $isPlainText = $autoLogon.AutoLogon.Password.PlainText -eq "true" # Decode password if necessary $password = Decode-PasswordIfNeeded -passwordValue $password -isPlainText $isPlainText $credentials += [pscustomobject]@{ User = $username Password = $password Source = "AutoLogon" } } # Parse LocalAccounts credentials $localAccounts = $xmlContent.unattend.settings.component.UserAccounts.LocalAccounts.LocalAccount | Where-Object { $_ -ne $null } foreach ($account in $localAccounts) { $username = $account.Name $password = $account.Password.Value $isPlainText = $account.Password.PlainText -eq "true" # Decode password if necessary $password = Decode-PasswordIfNeeded -passwordValue $password -isPlainText $isPlainText $credentials += [pscustomobject]@{ User = $username Password = $password Source = "LocalAccount" } } # Return the collected credentials as an array of objects return $credentials } # Example usage: $parsedCredentials = Parse-UnattendFile -filePath "c:\temp\configs\unattend-base64.xml" # Display the results $parsedCredentials | Format-Table -AutoSize <# unattend.xml * acme corp. acme corp. false LocalAdmin true 10 UEBzc3dvcmQxMjMh false</PlainText> </Password> </AutoLogon> </component> </settings> <settings pass="oobeSystem"> <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"> <UserAccounts> <LocalAccounts> <LocalAccount wcm:action="add"> <Password> <Value>UEBzc3dvcmQxMjMh</Value> <!-- This is Base64 for 'P@ssword123!' --> <PlainText>false</PlainText> </Password> <Group>Administrators</Group> <Description>Provisioning Admin</Description> <DisplayName>LocalAdmin</DisplayName> <Name>LocalAdmin</Name> </LocalAccount> </LocalAccounts> </UserAccounts> <OOBE> <HideEULAPage>true</HideEULAPage> <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen> <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE> <HideOnlineAccountScreens>true</HideOnlineAccountScreens> <HideLocalAccountScreen>true</HideLocalAccountScreen> <ProtectYourPC>1</ProtectYourPC> </OOBE> </component> </settings> </unattend> #>