3

I have been discussing the effectiveness of GNU libc's fclose() function to thwart successful exploitation of trivial vulnerabilities due to segmentation fault when called without a valid pointer to a FILE data structure (implementations of libc on other operating systems fail silently). For instance, given a simple vulnerable function using strcpy() such as:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void
foo(const char *str)
{
  char buffer[256];
  int ret = 123456;

  FILE *fp = tmpfile();
  strcpy(buffer, str);
  if (ret != 500)
    fclose(fp);
}

int 
main(int argc, const char **argv)
{
  foo(argv[1]);
  return 0;
}

Even when compiled with most memory-protection mechanisms disabled:

cc -m32 -ggdb -fno-stack-protector -z execstack -z norelro main.c

The function, regardless of being overwritten up to EIP would never return since:

(gdb) r $(python -c "print 'A' * 286")
Program received signal SIGSEGV, Segmentation fault.
0xf7e3e307 in fclose@@GLIBC_2.1 () from /lib/libc.so.6
(gdb) ba
#0  0xf7e3e307 in fclose@@GLIBC_2.1 () from /lib/libc.so.6
#1  0x080484bc in foo (str=0x41414141 <error: Cannot access memory...
#2  0x41414141 in ?? ()
#3  0x41414141 in ?? ()

Could there be a way to achieve code execution for such trivial function?. Since the ret variable can be overwritten, can it be set to 500 so that the if condition which determines if fclose() is called or not could be bypassed?

2
  • The only last problem you may encounter (outside the gdb prompt) is the ASLR. But, there is no problem exploiting this small program with this bug. What did you already tried and what was the problem ? Note that the fclose() is not really a problem.
    – perror
    Dec 21 '15 at 20:43
  • yeah, since ret and fp are overwritten it's possible to write fp with its address before the overflow and it'd work on non-ASLR systems, but the in-stack address would differ from system to system. As far as I know, ret cannot be overwritten to 500 again. Dec 21 '15 at 21:35
1

Since the ret variable can be overwritten, can it be set to 500 so that the if condition which determines if fclose() is called or not could be bypassed?

If the attacker can overwrite the ret variable on the stack with an arbitrary value, then yes.

And even if the attacker can't overwrite ret, they still might be able to overwrite fp with a value that is acceptable to fclose().

Furthermore, your approach relies on several implementation-specific details. For example:

  • You assume that fp is between buffer and the return address, but the compiler may not order the variables this way.
  • You assume that fclose() will always throw an exception when given a corrupted pointer, however this is not a requirement and may change in future versions of GNU libc.
3
  • The ret variable can be overwritten, but i don't think it can be changed to a value such as 500 in order to avoid calling fclose() because of the null byte in '\x00\x00\xf4\x01'. The assumption of the ordering was on purpose, the overflow should rewrite fp address. Regarding fclose() always throwing an exception when invalid pointers are given, I couldn't bruteforce a single address that did not ended on SIGSEGV, but sometimes advances up to FILE->_IO_file_close. Dec 21 '15 at 21:50
  • In addition, there is an old challenge that suggests GOT overwritting, could it be possible to achieve code execution with the code I posted using such technique?. What I'm proposing is that functions that contain trivial overflows that overwrite a FILE pointer cannot be reliably exploited. Dec 21 '15 at 21:56
  • "because of the null byte in '\x00\x00\xf4\x01'." -- Right, which is why I said that the attacker would need to be able to overwrite the ret variable with an arbitrary value, which isn't possible here. Dec 21 '15 at 22:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.