Vulnerability: An issue was discovered in mod_alias_physical_handler in mod_alias.c in lighttpd before 1.4.50. There is potential ../ path traversal of a single directory above an alias target, with a specific mod_alias configuration where the matched alias lacks a trailing '/' character, but the alias target filesystem path does have a trailing '/' character.
Vulnerability ID: CVE-2018-19052
CWE: CWE-22
Severity score: 2.9
Project: https://github.com/lighttpd/lighttpd1.4
Published: 2018-11-07
File: src/mod_alias.c
Commit message: [mod_alias] security: potential path traversal with specific configs

Security: potential path traversal of a single directory above the alias
target with a specific mod_alias config where the alias which is matched
does not end in '/', but alias target filesystem path does end in '/'.

e.g. server.docroot = "/srv/www/host/HOSTNAME/docroot"
     alias.url = ( "/img" => "/srv/www/hosts/HOSTNAME/images/" )

If a malicious URL "/img../" were passed, the request would be
for directory "/srv/www/hosts/HOSTNAME/images/../" which would resolve
to "/srv/www/hosts/HOSTNAME/".  If mod_dirlisting were enabled, which
is not the default, this would result in listing the contents of the
directory above the alias.  An attacker might also try to directly
access files anywhere under that path, which is one level above the
intended aliased path.

credit: Orange Tsai(@orange_8361) from DEVCORE
Commit author: Glenn Strauss

Vulnerable version (before fix):
 PHYSICALPATH_FUNC(mod_alias_physical_handler) { 					strncmp(uri_ptr, ds->key->ptr, alias_len))) { 			/* matched */  			buffer_copy_buffer(con->physical.basedir, ds->value); 			buffer_copy_buffer(srv->tmp_buf, ds->value); 			buffer_append_string(srv->tmp_buf, uri_ptr + alias_len);

Fixed version (after fix):
 PHYSICALPATH_FUNC(mod_alias_physical_handler) { 					strncmp(uri_ptr, ds->key->ptr, alias_len))) { 			/* matched */  			/* check for path traversal in url-path following alias if key 			 * does not end in slash, but replacement value ends in slash */ 			if (uri_ptr[alias_len] == '.') { 				char *s = uri_ptr + alias_len + 1; 				if (*s == '.') ++s; 				if (*s == '/' || *s == '\0') { 					size_t vlen = buffer_string_length(ds->value); 					if (0 != alias_len && ds->key->ptr[alias_len-1] != '/' 					    && 0 != vlen && ds->value->ptr[vlen-1] == '/') { 						con->http_status = 403; 						return HANDLER_FINISHED; 					} 				} 			}  			buffer_copy_buffer(con->physical.basedir, ds->value); 			buffer_copy_buffer(srv->tmp_buf, ds->value); 			buffer_append_string(srv->tmp_buf, uri_ptr + alias_len);

Patch:
patch: @@ -181,6 +181,21 @@ PHYSICALPATH_FUNC(mod_alias_physical_handler) {
 					strncmp(uri_ptr, ds->key->ptr, alias_len))) {
 			/* matched */
 
+			/* check for path traversal in url-path following alias if key
+			 * does not end in slash, but replacement value ends in slash */
+			if (uri_ptr[alias_len] == '.') {
+				char *s = uri_ptr + alias_len + 1;
+				if (*s == '.') ++s;
+				if (*s == '/' || *s == '\0') {
+					size_t vlen = buffer_string_length(ds->value);
+					if (0 != alias_len && ds->key->ptr[alias_len-1] != '/'
+					    && 0 != vlen && ds->value->ptr[vlen-1] == '/') {
+						con->http_status = 403;
+						return HANDLER_FINISHED;
+					}
+				}
+			}
+
 			buffer_copy_buffer(con->physical.basedir, ds->value);
 			buffer_copy_buffer(srv->tmp_buf, ds->value);
 			buffer_append_string(srv->tmp_buf, uri_ptr + alias_len);
path: src/mod_alias.c