1

I'm currently testing an application, which connects to a MS-SQL database. Through Wireshark, I can see that the application uses TDS to connect to the database. Furthermore, I can see that both the queries and results are UTF-16 encoded and provided in plaintext.

However, I am unable to find the credentials used to authenticate with the database. How are they protected? And how can I access them without reverse-engineering the application?

2 Answers2

1

It sounds like (§2.2.6.4) the password is obfuscated with some simple bit flipping:

Before submitting a password from the client to the server, for every byte in the password buffer starting with the position pointed to by ibPassword or ibChangePassword, the client SHOULD first swap the four high bits with the four low bits and then do a bit-XOR with 0xA5 (10100101).

If you can pull the password bytes out of the TDS stream - which presumes TLS is not in use, and I don't know how well Wireshark's parser highlights the password data - just reverse the process to get the password:

After reading a submitted password, for every byte in the password buffer starting with the position pointed to by ibPassword or ibChangePassword, the server SHOULD first do a bit-XOR with 0xA5 (10100101) and then swap the four high bits with the four low bits.

gowenfawr
  • 71,975
  • 17
  • 161
  • 198
  • Actually, if both the client and the server support encryption, the login is TLS encrypted. *Even if both are configured to disable encryption* –  Mar 11 '21 at 17:34
  • @MechMK1 True... I assumed since you were seeing queries and results that TLS wasn't in play here. I'll tweak the answer to highlight that distinction. – gowenfawr Mar 11 '21 at 18:32
  • So, I added my own answer with my own research now. TDS is a lot more complex than I thought –  Mar 12 '21 at 14:38
  • @MechMK1 TDS is certainly convoluted. See also [this question](https://security.stackexchange.com/q/142939/3365) where we learn that TDS wraps TLS, not the other way around. – gowenfawr Mar 12 '21 at 16:30
  • Yes, the Pre-Login message contains a TLS payload. It's so weird –  Mar 12 '21 at 16:34
1

So, I did my own research, and here are my findings:

So the first message being sent is always the Pre-Login (§2.2.1.1):

Before a login occurs, a Pre-Login handshake occurs between client and server, setting up contexts such as encryption and MARS-enabled. For more details, see section 2.2.6.5.

The very first message is a Pre-Login Message, which include the clients version number and a set of Pre-Login Option Tokens, among which is token 0x01 ENCRYPTION, which has the following values for the client:

  • ENCRYPT_OFF = 0x00: Encryption is 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.

After this, the server responds with a Pre-Login Response, which mostly contains the server's version number, as well as its own set of Pre-Login Option Tokens, which also contain the server's Encryption capabilities.

The following table describes the behavior, depending on what both client and server have negotiated:

Client Value returned
from server
is ENCRYPT_OFF
Value returned
from server
is ENCRYPT_ON
Value returned
from server
is ENCRYPT_REQ
Value returned
from server
is ENCRYPT_NOT_SUP
ENCRYPT_OFF Encrypt login packet only Encrypt Entire Connection Encrypt Entire Connection No Encryption
ENCRYPT_ON Error (Terminate Connection) Encrypt Entire Connection Encrypt Entire Connection Error (Terminate Connection)

As you can see, even if both server and client have encryption disabled (both send 0x00 as their encryption capabilities), they still send the LOGIN7 stream described in §2.2.6.4 encrypted via TLS, encapsulated within a PRE-LOGIN packet. This process is described in §2.2.6.5.

A downgrade attack

By intercepting the communication between the client and the server, the value of the encryption token can be set to 0x02 ENCRYPT_NOT_SUP, which works when both client and server don't require fully encrypted connections.

The client will believe they support encryption, but the server doesn't, so it falls back to the case where nothing will be encrypted. The server will also believe they support encryption, but the client doesn't, and will thus also fall back to fully unencrypted connections. Note that this only works when the client natively sends ENCRYPT_OFF. If the client sends ENCRYPT_ON, then downgrading the server's response to ENCRYPT_NOT_SUP will lead to connection termination.

After the connection has successfully been downgraded to plain text, then the process described in gowenfawr's answer can be used to retrieve the credentials.

For server administrators

This attack can be prevented by configuring both clients and servers to always use encryption and refuse any connection attempt that is not encrypted.