Intro to Crypto Attacks – Part 2

This is the 2nd post in a recap of my “Intro to Crypto Attacks” workshop given at Bsides Seattle. In my previous post, I covered some background of the workshop session, linked to the files you’ll need for the exercises, and went over some core background you’ll use in the first challenge exercise.

Since the exercises are implemented as Azure Cloud Services (or you can deploy on your IIS server as a regular WCF service), you can interact with the services either by:

  • Using the WCF Test Client, which if you have Visual Studio should already be installed. On my system it’s in C:\Program Files (x86)\Microsoft Visual Studio X.X\Common7\IDE\WcfTestClient.exe.
  • Using SoapUI
  • Other useful tools include Burp Suite for its encoding/decoding functions as well as intruder (for automating an attack later if you’re not as experienced writing code) and a really simple, short python file I used during the workshop so that I wouldn’t have to type as much and could avoid typos.

    Part 2: ECB Mode

    Block ciphers divide the plaintext into blocks by block size N. By default, this block size is 128 bits (i.e., 16 characters). From here, what happens depends on the cipher mode. A look at the MSDN article shows several cipher mode options when using block ciphers in C#.

    What’s the default mode? With these AES classes, it’s actually Cipher Block Chaining (CBC). But, we’ll discuss and conduct attacks against CBC in a later exercise. For now, let’s take a look at the Electronic Codebook (ECB) mode. Though in C# you have to go out of your way to specify this mode, it’s the default mode in some other languages but not recommended since it exposes some vulnerabilities. That is, it can be dangerous to use in modern systems, considered insecure, and should be avoided. Take another look at the description above, it has an important note and everything. To understand the insecurity, let’s look at how it works.

    The plaintext is divided into the blocks, and together with the key, each block is individually encrypted. The application will typically return CiphertextBlock1 + CiphertextBlock2 + CiphertextBlockN as a concatenated result. Depending on the protocol transporting the ciphertext, if you’re looking at something going over the network, the ciphertext may be encoded in some manner (like Base64) to avoid having ciphertext characters accidentally modified or misinterpreted during transport, parsing, etc.

    We start by encrypting the letter A. This gives us the ciphertext of A with 15 padding bytes.

    Now let’s try 15 A’s, which will give back the ciphertext for 15 A’s and 1 padding byte, so the ciphertext lengths when encrypting 1 A or 15 A’s is the same (1 block).

    Next, we’ll encrypt 16 A’s. Our ciphertext increases by another block, because recall we need at least one byte of padding, and if our plaintext divides evenly by the block size, we’ll have a full block of padding.

    Let’s encrypt 64 “A”s and see what happens when we inspect the ciphertext. If we base64 decode the data and divide the resulting hex bytes into the block size… Notice a pattern?

    $ python
    Python 2.7.1 
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import crypto
    >>> crypto.decode("W/qVlI7fggiYQTdqTvoJUFv6lZSO34IImEE3ak76CVBb+pWUjt+CCJhBN2pO+glQW/qVlI7fggiYQTdqTvoJUJv3MwVDvIR/RmOyMw1Hu8c=")
    >>> crypto.blocks("5bfa95948edf82089841376a4efa09505bfa95948edf82089841376a4efa09505bfa95948edf82089841376a4efa09505bfa95948edf82089841376a4efa09509bf7330543bc847f4663b2330d47bbc7")

    We have 4 blocks of encrypted “A”s, and an encrypted padding block. If we try to encrypt 2 blocks of “A”s, a block of “B”s and another 2 blocks of “A”s, we learn that no matter where in the message they are, a full block of “A”s will always encrypt to “5bfa95948edf82089841376a4efa0950”.

    >>> print "A"*32 + "B"*16 + "A"*32 
    <Got the base64 encoded ciphertext from the service>
    >>> crypto.decode("W/qVlI7fggiYQTdqTvoJUFv6lZSO34IImEE3ak76CVBUSdikF55w6Bx4nh8GNhNEW/qVlI7fggiYQTdqT" +
    ...     "voJUFv6lZSO34IImEE3ak76CVCb9zMFQ7yEf0ZjsjMNR7vH")
    >>> crypto.blocks("5bfa95948edf82089841376a4efa09505bfa95948edf82089841376a4efa09505449d8a4179e70e81c789e1f063613445bfa95948edf82089841376a4efa09505bfa95948edf82089841376a4efa09509bf7330543bc847f4663b2330d47bbc7")

    Due to this behavior, we know we can systematically slice ciphertext blocks in and out, or rearrange them to attack the application. Where might we see implementations that actually use ECB mode? It looks like its the default mode in some things.

    In others, it’s the code example used to demonstrate how to encrypt something.

    To be fair, one of the user contributed notes mentions CBC mode (but I don’t see it there anymore).

    Then again, another suggests you can write your own encryption algorithm.

    Overall, many of the notes seem to suggest ECB mode for things like encrypting cookies; keep that in mind next time you’re conducting a security assessment against a web application.

    With that, let’s look at the first challenge exercise.

    Lab 1

    1) Disclose the balance of account number 14100305921007
    2) Open an account for yourself and give yourself $1,000,000

    The two objectives are not related. In fact, you can technically solve #2 without leveraging any crypto flaws. If you’ve stood up your target using the resources provided in the last post, you can start interacting with the service and learn how it works by opening an account in which you provide a clear text account name (or number, it’s equivalent) and get back the ciphertext, checking your balance in which you provide your encrypted account number, and closing an account in which you also provide your encrypted account number. The security of this banking application relies on the user not being able to encrypt values (existing account names/numbers) which have already been encrypted. The (important) code is also available in the Lab1 files you downloaded in Part 1, an interesting piece being:

    static byte[] EncryptStringToBytes(string plainText, byte[] Key)
                byte[] encrypted;
                using (Rijndael rijAlg = Rijndael.Create())
                    rijAlg.Key = Key;
                    rijAlg.Mode = CipherMode.ECB;
                    ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, Encoding.ASCII.GetBytes(""));
                    using (MemoryStream msEncrypt = new MemoryStream())
                        using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                            using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                            encrypted = msEncrypt.ToArray();
                return encrypted;

    If you’re having trouble thinking through the attack, there are tons of articles on simple ECB mode attacks that might help you get started, like this one from 2007. One of the (free) “Introduction to Cryptography” course lectures on also covers insecurities of ECB mode.


    In the next post, I’ll step through the solution for this challenge and talk about attacks against CBC mode and kick off the next lab.


    Leave a Reply

    Fill in your details below or click an icon to log in: Logo

    You are commenting using your account. Log Out / Change )

    Twitter picture

    You are commenting using your Twitter account. Log Out / Change )

    Facebook photo

    You are commenting using your Facebook account. Log Out / Change )

    Google+ photo

    You are commenting using your Google+ account. Log Out / Change )

    Connecting to %s