5

We have a legacy ASP.NET site which uses the encryption methods here:

http://www.codekeep.net/snippets/af1cd375-059a-4175-93d7-25eea2c5c660.aspx

When we call the following method, the page loads very slowly and eventually Connection Reset is returned:

Decrypt(" ", true);

If the method is called multiple times in subsequent page requests, the Application Pool goes down.

This is occurring on a Windows 2008 server running .NET framework v3.5.

I narrowed the problem down to the TransformFinalBlock() call.

NOTE: on Cassini, I do not get a connection timeout; instead the following exception is thrown:

System.Security.Cryptography.CryptographicException: Bad Data

Calling Decrypt() for other strings causes no problems in any environment.

Why is this happening? Is it a bug in TripleDESCryptoServiceProvider?

Obviously, I could filter the cipherString to reject " " and avoid this particular issue. However, I am worried that some other cipherString values that I am not suspecting will cause the DoS.

UPDATE 2011.06.28

The following is the minimal code to reproduce the issue:

// problem occurs when toEncryptArray is an empty array {}
      byte[] toEncryptArray = {};

      MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
      byte[] keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes("dummy_key"));
      hashmd5.Clear();

      TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
      tdes.Key = keyArray;
      tdes.Mode = CipherMode.ECB;
      tdes.Padding = PaddingMode.PKCS7;
      ICryptoTransform cTransform = tdes.CreateDecryptor();

      // the following line can crashes the ASP.NET Application Pool (may need to call multiple times).
      byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

      tdes.Clear();
frankadelic
  • 19,333
  • 31
  • 105
  • 161
  • Why don't you step through your Decrypt function and see what happens when you give it a string with a single space? – President James K. Polk Jun 23 '11 at 00:15
  • @GregS - that's exactly what I did. As mentioned above, I found that TransformFinalBlock() was where the code hangs up. – frankadelic Jun 23 '11 at 19:13
  • Yes, but what happens in the other steps? What does the base64 decoder return, and so on. – President James K. Polk Jun 23 '11 at 22:38
  • It is returning byte[0]. Updated my question with the details. – frankadelic Jun 24 '11 at 17:58
  • I can't help wonder why your buffer is named "toEncryptArray" and you are creating a 3DES decryptor. Do you have a cipher text to test with your sample key (6619 f8cf 6cf9 daf6 67c4 ffe4 34e2 04c2) – ixe013 Jul 02 '11 at 02:20
  • @ixe013 - I am using the code from the codekeep link above. You are correct that a different variable name should have been used. I have lots of cipher text which is properly decrypted using this logic. It is functioning as expected (although we are obviously using a different key in production). – frankadelic Jul 05 '11 at 19:33

2 Answers2

1

The issue, as mentioned above, is that the decryption logic does not properly handle the case where the input cipher is a zero-length array.

A ticket was created for this:

http://connect.microsoft.com/VisualStudio/feedback/details/678150/denial-of-service-in-tripledescryptoserviceprovider

Note, it seems to work OK when running .NET framework 4.0.

frankadelic
  • 19,333
  • 31
  • 105
  • 161
0

The final block is where the padding is. In your example, the single space is the first and last block. DES/Triple DES is a 64 bit block cipher, the ciphet text should should be a multiple of 8 bytes (64 bits).

I don't have the environment to test it, but did you try playing with padding options ? Padding with more spaces won't do, because padding won't match.

A common padding scheme is PKCS5. For a single byte (that encrypted to the space caracter), your plain text should be, in hex :

0x?? 0x07 0x07 0x07 0x07 0x07 0x07 0x07

But in your code sample, a base64 input is expected. Wich means that your input string must be :

  • A multiple of 12 caracters
  • A valid base64 string

Any other string can be rejected.

The true value looks like a MAC, which means that your input plain text should be followed by a hash (MD5 in your code). It is there to help you detect changes to the cipher text. It is usefull when you encrypt binary data. If you can easily detect garbled plain text, you can set it to false.

ixe013
  • 8,508
  • 3
  • 43
  • 68
  • You mentioned the cipher Text should be a multiple of 8 bytes. However, if I change the input to 8 spaces, I still have the issue... Maybe a workaround solution is to reject any cipherString which is not a valid Base64 string., e.g.: http://stackoverflow.com/questions/475074/regex-to-parse-or-validate-base64-data – frankadelic Jun 23 '11 at 19:36
  • Yes, but the plain text will give you random cipher text. In other words, a cipher text that consist of exactly 8 spaces is very, very, extremely unlikely. If you get a space as the first encrypted byte (1 chance in 256), the other bytes will be random. – ixe013 Jun 24 '11 at 03:02
  • See my updated question. The core issue was that the decryption method would choke if the input byte array was empty. – frankadelic Jul 01 '11 at 22:18
  • The thing is that you never know if the cipher text is any good until you decrypt it. That's the way cryptography works, regardless of .Net or 3DES. There are some quick tests you can do on the cipher text : must be a multiple of 8 bytes in lenght, with 0 bytes always bad (raw bytes, not base64). If the input looks good, you must decrypt and catch the exception. – ixe013 Jul 02 '11 at 02:14