Introduction
I wrote an article in my LinkedIn profile, so I thought to post it also in code project. This attack is originally written by Prof Bill Buchanan OBE in the following link,
https://billatnapier.medium.com/can-i-break-aes-encryption-yes-31bdf539aba0
It explains in detail, how this attack work. He is originally using OpenSSL through command line.
I have automated the attack using C++ (CygWin GCC with Eclipse over Windows OS) OpenSSL library.
Background
1. It generates the key from the password using Password-Based Key Derivation Function.
2. Using the key it encrypts the message "Pay Bob 1 dollar" using AES counter mode.
3. The 9th byte contains the amount and with simple bitwise operation I modify the amount on the cipher text.
4. Decrypting the cipher text give the modified message "Pay Bob 9 dollar".
This is how man-in-the-middle sitting between two parties attack by capturing the data and modifying it without knowing the key and without decrypting and it happens between 2 and 3.
The output of the code is as follows.
How It Works
The process of encryption of data in AES Counter mode is as shown in the image below.
The process of decryption of data in AES counter mode is as shown in the image below.
The process of encryption and decryption is almost the same. It take the counter and encrypts it with the key to get the encrypted counter [EC]. There are lot of article on the internet about how AES-CTR works.
They both perform the encryption of the counter and get the encrypted counter [EC]. The plaintext is performed XOR operation with [EC] to get the cipher text during encryption process or the ciphertext is performed XOR operation with [EC] during decryption process as shown in the image.
Example
Assume, as in the case above, that Bob's hired attacker Eve is able to decipher the cypher text while Alice is sending a transaction to the bank. The transaction appears as "Pay Bob 1 dollar" but is encrypted, as shown below. Assume once more that the attacker using the educated estimate is aware that the ninth byte contains the number 1. Before the transaction reaches the bank, Eve, the attacker, wants to alter the amount from 1 to 9 using only the ciphertext.
Quote:
0x7b 0xfe 0x7c 0x81 0x60 0x3e 0x86 0xe3 - 0x12 0x82 0x8e 0x1e 0x1e 0xc2 0xd6 0x42
Let Pi be the one-byte plan text (as determined by the attacker) at ith location, Ci bet the one-byte cypher text at ith position, and ECi be the one-byte encrypted counter at ith position. Thus, by encrypting data in AES counter mode, the cipher text,
Ci = ECi xor Pi
Considering that ECi is unknown and Ci and Pi are known. Let NPi represent the new plain text that has to be altered at the attacker-determined ith place.
The attacker's objective is to use Ci to determine the new cypher text NCi, so that the plain text should be the new plain text NPi once the cypher text has been decrypted by the bank. That is, based on the picture above.
NCi = ECi xor NPi
Let,
Ci=ECi xor Pi, xor on both side with Pi.
Ci xor Pi = (ECi xor Pi xor Pi). Since (Pi xor Pi)= 0, therefore,
Ci xor Pi = ECi.
XOR both side with NPi, so,
Ci xor Pi xor NPi = ECi xor NPi = NCi, from the above definition.
Therefore,
NCi = Ci xor Pi xor NPi.
From the above example
NCi = 0x12 xor 0x01 xor 0x09 = 0x1a.
Therefore the new cipher is,
Quote:
0x7b 0xfe 0x7c 0x81 0x60 0x3e 0x86 0xe3 - 0x1a 0x82 0x8e 0x1e 0x1e 0xc2 0xd6 0x42
Decrypting the above cipher text using AES Counter mode will result in
Quote:
Pay Bob 9 dollar
Using the code
The code is as follows and is self explanatory.
#include <string.h>
#include <iostream>
using namespace std;
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/bioerr.h>
#include <openssl/evp.h>
#include <openssl/evperr.h>
int main()
{
unsigned char szPass[] = { "strongestpassword" };
cout << "Password:" << endl;
BIO_dump_fp(stdout, (const char*)szPass, strlen((const char*)szPass));
cout << endl << endl;
unsigned char szKey[16];
PKCS5_PBKDF2_HMAC((const char*)szPass, strlen((const char*)szPass), NULL, 0, 5, EVP_md5(), 16, szKey);
cout << "Key derived from password using MD5:" << endl;
BIO_dump_fp(stdout, (const char*)szKey, 16);
cout << endl << endl;
unsigned char szMessage[] = { "Pay Bob 1 dollar" };
cout << "Transaction message:" << endl;
BIO_dump_fp(stdout, (const char*)szMessage, strlen((const char*)szMessage));
cout << endl << endl;
unsigned char szCipher[16];
int nCipherLen = 0;
int nTotCipherLen = 0;
EVP_CIPHER_CTX* pAES128Ctx = EVP_CIPHER_CTX_new();;
EVP_EncryptInit(pAES128Ctx, EVP_aes_128_ctr(), szKey, NULL);
EVP_EncryptUpdate(pAES128Ctx, szCipher, &nCipherLen, szMessage, strlen((const char*)szMessage));
nTotCipherLen = nCipherLen;
nCipherLen = 0;
EVP_EncryptFinal(pAES128Ctx, szCipher, &nCipherLen);
nTotCipherLen += nCipherLen;
EVP_CIPHER_CTX_free(pAES128Ctx);
cout << "Encrypted message:" << endl;
BIO_dump_fp(stdout, (const char*)szCipher, nTotCipherLen);
cout << "If you pass this cipher over the network," << endl << "attacker Bob (Man-In-The-Middle) may capture and modify 9th encrypted byte and send the encrypted transaction!" << endl;
cout << endl << endl;
szCipher[8] ^= 0x01; szCipher[8] ^= 0x09;
cout << "Modified Encrypted message:" << endl;
BIO_dump_fp(stdout, (const char*)szCipher, nTotCipherLen);
cout << endl << endl;
unsigned char szDycMsg[16];
int nDycMsgLen = 0;
int nTotDycMsgLen = 0;
EVP_CIPHER_CTX* pMSG128Ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit(pMSG128Ctx, EVP_aes_128_ctr(), szKey, NULL);
EVP_DecryptUpdate(pMSG128Ctx, szDycMsg, &nDycMsgLen, szCipher, nTotCipherLen);
nTotDycMsgLen = nDycMsgLen;
nDycMsgLen = 0;
EVP_DecryptFinal(pMSG128Ctx, szDycMsg, &nDycMsgLen);
nTotDycMsgLen += nDycMsgLen;
EVP_CIPHER_CTX_free(pMSG128Ctx);
cout << "Modified derypted message the bancker receives:" << endl;
BIO_dump_fp(stdout, (const char*)szDycMsg, nTotDycMsgLen);
cout << endl << endl;
return 0;
}
Conclusion
There are many encryption algorithms available but not all are recommended, such as AES-CTR but there are other counter modes that are highly recommended such as GCM and CCM.