Bug #3062


Server Aborted due to Malicious Data sent through CGI Sendfile

Added by axe34 over 3 years ago. Updated over 3 years ago.

Target version:


My configuration file for this is as following.

cgi.assign = ( ".pl"  => "/usr/bin/perl",
                   ".cgi" => "/usr/bin/perl" )
$HTTP["url"] =~ "^/cgi/" {
         cgi.x-sendfile = "enable" 
         cgi.upgrade = "enable" 
         cgi.local-redir = "enable" 

My cgi script looks like this:
use CGI;

my $q = CGI->new;
my %headers = map { $_ => $q->http($_) } $q->http();
for my $header ( keys %headers ) {
    if ($header = "HTTP_X_SENDFILE") {
        print "X-Sendfile: $headers{$header}\n\n";


Basically, the script takes the data from the X_SENDFILE http header and passes it to the response header.
While testing this functionality, I found a couple crashes. None of them actually crashed the server, they all resulted in an SIGABORT. I read up on some past issues and know that this a security mechanism of lighttpd.

Here is the proof of concept

GET /cgi/sendfile.cgi HTTP/1.1
X-Sendfile: .

This single dot triggers an SIGABORT on the application.
Here is the full backtrace.
stat_cache.c.1257: assertion failed: 0 != len

Thread 1 "lighttpd" received signal SIGABRT, Aborted.
__GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
50      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1  0x00007ffff7d05859 in __GI_abort () at abort.c:79
#2  0x0000000000266477 in log_failed_assert (filename=<optimized out>,
    line=<optimized out>, msg=<optimized out>) at buffer.c:1016
#3  0x000000000029e2cf in stat_cache_get_entry (name=<optimized out>)
    at stat_cache.c:1257
#4  0x000000000029f302 in stat_cache_get_entry_open (name=0x3f1d80, symlinks=1)
    at stat_cache.c:1386
#5  0x00000000002afd56 in http_response_send_file (r=0x3a08c0, path=0x3f1d80)
    at http-header-glue.c:566
#6  0x00000000002b7a01 in http_response_parse_headers (r=<optimized out>,
    opts=<optimized out>, b=<optimized out>) at http-header-glue.c:765
#7  0x00000000002b8b17 in http_response_read (r=0x3a08c0, opts=0x3ed608, b=0x3ed6c0,
    fdn=0x3f1ca0) at http-header-glue.c:1513
#8  0x00007ffff536b4b6 in cgi_recv_response (r=0x3a08c0, hctx=0x3ed5c0)
    at mod_cgi.c:433
#9  cgi_handle_fdevent (ctx=0x3ed5c0, revents=1) at mod_cgi.c:459
#10 0x00000000002aa694 in fdevent_linux_sysepoll_poll (ev=0x3964f0,
    timeout_ms=<optimized out>) at fdevent_linux_sysepoll.c:43
#11 0x0000000000285c91 in fdevent_poll (ev=0x3964f0, timeout_ms=-11120)
    at fdevent.c:436
#12 0x000000000022ad17 in server_main_loop (srv=0x3131b0) at server.c:1902
#13 0x0000000000227108 in main (argc=6, argv=<optimized out>) at server.c:2040

Actions #1

Updated by gstrauss over 3 years ago

  • Status changed from New to Patch Pending
  • Target version changed from 1.4.x to 1.4.59

Thanks for the report. Here's a patch.

--- a/src/http-header-glue.c
+++ b/src/http-header-glue.c
@@ -737,6 +737,10 @@ static void http_response_xsendfile (request_st * const r, buffer * const path,
        if (r->conf.force_lowercase_filenames) {
+       if (buffer_string_is_empty(path)) {
+               r->http_status = 502;
+               valid = 0;
+       }

        /* check that path is under xdocroot(s)
         * - xdocroot should have trailing slash appended at config time
@@ -815,6 +819,10 @@ static void http_response_xsendfile2(request_st * const r, const buffer * const
         if (r->conf.force_lowercase_filenames) {
+        if (buffer_string_is_empty(b)) {
+            r->http_status = 502;
+            break;
+        }
         if (xdocroot) {
             size_t i, xlen = buffer_string_length(b);
             for (i = 0; i < xdocroot->used; ++i) {

Please note that this would not have crashed if you had set "x-sendfile-docroot" to restrict the locations of files that can be sent via X-Sendfile.
If you do not trust your users, you should not enable "x-sendfile" without also configuring "x-sendfile-docroot"

Actions #2

Updated by stbuehler over 3 years ago

Also: if you don't trust your CGI scripts you shouldn't use CGI - instead you need to run them via a FastCGI wrapper (e.g. as a different user; a normal CGI script can simply kill -ABRT your webserver anyway.

Actions #3

Updated by gstrauss over 3 years ago

  • Status changed from Patch Pending to Fixed
Actions #4

Updated by axe34 over 3 years ago

May I file a cve for this bug, and moreover, how many versions does this affect?

Actions #5

Updated by gstrauss over 3 years ago

I do not think this is CVE-worthy, and if you choose to file one, I will dispute it.

  • First, "x-sendfile" it is not enabled by default.
  • Second, as stated above, "x-sendfile-docroot" should be configured whenever "x-sendfile" is configured, especially with untrusted users.
    "x-sendfile-docroot" has been available in lighttpd since lighttpd 1.4.40 (Jul 2016)
  • Third, since Jan 2010 (yes 2010) lighttpd explicitly documents that "x-sendfile" should not be enabled for untrusted users. (This alone disqualifies this bug as a CVE)
  • Fourth, as you already acknowledged, the behavior is intentional. An assertion failure is explicitly triggered by lighttpd. This is not "exploitable" beyond triggering the assertion.

Not every bug is CVE-worthy. Thank you for reporting a minor bug. A brief thanks is included in the commit message for commit 649829f9 if you would like to reference that and this issue (#3062) to record your efforts.

how many versions does this affect?

The call to stat_cache_get_entry_open() was introduced in lighttpd 1.4.56 (Nov 2020)

However, it bears repeating that any admin who enabled "x-sendfile" for untrusted users has exposed their system to dangers far worse than triggering an assertion failure. Again, please read the documentation for X-Sendfile (which is referenced from the mod_cgi documentation.


Also available in: Atom