A comment's creation automatically invokes this function, that's why there is the auto-reply part. What is notable compared to the comment init in that most fields are not initialized there. Replies' delete_reply_func field is filled on the fly during the destruction of the parent comment at 0x08048f82 :

The deletion is easy to understand : if a comment has no replies, it is removed from the linked list - obviously causing potential null pointers faults for the first and last comments here. Then if its status is still the same 0xDEADBEEF set during initialization, the function pointers for its replies are filled. Then its own deletion's function pointer is called.

We see that comments should never be destroyed, as there is not way of removing replies, yet the reply_count must be <= 0 for the comment to be deleted. And as there is always at least the auto-reply this should never happen. However, the jump is a jle - signed comparison, so if we add 0x7f replies to the first auto-reply, we have an integer overflow as 0x80 is negative.

It basically checks that the first replies have had their function pointer initialized and hop to the actual deletion if it is the case. Many logical errors in this function, the most obvious being the fact that most function pointers are left unchecked before being executed.

Adding the fact that the "modify comment" feature changes the status code of a comment, we have everything in hands to hijack EIP :

1. spray the heap with large comments

2. add 0x7f replies to each one to remove them

3. add another comment with 0x7f replies

4. modify the comment to change the 0xDEADBEEF status

5. delete the comment to have delete_reply_func pointing to uninitialized data

From there, the program should exit after printing "Detected" as the first replies's function pointers do not match free_wrapper (0x080487c4). With a simple metasploit pattern we see that those two function pointers are at offsets 36 and 644 of our third comment's content.

There it is, we control EIP. However heap is not executable and I did not find much interesting things in registers nor around the top of the stack. However, grimmlin spotted that system() was in the plt and that his *esp pointed to the "x" from one of the replies added to comment 8 - I wonder why it wasn't the case for me. Having that, the last replies' content has to be changed with any command to be executed, and the target by 0x8048630 - system@plt. With successive commands, we find that the key file is /home/onetime/key.txt, end of story.