CVE-2018-25103 (C, published 2024-06-17T18:15Z)
Description: value: There exists use-after-free vulnerabilities in lighttpd <= 1.4.50 request parsing which might read from invalid pointers to memory used in the same request, not from other requests.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/lighttpd/lighttpd1.4
Files changed:
src/base.h
src/connections.c
src/http-header-glue.c
src/request.c
src/t/test_request.c
tests/request.t
Commit message: [core] security: use-after-free invalid Range req

(thx Marcus Wengelin)


Vulnerable code:
	const char   *http_range;
	/* internal representation */
	int     accept_encoding;

	CLEAN(http_range);
static int http_response_parse_range(server *srv, connection *con, buffer *path, stat_cache_entry *sce) {
	for (s = con->request.http_range, error = 0;
			la = strtoll(s, &err, 10);

			if (err == minus) {
			} else {
				/* error */
				error = 1;
			}
			la = strtoll(s, &err, 10);

			if (err == minus) {
			} else {
				/* error */

				error = 1;
			}
	if (con->request.http_range && con->conf.range_requests
		if (do_range_request) {
			if (0 == http_response_parse_range(srv, con, path, sce)) {
							} else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) {
								if (!con->request.http_range) {
									/* bytes=.*-.* */

									if (0 == strncasecmp(ds->value->ptr, "bytes=", 6) &&
									    NULL != strchr(ds->value->ptr+6, '-')) {

										/* if dup, only the first one will survive */
										con->request.http_range = ds->value->ptr + 6;
									}
								} else {
									con->http_status = 400;
									con->keep_alive = 0;

									if (srv->srvconf.log_request_header_on_error) {
										log_error_write(srv, __FILE__, __LINE__, "s",
												"duplicate Range-header -> 400");
										log_error_write(srv, __FILE__, __LINE__, "Sb",
												"request-header:\n",
												con->request.request);
									}
									array_insert_unique(con->request.headers, (data_unset *)ds);
									return 0;
								}
    con->request.http_range = NULL;
    run_http_request_parse(srv, con, __LINE__, 400,
      "Duplicate Range headers",
use Test::More tests => 50;

Fixed code:
static int http_response_parse_range(server *srv, connection *con, buffer *path, stat_cache_entry *sce, const char *range) {
	for (s = range, error = 0;
		if (s != minus) {
			la = strtoll(s, &err, 10);
			if (err != minus) {
				/* should not have multiple range-unit in Range, but
				 * handle just in case multiple Range headers merged */
				while (*s == ' ' || *s == '\t') ++s;
				if (0 != strncmp(s, "bytes=", 6)) return -1;
				s += 6;
				if (s != minus) {
					la = strtoll(s, &err, 10);
					if (err != minus) return -1;
				}
			}
		}

	if (con->conf.range_requests
	    && NULL != (ds = (data_string *)array_get_element(con->request.headers, "Range"))
		buffer *range = ds->value;
		if (do_range_request
		    && !buffer_string_is_empty(range)
		    && 0 == strncmp(range->ptr, "bytes=", 6)) {
			/* support only "bytes" byte-unit */
			if (0 == http_response_parse_range(srv, con, path, sce, range->ptr+6)) {
    /* (not actually testing Range here anymore; parsing deferred until use) */

    run_http_request_parse(srv, con, __LINE__, 0,
      "Duplicate Range headers (get appended)",
    run_http_request_parse(srv, con, __LINE__, 0,
      "Duplicate Range headers with invalid range (a)",
      CONST_STR_LEN("GET / HTTP/1.0\r\n"
                    "Range: bytes=0\r\n"
                    "Range: bytes=5-9\r\n"
                    "\r\n"));
    run_http_request_parse(srv, con, __LINE__, 0,
      "Duplicate Range headers with invalid range (b)",
      CONST_STR_LEN("GET / HTTP/1.0\r\n"
                    "Range: bytes=5-9\r\n"
                    "Range: bytes=0\r\n"
                    "\r\n"));
    run_http_request_parse(srv, con, __LINE__, 0,
      "Duplicate Range headers with invalid range (c)",
      CONST_STR_LEN("GET / HTTP/1.0\r\n"
                    "Range: 0\r\n"
                    "Range: bytes=5-9\r\n"
                    "\r\n"));
    run_http_request_parse(srv, con, __LINE__, 0,
      "Duplicate Range headers with invalid range (d)",
      CONST_STR_LEN("GET / HTTP/1.0\r\n"
                    "Range: bytes=5-9\r\n"
                    "Range: 0\r\n"
                    "\r\n"));

use Test::More tests => 52;
$t->{REQUEST}  = ( <<EOF
GET /12345.txt HTTP/1.0
Host: 123.example.org
Range: 0
Range: bytes=0-3
EOF
 );
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => "12345\n" } ];
ok($tf->handle_http($t) == 0, 'GET, Range invalid range-unit (first)');

$t->{REQUEST}  = ( <<EOF
GET /12345.txt HTTP/1.0
Host: 123.example.org
Range: bytes=0-3
Range: 0
EOF
 );
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 206 } ];
ok($tf->handle_http($t) == 0, 'GET, Range ignore invalid range (second)');