There are people in the world that believe that CTR resists bit flipping attacks of the kind to which CBC mode is susceptible.

Re-implement the CBC bitflipping exercise from earlier to use CTR mode instead of CBC mode. Inject an

`admin=true`

token.

Flipping bits with CTR is extremely easy: flip a bit in the ciphertext, it flips the corresponding bit in the plaintext.

Remember that CTR mode builds a pseudo-random keystream that is XORed against the message. That is for each bit $ M_i $ of the message:

$$ C_i = M_i \oplus K_i $$If you XOR ciphertext bit $ C_i $ with some bit $ X_i $ (noting result $ C'_i $) then the decryption procedure will result in:

$$ \begin{align} M'_i & = C'_i \oplus K_i \\ & = C_i \oplus X_i \oplus K_i \\ & = M_i \oplus K_i \oplus X_i \oplus K_i \\ & = M_i \oplus X_i \end{align} $$Now in this challenge we have the same instructions as in challenge 16:
we control some part of the plaintext
but cannot insert characters like ";" and "=",
and yet we want to get a ciphertext that decrypts to `admin=true`

.

And just like in challenge 16,
we are going to encrypt just any message like `XXX...`

and to manipulate the ciphertext to flip bits in the underlying plaintext
until we get this `admin=true`

.

Only this time it will be much simpler: flipping plaintext bits in CBC mode required to flip bits in the previous block of ciphertext and we had to write a function to manage all that. With CTR however we simply have to apply our XOR on the ciphertext just like we would apply it to the plaintext itself.

In [1]:

```
import os
import urllib
from libmatasano import transform_aes_128_ctr, bxor, html_test
# taken from challenge 16 and adapted
class Oracle:
def __init__(self):
self.key = os.urandom(16)
self.nonce = None
def encrypt(self, msg):
# using urllib to quote characters (a bit overkill)
quoted_msg = urllib.parse.quote_from_bytes(msg).encode()
full_msg = (
b"comment1=cooking%20MCs;userdata="
+ quoted_msg
+ b";comment2=%20like%20a%20pound%20of%20bacon"
)
if self.nonce == None:
self.nonce = 0
else:
self.nonce += 1
ciphertext = transform_aes_128_ctr(full_msg, self.key, self.nonce)
return self.nonce, ciphertext
def decrypt_and_check_admin(self, ctxt, nonce):
ptxt = transform_aes_128_ctr(ctxt, self.key, nonce)
if b";admin=true;" in ptxt:
return True
else:
return False
```

In [2]:

```
oracle = Oracle()
chosen_plaintext = b'X'*(len(';admin=true'))
nonce, ctxt = oracle.encrypt(chosen_plaintext)
to_xor = (b'\x00'*len(b"comment1=cooking%20MCs;userdata=")
+bxor(b';admin=true', chosen_plaintext))
altered_ctxt = bxor(ctxt, to_xor)
print(transform_aes_128_ctr(altered_ctxt, oracle.key, nonce))
html_test(oracle.decrypt_and_check_admin(altered_ctxt, nonce))
```