DEV Community

Moritz Höppner
Moritz Höppner

Posted on

Bitflip Attack on CBC: Change of the Ciphertext

Just like in yesterday's tutorial, I want to demonstrate a bitflip attack on an AES-CBC encrypted ciphertext. This time, we won't change the first plaintext block by tampering with the IV but the second one by tampering with the first ciphertext block. To follow along, you'll need a shell on a *nix system, the OpenSSL command line utility, hexdump, dd and basenc (which is part of GNU corutils).

The attack doesn't depend on the block cipher (although I'll be using the fact that AES has a block size of 128 bit) but only on the mode of operation, which is CBC. For an introduction to CBC, take a look at Crypto 101, which contains a nice explanation of the kind of attack I'm about to show. My goal here is simply to show how to actually do it, how to calculate the bytes, how to read and write the encrypted files.

The original plaintext is the following HTML document (UTF-8 encoded):

<!DOCTYPE html>
<html>
  <body>
    Hello World!
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

We save it as secret.html and generate the ciphertext with OpenSSL (the exact values for the IV and key don't matter, I'm using simply some MD5 hash):

$ openssl enc -aes-256-cbc \
    -in secret.html \
    -iv be154e2343408caa1f11ab3445bdd34c \
    -K be154e2343408caa1f11ab3445bdd34cbe154e2343408caa1f11ab3445bdd34c \
    > secret.enc
Enter fullscreen mode Exit fullscreen mode

Let's take a look at both files with hexdump:

$ hexdump -C secret.html    
00000000  3c 21 44 4f 43 54 59 50  45 20 68 74 6d 6c 3e 0a  |<!DOCTYPE html>.|
00000010  3c 68 74 6d 6c 3e 0a 20  20 3c 62 6f 64 79 3e 0a  |<html>.  <body>.|
00000020  20 20 20 20 48 65 6c 6c  6f 20 57 6f 72 6c 64 21  |    Hello World!|
00000030  0a 20 20 3c 2f 62 6f 64  79 3e 0a 3c 2f 68 74 6d  |.  </body>.</htm|
00000040  6c 3e                                             |l>|
00000042
$ hexdump -C secret.enc     
00000000  09 58 ca bb 89 66 e7 85  31 70 5a 4b 54 59 1e b0  |.X...f..1pZKTY..|
00000010  b4 1d 57 94 12 53 d0 0b  79 3f 0e b6 67 c7 a6 7e  |..W..S..y?..g..~|
00000020  42 78 e1 39 5f 3c 8a f6  9b 2a c1 0d 3e bd 97 75  |Bx.9_<...*..>..u|
00000030  c0 c9 42 08 cf d2 c5 a8  16 cd ee 73 1a af ce 7c  |..B........s...||
00000040  d8 2b bc 4a 38 04 69 80  01 91 6b d1 74 5d 52 9d  |.+.J8.i...k.t]R.|
00000050
Enter fullscreen mode Exit fullscreen mode

The AES block size is 128 bit, which means 16 bytes, so exactly one row of the hexdump output. You can see that the last plaintext row is smaller than one block. In this case the missing bytes are added before the encryption. This is called padding and is the reason the ciphertext has 5 complete blocks.

In CBC mode, decryption of ciphertext blocks other than the first one works like this:

{n+1-th plaintext block} =
  AES_decrypt({n+1-th ciphertext block}, key) XOR
  {n-th ciphertext block}
Enter fullscreen mode Exit fullscreen mode

So by changing the n-th ciphertext block, we can change the outcome of the decryption of the n+1-th ciphertext block.

Okay, let's change the start of the second block from <html> to :)____, where _ denotes a whitespace character. We do this by changing the first block of the ciphertext. The algorithm for this is exactly the same as in the case of the IV, with the only difference that we use the first ciphertext block instead of the IV.

As we can see in the hexdump, the hexadecimal UTF-8 representation of <html> is 3c68746d6c3e. The one of :)____ is 3a2920202020. Let C be the first six bytes of the ciphertext. We replace them by:

  C XOR ("<html>" XOR ":)    ")
= 399ef6c358ca XOR (3c68746d6c3e XOR 3a2920202020)
Enter fullscreen mode Exit fullscreen mode

