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.
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.
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.)
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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