Writeup: CakeCTF 2023
on nevi.dev- Website: https://2023.cakectf.com/
- CTFTime: https://ctftime.org/event/1973
I participated in the team “bacchus-snu”, along with my teammates.
simple signature
It must be a piece of cake.
nc crypto.2023.cakectf.com 10444
We need to forge a signature for the message cake_does_not_eat_cat
. That is,
we need to produce $s, t$ for which $s^w t^{-v} = g^m$.
If we let $s = g^a$ and $t = g^b$, then the signature verification scheme looks like $g^{aw} g^{-bv} \equiv g^m \pmod{p}$, or equivalently, $aw - bv \equiv m \pmod{p-1}$.
From the key generation, we have $v = wy \pmod{p-1}$, so we now have $w(a - by) \equiv m \pmod{p-1}$. We know the values of every variable except $y$, which we can compute as $y \equiv vw^{-1} \pmod{p-1}$.
We can now forge the signature by choosing $a = w^{-1}m + 1$ and $b = y^{-1}$. This works because:
$$ \begin{align*} aw-bv &= w(a-by) \\ &= w(w^{-1}m + 1 - y^{-1}y) \\ &= m \end{align*} $$
We then send $\mathtt{“cake\_does\_not\_eat\_cat”}, g^a, g^b$ and obtain the flag.
$ ./solve.py
[+] Opening connection to crypto.2023.cakectf.com on port 10444: Done
[*] Switching to interactive mode
verified
flag = CakeCTF{does_yoshiking_eat_cake_or_cat?}
[*] Got EOF while reading in interactive
$
[*] Closed connection to crypto.2023.cakectf.com port 10444
ding-dong-ting-ping
nc crypto.2023.cakectf.com 11111
First, we determine the secret prefix length, which turns out to be 17 bytes.
This means we need to forge a ciphertext that decrypts to something that looks
like ?????????????????|user=root|${DATE}
. Split into 16-bytes blocks, this
looks like ["????????????????", "?|user=root|date", "|..."]
. We achieve this
as follows:
- Register with the username
"room|date"
(or any other 9-character username), obtaining ciphertextIV || c0 || c1 || ...
.- This corresponds to the plaintext blocks
["????????????????", "?|user=room|date", "2023..."
. c1
corresponds to the second plaintext block, which was XORed withMD5(c0)
before encryption.
- This corresponds to the plaintext blocks
- Register with the username
"room|date" || XOR(MD5(c0), MD5(c1), "?|user=root|date")
, obtaining the ciphertextIV || c0 || c1 || c2 || ...
.- The ciphertext blocks up to
c1
are the same, because the early plaintext blocks have not changed. c2
corresponds to the third plaintext block, which was XORed withMD5(c1)
before encryption.- That is, just before encryption, this block looks like
XOR(MD5(c0), "?|user=root|date")
.
- The ciphertext blocks up to
- Log in with the ciphertext
IV || c0 || c2...
.- When the server decrypts
c2
, it XORs the resulting plaintext withMD5(c0)
, resulting in"?|user=root|date"
.
- When the server decrypts
We don’t know the value of the secret prefix, but we only need to guess its last byte. We repeat steps 2-3 for every possible value of the prefix, and we obtain the flag.
$ python solution.py
[+] Opening connection to crypto.2023.cakectf.com on port 11111: Done
[+] guess: Done
[*] Switching to interactive mode
Ding-Dong, Ding-Dong, Welcome, root. The ultimate authority has logged in.
This is for you => CakeCTF{dongdingdongding-dingdong-dongdingdong-ding}
===== MENU =====
[1]register [2]login: $
[*] Closed connection to crypto.2023.cakectf.com port 11111