Skip to content

Instantly share code, notes, and snippets.

@nil0x42
Created March 2, 2021 08:09
Show Gist options
  • Save nil0x42/8bb48b337d64971fb296b8b9b6e89a0d to your computer and use it in GitHub Desktop.
Save nil0x42/8bb48b337d64971fb296b8b9b6e89a0d to your computer and use it in GitHub Desktop.
Simple, easy to understand implementation of Bit-Flipping attack on CBC mode
#!/usr/bin/python3 -u
# requirements: PyCryptodome
import base64
import subprocess
from Crypto.Util.strxor import strxor
from Crypto.Util.Padding import pad
### variables to set
PLAINTEXT = b"id=12345678;name=myname;is_admin=false;[email protected]"
CIPHERTEXT = base64.b64decode("RlDfIOgTrnUsIZJE802+wNr0jll/3ZiM4BGHH7xMO8TF0QBkebuuychCaeDBhUP2kOJnerZm3kQoe3h9Sv12oA==")
BLOCK_SIZE = 16 # AES
PADDING_TYPE = "pkcs7"
OLD_STR = b"false" # string to flip
NEW_STR = b"true;" # string that will replace OLD_STR
xxd_cmd = ["xxd", "-g1", "-c", str(BLOCK_SIZE)]
print("\n[+] Infos:")
print("OLD_STR = %s" % OLD_STR)
print("NEW_STR = %s" % NEW_STR)
print("\n[+] Plaintext (%d bytes):" % len(PLAINTEXT))
subprocess.run(xxd_cmd, input=PLAINTEXT)
if len(PLAINTEXT) != len(CIPHERTEXT):
PLAINTEXT = pad(PLAINTEXT, block_size=BLOCK_SIZE, style=PADDING_TYPE)
print("\n[+] Plaintext [Padded with %s] (%d bytes):" % (PADDING_TYPE, len(PLAINTEXT)))
subprocess.run(xxd_cmd, input=PLAINTEXT)
print("\n[+] Ciphertext (%d bytes):" % len(CIPHERTEXT))
subprocess.run(xxd_cmd, input=CIPHERTEXT)
assert len(PLAINTEXT) == len(CIPHERTEXT)
assert len(CIPHERTEXT) % BLOCK_SIZE == 0
assert OLD_STR in PLAINTEXT
assert len(OLD_STR) == len(NEW_STR)
blocks = [PLAINTEXT[i:i + BLOCK_SIZE] for i in
range(0, len(PLAINTEXT), BLOCK_SIZE)]
block_offset = 0
in_block = -1
for block_id, block in enumerate(blocks):
if OLD_STR in block:
in_block = block_id
block_offset = block.find(OLD_STR)
break
if in_block == -1:
raise Exception("String to flip must be contained in one single block")
elif in_block == 0:
raise Exception("String to flip cannot be part of the first block")
# pos = same block offset ,in previous block
pos = (in_block - 1) * BLOCK_SIZE + block_offset
end_pos = pos + len(OLD_STR)
# here the magic happens...
result = CIPHERTEXT[:pos]
result+= strxor( strxor(OLD_STR,NEW_STR), CIPHERTEXT[pos:end_pos] )
result+= CIPHERTEXT[end_pos:]
print("\033[32m\n[+] Flipped ciphertext:")
subprocess.run(xxd_cmd, input=result)
print("\n[+] Flipped ciphertext [BASE64]:")
print(base64.b64encode(result).decode() + "\033[0m")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment