This is a short post to (hopefully) answer some of the questions that I have received
about my exploit auir.py and talk about a few interesting things that I have found.
Why Free?
In my exploit, I triggered the double free corruption error instead of allocating another chunk to invoke __malloc_hook because of the constraints on the one shot gadget that I used.
Does malloc actually get called when you trigger the double free corruption error?
Yes, it does invoke __malloc_hook. To be more exact, it does not directly call __malloc_hook but one of the functions that is used calls the malloc internally.
So, which function triggers the malloc?
This is where I was stuck because I genuinely did not know. I knew for sure that malloc gets called because some of the public ctf write-ups (sorry, can not exactly remember the link) talk about freeing the chunk twice triggers malloc. However, none of the write-ups (as far as I am aware) talks about the details on which function calls the malloc. Based on the advice I have received, it seems __fortify_fail was the function that calls the malloc internally.
Does __fortify_fail actually get called when you raise the double free corruption error?
I knew that __foritfy_fail gets triggered by __stack_check_fails:
This always gets triggered when you overwrite the stack canary (stack smashing, for instance). However, I was not quite sure if the function __fortify_fail actually gets triggered for the heap related error. In order to verify this, I created a small toy program that intentionally frees the same chunk twice:
Using gdb, I set a breakpoint on __fortify_fail and ran the program. The result of the program as expected threw the double free corruption error. Looking through the backtrace, __fortify_faildid not seem to get called:
Instead, malloc_printerr was called. This was good to know but there were still no traces of call to malloc. Since I did not want to read the source code line by line, I set a breakpoint at __malloc_hook and re-run the program to see which function actually calls the malloc internally:
Looking at the backtrace again, I noticed that it succesfully hit __malloc_hook (the current rip is pointing at cc,which is the breakpoint I set earlier) and __strdup function was called before.
__strdup function returns the pointer to the duplicated string and sure enough it internally uses malloc to allocate the new buffer for a copy of the string:
__strdup function gets called by _dl_load_cache_lookup, which is responsible for looking up the passed in name in ld.so and returning the stored filename there:
Does __fortify_fail call malloc internally?
Since __fortify_fail calls backtrace_and_maps internally, it should theoretically reach the similar path. However, when I ran my toy bof program, it was aborted as soon as it printed out an error message. After reading through the source code, I realized the first argument to libc_message is the flag that tells whether it requires to do backtrace. In this case, it had the value of 1, which is to do_abort. The rest of the value can be found in stdio.h:
Since the value of the flag was always 1, it made sense that it did not go through the same path (like printing out the traces).
But, I set a breakpoint at __malloc_hook again just to make sure and overwrote the canary to trigger __fortify_fail:
Based on the result, I was able to confirm that malloc did not get called….
What’s next?
It will be interesting to dig in and see if there are any writable places in __fortify_fail because I did not look into it deeply. Even better, it will be very useful to discover the other writable places by reading through the glibc source code. Let me know if you find any :)
And…
That’s it for this challenge. I hope this answers the questions and gives a bit of explanation on whether malloc function actually gets called when you raise the doulbe free corruption error.