Microsoft SQL Server Downgrade Attack

I took a look at the authentication mechanisms of the native network protocols of some of the more prominent dbms vendors. One of my targets was Microsofts SQL Server 2008 R2. MSSQL provides two methods for authentication: Integrated and Native Authentication. Integrated uses Windows OS user credentials to log into the database. Native Authentication uses login credentials stored inside the SQL Server.

MSSQL uses the Tabular DataStream protocol on the application layer. The specification is open for everyone to see at mssqlspec. Native Authentication is done inside the TDS protocol. So let's take a look at that.

Native Authentication

TDS uses no challenge-response-protocol to transmit the login credentials. There is a message called LOGIN7, which contains username in cleartext and an obfuscated version of the password. The obfuscation algorithm is known and described in the specification. So it's actually cleartext. So I thought: "Nice! Easy sniffing.". Let's have a look at typical login traffic in wireshark:

wireshark view - normal login

As you can see there is no LOGIN7 packet, which should be decoded by wireshark. But if we have a closer look at the transmitted packets, we discover SSLish patterns (like the "SSL Self Signed Fallback" certificate). Digging deeper into the specification, I found out, that Microsoft does something very strange here. They use SSL inside their application layer protocol. The whole SSL handshake is wrapped inside those PRELOGIN packets. Wireshark let's us decode the login traffic as SSL and we can see that packet number 10 can be decoded now as "SSL Application Data" record.

wireshark view - login decoded as ssl

So we can't really look into this SSL mess. Which sucks. So my first guess was to do the usual SSL MITM, serving a fake certificates. It turns out, this is pretty annoying to code, because of the handshake inside PRELOGIN thing. Maybe I'll hack something together another time. Fortunately it turns out, there is an easier way :)

First PRELOGIN

The first packet, a PRELOGIN packet, is sent by the client to the server. The server answers with another packet of the same kind. It contains several fields to ensure compatibility, such as version, instance name and encryption :)

The possible values for the ENCRYPTION field are the following:

ENCRYPT_OFF 0x00 Encryption available but off.
ENCRYPT_ON 0x01 Encryption is available and on.
ENCRYPT_NOT_SUP 0x02 Encryption is not available.
ENCRYPT_REQ 0x03 Encryption is required.

This values can be configured on the client and server. Default for both is ENCRYPT_OFF, which means that only the LOGIN7 packet is encrypted. One can force Encryption of the whole connection on the client and server.

It is specified how the client/server react to the value of the encryption field the other one sends:

  Server Setting:
Client Sends: ENCRYPT_OFF ENCRYPT_ON ENCRYPT_NOT_SUP
ENCRYPT_OFF ENCRYPT_OFF ENCRYPT_REQ ENCRYPT_NOT_SUP
ENCRYPT_ON ENCRYPT_ON ENCRYPT_ON ENCRYPT_NOT_SUP (connection terminated)
ENCRYPT_NOT_SUP ENCRYPT_NOT_SUP ENCRYPT_REQ (connection terminated) ENCRYPT_NOT_SUP

So a Client cannot differntiate between a server, which does not support encryption at all, and a server which accepts a connection from a client not supporting encryption.

Downgrade Attack

So let's assume we are able to do a MITM attack and are able to modify the traffic passing through us. So we can just change the value of the encryption field to ENCRYPT_NOT_SUP and we get a plaintext LOGIN7 packet. Yay :) I implemented this as some ugly scapy code, but also as a fancy metasploit module. Using this attack we can see something like this in wireshark:

wireshark view - downgrade attack in action

You can download the metasploit module here: mssql_auth_downgrade.rb and a little script I used to prepare my MITM attack: mssql_mitm_prepare.sh

Disclosure & Mitigation

So I told Microsoft about this flaw in september and they told me the following:

Please note that SQL Server does not offer an option to enforce encryption of only the login packet (a.k.a. username & password), and at this point we have no plans to introduce such option.

Ok so no plans to change something here. So I guess the best option is to use the Force Encryption option of your server and clients to encrypt the whole connection or switch to integrated authentication, which is Microsofts recomendation anyway.