While being super relevant with my meme references, I wrote a program to see how much you understand heap allocations.
Here, we do not have the source code, but only provided with binary. So, as it's once said "The one who knows reverse engineering has everything open-source". So, we will try to reverse the application.
I was using Ghidra for this part and here is sample code I got:
For better understanding, let's convert the byte-data into readable format. It can be done using CyberChef - convert from hex and revert. So, let's compare the results. Here is the section below:
some_var_1 = 0x2073692073696874;
some_var_2 = 0x6d6f646e61722061;
some_var_3 = 0x2e676e6972747320;
some_var_4 = 0;
This is equal to this is a random string. Let's call it end_buf.
Let's look at the next section:
*many_malloc_variable = 0x73746172676e6f43;
many_malloc_variable[1] = 0x662072756f592021;
many_malloc_variable[2] = 0x203a73692067616c;
*(undefined *)(many_malloc_variable + 3) = 0;
strcat((char *)many_malloc_variable,flag_buf);
This makes the following string: Congrats! Your flag is: <flag>. Let's call it flag_output. It seems that our goal is to retrieve this string. It is stored in many_malloc_variable. We will return to it later, for now - let's continue analyzing strings.
*strange_malloc_var = 0x5420217972726f53;
strange_malloc_var[1] = 0x276e6f7720736968;
strange_malloc_var[2] = 0x7920706c65682074;
*(undefined4 *)(strange_malloc_var + 3) = 0x203a756f;
*(undefined *)((long)strange_malloc_var + 28) = 0;
This converts to Sorry! This won't help you: string. Let's call it start_buf. Then, it concatinates with end_buf and makes the string Sorry! This won't help you: this is a random string. that will store in strange_malloc_var. This output we should receive if we don't solve the task, I think.
Now, we've understood that we should capture flag_output. But, in the code, we see that many_malloc_variable and start_buf were freed at the end of the program. many_malloc_variable contained the flag, so in order to retrieve it we should implement Use-after-free vulnerability (as we can provide changes only after the memory was freed). And, let's think, how it should be done.
And we have the hint in the tasks's name - we can use cache. There is a special option in glibc called tcache. You can read about it here. So, this is an optimization mechanism for programs which stores the freed memory in the special place. This is made in to make easier new allocation as when we use malloc to allocate new space of the same size, instead of using new memory - it will use the memory that was saved to tcache. That's why when we insert some random values we receive the part of strange_malloc_var that was freed - the program prints the new allocated buffer of size 128. When it was allocated - it took the buffer from tcache (there is no initialization of this buffer - this is the only explanation why we have something as the output).
So, the new allocated buffer took the first buffer that was situated in the tcache. Actually, it is the last buffer that was freed. So, in order to get the second one that contains the flag, we should either make new malloc of the same size, but we do not have the possibility to do it according to the provided functionality, or make the tcache point to the buffer we need before the last allocation. This way seems possible here.