Yay, crypto :D
Once we open the .zip file, we get two files:
-rw-r--r-- 1 neowo neowo 1980 Sep 27 15:22 crypt.c
-rw-rw-r-- 1 neowo neowo 42 Sep 27 15:19 flag.txt.encrypted
where flag.txt.encrypted is our flag, but encrypted using crypt.c
Next obvious step: we open crypt.c (how kind of the challenge creator to give us the source code), aaand its scary. Especially if you don’t know C.
The title gives us a great hint: we’re playing with PRNG, aka Pseudo-Number generators.
How do they work? Pretty simple: We know that computers cannot generate “True” randomness. So instead, we use some smart algorithms that do the something close enough: “false” randomness. A bunch of equations that will, given an original state or “seed”, produce seemingly random values. Depending on the algorithm chosen, it can be rather hard to compute the seed from a small amount of random values. All the security resides in the secrecy and entropy of the seed. If we know the seed, we can use the same algorithm to get the exact same values every time; likewise, if the seed has low entropy (aka is chosen from predictable sources), we can find it.
Most modern algorithms use CSPRNG (Cryptographically Secure (or Suitable) Pseudo-Random Number Generators". These algorithms not only use mathematical concepts making it very hard to compute the seed from the random values but also environments of high entropy to generate the seed. Those environments could be a combination of time, user mouse movements, CPU temperatures, weather data or even Cloudflare’s lava lamp wall. In conclusion, a good random number generator needs two elements:
crypt.cIt can be hard to find at first glance what the flaw is. An experimented user, however, would instantly notice these lines of code:
int main(int argc, char **argv) {
char file_path[128];
FILE *input_file, *output_file;
SetSeed(time(NULL));
SetSeed(time(NULL)); ? That’s very interesting. After a quick google search, we can realize that time(NULL); returns the current system’s time as a timestamp, whereas SetSeed(); sets the seed. Remember what we said? If we can find the seed, we can use the same algorithm to generate the exact same values in the same order. So all we need, is to find the exact time the file was encrypted. But wait! Our system store’s that in the file’s metadata!
>>> stat flag.txt.encrypted
File: flag.txt.encrypted
Size: 42 Blocks: 8 IO Block: 4096 regular file
Device: 10302h/66306d Inode: 22186554 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ neowo) Gid: ( 1000/ neowo)
Access: 2024-09-27 15:21:13.000000000 +0100
Modify: 2024-09-27 15:19:48.000000000 +0100
Change: 2024-10-02 17:34:25.545820435 +0100
Birth: 2024-10-02 17:34:25.545820435 +0100
But this isn’t timestamp format… Most people would use online tools to manually convert the date to timestamp, but we’re beyond that:
$ date -d "$(stat --printf=%y flag.txt.encrypted | cut -d. -f1)" +"%s"
1727446788
Now we can modify crypt.c and add our seed: 1727446788, compile then run:
$ sed -n 64,68p crypt.c
int main(int argc, char **argv) {
char file_path[128];
FILE *input_file, *output_file;
SetSeed(1727446788);
$ gcc crypt.c
$ ./a.out flag.txt.encrypted
Key used: 5aWHFANxfFtHwimgpceHzS5TOc21RYvw
File flag.txt.encrypted.encrypted has been encrypted!
DONE.
$ cat flag.txt.encrypted.encrypted
Intake24{PrnG_1s_n0+_aS_SAFE_!5_1T_sEEM5}
Challenge solved :D