Description
An easy RE…
Flag Format : KCTF{flag}
File Provided: E4sy_P3asy.zip
Solution:
If we execute the binary, we see the following:
$ ./E4sy_P3asy.ks
========================================
E4sy P3asy - KnightCTF 2026
========================================
[*] Enter the flag to prove your worth!
flag> test
Try again!The executable asks for a value from the user, then prints a message. Looks like we need to provide the flag, and if it is correct, the program will tell us it was the right one.
I checked the binary with strings. The output had some key pieces that indicated how to get the solution:
EVP_MD_CTX_free
EVP_md5
EVP_MD_CTX_new
EVP_DigestFinal_ex
EVP_DigestUpdate
EVP_DigestInit_ex
The above suggested the use of MD5.
T$ H
D$PKnigH
CTF_2026f
D$ThtH
D$^_
D$_s@ltH
The above suggests that the MD5 was salted with a value in the lines of: KnigHCTF_2026f_s@ltH. From that and given that this CTF is KnightCTF 2026, a likely salt value would be: KnightCTF_2026_s@lt. Note: the salt can also be extracted with gdb in a more precise manner.
Then we can see the following string:
%s%zu%c
and towards the end:
========================================
E4sy P3asy - KnightCTF 2026
[*] Enter the flag to prove your worth!
[!] Interesting... but that's a decoy flag from a different universe.
[!] You're in KnightCTF, not GoogleCTF :)
[?] Format looks suspicious... but not quite.
781011edfb2127ee5ff82b06bb1d2959
4cf891e0ddadbcaae8e8c2dc8bb15ea0
d06d0cbe140d0a1de7410b0b888f22b4
d44c9a9b9f9d1c28d0904d6a2ee3e109
e20ab37bee9d2a1f9ca3d914b0e98f09
d0beea4ce1c12190db64d10a82b96ef8
ac87da74d381d253820bcf4e5f19fcea
...
There are 32 of what looks like MD5 hashes. The %s%zu%c would indicate that a character was used to call EVP_DigestUpdate, so basically, each of the hashes were calculated as follow:
MD5(salt, character)
We need to create a script that bruteforces each MD5 hash using a character at a time with the salt:
import hashlib
import string
salt = "KnightCTF_2026_s@lt"
hashes = [
"781011edfb2127ee5ff82b06bb1d2959",
"4cf891e0ddadbcaae8e8c2dc8bb15ea0",
"d06d0cbe140d0a1de7410b0b888f22b4",
"d44c9a9b9f9d1c28d0904d6a2ee3e109",
"e20ab37bee9d2a1f9ca3d914b0e98f09",
"d0beea4ce1c12190db64d10a82b96ef8",
"ac87da74d381d253820bcf4e5f19fcea",
"ce3f3a34a04ba5e5142f5db272b6cb1f",
"13843aca227ef709694bbfe4e5a32203",
"ca19a4c4eb435cb44d74c1e589e51a10",
"19edec8e46bdf97e3018569c0a60baa3",
"972e078458ce3cb6e32f795ff4972718",
"071824f6039981e9c57725453e005beb",
"66cd6098426b0e69e30e7fa360310728",
"f78d152df5d277d0ab7d25fb7d1841f3",
"dba3a36431c4aaf593566f7421abaa22",
"8820bbdad85ebee06632c379231cfb6b",
"722bc7cde7d548b81c5996519e1b0f0f",
"c2862c390c830eb3c740ade576d64773",
"94da978fe383b341f9588f9bab246774",
"bea3bb724dbd1704cf45aea8e73c01e1",
"ade2289739760fa27fd4f7d4ffbc722d",
"3cd0538114fe416b32cdd814e2ee57b3",
"8af7f29b21564b87336ed4e4cdfb1a20",
"4d3f509284784ab67818b13e74fd5ebe",
"5a45666e1387ff739eb470f840532099",
"c96997e23323e502d5d0b07d24a68d50",
"efb388057adc3fe734d8b6ffb2bdd1e1",
"6df2aaf58e39f89d7a31a72e19e0efbf",
"9bbd077d3df7faedfd22fae85043d6c0",
"dc72837ea6ba778eebbd0401a35182f4",
"efa0bc7c5da3545ba15548b4b85eaf76",
"1c36dbad9144b1e1d23ddecb5d4df3e9",
"980663d212aed1ba720f7735873fa73c",
"9df295c8874bcac93c98babe78b9c946",
"2672e4d30c7da0d30749f09bc1b1eefa",
]
charset = string.printable # full printable ASCII
flag = []
for i, target in enumerate(hashes):
found = False
for c in charset:
# try both index styles defensively
for idx in (i, i + 1):
s = f"{salt}{idx}{c}"
if hashlib.md5(s.encode()).hexdigest() == target:
flag.append(c)
found = True
break
if found:
break
print("Recovered flag:")
print("KCTF{" + "".join(flag) + "}")python solution.py
Recovered flag:
KCTF{_L0TS_oF_bRuTE_foRCE_:P}Flag: KCTF{_L0TS_oF_bRuTE_foRCE_:P}