[Crypto] Baby Web Crypto
Simple web challenge with some crypto.
For this challenge we’re given a URL to a site that only shows us a robot image.
It’s pretty clear that we should now visit robots.txt.
1
2
User-Agent: ctf-players
Disallow: /secret-flag-site.html
On the “secret flag site” we’re presented with a simple form asking us for a password.
In the source code of the page we see a input field and some JavaScript code that handles the logic.
1
2
3
<h3>enter code to get flag</h3>
<input type="text" pattern="\d*" maxlength="4" id="code" />
<button>get flag</button>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
const enc = (t) => new TextEncoder().encode(t);
const dec = (t) => new TextDecoder().decode(t);
async function getKey(code) {
const digest = await crypto.subtle.digest("SHA-512", enc(code));
const array = new Uint8Array(digest);
return crypto.subtle.importKey("raw", array.slice(0, 16), "AES-CBC", true, ["encrypt", "decrypt"]);
}
async function decode(code) {
const key = await getKey(code);
const message = new Uint8Array([
33, 148, 65, 177, 82, 219, 127, 213, 218,
107, 19, 190, 78, 99, 28, 243, 246, 178,
189, 217, 76, 107, 19, 165, 230, 50, 180,
196, 24, 177, 59, 139,
]);
let decrypted;
try {
decrypted = await crypto.subtle.decrypt({
name: "AES-CBC",
iv: new Uint8Array(16),
}, key, message);
} catch (err) {
return "invalid code!";
}
const flag = dec(decrypted);
if (!flag.startsWith("CTF{")) {
return "invalid code!";
}
return flag;
}
const button = document.querySelector("button");
const codeInput = document.querySelector("#code");
const result = document.querySelector("#result");
async function handler() {
const code = codeInput.value;
const flag = await decode(code);
result.textContent = flag;
}
button.addEventListener("click", handler);
codeInput.addEventListener("keydown", (ev) => {
if (ev.key === "Enter") {
handler();
}
});
The input field has a pattern and maxlength defined which limits the inputs to the logic. It boils down to this: “\d{1,4}”
The logic portion of the page fetches the input value and attempts to decrypt the array with the sha512 hash of the input as the key.
We can write a simple python script to bruteforce all 4 digit combinations and attempt to decrypt the ciphertext.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from Crypto.Cipher import AES
import hashlib
ct = bytes([
33, 148, 65, 177, 82, 219, 127, 213,
218, 107, 19, 190, 78, 99, 28, 243,
246, 178, 189, 217, 76, 107, 19, 165,
230, 50, 180, 196, 24, 177, 59, 139,
])
def decrypt(key):
try:
key = hashlib.sha512(key.encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_CBC, b'\x00' * 16)
return cipher.decrypt(ct)
except:
return false
for i in range(0, 10000):
res = decrypt(str(i).ljust(4, '0'))
if res != False and b'CTF{' in res:
print(f'Key: {i}; Plaintext: {res}')
break
After a short while the script will return us the key used and the decrypted value:
1
Key: 5826; Plaintext: b'CTF{53cR3t_w3b_F14G}\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c'
Flag: CTF{53cR3t_w3b_F14G}