Functions/Get-SdtMSSQLLinkPasswords.ps1
function Get-SdtMSSQLLinkPasswords { [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact='High')] Param ( [string[]]$SqlInstance = $Env:computername ) # Store multiple servers result $Results = @(); Write-Debug "Start debugging Get-SdtMSSQLLinkPasswords" foreach($sqlInst in $SqlInstance) { $qrySqlInstance = Retry-SdtCommand { Invoke-Sqlcmd -ServerInstance $sqlInst ` -Query "SELECT SERVERPROPERTY('ComputerNamePhysicalNetBIOS') as computer_name, SERVERPROPERTY('InstanceName') as instance_name, SERVERPROPERTY('MachineName') as machine_name, @@SERVERNAME as servername" } -Maximum 5 -ErrorAction Continue $ComputerName = $qrySqlInstance.computer_name if([string]::IsNullOrEmpty($qrySqlInstance.instance_name)) { $InstanceName = 'MSSQLSERVER'; } else { $InstanceName = $qrySqlInstance.instance_name; $sqlInst = $qrySqlInstance.servername; } Write-Verbose "`$ComputerName = $ComputerName"; $ScriptBlock = { $ComputerName = $Using:ComputerName; $InstanceName = $Using:InstanceName; $SqlInstance = $Using:SqlInst; Write-Verbose "`$SqlInstance = $SqlInstance"; Add-Type -assembly System.Security Add-Type -assembly System.Core # Set local computername and get all SQL Server instances $Results = New-Object "System.Data.DataTable" $Results.Columns.Add("Instance") | Out-Null $Results.Columns.Add("Linkserver") | Out-Null $Results.Columns.Add("User") | Out-Null $Results.Columns.Add("Password") | Out-Null Write-Verbose "Created System.Data.DataTable "; # Start DAC connection to SQL Server # Default instance MSSQLSERVER -> instance name cannot be used in connection string if ($InstanceName -eq "MSSQLSERVER") { $ConnString = "Server=ADMIN:$ComputerName;Trusted_Connection=True" } else { $ConnString = "Server=ADMIN:$SqlInstance;Trusted_Connection=True" } Write-Verbose "`$ConnString = `"$ConnString`""; $Conn = New-Object System.Data.SqlClient.SQLConnection($ConnString); Try{$Conn.Open();} Catch{ Write-Error "Error creating DAC connection: $_.Exception.Message" Continue } if ($Conn.State -eq "Open") { Write-Verbose "DAC connection successfull"; Write-Verbose "Query Service Master Key from the database - remove padding from the key"; # key_id 102 eq service master key, thumbprint 3 means encrypted with machinekey $SqlCmd="SELECT substring(crypt_property,9,len(crypt_property)-8) FROM sys.key_encryptions WHERE key_id=102 and (thumbprint=0x03 or thumbprint=0x0300000001)" $Cmd = New-Object System.Data.SqlClient.SqlCommand($SqlCmd,$Conn); $SmkBytes=$Cmd.ExecuteScalar() Write-Verbose "`$SmkBytes = `"$SmkBytes`""; # Get entropy from the registry - hopefully finds the right SQL server instance Write-Verbose "Get entropy from the registry - hopefully finds the right SQL server instance"; $RegPath = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\sql\").$InstanceName [byte[]]$Entropy = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server\$RegPath\Security\").Entropy Write-Verbose "`$RegPath = `"$RegPath`""; Write-Verbose "`$Entropy = `"$Entropy`""; # Decrypt the service master key Write-Verbose "Decrypt the service master key"; $ServiceKey = [System.Security.Cryptography.ProtectedData]::Unprotect($SmkBytes, $Entropy, 'LocalMachine') # Choose the encryption algorithm based on the SMK length - 3DES for 2008, AES for 2012 # Choose IV length based on the algorithm if (($ServiceKey.Length -eq 16) -or ($ServiceKey.Length -eq 32)) { if ($ServiceKey.Length -eq 16) { $Decryptor = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider $IvLen=8 } elseif ($ServiceKey.Length -eq 32){ $Decryptor = New-Object System.Security.Cryptography.AESCryptoServiceProvider $IvLen=16 } # Query link server password information from the DB # Remove header from pwdhash, extract IV (as iv) and ciphertext (as pass) # Ignore links with blank credentials (integrated auth ?) $SqlCmd = "SELECT sysservers.srvname,syslnklgns.name,substring(syslnklgns.pwdhash,5,$ivlen) iv,substring(syslnklgns.pwdhash,$($ivlen+5), len(syslnklgns.pwdhash)-$($ivlen+4)) pass FROM master.sys.syslnklgns inner join master.sys.sysservers on syslnklgns.srvid=sysservers.srvid WHERE len(pwdhash)>0" $Cmd = New-Object System.Data.SqlClient.SqlCommand($SqlCmd,$Conn); $Data=$Cmd.ExecuteReader() $Dt = New-Object "System.Data.DataTable" $Dt.Load($Data) # Go through each row in results foreach ($Logins in $Dt) { # decrypt the password using the service master key and the extracted IV $Decryptor.Padding = "None" $Decrypt = $Decryptor.CreateDecryptor($ServiceKey,$Logins.iv) $Stream = New-Object System.IO.MemoryStream (,$Logins.pass) $Crypto = New-Object System.Security.Cryptography.CryptoStream $Stream,$Decrypt,"Write" $Crypto.Write($Logins.pass,0,$Logins.pass.Length) [byte[]]$Decrypted = $Stream.ToArray() # convert decrypted password to unicode $EncodingType = "System.Text.UnicodeEncoding" $Encode = New-Object $EncodingType # Print results - removing the weird padding (8 bytes in the front, some bytes at the end)... # Might cause problems but so far seems to work.. may be dependant on SQL server version... # If problems arise remove the next three lines.. $i=8 foreach ($b in $Decrypted) {if ($Decrypted[$i] -ne 0 -and $Decrypted[$i+1] -ne 0 -or $i -eq $Decrypted.Length) {$i -= 1; break;}; $i += 1;} $Decrypted = $Decrypted[8..$i] $Results.Rows.Add($SqlInstance,$($Logins.srvname),$($Logins.name),$($Encode.GetString($Decrypted))) | Out-Null } } else { Write-Error "Unknown key size" } $Conn.Close(); } $Results } # ScriptBlock $Results += Invoke-Command -ComputerName ([System.Net.Dns]::GetHostByName(($ComputerName))).Hostname -ScriptBlock $ScriptBlock; } # Loop #$Results | Select-Object @{l='SqlInstance';e={if($_.Instance -eq 'MSSQLSERVER') {$_.PSComputerName} else {$_.PSComputerName+'\'+$_.Instance} }}, Linkserver, User, Password; $Results | Select-Object @{l='SqlInstance';e={$_.Instance}}, Linkserver, User, Password; <# .SYNOPSIS Extract and decrypt MSSQL linked server passwords. Author: Antti Rantasaari 2014, NetSPI Modified By: Ajay Dwivedi License: BSD 3-Clause .DESCRIPTION Get-SdtMSSQLLinkPasswords extracts and decrypts the connection credentials for all linked servers that use SQL Server authentication on all local MSSQL instances. .PARAMETER SqlInstance Name of the Sql Instance where Linked Server username passwords have to be decrypted. .INPUTS None .OUTPUTS System.Data.DataRow Returns a datatable consisting of MSSQL instance name, linked server name, user account, and decrypted password. .EXAMPLE C:\PS> Get-SdtMSSQLLinkPasswords SqlInstance Linkserver User Password -------- ---------- ---- -------- SQL2012 DEV-SQL dev Passw0rd01! SQL2012 WEBDB sa W3bDB$4P4ssw0rd .NOTES For successful execution, the following configurations and privileges are needed: - DAC connectivity to MSSQL instances on DEfault port 1434 - Local administrator privileges (needed to access registry key) - Sysadmin privileges to MSSQL instances .LINK http://www.netspi.com/blog/ .LINK https://github.com/imajaydwivedi/SQLDBATools #> } |