Home IrisCTF 2023: Nonces and Keys

IrisCTF 2023: Nonces and Keys

Challenge Description

Because of our revolutionary AES-128-OFB technology we have encrypted your user data so securely that even with the key (k=0x13371337133713371337133713371337) evil hackers can’t read out the passwords!!!

Nonces and Keys

The description of this challenge gives quite a bit of information about the methodology. It states, that AES-128 with the OutputFeedBack mode was used and even gives us the key. The OFB Mode uses the AES encryption function in combination with an IV to generate a keystream. This keystream can then easily be XOR-ed to the plaintext to get the cipher.

AES-128-OFB Block Diagram

To decrypt the attached sqlite file we would need the IV Nonce, which is unfortunately missing. So let’s look at what we actually have here:

  • The Ciphertext
  • The Key

What we also have is the Plaintext of the first block, because Sqlite3 has a constant header. By XOR-ing the known Plaintext “SQLite format 3\0” (53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00) with the first 16 bytes of the ciphertext (which is conveniently exactly the block size of AES), we get the first 16 Bytes of the keystream.

We got the keystream

Now we can use a tool like Cyberchef (or some CTF-Quality code) to perform the XOR and AES operations to recover the IV. Because of the way Cyberchef handles AES the IV of the AES Decrypt block has to be set to 00000000000000000000000000000000 with mode set to ECB/Nopadding to perform a plain AES decrypt.

(If you look closely at the block diagram, you can see that even without ever getting the original nonce, we could get all the following blocks because the previous stream (that we just calculated using the known plaintext) is used to generate the next keystream.)

Cyberchef IV recovery

This operation gives us the originally used (and randomly generated) Nonce of c15b1b373a1d73473475e81decb67daa. We can now use this with together with the given key to extract the original SQLite file. We could now open this with sqlite or just pull the flag out of the output directly, because sqlite does not do any special encoding:


Challenge Creation

The database entries were just randomized entries created using faker-js. This script simply encrypts the given sqlite database using a random IV:

import crypto from 'crypto';
import { pipeline } from 'stream';
import { createReadStream, createWriteStream } from 'fs';

const key = Buffer.from("13371337133713371337133713371337", 'hex')
const iv = crypto.randomBytes(16);

const cipher = crypto.createCipheriv("aes-128-ofb", key, iv);

console.log("key=" + key.toString('hex'), "iv=" + iv.toString('hex'));

const rs = createReadStream("challenge.sqlite3");
const ws = createWriteStream("challenge_enc.sqlite3");

pipeline(rs, cipher, ws, (err) => {
    if (err) throw err;

This was the first real CTF challenge I ever built and I hope it was fun to solve :P

This post is licensed under CC BY 4.0 by the author.