Stream Ciphers

Symmetry

Challenge website: https://aes.cryptohack.org/symmetry/arrow-up-right

OFB works like a basic stream cipher, using an XOR operation between the plaintext and a keystream to produce ciphertext. Because XOR is reversible, if you apply the same process to the ciphertext using the same keystream, you’ll get back the original plaintext.

In OFB sense, encrypting a plaintext twice will get you to the original plaintext.

import requests

url = "https://aes.cryptohack.org/symmetry/"

def encrypt(plaintext, iv):
    response = requests.get(url + "encrypt/" + plaintext + "/" + iv)
    return response.json()['ciphertext']

def encrypt_flag():
    response = requests.get(url + "encrypt_flag/")
    return response.json()['ciphertext']

# Get encrypted flag
ct = encrypt_flag()

# Encryption == Decryption
plaintext = ct[32:]
iv = ct[:32]
flag = encrypt(plaintext, iv)

print(bytes.fromhex(flag))

Flag: crypto{0fb_15_5ymm37r1c4l_!!!11!}

Bean Counter

Challenge website: https://aes.cryptohack.org/bean_counter/arrow-up-right

Analyzing the code, we realize that the value for self.stup never changes. Consequently, all blocks are encrypted with the same key. We can get the key by XOR-ing the first block's ciphertext with its plaintext. The plaintext is known to us because the first 16 bytes of a PNG file is the same everywhere. After obtaining the key, we could just decrypt the rest of the blocks.

Flag: crypto{hex_bytes_beans}

CTRIME

Challenge website: https://aes.cryptohack.org/ctrime/arrow-up-right

In this challenge, we are given an oracle. The oracle takes our input and append it with the flag. Then, the combined data are compressed using zlib before being encrypted using the CTR mode.

In zlib, there's a matching and replacement of duplicate strings with pointers.arrow-up-right So, what happens if we input a known part of the flag to the encrypt function? Here, I replicated the code locally, and added a dummy flag for testing.

As we can see, encrypting crypto{ gave us shorter ciphertext, as the data was able to be compressed further because of the duplicate string. The logic for the exploit is that we could brute-force characters to append to known and choose the character that produces the shortest ciphertext.

However, in practice, it's not that simple. Most of the time, adding characters that are already in the flag should not even change the length of the original ciphertext. But of course, there are times where adding any character would result in adding the length of the ciphertext, no matter what character. For this, I make a modification to the code so that the code stops and ask for my own input on what I think the character should be (this would make sense if you run the code below). In addition, from trial and errors, I noticed that you should just shift the known characters instead of always appending them, because this can mess with the compression stuffs.

Anyways, here's the solver script.

Flag: crypto{CRIME_571ll_p4y5}

Last updated