Ticket #1116 (assigned defect)

Opened 1 year ago

Last modified 3 weeks ago

lighttpd 1.4.13 reproducible (every time) segfault when file cannot be stat-ed (with simple test-case)

Reported by: scroffer52 Assigned to: jan (accepted)
Priority: highest Milestone: 1.4.20
Component: core Version: 1.4.13
Severity: critical Keywords:
Cc: Blocking:
Need Feedback: 1

Description

Short version

If you have files which can't be accessed by lighttpd 1.4.13's normal user (i.e. are run via suexec), then lighttpd can easily be tricked into trying to open() them, causing a segfault every time. Do this by post-fixing a slash.

Long version

I have a lighttpd 1.4.13 + FastCGI with SuExec?, running on some files which can't be read by the normal lighttpd user.

This all works fine... unless you try to access specially crafted URLs - which cause lighty to crash _every time_.

Here's my (slightly obscured) set-up:

server.name = "1.2.3.4"

server.document-root = "/var/www/html/"

server.indexfiles = ( "index.html", "index.htm", "index.php" )

fastcgi.server = ( ".php" =>
   ( "localhost" =>
      ( "socket" => "/tmp/webadmin.socket",
        "bin-path" => "/etc/lighttpd/suexec.sh",
        "idle-timeout" => 30,
        "check-local" => "disable",
        "min-procs" => 1,
        "max-procs" => 1,
     ) ) )

I have a file /var/www/html/x.php. Its contents can be anything. For a test case, I used:

<?

phpinfo();

?>

My suexec script is not relevant to the crash, but is completely simple (just execs a shell script that calls php-cgi).

The test file x.php is only accessible by the SuExec? user:

[root@ ~]# ls -l /var/www/html/x.php
-rwxr-x---  1 suexecuser suexecgroup 19 Apr 12 22:06 /var/www/html/x.php

If I access it as http://1.2.3.4/x.php then all is fine - I get what I'd expect. No problems.

However, if I try http://1.2.3.4/x.php/y (i.e. appending a pseudo-directory), then - boom! segfault! This happens every single time. Not good on a live server... :-(

Here are the last few lines of the strace:

poll([{fd=4, events=POLLIN}, {fd=5, events=POLLIN}, {fd=25, events=POLLIN, revents=POLLIN}], 3, 1000) = 1
read(25, "\27\3\1\2\0\7\273)\315YK:\36j#\345\30\321\316\2623!\357"..., 18437) = 517
read(25, 0x8780820, 18437)              = -1 EAGAIN (Resource temporarily unavailable)
stat64("/var/www/html/x.php/y", 0xbffcaa70) = -1 ENOTDIR (Not a directory)
stat64("/var/www/html/x.php/y", 0xbffcab30) = -1 ENOTDIR (Not a directory)
stat64("/var/www/html/x.php", {st_mode=S_IFREG|0750, st_size=19, ...}) = 0
stat64("/var/www/html/x.php", {st_mode=S_IFREG|0750, st_size=19, ...}) = 0
open("/var/www/html/x.php", O_RDONLY|O_LARGEFILE) = -1 EACCES (Permission denied)
--- SIGSEGV (Segmentation fault) @ 0 (0) ---

Attachments

Change History

04/13/2007 03:05:40 AM changed by darix

can you try to reproduce it with http://zen.sh.nu/~darix/lighttpd-1.4.x.r1745.tar.gz. i cant reproduce it here.

do you have follow-symlinks enabled or disabled? in my setup i had php running via external spawning.

04/13/2007 10:16:28 AM changed by scroffer52

The problem exists too in r1745, compiled with identical options.

But... I've now traced this by simplifying my configuration:

The offending lines in my configuration are:

compress.cache-dir         = "/var/cache/lighttpd/compress/"
compress.filetype          = ("text/plain", "text/html")
compress.max-filesize      = 1024000

That compression directory isn't writable by the su-execed user; but I don't think that should matter (as mod_compress doesn't touch PHP output anyway, and as mod_compress is running as the correct user). In any case, changing it to a 777-ed directory doesn't fix the segfault.

Without those lines, no segfault. With those lines, a segfault happens.

04/13/2007 10:16:52 AM changed by scroffer52

Sorry - forgot to mention that I'm not using follow-symlinks anywhere, so it must be the default (on?).

04/13/2007 10:32:12 AM changed by scroffer52

Here's what I got from valgrind:

==3501== Invalid read of size 4
==3501==    at 0x44F69DE: (within /usr/lib/lighttpd/mod_compress.so)
==3501==    by 0x805F4D4: plugins_call_handle_subrequest_start (in /usr/sbin/lighttpd)
==3501==    by 0x804FDCA: http_response_prepare (in /usr/sbin/lighttpd)
==3501==    by 0x8052CB4: connection_state_machine (in /usr/sbin/lighttpd)
==3501==    by 0x8053C9B: network_server_handle_fdevent (in /usr/sbin/lighttpd)
==3501==    by 0x804E4CE: main (in /usr/sbin/lighttpd)
==3501==  Address 0x38 is not stack'd, malloc'd or (recently) free'd
==3501==
==3501== Process terminating with default action of signal 11 (SIGSEGV)
==3501==  Access not within mapped region at address 0x38
==3501==    at 0x44F69DE: (within /usr/lib/lighttpd/mod_compress.so)
==3501==    by 0x805F4D4: plugins_call_handle_subrequest_start (in /usr/sbin/lighttpd)
==3501==    by 0x804FDCA: http_response_prepare (in /usr/sbin/lighttpd)
==3501==    by 0x8052CB4: connection_state_machine (in /usr/sbin/lighttpd)
==3501==    by 0x8053C9B: network_server_handle_fdevent (in /usr/sbin/lighttpd)
==3501==    by 0x804E4CE: main (in /usr/sbin/lighttpd)
--3501-- discard syms at 0x451C000-0x4527000 in /lib/libnss_files-2.3.4.so due to munmap()

04/13/2007 12:21:47 PM changed by jan

  • status changed from new to assigned.
  • milestone set to 1.4.15.

05/21/2007 03:23:02 PM changed by scroffer52

Do the lighttpd developers count this as a low-priority bug?

I was expecting a consistently remotely exploitable crash to be the kind of thing that was a level 1 priority!

04/24/2008 09:46:59 PM changed by stbuehler

  • pending set to 1.

I couldn't reproduce it either; if open (in stat_cache_get_entry) fails with EACCES, lighty returns 403 Forbidden and does not start mod_compress.

If you still have trouble with this in the current version, please provide a backtracke of a debug build (so we have the line number in which the segfault happens).


Add/Change #1116 (lighttpd 1.4.13 reproducible (every time) segfault when file cannot be stat-ed (with simple test-case))




Change Properties