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>
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
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
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}
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)
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
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
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
Finally, we decrypt the modified file:
$ openssl enc -aes-256-cbc -d \
-in secret-modified.enc \
-iv be154e2343408caa1f11ab3445bdd34c \
-K be154e2343408caa1f11ab3445bdd34cbe154e2343408caa1f11ab3445bdd34c \
> secret-modified.html
Now, the secret-modified.html
file should look like this:
{ binary garbage }:)
<body>
Hello World!
</body>
</html>
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
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
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)