Advanced SQL Server Man-in-the-Middle Attacks
During an application security assessment performed for a client, we encountered an application that was relying heavily on the encryption features of the Tabular Data Stream (TDS) protocol implemented in Microsoft SQL Server to protect communications over untrusted networks. Out of curiosity, we investigated how different configuration settings on both the server and client change the security properties of this protocol. We quickly realized that their communications were insecure. To demonstrate the risk to our client, we developed a man-in-the-middle (MitM) tool which exploited two separate insecure configurations. In sharing with the community, we hope this article will raise awareness about how easy it is to make similar mistakes when implementing TDS encryption.
The Tabular Data Stream (TDS) protocol is used by Microsoft SQL Server as the primary way in which clients interact with the database server. The protocol has been updated many times over the years to support additional features, including the use of TLS-based encryption implemented as an opportunistic mechanism. This opportunistic encryption is implemented in a way that is similar to STARTTLS. An initial unencrypted handshake occurs, and if both sides advertise support for encryption, then a TLS handshake occurs on the same TCP connection, therafter allowing further communications to continue over this encrypted tunnel. As with any opportunistic encryption, the protocol can be highly vulnerable to downgrade attacks and other man-in-the-middle attacks if clients and servers are not carefully configured. Exacerbating this issue is the fact that much of Microsoft’s documentation on the topic is confusing, incorrect, or creates a false sense of security about certain configuration settings.
Attack 1: Certificate Forgery
Anyone using TLS must be mindful of how certificates are validated. The first thing an attacker is likely to try against any TLS implementation is to conduct a man-in-the-middle attack that presents self-signed or otherwise forged certificates to TLS clients (and servers, if client certificates are in use). To its credit, Microsoft’s implementation of TDS is safe in the sense that it enables certificate validation by default, which prevents this attack. Developers would need to explicitly disable certificate validation to be vulnerable. With that said, this is fairly common in development environments given that developers often want to avoid the effort of setting up certificates on non-production machines. During security assessments, we want to ensure certificate validation is enabled in the actual production deployment. In this particular engagement, we couldn’t find an off-the-shelf tool that made it easy to test for this issue in TDS, so we rolled our own.
Our TDS MitM script can be run in “cert” mode through the “–mitm_type” option. This causes the script to perform a classic certificate man-in-the-middle attack whereby a TLS connection is accepted from the client, but a second one is initiated to the server before the handshake is finished. The script leverages the Bletchley SSL/TLS library to automatically clone the server’s advertised certificate, and then presents a fake version to the client.
Attack 2: Asymmetric Downgrade
When TDS clients connect to Microsoft SQL Server, an unencrypted handshake ensues where both parties advertise whether or not they are configured to use encryption. Each side can claim one of the following: encryption is not supported (ENCRYPT_NOT_SUP); encryption is supported, but prefer not to use it (ENCRYPT_OFF); encryption is supported and prefer to use it (ENCRYPT_ON); encryption is required (ENCRYPT_REQ). The first three are useful for backward compatibility, but from a security perspective, requiring encryption is the only secure option. If neither party explicitly requires encryption, then a trivial man-in-the-middle attack is possible whereby the attacker can update the values of both handshake messages to say that encryption is not supported. From there, both the client and server will just assume they can’t use encryption and will move forward with an insecure conversation. This classic downgrade attack, as applied to TDS, was discussed in detail by Azhar Desai in 2015. Interestingly enough, the story doesn’t end there. What happens if one party requires encryption, but the other doesn’t? Is an attack still possible?
As it turns out, yes. In the specific case where the server requires encryption to be used, but the client does not, then an asymmetric downgrade attack is fairly easy to conduct. In the early handshake packets, we know the server will advertise ENCRYPT_REQ, attempting to signal to the client that encryption must be used. Meanwhile the client will advertise some other level of support for encryption (ENCRYPT_ON or ENCRYPT_OFF). During the exploit, our attacker modifies the server’s handshake packet and sets it to ENCRYPT_NOT_SUP. This will trigger the client to disable encryption during further transactions. However, the server is still going to expect a TLS handshake to come next. At that point, the attacker impersonates the client and initiates a TLS handshake on their behalf. Since the server doesn’t require any TLS client certificate (only server certificates are verified in TDS), the server is none the wiser and continues to communicate with the attacker posing as the client.
Next, the attacker simply relays all further communications between the two parties. As messages come from the server over the TLS channel, they are decrypted and forwarded over an unencrypted TDS connection to the client. Likewise, when the client sends messages unencrypted to the attacker’s proxy, the attacker just relays those over the TLS link to the server. Included in these forwarded messages is the client’s password authentication handshake, which typically exposes the database user’s password hash, or perhaps even the plaintext password.
The following diagrams summarize the steps of this attack. In the first diagram, we have an unaltered handshake where a client wants to use encryption (but doesn’t require it) and the server does require it:
That communication would be vulnerable to attack, however, since the client doesn’t require encryption. The man-in-the-middle attack in that situation would look like the following:
Our TDS MitM script executes this attack when you use “downgrade” through the “–mitm_type” option.
Ok, that’s spiffy, but what if a TDS client requires encryption and the server doesn’t? Is an attack still possible? I’m not aware of one. It is different in this case, because TLS is authenticated in only one direction. If the client is both requiring encryption to be used and is validating the server’s certificate on that connection (assuming certificate validation hasn’t been explicitly turned off), then the connection is sound. An attacker could clearly fool the server into using no encryption on one side of the man-in-the-middle proxy, but the client-side conversation won’t get very far if the attacker can’t fool the client into trusting an invalid server certificate.
As it turns out, only two things actually matter for providing communications security in TDS traffic with TLS: the client must be configured to require encryption and it must be configured to validate the server certificate. All of the server-side settings are just backward-compatibility baggage that add to confusion.
Throughout this research, we reviewed a great deal of documentation provided by Microsoft, but found many of the documents create a false sense of security, mislead developers, or in the worst cases, contained incorrect statements about security-critical details. Here we list some of these errata to help set the record straight and encourage Microsoft to address these items to reduce confusion within the MSSQL user population.
- In the now out-of-date article How to enable SSL encryption for an instance of SQL Server by using Microsoft Management Console, the following advice appears in a highlighted note: “Do not enable the Force Protocol Encryption option on both the client and the server. To enable Force Protocol Encryption on the server, use the Server Network Utility or SQL Server Configuration Manager, depending on the version of SQL Server. To enable Force Protocol Encryption on the client, use the Client Network Utility or SQL Server Configuration Manager.” This is odd, since requiring protocol encryption on the client and server at the same time should work just fine. In addition, we now know that the setting on the server side is irrelevant to security.
- In Enable Encrypted Connections to the Database Engine, step-by-step procedures are provided showing how to configure a server to require encryption under the heading “To configure the server to accept encrypted connections”. The “to accept” wording of this heading is confusing, since SQL Server already accepts encrypted connections by default. It just doesn’t require them by default. Also, there is absolutely no indication to the reader that this setting provides no additional communications security, whereas the client-side setting described in the same article actually does.
- In one of Microsoft’s most recent articles, Using Encryption Without Validation, there are almost too many misguided or incorrect statements to enumerate in a blog post like this.
- For one, the article’s title itself should be a huge red flag to anyone in security. Using TLS without certificate validation clearly defeats the whole purpose of using encryption in the first place. We strongly urge Microsoft to include an explicit warning in this article to highlight this fact.
- From the very first sentence of the article, we have a problem: “SQL Server always encrypts network packets associated with logging in.” – This fails to mention that the encrypted handshake is based on NTLM authentication, which any seasoned security expert would know has been riddled with cryptographic flaws for decades. Such flaws can allow for relay attacks and offline password cracking, at a minimum. Even worse, there are fairly recent claims that a downgrade attack is possible on the password authentication handshake itself, allowing for full plaintext password retrieval. There is even a Metasploit module designed specifically to conduct these attacks! Finally, even if this password authentication handshake were securely designed, an attacker could just hijack the TCP connection after authentication is completed to gain the same access as the victimized client.
- In the next paragraph, we find: “This may also be configured by SQL Server Configuration Manager using the Force Protocol Encryption option.” – As mentioned several times before, we know now that the server-side setting provides no security.
- Shortly thereafter, we have: “To enable encryption to be used when a certificate has not been provisioned on the server, SQL Server Configuration Manager can be used to set both the Force Protocol Encryption and the Trust Server Certificate options. In this case, encryption will use a self-signed server certificate without validation if no verifiable certificate has been provisioned on the server.” – This last sentence is very misleading. If the Trust Server Certificate option is set on the client, then the communications are vulnerable to certificate man-in-the-middle attacks. That’s true even if you later deploy a verifiable certificate on the server. This sentence might lead a reader to believe the “fix” is just to deploy a verifiable certificate without also correcting the client-side settings.
- The article provides a table with a breakdown of server and client-side settings, describing what would happen in each case. This proves useful for understanding behavior, but it repeatedly reinforces the idea that using some encryption with no certificate validation is somehow OK, or better than using no encryption at all. It isn’t. With the right tool, active man-in-the-middle attacks are just as easy to conduct as passive sniffing in the vast majority of networking technologies we use today.
With that said, there are some articles from Microsoft that do a better job of explaining these settings. The Encrypting Connections to SQL Server article includes a clear warning that “SSL connections that are encrypted by using a self-signed certificate do not provide strong security. They are susceptible to man-in-the-middle attacks. You should not rely on SSL using self-signed certificates in a production environment or on servers that are connected to the Internet.” This is a great warning and should exist in any article that discusses Trust Server Certificate settings (though I’d like to see a warning about requiring encryption as well).
Finally, the folks at Azure seem to have done their homework. All of the client connection strings they provide to customers as samples seem to include both an explicit TrustServerCertificate=False flag (which just explicitly enables certificate validation), as well as the appropriate flag to require encryption.
We contacted the Microsoft Security Response Center (MSRC) on November 22, 2017 and sent them a draft version of this blog post along with the MitM script. We asked Microsoft to comment on our findings, offer any corrections, and to comment on whether their documentation would be updated. While the MSRC was responsive to our emails, their SQL Server product team has yet to respond.
Those Who Came Before
The concept of an asymmetric downgrade on SSL/TLS is hardly new. Moxie Marlinspike’s sslstrip tool implemented this approach over five years ago. Downgrade attacks on STARTTLS mechanisms are not new either. Sadly, the application of these techniques to proprietary (if documented) protocols tends to be slow. At the time of our testing in early 2016, we weren’t aware of any tools that allow for an asymmetric downgrade of TDS, let alone a simple certificate spoofing attack. As we polished up this document, we came across another tool, TDSBridge, which acts as a TDS proxy and includes a brief comment in the documentation: “it even works with server side forced encryption“. Of course that should be an ominous sign to security folk and helps confirm our observations. While TDSBridge isn’t a security-focused tool, we feel obligated to mention it since the author clearly discovered this fact before we did, even if it wasn’t called out as a security risk.
Microsoft SQL Server database traffic is insecure by default, because encryption is not required by client libraries. This is a forgivable default setting, considering the fact that correctly configured server certificates are needed to make communications secure anyway. However, the documentation guiding administrators on how to add communications security are deeply flawed in multiple ways and require revision. In light of the insecure default settings and the misleading documentation, it is our hunch that the vast majority of Microsoft SQL Server network traffic is vulnerable to man-in-the-middle attacks, even if SQL Server administrators have taken steps to secure it.