Some block cipher modes, such as OFB, CTR, or CFB, turn a block cipher into a stream cipher. The idea behind stream ciphers is to produce a pseudorandom keystream which is then XORed with the plaintext. One advantage of stream ciphers is that they can work of plaintext of arbitrary length, with no padding required.

OFB is an obscure cipher mode, with no real benefits these days over using CTR. This challenge introduces an unusual property of OFB.


This page offers a convenient way for you to interact with the challenge functions. You can also use GET requests to send and receive data directly from the listed routes/endpoints if you wish. For more information see the FAQ.

Your aim is to recover the FLAG value. Once you have it, submit it on the CryptoHack Symmetric Ciphers page.


from Crypto.Cipher import AES

KEY = ?
FLAG = ?

def encrypt(plaintext, iv):
    plaintext = bytes.fromhex(plaintext)
    iv = bytes.fromhex(iv)
    if len(iv) != 16:
        return {"error": "IV length must be 16"}

    cipher =, AES.MODE_OFB, iv)
    encrypted = cipher.encrypt(plaintext)
    ciphertext = encrypted.hex()

    return {"ciphertext": ciphertext}

def encrypt_flag():
    iv = os.urandom(16)

    cipher =, AES.MODE_OFB, iv)
    encrypted = cipher.encrypt(FLAG.encode())
    ciphertext = iv.hex() + encrypted.hex()

    return {"ciphertext": ciphertext}


Hex Input Only
Hex Input Only

XOR tool

Use this form to XOR two hex strings together.

Hex Encoder/Decoder

This is a convenient encoder designed for ASCII <-> Hex translations. It won't work for decoding hex to byte streams and will just show [unprintable] in that case.