Let's do it!

    0011 1100 0110 1000 0111 0100 0110 1101 0110 1100 0011 1110 (3c68746d6c3e "<html>")
XOR 0011 1010 0010 1001 0010 0000 0010 0000 0010 0000 0010 0000 (3a2920202020, ":)   ")
    -----------------------------------------------------------
    0000 0110 0100 0001 0101 0100 0100 1101 0100 1100 0001 1110

    0000 1001 0101 1000 1100 1010 1011 1011 1000 1001 0110 0110 (0958cabb8966, start of first block)
XOR 0000 0110 0100 0001 0101 0100 0100 1101 0100 1100 0001 1110 (last result)
    -----------------------------------------------------------
    0000 1111 0001 1001 1001 1110 1111 0110 1100 0101 0111 1000
Enter fullscreen mode Exit fullscreen mode

So we must replace the first 6 bytes of the ciphertext by 0f199ef6c578. First, we write these bytes in a file:

$ echo 0f199ef6c578 | basenc --base16 -d > secret-modified.enc
Enter fullscreen mode Exit fullscreen mode

Now, we add everything from secret.enc but the first 6 bytes to the new file:

$ dd if=secret.enc bs=6 skip=1 >> secret-modified.enc
Enter fullscreen mode Exit fullscreen mode

Finally, we decrypt the modified file:

$ openssl enc -aes-256-cbc -d \
    -in secret-modified.enc \
    -iv be154e2343408caa1f11ab3445bdd34c \
    -K be154e2343408caa1f11ab3445bdd34cbe154e2343408caa1f11ab3445bdd34c \
    > secret-modified.html
Enter fullscreen mode Exit fullscreen mode

Now, the secret-modified.html file should look like this:

{ binary garbage }:)
  <body>
    Hello World!
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Great, we managed to replace <html> (the start of the second block) by :). The only problem is that we had to change the first ciphertext block for this. So the first plaintext block is of course no longer <!DOCTYPE html> but just 16 bytes of garbage.

Two thoughts on this garbage phenomenon. First, it shows how resistant the CBC mode is against data corruption. After all, we have only one block of garbage, the rest of the decrypted file seems okay. And in fact, if one block of the ciphertext is corrupted, only itself and the following block are impacted.

Second, if we are lucky, we might be able to hide the 16 bytes of garbage from our victim that decrypts and displays the file we tampered with. An HTML document will likely be displayed in a browser. So we could try to choose the changed block in a way that the garbage bytes become part of an (not exactly spec compliant) HTML tag. For example, if there was no newline after <!DOCTYPE html>, the hexdump of our original secret.html would look like this:

$ hexdump -C secret.html       
00000000  3c 21 44 4f 43 54 59 50  45 20 68 74 6d 6c 3e 3c  |<!DOCTYPE html><|
00000010  68 74 6d 6c 3e 0a 20 20  3c 62 6f 64 79 3e 0a 20  |html>.  <body>. |
00000020  20 20 20 48 65 6c 6c 6f  20 57 6f 72 6c 64 21 0a  |   Hello World!.|
00000030  20 20 3c 2f 62 6f 64 79  3e 0a 3c 2f 68 74 6d 6c  |  </body>.</html|
00000040  3e                                                |>|
00000041
Enter fullscreen mode Exit fullscreen mode

In such a scenario we could tamper with the second ciphertext block and try to change the third block to something containing >:). For example, the result could be:

$ hexdump -C secret-modified.html       
00000000  3c 21 44 4f 43 54 59 50  45 20 68 74 6d 6c 3e 3c  |<!DOCTYPE html><|
00000010  xx xx xx xx xx xx xx xx  xx xx xx xx xx xx xx xx  |    garbage     |
00000020  3e 3a 29 48 65 6c 6c 6f  20 57 6f 72 6c 64 21 0a  |>:)Hello World!.|
00000030  20 20 3c 2f 62 6f 64 79  3e 0a 3c 2f 68 74 6d 6c  |  </body>.</html|
00000040  3e                                                |>|
00000041
Enter fullscreen mode Exit fullscreen mode

In this case, the user will never see the suspicous 16 bytes of garbage, because the browser ignores tags it doesn't know.

Top comments (0)