4

Is the following code vulnerable to a padding oracle attack, because it will return if the padding is valid or not (CBC, PKCS#7)?

The code is taken directly from Microsoft's webpage and can be run easily on a online compiler like dotnetfiddle.net.

using System;
using System.IO;
using System.Security.Cryptography;

namespace RijndaelManaged_Example
{
    class RijndaelExample
    {
        public static void Main()
        {
            try
            {

                string original = "Here is some data to encrypt!";

                // Create a new instance of the RijndaelManaged
                // class.  This generates a new key and initialization 
                // vector (IV).
                using (RijndaelManaged myRijndael = new RijndaelManaged())
                {

                    myRijndael.GenerateKey();
                    myRijndael.GenerateIV();
                    // Encrypt the string to an array of bytes.
                    byte[] encrypted = EncryptStringToBytes(original, myRijndael.Key, myRijndael.IV);

                    // Decrypt the bytes to a string.
                    string roundtrip = DecryptStringFromBytes(encrypted, myRijndael.Key, myRijndael.IV);

                    //Display the original data and the decrypted data.
                    Console.WriteLine("Original:   {0}", original);
                    Console.WriteLine("Round Trip: {0}", roundtrip);
                }

            }
            catch (Exception e)
            {
                Console.WriteLine("Error: {0}", e.Message);
            }
        }
        static byte[] EncryptStringToBytes(string plainText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");
            byte[] encrypted;
            // Create an RijndaelManaged object
            // with the specified key and IV.
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = Key;
                rijAlg.IV = IV;

                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                        {

                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }


            // Return the encrypted bytes from the memory stream.
            return encrypted;

        }

        static string DecryptStringFromBytes(byte[] cipherText, byte[] Key, byte[] IV)
        {
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;

            // Create an RijndaelManaged object
            // with the specified key and IV.
            using (RijndaelManaged rijAlg = new RijndaelManaged())
            {
                rijAlg.Key = Key;
                rijAlg.IV = IV;

                // Create a decryptor to perform the stream transform.
                ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV);

                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {
                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }

            }

            return plaintext;

        }
    }
}

The program I am currently looking into uses the code above to encrypt some XML files. Here is an example file from that program (an XML file with only one element).

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • 2
    Welcome to cryptography. You migt look at [How does a padding oracle attack work?](https://crypto.stackexchange.com/q/3714/18298) – kelalaka May 02 '19 at 16:47
  • I've removed the late add-on question for example code for the attack as that is explicitly off topic on StackOverflow. – Maarten Bodewes May 04 '19 at 15:45

1 Answers1

4

Yes, the code is vulnerable because .NET defaults to CBC with PKCS#7 compatible padding by default. You can test this easily by changing the ciphertext (the last 16 bytes of it) and checking that an exception is thrown. Note that error conditions are not required for padding oracles to work, timing differences may already leak enough information.

Of course, that doesn't necessarily mean that the system that uses the code is vulnerable. If the code is used to perform encryption on data at rest (e.g. encryption of a file) then it is likely that no padding oracle can be constructed, and the necessary requisites for the attack are not fulfilled.

Note that a padding oracle attack is a specific type of plaintext oracle attack. Other attacks may be possible, even if a different block cipher mode is used. Generally you'd need authenticated encryption to make sure that plaintext oracles are not possible: only act after the message integrity and authenticity has been verified.

The code as shown is not secure for transport mode security. Of course, to have secure transport security, the CBC padding oracle possibility is just one of the many many vulnerabilities; suffice to say that the code fragment does not demonstrate transport security at all.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/193278/discussion-on-answer-by-maarten-bodewes-is-this-code-vulnerable-to-a-padding-ora). – Brad Larson May 13 '19 at 15:42
  • Does the oracle have to have access to a private key to this to work? – cryptisloveifcandecrypt May 31 '19 at 20:06